diff options
Diffstat (limited to 'drivers/tty')
166 files changed, 13189 insertions, 5147 deletions
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index a45d423ad10f..149f3d53b760 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -210,7 +210,7 @@ config SERIAL_NONSTANDARD config MOXA_INTELLIO tristate "Moxa Intellio support" - depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI) + depends on SERIAL_NONSTANDARD && PCI select FW_LOADER help Say Y here if you have a Moxa Intellio multiport serial card. @@ -220,7 +220,7 @@ config MOXA_INTELLIO config MOXA_SMARTIO tristate "Moxa SmartIO support v. 2.0" - depends on SERIAL_NONSTANDARD && PCI + depends on SERIAL_NONSTANDARD && PCI && HAS_IOPORT help Say Y here if you have a Moxa SmartIO multiport serial card and/or want to help develop a new version of this driver. @@ -302,7 +302,7 @@ config GOLDFISH_TTY_EARLY_CONSOLE config IPWIRELESS tristate "IPWireless 3G UMTS PCMCIA card support" - depends on PCMCIA && NETDEVICES + depends on PCMCIA && NETDEVICES && HAS_IOPORT select PPP help This is a driver for 3G UMTS PCMCIA card from IPWireless company. In @@ -383,7 +383,24 @@ config NULL_TTY available or desired. In order to use this driver, you should redirect the console to this - TTY, or boot the kernel with console=ttynull. + TTY, boot the kernel with console=ttynull, or enable + NULL_TTY_DEFAULT_CONSOLE. + + If unsure, say N. + +config NULL_TTY_DEFAULT_CONSOLE + bool "Support for console on ttynull" + depends on NULL_TTY=y && !VT_CONSOLE + help + Say Y here if you want the NULL TTY to be used as a /dev/console + device by default. + + For example, it might be useful to prevent a VT-less kernel from + writing the system log to a random device connected to the serial + port. + + Another console driver still might get preferred via the command + line, SPCR, or the device tree. If unsure, say N. diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index e27360652d9b..5af46442a792 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1578,8 +1578,14 @@ static void __exit amiga_serial_remove(struct platform_device *pdev) free_irq(IRQ_AMIGA_RBF, state); } -static struct platform_driver amiga_serial_driver = { - .remove_new = __exit_p(amiga_serial_remove), +/* + * amiga_serial_remove() lives in .exit.text. For drivers registered via + * module_platform_driver_probe() this is ok because they cannot get unbound at + * runtime. So mark the driver struct with __refdata to prevent modpost + * triggering a section mismatch warning. + */ +static struct platform_driver amiga_serial_driver __refdata = { + .remove = __exit_p(amiga_serial_remove), .driver = { .name = "amiga-serial", }, @@ -1654,5 +1660,6 @@ console_initcall(amiserial_console_init); #endif /* CONFIG_SERIAL_CONSOLE && !MODULE */ +MODULE_DESCRIPTION("Serial driver for the amiga builtin port"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:amiga-serial"); diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index 34421ec06a69..3a9582029005 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -461,7 +461,7 @@ MODULE_DEVICE_TABLE(of, goldfish_tty_of_match); static struct platform_driver goldfish_tty_platform_driver = { .probe = goldfish_tty_probe, - .remove_new = goldfish_tty_remove, + .remove = goldfish_tty_remove, .driver = { .name = "goldfish_tty", .of_match_table = goldfish_tty_of_match, @@ -470,4 +470,5 @@ static struct platform_driver goldfish_tty_platform_driver = { module_platform_driver(goldfish_tty_platform_driver); +MODULE_DESCRIPTION("Goldfish TTY Driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index b1149bc62ca1..4ca7472c38e0 100644 --- a/drivers/tty/hvc/hvc_iucv.c +++ b/drivers/tty/hvc/hvc_iucv.c @@ -24,6 +24,7 @@ #include <linux/tty.h> #include <linux/wait.h> #include <net/iucv/iucv.h> +#include <asm/machine.h> #include "hvc_console.h" @@ -1035,11 +1036,6 @@ static const struct attribute_group *hvc_iucv_dev_attr_groups[] = { NULL, }; -static void hvc_iucv_free(struct device *data) -{ - kfree(data); -} - /** * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance * @id: hvc_iucv_table index @@ -1090,18 +1086,12 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console) memcpy(priv->srv_name, name, 8); ASCEBC(priv->srv_name, 8); - /* create and setup device */ - priv->dev = kzalloc(sizeof(*priv->dev), GFP_KERNEL); + priv->dev = iucv_alloc_device(hvc_iucv_dev_attr_groups, NULL, + priv, "hvc_iucv%d", id); if (!priv->dev) { rc = -ENOMEM; goto out_error_dev; } - dev_set_name(priv->dev, "hvc_iucv%d", id); - dev_set_drvdata(priv->dev, priv); - priv->dev->bus = &iucv_bus; - priv->dev->parent = iucv_root; - priv->dev->groups = hvc_iucv_dev_attr_groups; - priv->dev->release = hvc_iucv_free; rc = device_register(priv->dev); if (rc) { put_device(priv->dev); @@ -1251,7 +1241,7 @@ static int param_set_vmidfilter(const char *val, const struct kernel_param *kp) { int rc; - if (!MACHINE_IS_VM || !hvc_iucv_devices) + if (!machine_is_vm() || !hvc_iucv_devices) return -ENODEV; if (!val) @@ -1280,7 +1270,7 @@ static int param_get_vmidfilter(char *buffer, const struct kernel_param *kp) size_t index, len; void *start, *end; - if (!MACHINE_IS_VM || !hvc_iucv_devices) + if (!machine_is_vm() || !hvc_iucv_devices) return -ENODEV; rc = 0; @@ -1317,7 +1307,7 @@ static int __init hvc_iucv_init(void) if (!hvc_iucv_devices) return -ENODEV; - if (!MACHINE_IS_VM) { + if (!machine_is_vm()) { pr_notice("The z/VM IUCV HVC device driver cannot " "be used without z/VM\n"); rc = -ENODEV; diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c index 095c33ad10f8..b2ec1f6efa0a 100644 --- a/drivers/tty/hvc/hvc_opal.c +++ b/drivers/tty/hvc/hvc_opal.c @@ -247,7 +247,7 @@ static void hvc_opal_remove(struct platform_device *dev) static struct platform_driver hvc_opal_driver = { .probe = hvc_opal_probe, - .remove_new = hvc_opal_remove, + .remove = hvc_opal_remove, .driver = { .name = hvc_opal_name, .of_match_table = hvc_opal_match, diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c index 0e497501f8e3..388a71afd6ef 100644 --- a/drivers/tty/hvc/hvc_xen.c +++ b/drivers/tty/hvc/hvc_xen.c @@ -558,7 +558,7 @@ static void xencons_backend_changed(struct xenbus_device *dev, break; fallthrough; /* Missed the backend's CLOSING state */ case XenbusStateClosing: { - struct xencons_info *info = dev_get_drvdata(&dev->dev);; + struct xencons_info *info = dev_get_drvdata(&dev->dev); /* * Don't tear down the evtchn and grant ref before the other diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c index 22e1bc4d8a66..b35c44caf3d7 100644 --- a/drivers/tty/hvc/hvsi_lib.c +++ b/drivers/tty/hvc/hvsi_lib.c @@ -303,7 +303,7 @@ int hvsilib_write_mctrl(struct hvsi_priv *pv, int dtr) pr_devel("HVSI@%x: %s DTR...\n", pv->termno, dtr ? "Setting" : "Clearing"); - ctrl.hdr.type = VS_CONTROL_PACKET_HEADER, + ctrl.hdr.type = VS_CONTROL_PACKET_HEADER; ctrl.hdr.len = sizeof(struct hvsi_control); ctrl.verb = cpu_to_be16(VSV_SET_MODEM_CTL); ctrl.mask = cpu_to_be32(HVSI_TSDTR); diff --git a/drivers/tty/ipwireless/hardware.c b/drivers/tty/ipwireless/hardware.c index 001ec318a918..e18848267be4 100644 --- a/drivers/tty/ipwireless/hardware.c +++ b/drivers/tty/ipwireless/hardware.c @@ -1496,7 +1496,7 @@ exit_nomem: static void handle_setup_get_version_rsp(struct ipw_hardware *hw, unsigned char vers_no) { - del_timer(&hw->setup_timer); + timer_delete(&hw->setup_timer); hw->initializing = 0; printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": card is ready.\n"); @@ -1676,7 +1676,7 @@ void ipwireless_init_hardware_v2_v3(struct ipw_hardware *hw) static void ipwireless_setup_timer(struct timer_list *t) { - struct ipw_hardware *hw = from_timer(hw, t, setup_timer); + struct ipw_hardware *hw = timer_container_of(hw, t, setup_timer); hw->init_loops++; @@ -1721,7 +1721,7 @@ void ipwireless_stop_interrupts(struct ipw_hardware *hw) if (!hw->shutting_down) { /* Tell everyone we are going down. */ hw->shutting_down = 1; - del_timer(&hw->setup_timer); + timer_delete(&hw->setup_timer); /* Prevent the hardware from sending any more interrupts */ do_close_hardware(hw); diff --git a/drivers/tty/mips_ejtag_fdc.c b/drivers/tty/mips_ejtag_fdc.c index afbf7738c7c4..ba7690c3e15e 100644 --- a/drivers/tty/mips_ejtag_fdc.c +++ b/drivers/tty/mips_ejtag_fdc.c @@ -683,7 +683,8 @@ static irqreturn_t mips_ejtag_fdc_isr(int irq, void *dev_id) */ static void mips_ejtag_fdc_tty_timer(struct timer_list *t) { - struct mips_ejtag_fdc_tty *priv = from_timer(priv, t, poll_timer); + struct mips_ejtag_fdc_tty *priv = timer_container_of(priv, t, + poll_timer); mips_ejtag_fdc_handle(priv); if (!priv->removing) @@ -1031,7 +1032,7 @@ err_stop_irq: raw_spin_unlock_irq(&priv->lock); } else { priv->removing = true; - del_timer_sync(&priv->poll_timer); + timer_delete_sync(&priv->poll_timer); } kthread_stop(priv->thread); err_destroy_ports: @@ -1061,7 +1062,7 @@ static int mips_ejtag_fdc_tty_cpu_down(struct mips_cdmm_device *dev) raw_spin_unlock_irq(&priv->lock); } else { priv->removing = true; - del_timer_sync(&priv->poll_timer); + timer_delete_sync(&priv->poll_timer); } kthread_stop(priv->thread); @@ -1154,7 +1155,7 @@ static char kgdbfdc_rbuf[4]; /* write buffer to allow compaction */ static unsigned int kgdbfdc_wbuflen; -static char kgdbfdc_wbuf[4]; +static u8 kgdbfdc_wbuf[4]; static void __iomem *kgdbfdc_setup(void) { @@ -1215,7 +1216,7 @@ static int kgdbfdc_read_char(void) /* push an FDC word from write buffer to TX FIFO */ static void kgdbfdc_push_one(void) { - const char *bufs[1] = { kgdbfdc_wbuf }; + const u8 *bufs[1] = { kgdbfdc_wbuf }; struct fdc_word word; void __iomem *regs; unsigned int i; diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index ebaada8db929..329b30fac8fc 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -43,15 +43,6 @@ #include <linux/ratelimit.h> #include <asm/io.h> -#include <linux/uaccess.h> - -#define MOXA 0x400 -#define MOXA_GET_IQUEUE (MOXA + 1) /* get input buffered count */ -#define MOXA_GET_OQUEUE (MOXA + 2) /* get output buffered count */ -#define MOXA_GETDATACOUNT (MOXA + 23) -#define MOXA_GET_IOQUEUE (MOXA + 27) -#define MOXA_FLUSH_QUEUE (MOXA + 28) -#define MOXA_GETMSTATUS (MOXA + 65) /* * System Configuration @@ -347,8 +338,6 @@ #define MX_PARMARK 0xA0 #define MX_PARSPACE 0x20 -#define MOXA_VERSION "6.0k" - #define MOXA_FW_HDRLEN 32 #define MOXAMAJOR 172 @@ -357,33 +346,21 @@ #define MAX_PORTS_PER_BOARD 32 /* Don't change this value */ #define MAX_PORTS (MAX_BOARDS * MAX_PORTS_PER_BOARD) -#define MOXA_IS_320(brd) ((brd)->boardType == MOXA_BOARD_C320_ISA || \ - (brd)->boardType == MOXA_BOARD_C320_PCI) - -/* - * Define the Moxa PCI vendor and device IDs. - */ -#define MOXA_BUS_TYPE_ISA 0 -#define MOXA_BUS_TYPE_PCI 1 +#define MOXA_IS_320(brd) ((brd)->boardType == MOXA_BOARD_C320_PCI) enum { MOXA_BOARD_C218_PCI = 1, - MOXA_BOARD_C218_ISA, MOXA_BOARD_C320_PCI, - MOXA_BOARD_C320_ISA, MOXA_BOARD_CP204J, }; static char *moxa_brdname[] = { "C218 Turbo PCI series", - "C218 Turbo ISA series", "C320 Turbo PCI series", - "C320 Turbo ISA series", "CP-204J series", }; -#ifdef CONFIG_PCI static const struct pci_device_id moxa_pcibrds[] = { { PCI_DEVICE(PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C218), .driver_data = MOXA_BOARD_C218_PCI }, @@ -394,14 +371,12 @@ static const struct pci_device_id moxa_pcibrds[] = { { 0 } }; MODULE_DEVICE_TABLE(pci, moxa_pcibrds); -#endif /* CONFIG_PCI */ struct moxa_port; static struct moxa_board_conf { int boardType; int numPorts; - int busType; unsigned int ready; @@ -413,19 +388,6 @@ static struct moxa_board_conf { void __iomem *intTable; } moxa_boards[MAX_BOARDS]; -struct mxser_mstatus { - tcflag_t cflag; - int cts; - int dsr; - int ri; - int dcd; -}; - -struct moxaq_str { - int inq; - int outq; -}; - struct moxa_port { struct tty_port port; struct moxa_board_conf *board; @@ -440,12 +402,6 @@ struct moxa_port { u8 lowChkFlag; }; -struct mon_str { - int tick; - int rxcnt[MAX_PORTS]; - int txcnt[MAX_PORTS]; -}; - /* statusflags */ #define TXSTOPPED 1 #define LOWWAIT 2 @@ -455,17 +411,11 @@ struct mon_str { #define WAKEUP_CHARS 256 static int ttymajor = MOXAMAJOR; -static struct mon_str moxaLog; static unsigned int moxaFuncTout = HZ / 2; static unsigned int moxaLowWaterChk; static DEFINE_MUTEX(moxa_openlock); static DEFINE_SPINLOCK(moxa_lock); -static unsigned long baseaddr[MAX_BOARDS]; -static unsigned int type[MAX_BOARDS]; -static unsigned int numports[MAX_BOARDS]; -static struct tty_port moxa_service_port; - MODULE_AUTHOR("William Chen"); MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver"); MODULE_LICENSE("GPL"); @@ -473,13 +423,6 @@ MODULE_FIRMWARE("c218tunx.cod"); MODULE_FIRMWARE("cp204unx.cod"); MODULE_FIRMWARE("c320tunx.cod"); -module_param_array(type, uint, NULL, 0); -MODULE_PARM_DESC(type, "card type: C218=2, C320=4"); -module_param_hw_array(baseaddr, ulong, ioport, NULL, 0); -MODULE_PARM_DESC(baseaddr, "base address"); -module_param_array(numports, uint, NULL, 0); -MODULE_PARM_DESC(numports, "numports (ignored for C218)"); - module_param(ttymajor, int, 0); /* @@ -583,104 +526,6 @@ static void moxa_low_water_check(void __iomem *ofsAddr) * TTY operations */ -static int moxa_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct moxa_port *ch = tty->driver_data; - void __user *argp = (void __user *)arg; - int status, ret = 0; - - if (tty->index == MAX_PORTS) { - if (cmd != MOXA_GETDATACOUNT && cmd != MOXA_GET_IOQUEUE && - cmd != MOXA_GETMSTATUS) - return -EINVAL; - } else if (!ch) - return -ENODEV; - - switch (cmd) { - case MOXA_GETDATACOUNT: - moxaLog.tick = jiffies; - if (copy_to_user(argp, &moxaLog, sizeof(moxaLog))) - ret = -EFAULT; - break; - case MOXA_FLUSH_QUEUE: - MoxaPortFlushData(ch, arg); - break; - case MOXA_GET_IOQUEUE: { - struct moxaq_str __user *argm = argp; - struct moxaq_str tmp; - struct moxa_port *p; - unsigned int i, j; - - for (i = 0; i < MAX_BOARDS; i++) { - p = moxa_boards[i].ports; - for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) { - memset(&tmp, 0, sizeof(tmp)); - spin_lock_bh(&moxa_lock); - if (moxa_boards[i].ready) { - tmp.inq = MoxaPortRxQueue(p); - tmp.outq = MoxaPortTxQueue(p); - } - spin_unlock_bh(&moxa_lock); - if (copy_to_user(argm, &tmp, sizeof(tmp))) - return -EFAULT; - } - } - break; - } case MOXA_GET_OQUEUE: - status = MoxaPortTxQueue(ch); - ret = put_user(status, (unsigned long __user *)argp); - break; - case MOXA_GET_IQUEUE: - status = MoxaPortRxQueue(ch); - ret = put_user(status, (unsigned long __user *)argp); - break; - case MOXA_GETMSTATUS: { - struct mxser_mstatus __user *argm = argp; - struct mxser_mstatus tmp; - struct moxa_port *p; - unsigned int i, j; - - for (i = 0; i < MAX_BOARDS; i++) { - p = moxa_boards[i].ports; - for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) { - struct tty_struct *ttyp; - memset(&tmp, 0, sizeof(tmp)); - spin_lock_bh(&moxa_lock); - if (!moxa_boards[i].ready) { - spin_unlock_bh(&moxa_lock); - goto copy; - } - - status = MoxaPortLineStatus(p); - spin_unlock_bh(&moxa_lock); - - if (status & 1) - tmp.cts = 1; - if (status & 2) - tmp.dsr = 1; - if (status & 4) - tmp.dcd = 1; - - ttyp = tty_port_tty_get(&p->port); - if (!ttyp) - tmp.cflag = p->cflag; - else - tmp.cflag = ttyp->termios.c_cflag; - tty_kref_put(ttyp); -copy: - if (copy_to_user(argm, &tmp, sizeof(tmp))) - return -EFAULT; - } - } - break; - } - default: - ret = -ENOIOCTLCMD; - } - return ret; -} - static int moxa_break_ctl(struct tty_struct *tty, int state) { struct moxa_port *port = tty->driver_data; @@ -697,7 +542,6 @@ static const struct tty_operations moxa_ops = { .write_room = moxa_write_room, .flush_buffer = moxa_flush_buffer, .chars_in_buffer = moxa_chars_in_buffer, - .ioctl = moxa_ioctl, .set_termios = moxa_set_termios, .stop = moxa_stop, .start = moxa_start, @@ -725,7 +569,6 @@ static DEFINE_TIMER(moxaTimer, moxa_poll); static int moxa_check_fw_model(struct moxa_board_conf *brd, u8 model) { switch (brd->boardType) { - case MOXA_BOARD_C218_ISA: case MOXA_BOARD_C218_PCI: if (model != 1) goto err; @@ -769,7 +612,6 @@ static int moxa_load_bios(struct moxa_board_conf *brd, const u8 *buf, msleep(2000); switch (brd->boardType) { - case MOXA_BOARD_C218_ISA: case MOXA_BOARD_C218_PCI: tmp = readw(baseAddr + C218_key); if (tmp != C218_KeyCode) @@ -833,7 +675,6 @@ static int moxa_real_load_code(struct moxa_board_conf *brd, const void *ptr, switch (brd->boardType) { case MOXA_BOARD_CP204J: - case MOXA_BOARD_C218_ISA: case MOXA_BOARD_C218_PCI: key = C218_key; loadbuf = C218_LoadBuf; @@ -898,15 +739,9 @@ static int moxa_real_load_code(struct moxa_board_conf *brd, const void *ptr, return -EIO; if (MOXA_IS_320(brd)) { - if (brd->busType == MOXA_BUS_TYPE_PCI) { /* ASIC board */ - writew(0x3800, baseAddr + TMS320_PORT1); - writew(0x3900, baseAddr + TMS320_PORT2); - writew(28499, baseAddr + TMS320_CLOCK); - } else { - writew(0x3200, baseAddr + TMS320_PORT1); - writew(0x3400, baseAddr + TMS320_PORT2); - writew(19999, baseAddr + TMS320_CLOCK); - } + writew(0x3800, baseAddr + TMS320_PORT1); + writew(0x3900, baseAddr + TMS320_PORT2); + writew(28499, baseAddr + TMS320_CLOCK); } writew(1, baseAddr + Disable_IRQ); writew(0, baseAddr + Magic_no); @@ -957,7 +792,6 @@ static int moxa_load_code(struct moxa_board_conf *brd, const void *ptr, return retval; switch (brd->boardType) { - case MOXA_BOARD_C218_ISA: case MOXA_BOARD_C218_PCI: case MOXA_BOARD_CP204J: port = brd->ports; @@ -1141,7 +975,6 @@ static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev) } switch (brd->boardType) { - case MOXA_BOARD_C218_ISA: case MOXA_BOARD_C218_PCI: file = "c218tunx.cod"; break; @@ -1227,7 +1060,6 @@ static void moxa_board_deinit(struct moxa_board_conf *brd) kfree(brd->ports); } -#ifdef CONFIG_PCI static int moxa_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -1270,7 +1102,6 @@ static int moxa_pci_probe(struct pci_dev *pdev, board->boardType = board_type; switch (board_type) { - case MOXA_BOARD_C218_ISA: case MOXA_BOARD_C218_PCI: board->numPorts = 8; break; @@ -1282,7 +1113,6 @@ static int moxa_pci_probe(struct pci_dev *pdev, board->numPorts = 0; break; } - board->busType = MOXA_BUS_TYPE_PCI; retval = moxa_init_board(board, &pdev->dev); if (retval) @@ -1318,21 +1148,12 @@ static struct pci_driver moxa_pci_driver = { .probe = moxa_pci_probe, .remove = moxa_pci_remove }; -#endif /* CONFIG_PCI */ static int __init moxa_init(void) { - unsigned int isabrds = 0; int retval = 0; - struct moxa_board_conf *brd = moxa_boards; - unsigned int i; - - printk(KERN_INFO "MOXA Intellio family driver version %s\n", - MOXA_VERSION); - tty_port_init(&moxa_service_port); - - moxaDriver = tty_alloc_driver(MAX_PORTS + 1, + moxaDriver = tty_alloc_driver(MAX_PORTS, TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV); if (IS_ERR(moxaDriver)) @@ -1348,8 +1169,6 @@ static int __init moxa_init(void) moxaDriver->init_termios.c_ispeed = 9600; moxaDriver->init_termios.c_ospeed = 9600; tty_set_operations(moxaDriver, &moxa_ops); - /* Having one more port only for ioctls is ugly */ - tty_port_link_device(&moxa_service_port, moxaDriver, MAX_PORTS); if (tty_register_driver(moxaDriver)) { printk(KERN_ERR "can't register MOXA Smartio tty driver!\n"); @@ -1357,66 +1176,18 @@ static int __init moxa_init(void) return -1; } - /* Find the boards defined from module args. */ - - for (i = 0; i < MAX_BOARDS; i++) { - if (!baseaddr[i]) - break; - if (type[i] == MOXA_BOARD_C218_ISA || - type[i] == MOXA_BOARD_C320_ISA) { - pr_debug("Moxa board %2d: %s board(baseAddr=%lx)\n", - isabrds + 1, moxa_brdname[type[i] - 1], - baseaddr[i]); - brd->boardType = type[i]; - brd->numPorts = type[i] == MOXA_BOARD_C218_ISA ? 8 : - numports[i]; - brd->busType = MOXA_BUS_TYPE_ISA; - brd->basemem = ioremap(baseaddr[i], 0x4000); - if (!brd->basemem) { - printk(KERN_ERR "MOXA: can't remap %lx\n", - baseaddr[i]); - continue; - } - if (moxa_init_board(brd, NULL)) { - iounmap(brd->basemem); - brd->basemem = NULL; - continue; - } - - printk(KERN_INFO "MOXA isa board found at 0x%.8lx and " - "ready (%u ports, firmware loaded)\n", - baseaddr[i], brd->numPorts); - - brd++; - isabrds++; - } - } - -#ifdef CONFIG_PCI retval = pci_register_driver(&moxa_pci_driver); - if (retval) { + if (retval) printk(KERN_ERR "Can't register MOXA pci driver!\n"); - if (isabrds) - retval = 0; - } -#endif return retval; } static void __exit moxa_exit(void) { - unsigned int i; - -#ifdef CONFIG_PCI pci_unregister_driver(&moxa_pci_driver); -#endif - - for (i = 0; i < MAX_BOARDS; i++) /* ISA boards */ - if (moxa_boards[i].ready) - moxa_board_deinit(&moxa_boards[i]); - del_timer_sync(&moxaTimer); + timer_delete_sync(&moxaTimer); tty_unregister_driver(moxaDriver); tty_driver_kref_put(moxaDriver); @@ -1457,9 +1228,6 @@ static int moxa_open(struct tty_struct *tty, struct file *filp) int port; port = tty->index; - if (port == MAX_PORTS) { - return capable(CAP_SYS_ADMIN) ? 0 : -EPERM; - } if (mutex_lock_interruptible(&moxa_openlock)) return -ERESTARTSYS; brd = &moxa_boards[port / MAX_PORTS_PER_BOARD]; @@ -2182,7 +1950,6 @@ static ssize_t MoxaPortWriteData(struct tty_struct *tty, const u8 *buffer, c = (head > tail) ? (head - tail - 1) : (head - tail + tx_mask); if (c > len) c = len; - moxaLog.txcnt[port->port.tty->index] += c; total = c; if (spage == epage) { bufhead = readw(ofsAddr + Ofs_txb); @@ -2224,7 +1991,6 @@ static ssize_t MoxaPortWriteData(struct tty_struct *tty, const u8 *buffer, static int MoxaPortReadData(struct moxa_port *port) { - struct tty_struct *tty = port->port.tty; void __iomem *baseAddr, *ofsAddr, *ofs; u8 *dst; unsigned int count, len, total; @@ -2243,7 +2009,6 @@ static int MoxaPortReadData(struct moxa_port *port) return 0; total = count; - moxaLog.rxcnt[tty->index] += total; if (spage == epage) { bufhead = readw(ofsAddr + Ofs_rxb); writew(spage, baseAddr + Control_reg); @@ -2331,8 +2096,6 @@ static int moxa_get_serial_info(struct tty_struct *tty, { struct moxa_port *info = tty->driver_data; - if (tty->index == MAX_PORTS) - return -EINVAL; if (!info) return -ENODEV; mutex_lock(&info->port.mutex); @@ -2352,8 +2115,6 @@ static int moxa_set_serial_info(struct tty_struct *tty, struct moxa_port *info = tty->driver_data; unsigned int close_delay; - if (tty->index == MAX_PORTS) - return -EINVAL; if (!info) return -ENODEV; diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 458bb1280ebf..2fc13cc02cc5 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -208,9 +208,6 @@ static const struct { }; #define UART_INFO_NUM ARRAY_SIZE(Gpci_uart_info) - -/* driver_data correspond to the lines in the structure above - see also ISA probe function before you change something */ static const struct pci_device_id mxser_pcibrds[] = { { PCI_DEVICE_DATA(MOXA, C168, 8) }, { PCI_DEVICE_DATA(MOXA, C104, 4) }, @@ -288,7 +285,7 @@ struct mxser_board { enum mxser_must_hwid must_hwid; speed_t max_baud; - struct mxser_port ports[] __counted_by(nports); + struct mxser_port ports[] /* __counted_by(nports) */; }; static DECLARE_BITMAP(mxser_boards, MXSER_BOARDS); @@ -986,7 +983,7 @@ static int mxser_get_serial_info(struct tty_struct *tty, ss->baud_base = MXSER_BAUD_BASE; ss->close_delay = close_delay; ss->closing_wait = closing_wait; - ss->custom_divisor = MXSER_CUSTOM_DIVISOR, + ss->custom_divisor = MXSER_CUSTOM_DIVISOR; mutex_unlock(&port->mutex); return 0; } @@ -1773,8 +1770,6 @@ static void mxser_initbrd(struct mxser_board *brd, bool high_baud) mxser_process_txrx_fifo(info); - info->port.close_delay = 5 * HZ / 10; - info->port.closing_wait = 30 * HZ; spin_lock_init(&info->slock); /* before set INT ISR, disable all int */ @@ -1817,7 +1812,7 @@ static int mxser_probe(struct pci_dev *pdev, /* io address */ ioaddress = pci_resource_start(pdev, 2); - retval = pci_request_region(pdev, 2, "mxser(IO)"); + retval = pcim_request_region(pdev, 2, "mxser(IO)"); if (retval) goto err_zero; @@ -1827,7 +1822,7 @@ static int mxser_probe(struct pci_dev *pdev, /* vector */ ioaddress = pci_resource_start(pdev, 3); - retval = pci_request_region(pdev, 3, "mxser(vector)"); + retval = pcim_request_region(pdev, 3, "mxser(vector)"); if (retval) goto err_zero; brd->vector = ioaddress; diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 4036566febcb..7fc535452c0b 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -245,16 +245,18 @@ enum gsm_encoding { enum gsm_mux_state { GSM_SEARCH, - GSM_START, - GSM_ADDRESS, - GSM_CONTROL, - GSM_LEN, - GSM_DATA, - GSM_FCS, - GSM_OVERRUN, - GSM_LEN0, - GSM_LEN1, - GSM_SSOF, + GSM0_ADDRESS, + GSM0_CONTROL, + GSM0_LEN0, + GSM0_LEN1, + GSM0_DATA, + GSM0_FCS, + GSM0_SSOF, + GSM1_START, + GSM1_ADDRESS, + GSM1_CONTROL, + GSM1_DATA, + GSM1_OVERRUN, }; /* @@ -1939,7 +1941,7 @@ static void gsm_control_response(struct gsm_mux *gsm, unsigned int command, /* Does the reply match our command */ if (ctrl != NULL && (command == ctrl->cmd || command == CMD_NSC)) { /* Our command was replied to, kill the retry timer */ - del_timer(&gsm->t2_timer); + timer_delete(&gsm->t2_timer); gsm->pending_cmd = NULL; /* Rejected by the other end */ if (command == CMD_NSC) @@ -1969,7 +1971,7 @@ static void gsm_control_response(struct gsm_mux *gsm, unsigned int command, static void gsm_control_keep_alive(struct timer_list *t) { - struct gsm_mux *gsm = from_timer(gsm, t, ka_timer); + struct gsm_mux *gsm = timer_container_of(gsm, t, ka_timer); unsigned long flags; spin_lock_irqsave(&gsm->control_lock, flags); @@ -2026,7 +2028,7 @@ static void gsm_control_transmit(struct gsm_mux *gsm, struct gsm_control *ctrl) static void gsm_control_retransmit(struct timer_list *t) { - struct gsm_mux *gsm = from_timer(gsm, t, t2_timer); + struct gsm_mux *gsm = timer_container_of(gsm, t, t2_timer); struct gsm_control *ctrl; unsigned long flags; spin_lock_irqsave(&gsm->control_lock, flags); @@ -2129,7 +2131,7 @@ static int gsm_control_wait(struct gsm_mux *gsm, struct gsm_control *control) static void gsm_dlci_close(struct gsm_dlci *dlci) { - del_timer(&dlci->t1); + timer_delete(&dlci->t1); if (debug & DBG_ERRORS) pr_debug("DLCI %d goes closed.\n", dlci->addr); dlci->state = DLCI_CLOSED; @@ -2142,7 +2144,7 @@ static void gsm_dlci_close(struct gsm_dlci *dlci) tty_port_set_initialized(&dlci->port, false); wake_up_interruptible(&dlci->port.open_wait); } else { - del_timer(&dlci->gsm->ka_timer); + timer_delete(&dlci->gsm->ka_timer); dlci->gsm->dead = true; } /* A DLCI 0 close is a MUX termination so we need to kick that @@ -2164,7 +2166,7 @@ static void gsm_dlci_open(struct gsm_dlci *dlci) /* Note that SABM UA .. SABM UA first UA lost can mean that we go open -> open */ - del_timer(&dlci->t1); + timer_delete(&dlci->t1); /* This will let a tty open continue */ dlci->state = DLCI_OPEN; dlci->constipated = false; @@ -2222,12 +2224,12 @@ static int gsm_dlci_negotiate(struct gsm_dlci *dlci) * * Some control dlci can stay in ADM mode with other dlci working just * fine. In that case we can just keep the control dlci open after the - * DLCI_OPENING retries time out. + * DLCI_OPENING receives DM. */ static void gsm_dlci_t1(struct timer_list *t) { - struct gsm_dlci *dlci = from_timer(dlci, t, t1); + struct gsm_dlci *dlci = timer_container_of(dlci, t, t1); struct gsm_mux *gsm = dlci->gsm; switch (dlci->state) { @@ -2241,16 +2243,19 @@ static void gsm_dlci_t1(struct timer_list *t) } break; case DLCI_OPENING: - if (dlci->retries) { - dlci->retries--; - gsm_command(dlci->gsm, dlci->addr, SABM|PF); - mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); - } else if (!dlci->addr && gsm->control == (DM | PF)) { + if (!dlci->addr && gsm->control == (DM | PF)) { if (debug & DBG_ERRORS) - pr_info("DLCI %d opening in ADM mode.\n", - dlci->addr); + pr_info("DLCI 0 opening in ADM mode.\n"); dlci->mode = DLCI_MODE_ADM; gsm_dlci_open(dlci); + } else if (dlci->retries) { + if (!dlci->addr || !gsm->dlci[0] || + gsm->dlci[0]->state != DLCI_OPENING) { + dlci->retries--; + gsm_command(dlci->gsm, dlci->addr, SABM|PF); + } + + mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); } else { gsm->open_error++; gsm_dlci_begin_close(dlci); /* prevent half open link */ @@ -2306,7 +2311,9 @@ static void gsm_dlci_begin_open(struct gsm_dlci *dlci) dlci->retries = gsm->n2; if (!need_pn) { dlci->state = DLCI_OPENING; - gsm_command(gsm, dlci->addr, SABM|PF); + if (!dlci->addr || !gsm->dlci[0] || + gsm->dlci[0]->state != DLCI_OPENING) + gsm_command(gsm, dlci->addr, SABM|PF); } else { /* Configure DLCI before setup */ dlci->state = DLCI_CONFIGURE; @@ -2482,7 +2489,7 @@ static void gsm_dlci_command(struct gsm_dlci *dlci, const u8 *data, int len) */ static void gsm_kick_timer(struct timer_list *t) { - struct gsm_mux *gsm = from_timer(gsm, t, kick_timer); + struct gsm_mux *gsm = timer_container_of(gsm, t, kick_timer); unsigned long flags; int sent = 0; @@ -2847,6 +2854,30 @@ invalid: return; } +/** + * gsm0_receive_state_check_and_fix - check and correct receive state + * @gsm: gsm data for this ldisc instance + * + * Ensures that the current receive state is valid for basic option mode. + */ + +static void gsm0_receive_state_check_and_fix(struct gsm_mux *gsm) +{ + switch (gsm->state) { + case GSM_SEARCH: + case GSM0_ADDRESS: + case GSM0_CONTROL: + case GSM0_LEN0: + case GSM0_LEN1: + case GSM0_DATA: + case GSM0_FCS: + case GSM0_SSOF: + break; + default: + gsm->state = GSM_SEARCH; + break; + } +} /** * gsm0_receive - perform processing for non-transparency @@ -2860,26 +2891,27 @@ static void gsm0_receive(struct gsm_mux *gsm, u8 c) { unsigned int len; + gsm0_receive_state_check_and_fix(gsm); switch (gsm->state) { case GSM_SEARCH: /* SOF marker */ if (c == GSM0_SOF) { - gsm->state = GSM_ADDRESS; + gsm->state = GSM0_ADDRESS; gsm->address = 0; gsm->len = 0; gsm->fcs = INIT_FCS; } break; - case GSM_ADDRESS: /* Address EA */ + case GSM0_ADDRESS: /* Address EA */ gsm->fcs = gsm_fcs_add(gsm->fcs, c); if (gsm_read_ea(&gsm->address, c)) - gsm->state = GSM_CONTROL; + gsm->state = GSM0_CONTROL; break; - case GSM_CONTROL: /* Control Byte */ + case GSM0_CONTROL: /* Control Byte */ gsm->fcs = gsm_fcs_add(gsm->fcs, c); gsm->control = c; - gsm->state = GSM_LEN0; + gsm->state = GSM0_LEN0; break; - case GSM_LEN0: /* Length EA */ + case GSM0_LEN0: /* Length EA */ gsm->fcs = gsm_fcs_add(gsm->fcs, c); if (gsm_read_ea(&gsm->len, c)) { if (gsm->len > gsm->mru) { @@ -2889,14 +2921,14 @@ static void gsm0_receive(struct gsm_mux *gsm, u8 c) } gsm->count = 0; if (!gsm->len) - gsm->state = GSM_FCS; + gsm->state = GSM0_FCS; else - gsm->state = GSM_DATA; + gsm->state = GSM0_DATA; break; } - gsm->state = GSM_LEN1; + gsm->state = GSM0_LEN1; break; - case GSM_LEN1: + case GSM0_LEN1: gsm->fcs = gsm_fcs_add(gsm->fcs, c); len = c; gsm->len |= len << 7; @@ -2907,26 +2939,29 @@ static void gsm0_receive(struct gsm_mux *gsm, u8 c) } gsm->count = 0; if (!gsm->len) - gsm->state = GSM_FCS; + gsm->state = GSM0_FCS; else - gsm->state = GSM_DATA; + gsm->state = GSM0_DATA; break; - case GSM_DATA: /* Data */ + case GSM0_DATA: /* Data */ gsm->buf[gsm->count++] = c; - if (gsm->count == gsm->len) { + if (gsm->count >= MAX_MRU) { + gsm->bad_size++; + gsm->state = GSM_SEARCH; + } else if (gsm->count >= gsm->len) { /* Calculate final FCS for UI frames over all data */ if ((gsm->control & ~PF) != UIH) { gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->count); } - gsm->state = GSM_FCS; + gsm->state = GSM0_FCS; } break; - case GSM_FCS: /* FCS follows the packet */ + case GSM0_FCS: /* FCS follows the packet */ gsm->fcs = gsm_fcs_add(gsm->fcs, c); - gsm->state = GSM_SSOF; + gsm->state = GSM0_SSOF; break; - case GSM_SSOF: + case GSM0_SSOF: gsm->state = GSM_SEARCH; if (c == GSM0_SOF) gsm_queue(gsm); @@ -2940,6 +2975,29 @@ static void gsm0_receive(struct gsm_mux *gsm, u8 c) } /** + * gsm1_receive_state_check_and_fix - check and correct receive state + * @gsm: gsm data for this ldisc instance + * + * Ensures that the current receive state is valid for advanced option mode. + */ + +static void gsm1_receive_state_check_and_fix(struct gsm_mux *gsm) +{ + switch (gsm->state) { + case GSM_SEARCH: + case GSM1_START: + case GSM1_ADDRESS: + case GSM1_CONTROL: + case GSM1_DATA: + case GSM1_OVERRUN: + break; + default: + gsm->state = GSM_SEARCH; + break; + } +} + +/** * gsm1_receive - perform processing for non-transparency * @gsm: gsm data for this ldisc instance * @c: character @@ -2949,6 +3007,7 @@ static void gsm0_receive(struct gsm_mux *gsm, u8 c) static void gsm1_receive(struct gsm_mux *gsm, u8 c) { + gsm1_receive_state_check_and_fix(gsm); /* handle XON/XOFF */ if ((c & ISO_IEC_646_MASK) == XON) { gsm->constipated = true; @@ -2961,11 +3020,11 @@ static void gsm1_receive(struct gsm_mux *gsm, u8 c) } if (c == GSM1_SOF) { /* EOF is only valid in frame if we have got to the data state */ - if (gsm->state == GSM_DATA) { + if (gsm->state == GSM1_DATA) { if (gsm->count < 1) { /* Missing FSC */ gsm->malformed++; - gsm->state = GSM_START; + gsm->state = GSM1_START; return; } /* Remove the FCS from data */ @@ -2981,14 +3040,14 @@ static void gsm1_receive(struct gsm_mux *gsm, u8 c) gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->buf[gsm->count]); gsm->len = gsm->count; gsm_queue(gsm); - gsm->state = GSM_START; + gsm->state = GSM1_START; return; } /* Any partial frame was a runt so go back to start */ - if (gsm->state != GSM_START) { + if (gsm->state != GSM1_START) { if (gsm->state != GSM_SEARCH) gsm->malformed++; - gsm->state = GSM_START; + gsm->state = GSM1_START; } /* A SOF in GSM_START means we are still reading idling or framing bytes */ @@ -3009,30 +3068,30 @@ static void gsm1_receive(struct gsm_mux *gsm, u8 c) gsm->escape = false; } switch (gsm->state) { - case GSM_START: /* First byte after SOF */ + case GSM1_START: /* First byte after SOF */ gsm->address = 0; - gsm->state = GSM_ADDRESS; + gsm->state = GSM1_ADDRESS; gsm->fcs = INIT_FCS; fallthrough; - case GSM_ADDRESS: /* Address continuation */ + case GSM1_ADDRESS: /* Address continuation */ gsm->fcs = gsm_fcs_add(gsm->fcs, c); if (gsm_read_ea(&gsm->address, c)) - gsm->state = GSM_CONTROL; + gsm->state = GSM1_CONTROL; break; - case GSM_CONTROL: /* Control Byte */ + case GSM1_CONTROL: /* Control Byte */ gsm->fcs = gsm_fcs_add(gsm->fcs, c); gsm->control = c; gsm->count = 0; - gsm->state = GSM_DATA; + gsm->state = GSM1_DATA; break; - case GSM_DATA: /* Data */ - if (gsm->count > gsm->mru) { /* Allow one for the FCS */ - gsm->state = GSM_OVERRUN; + case GSM1_DATA: /* Data */ + if (gsm->count > gsm->mru || gsm->count > MAX_MRU) { /* Allow one for the FCS */ + gsm->state = GSM1_OVERRUN; gsm->bad_size++; } else gsm->buf[gsm->count++] = c; break; - case GSM_OVERRUN: /* Over-long - eg a dropped SOF */ + case GSM1_OVERRUN: /* Over-long - eg a dropped SOF */ break; default: pr_debug("%s: unhandled state: %d\n", __func__, gsm->state); @@ -3085,9 +3144,9 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc) } /* Finish outstanding timers, making sure they are done */ - del_timer_sync(&gsm->kick_timer); - del_timer_sync(&gsm->t2_timer); - del_timer_sync(&gsm->ka_timer); + timer_delete_sync(&gsm->kick_timer); + timer_delete_sync(&gsm->t2_timer); + timer_delete_sync(&gsm->ka_timer); /* Finish writing to ldisc */ flush_work(&gsm->tx_work); @@ -3103,6 +3162,8 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc) mutex_unlock(&gsm->mutex); /* Now wipe the queues */ tty_ldisc_flush(gsm->tty); + + guard(spinlock_irqsave)(&gsm->tx_lock); list_for_each_entry_safe(txq, ntxq, &gsm->tx_ctrl_list, list) kfree(txq); INIT_LIST_HEAD(&gsm->tx_ctrl_list); @@ -4010,7 +4071,7 @@ static int gsm_create_network(struct gsm_dlci *dlci, struct gsm_netconfig *nc) mux_net = netdev_priv(net); mux_net->dlci = dlci; kref_init(&mux_net->ref); - strncpy(nc->if_name, net->name, IFNAMSIZ); /* return net name */ + strscpy(nc->if_name, net->name); /* return net name */ /* reconfigure dlci for network */ dlci->prev_adaption = dlci->adaption; @@ -4195,7 +4256,7 @@ static const struct tty_port_operations gsm_port_ops = { static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty) { struct gsm_mux *gsm; - struct gsm_dlci *dlci; + struct gsm_dlci *dlci, *dlci0; unsigned int line = tty->index; unsigned int mux = mux_line_to_num(line); bool alloc = false; @@ -4218,10 +4279,20 @@ static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty) perspective as we don't have to worry about this if DLCI0 is lost */ mutex_lock(&gsm->mutex); - if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) { + + dlci0 = gsm->dlci[0]; + if (dlci0 && dlci0->state != DLCI_OPEN) { mutex_unlock(&gsm->mutex); - return -EL2NSYNC; + + if (dlci0->state == DLCI_OPENING) + wait_event(gsm->event, dlci0->state != DLCI_OPENING); + + if (dlci0->state != DLCI_OPEN) + return -EL2NSYNC; + + mutex_lock(&gsm->mutex); } + dlci = gsm->dlci[line]; if (dlci == NULL) { alloc = true; @@ -4580,5 +4651,6 @@ module_init(gsm_init); module_exit(gsm_exit); +MODULE_DESCRIPTION("GSM 0710 tty multiplexor"); MODULE_LICENSE("GPL"); MODULE_ALIAS_LDISC(N_GSM0710); diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index 1615f074ab86..4a4dc58b866a 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -822,6 +822,7 @@ static void __exit n_hdlc_exit(void) module_init(n_hdlc_init); module_exit(n_hdlc_exit); +MODULE_DESCRIPTION("HDLC line discipline support"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Paul Fulghum paulkf@microgate.com"); module_param(maxframe, int, 0); diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index f252d0b5a434..6af3f3a0b531 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -56,6 +56,8 @@ */ #define WAKEUP_CHARS 256 +#define N_TTY_BUF_SIZE 4096 + /* * This defines the low- and high-watermarks for throttling and * unthrottling the TTY driver. These watermarks are used for @@ -79,14 +81,6 @@ #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...) no_printk(f, ##args) -#endif - struct n_tty_data { /* producer-published */ size_t read_head; @@ -486,18 +480,13 @@ static int do_output_char(u8 c, struct tty_struct *tty, int space) static int process_output(u8 c, struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; - int space, retval; - mutex_lock(&ldata->output_lock); - - space = tty_write_room(tty); - retval = do_output_char(c, tty, space); + guard(mutex)(&ldata->output_lock); - mutex_unlock(&ldata->output_lock); - if (retval < 0) + if (do_output_char(c, tty, tty_write_room(tty)) < 0) return -1; - else - return 0; + + return 0; } /** @@ -522,17 +511,15 @@ static ssize_t process_output_block(struct tty_struct *tty, const u8 *buf, unsigned int nr) { struct n_tty_data *ldata = tty->disc_data; - int space; - int i; + unsigned int space, i; const u8 *cp; - mutex_lock(&ldata->output_lock); + guard(mutex)(&ldata->output_lock); space = tty_write_room(tty); - if (space <= 0) { - mutex_unlock(&ldata->output_lock); - return space; - } + if (space == 0) + return 0; + if (nr > space) nr = space; @@ -544,18 +531,18 @@ static ssize_t process_output_block(struct tty_struct *tty, if (O_ONLRET(tty)) ldata->column = 0; if (O_ONLCR(tty)) - goto break_out; + goto do_write; ldata->canon_column = ldata->column; break; case '\r': if (O_ONOCR(tty) && ldata->column == 0) - goto break_out; + goto do_write; if (O_OCRNL(tty)) - goto break_out; + goto do_write; ldata->canon_column = ldata->column = 0; break; case '\t': - goto break_out; + goto do_write; case '\b': if (ldata->column > 0) ldata->column--; @@ -563,18 +550,15 @@ static ssize_t process_output_block(struct tty_struct *tty, default: if (!iscntrl(c)) { if (O_OLCUC(tty)) - goto break_out; + goto do_write; if (!is_continuation(c, tty)) ldata->column++; } break; } } -break_out: - i = tty->ops->write(tty, buf, i); - - mutex_unlock(&ldata->output_lock); - return i; +do_write: + return tty->ops->write(tty, buf, i); } static int n_tty_process_echo_ops(struct tty_struct *tty, size_t *tail, @@ -696,7 +680,7 @@ static int n_tty_process_echo_ops(struct tty_struct *tty, size_t *tail, static size_t __process_echoes(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; - int space, old_space; + unsigned int space, old_space; size_t tail; u8 c; @@ -1619,15 +1603,25 @@ static void __receive_buf(struct tty_struct *tty, const u8 *cp, const u8 *fp, else if (ldata->raw || (L_EXTPROC(tty) && !preops)) n_tty_receive_buf_raw(tty, cp, fp, count); else if (tty->closing && !L_EXTPROC(tty)) { - if (la_count > 0) + if (la_count > 0) { n_tty_receive_buf_closing(tty, cp, fp, la_count, true); - if (count > la_count) - n_tty_receive_buf_closing(tty, cp, fp, count - la_count, false); + cp += la_count; + if (fp) + fp += la_count; + count -= la_count; + } + if (count > 0) + n_tty_receive_buf_closing(tty, cp, fp, count, false); } else { - if (la_count > 0) + if (la_count > 0) { n_tty_receive_buf_standard(tty, cp, fp, la_count, true); - if (count > la_count) - n_tty_receive_buf_standard(tty, cp, fp, count - la_count, false); + cp += la_count; + if (fp) + fp += la_count; + count -= la_count; + } + if (count > 0) + n_tty_receive_buf_standard(tty, cp, fp, count, false); flush_echoes(tty); if (tty->ops->flush_chars) @@ -2024,9 +2018,6 @@ static bool canon_copy_from_read_buf(const struct tty_struct *tty, u8 **kbp, tail = MASK(ldata->read_tail); 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) { @@ -2044,9 +2035,6 @@ static bool canon_copy_from_read_buf(const struct tty_struct *tty, u8 **kbp, if (!found || read_buf(ldata, eol) != __DISABLED_CHAR) n = c; - n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu tail:%zu more:%zu\n", - __func__, eol, found, n, c, tail, more); - tty_copy(tty, *kbp, tail, n); *kbp += n; *nr -= n; @@ -2123,6 +2111,66 @@ static int job_control(struct tty_struct *tty, struct file *file) return __tty_check_change(tty, SIGTTIN); } +/* + * We still hold the atomic_read_lock and the termios_rwsem, and can just + * continue to copy data. + */ +static ssize_t n_tty_continue_cookie(struct tty_struct *tty, u8 *kbuf, + size_t nr, void **cookie) +{ + struct n_tty_data *ldata = tty->disc_data; + u8 *kb = kbuf; + + if (ldata->icanon && !L_EXTPROC(tty)) { + /* + * If we have filled the user buffer, see if we should skip an + * EOF character before releasing the lock and returning done. + */ + if (!nr) + canon_skip_eof(ldata); + else if (canon_copy_from_read_buf(tty, &kb, &nr)) + return kb - kbuf; + } else { + if (copy_from_read_buf(tty, &kb, &nr)) + return kb - kbuf; + } + + /* No more data - release locks and stop retries */ + n_tty_kick_worker(tty); + n_tty_check_unthrottle(tty); + up_read(&tty->termios_rwsem); + mutex_unlock(&ldata->atomic_read_lock); + *cookie = NULL; + + return kb - kbuf; +} + +static int n_tty_wait_for_input(struct tty_struct *tty, struct file *file, + struct wait_queue_entry *wait, long *timeout) +{ + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) + return -EIO; + if (tty_hung_up_p(file)) + return 0; + /* + * Abort readers for ttys which never actually get hung up. + * See __tty_hangup(). + */ + if (test_bit(TTY_HUPPING, &tty->flags)) + return 0; + if (!*timeout) + return 0; + if (tty_io_nonblock(tty, file)) + return -EAGAIN; + if (signal_pending(current)) + return -ERESTARTSYS; + + up_read(&tty->termios_rwsem); + *timeout = wait_woken(wait, TASK_INTERRUPTIBLE, *timeout); + down_read(&tty->termios_rwsem); + + return 1; +} /** * n_tty_read - read function for tty @@ -2156,36 +2204,9 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, u8 *kbuf, bool packet; size_t old_tail; - /* - * Is this a continuation of a read started earler? - * - * If so, we still hold the atomic_read_lock and the - * termios_rwsem, and can just continue to copy data. - */ - if (*cookie) { - if (ldata->icanon && !L_EXTPROC(tty)) { - /* - * If we have filled the user buffer, see - * if we should skip an EOF character before - * releasing the lock and returning done. - */ - if (!nr) - canon_skip_eof(ldata); - else if (canon_copy_from_read_buf(tty, &kb, &nr)) - return kb - kbuf; - } else { - if (copy_from_read_buf(tty, &kb, &nr)) - return kb - kbuf; - } - - /* No more data - release locks and stop retries */ - n_tty_kick_worker(tty); - n_tty_check_unthrottle(tty); - up_read(&tty->termios_rwsem); - mutex_unlock(&ldata->atomic_read_lock); - *cookie = NULL; - return kb - kbuf; - } + /* Is this a continuation of a read started earlier? */ + if (*cookie) + return n_tty_continue_cookie(tty, kbuf, nr, cookie); retval = job_control(tty, file); if (retval < 0) @@ -2240,34 +2261,12 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, u8 *kbuf, tty_buffer_flush_work(tty->port); down_read(&tty->termios_rwsem); if (!input_available_p(tty, 0)) { - if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { - retval = -EIO; - break; - } - if (tty_hung_up_p(file)) - break; - /* - * Abort readers for ttys which never actually - * get hung up. See __tty_hangup(). - */ - if (test_bit(TTY_HUPPING, &tty->flags)) - break; - if (!timeout) - break; - if (tty_io_nonblock(tty, file)) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; + int ret = n_tty_wait_for_input(tty, file, &wait, + &timeout); + if (ret <= 0) { + retval = ret; break; } - up_read(&tty->termios_rwsem); - - timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, - timeout); - - down_read(&tty->termios_rwsem); continue; } } @@ -2282,21 +2281,8 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, u8 *kbuf, nr--; } - /* - * Copy data, and if there is more to be had - * and we have nothing more to wait for, then - * let's mark us for retries. - * - * NOTE! We return here with both the termios_sem - * and atomic_read_lock still held, the retries - * will release them when done. - */ - if (copy_from_read_buf(tty, &kb, &nr) && kb - kbuf >= minimum) { -more_to_be_read: - remove_wait_queue(&tty->read_wait, &wait); - *cookie = cookie; - return kb - kbuf; - } + if (copy_from_read_buf(tty, &kb, &nr) && kb - kbuf >= minimum) + goto more_to_be_read; } n_tty_check_unthrottle(tty); @@ -2323,6 +2309,18 @@ more_to_be_read: retval = kb - kbuf; return retval; +more_to_be_read: + /* + * There is more to be had and we have nothing more to wait for, so + * let's mark us for retries. + * + * NOTE! We return here with both the termios_sem and atomic_read_lock + * still held, the retries will release them when done. + */ + remove_wait_queue(&tty->read_wait, &wait); + *cookie = cookie; + + return kb - kbuf; } /** diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index df08f13052ff..8bb1a01fef2a 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -798,7 +798,7 @@ static int ptmx_open(struct inode *inode, struct file *filp) nonseekable_open(inode, filp); /* We refuse fsnotify events on ptmx, since it's a shared resource */ - filp->f_mode |= FMODE_NONOTIFY; + file_set_fsnotify_mode(filp, FMODE_NONOTIFY); retval = tty_alloc_file(filp); if (retval) diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index 613cb356b918..0213381fa358 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -85,7 +85,7 @@ static const struct device_type serdev_ctrl_type = { .release = serdev_ctrl_release, }; -static int serdev_device_match(struct device *dev, struct device_driver *drv) +static int serdev_device_match(struct device *dev, const struct device_driver *drv) { if (!is_serdev_device(dev)) return 0; @@ -118,12 +118,11 @@ int serdev_device_add(struct serdev_device *serdev) err = device_add(&serdev->dev); if (err < 0) { - dev_err(&serdev->dev, "Can't add %s, status %pe\n", - dev_name(&serdev->dev), ERR_PTR(err)); + dev_err(&serdev->dev, "Failed to add serdev: %d\n", err); goto err_clear_serdev; } - dev_dbg(&serdev->dev, "device %s registered\n", dev_name(&serdev->dev)); + dev_dbg(&serdev->dev, "serdev registered successfully\n"); return 0; @@ -316,17 +315,6 @@ void serdev_device_write_flush(struct serdev_device *serdev) } EXPORT_SYMBOL_GPL(serdev_device_write_flush); -int serdev_device_write_room(struct serdev_device *serdev) -{ - struct serdev_controller *ctrl = serdev->ctrl; - - if (!ctrl || !ctrl->ops->write_room) - return 0; - - return serdev->ctrl->ops->write_room(ctrl); -} -EXPORT_SYMBOL_GPL(serdev_device_write_room); - unsigned int serdev_device_set_baudrate(struct serdev_device *serdev, unsigned int speed) { struct serdev_controller *ctrl = serdev->ctrl; @@ -529,7 +517,7 @@ static int of_serdev_register_devices(struct serdev_controller *ctrl) bool found = false; for_each_available_child_of_node(ctrl->dev.of_node, node) { - if (!of_get_property(node, "compatible", NULL)) + if (!of_property_present(node, "compatible")) continue; dev_dbg(&ctrl->dev, "adding child %pOF\n", node); @@ -794,8 +782,7 @@ int serdev_controller_add(struct serdev_controller *ctrl) goto err_rpm_disable; } - dev_dbg(&ctrl->dev, "serdev%d registered: dev:%p\n", - ctrl->nr, &ctrl->dev); + dev_dbg(&ctrl->dev, "serdev controller registered: dev:%p\n", &ctrl->dev); return 0; err_rpm_disable: diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c index 3d7ae7fa5018..bab1b143b8a6 100644 --- a/drivers/tty/serdev/serdev-ttyport.c +++ b/drivers/tty/serdev/serdev-ttyport.c @@ -92,14 +92,6 @@ static void ttyport_write_flush(struct serdev_controller *ctrl) tty_driver_flush_buffer(tty); } -static int ttyport_write_room(struct serdev_controller *ctrl) -{ - struct serport *serport = serdev_controller_get_drvdata(ctrl); - struct tty_struct *tty = serport->tty; - - return tty_write_room(tty); -} - static int ttyport_open(struct serdev_controller *ctrl) { struct serport *serport = serdev_controller_get_drvdata(ctrl); @@ -259,7 +251,6 @@ static int ttyport_break_ctl(struct serdev_controller *ctrl, unsigned int break_ static const struct serdev_controller_ops ctrl_ops = { .write_buf = ttyport_write_buf, .write_flush = ttyport_write_flush, - .write_room = ttyport_write_room, .open = ttyport_open, .close = ttyport_close, .set_flow_control = ttyport_set_flow_control, diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 1aa3e55c8b47..18530c31a598 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -9,7 +9,7 @@ #include <linux/bits.h> #include <linux/serial_8250.h> -#include <linux/serial_reg.h> +#include <linux/serial_core.h> #include <linux/dmaengine.h> #include "../serial_mctrl_gpio.h" @@ -93,6 +93,10 @@ struct serial8250_config { #define UART_BUG_THRE BIT(3) /* UART has buggy THRE reassertion */ #define UART_BUG_TXRACE BIT(5) /* UART Tx fails to set remote DR */ +/* Module parameters */ +#define UART_NR CONFIG_SERIAL_8250_NR_UARTS + +extern unsigned int nr_uarts; #ifdef CONFIG_SERIAL_8250_SHARE_IRQ #define SERIAL8250_SHARE_IRQS 1 @@ -100,6 +104,9 @@ struct serial8250_config { #define SERIAL8250_SHARE_IRQS 0 #endif +extern unsigned int share_irqs; +extern unsigned int skip_txen_test; + #define SERIAL8250_PORT_FLAGS(_base, _irq, _flags) \ { \ .iobase = _base, \ @@ -111,6 +118,19 @@ struct serial8250_config { #define SERIAL8250_PORT(_base, _irq) SERIAL8250_PORT_FLAGS(_base, _irq, 0) +extern struct uart_driver serial8250_reg; +void serial8250_register_ports(struct uart_driver *drv, struct device *dev); + +/* Legacy ISA bus related APIs */ +typedef void (*serial8250_isa_config_fn)(int, struct uart_port *, u32 *); +extern serial8250_isa_config_fn serial8250_isa_config; + +void serial8250_isa_init_ports(void); + +extern struct platform_device *serial8250_isa_devs; + +extern const struct uart_ops *univ8250_port_base_ops; +extern struct uart_ops univ8250_port_ops; static inline int serial_in(struct uart_8250_port *up, int offset) { @@ -200,18 +220,13 @@ static inline bool serial8250_clear_THRI(struct uart_8250_port *up) return true; } +struct uart_8250_port *serial8250_setup_port(int index); struct uart_8250_port *serial8250_get_port(int line); -void serial8250_rpm_get(struct uart_8250_port *p); -void serial8250_rpm_put(struct uart_8250_port *p); - -void serial8250_rpm_get_tx(struct uart_8250_port *p); -void serial8250_rpm_put_tx(struct uart_8250_port *p); - int serial8250_em485_config(struct uart_port *port, struct ktermios *termios, struct serial_rs485 *rs485); -void serial8250_em485_start_tx(struct uart_8250_port *p); -void serial8250_em485_stop_tx(struct uart_8250_port *p); +void serial8250_em485_start_tx(struct uart_8250_port *p, bool toggle_ier); +void serial8250_em485_stop_tx(struct uart_8250_port *p, bool toggle_ier); void serial8250_em485_destroy(struct uart_8250_port *p); extern struct serial_rs485 serial8250_em485_supported; @@ -293,9 +308,6 @@ static inline int serial8250_in_MCR(struct uart_8250_port *up) return mctrl; } -bool alpha_jensen(void); -void alpha_jensen_set_mctrl(struct uart_port *port, unsigned int mctrl); - #ifdef CONFIG_SERIAL_8250_PNP int serial8250_pnp_init(void); void serial8250_pnp_exit(void); @@ -304,6 +316,12 @@ static inline int serial8250_pnp_init(void) { return 0; } static inline void serial8250_pnp_exit(void) { } #endif +#ifdef CONFIG_SERIAL_8250_RSA +void univ8250_rsa_support(struct uart_ops *ops); +#else +static inline void univ8250_rsa_support(struct uart_ops *ops) { } +#endif + #ifdef CONFIG_SERIAL_8250_FINTEK int fintek_8250_probe(struct uart_8250_port *uart); #else @@ -350,6 +368,7 @@ static inline int is_omap1510_8250(struct uart_8250_port *pt) #ifdef CONFIG_SERIAL_8250_DMA extern int serial8250_tx_dma(struct uart_8250_port *); +extern void serial8250_tx_dma_flush(struct uart_8250_port *); extern int serial8250_rx_dma(struct uart_8250_port *); extern void serial8250_rx_dma_flush(struct uart_8250_port *); extern int serial8250_request_dma(struct uart_8250_port *); @@ -382,6 +401,7 @@ static inline int serial8250_tx_dma(struct uart_8250_port *p) { return -1; } +static inline void serial8250_tx_dma_flush(struct uart_8250_port *p) { } static inline int serial8250_rx_dma(struct uart_8250_port *p) { return -1; diff --git a/drivers/tty/serial/8250/8250_alpha.c b/drivers/tty/serial/8250/8250_alpha.c deleted file mode 100644 index 58e70328aa4d..000000000000 --- a/drivers/tty/serial/8250/8250_alpha.c +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -#include <asm/machvec.h> -#include "8250.h" - -bool alpha_jensen(void) -{ - return !strcmp(alpha_mv.vector_name, "Jensen"); -} - -void alpha_jensen_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - /* - * Digital did something really horribly wrong with the OUT1 and OUT2 - * lines on Alpha Jensen. The failure mode is that if either is - * cleared, the machine locks up with endless interrupts. - */ - mctrl |= TIOCM_OUT1 | TIOCM_OUT2; - - serial8250_do_set_mctrl(port, mctrl); -} diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c index 53d8eee9b1c8..26fc0464f1cc 100644 --- a/drivers/tty/serial/8250/8250_aspeed_vuart.c +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c @@ -304,7 +304,8 @@ static void aspeed_vuart_unthrottle(struct uart_port *port) static void aspeed_vuart_unthrottle_exp(struct timer_list *timer) { - struct aspeed_vuart *vuart = from_timer(vuart, timer, unthrottle_timer); + struct aspeed_vuart *vuart = timer_container_of(vuart, timer, + unthrottle_timer); struct uart_8250_port *up = vuart->port; if (!tty_buffer_space_avail(&up->port.state->port)) { @@ -550,7 +551,7 @@ static void aspeed_vuart_remove(struct platform_device *pdev) { struct aspeed_vuart *vuart = platform_get_drvdata(pdev); - del_timer_sync(&vuart->unthrottle_timer); + timer_delete_sync(&vuart->unthrottle_timer); aspeed_vuart_set_enabled(vuart, false); serial8250_unregister_port(vuart->line); sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); @@ -561,6 +562,7 @@ static const struct of_device_id aspeed_vuart_table[] = { { .compatible = "aspeed,ast2500-vuart" }, { }, }; +MODULE_DEVICE_TABLE(of, aspeed_vuart_table); static struct platform_driver aspeed_vuart_driver = { .driver = { @@ -568,7 +570,7 @@ static struct platform_driver aspeed_vuart_driver = { .of_match_table = aspeed_vuart_table, }, .probe = aspeed_vuart_probe, - .remove_new = aspeed_vuart_remove, + .remove = aspeed_vuart_remove, }; module_platform_driver(aspeed_vuart_driver); diff --git a/drivers/tty/serial/8250/8250_bcm2835aux.c b/drivers/tty/serial/8250/8250_bcm2835aux.c index 121a5ce86050..0609582a62f7 100644 --- a/drivers/tty/serial/8250/8250_bcm2835aux.c +++ b/drivers/tty/serial/8250/8250_bcm2835aux.c @@ -13,6 +13,7 @@ */ #include <linux/clk.h> +#include <linux/console.h> #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> @@ -45,7 +46,7 @@ struct bcm2835aux_data { u32 cntl; }; -static void bcm2835aux_rs485_start_tx(struct uart_8250_port *up) +static void bcm2835aux_rs485_start_tx(struct uart_8250_port *up, bool toggle_ier) { if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) { struct bcm2835aux_data *data = dev_get_drvdata(up->port.dev); @@ -64,7 +65,7 @@ static void bcm2835aux_rs485_start_tx(struct uart_8250_port *up) serial8250_out_MCR(up, UART_MCR_RTS); } -static void bcm2835aux_rs485_stop_tx(struct uart_8250_port *up) +static void bcm2835aux_rs485_stop_tx(struct uart_8250_port *up, bool toggle_ier) { if (up->port.rs485.flags & SER_RS485_RTS_AFTER_SEND) serial8250_out_MCR(up, 0); @@ -213,14 +214,60 @@ static const struct acpi_device_id bcm2835aux_serial_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, bcm2835aux_serial_acpi_match); +static bool bcm2835aux_can_disable_clock(struct device *dev) +{ + struct bcm2835aux_data *data = dev_get_drvdata(dev); + struct uart_8250_port *up = serial8250_get_port(data->line); + + if (device_may_wakeup(dev)) + return false; + + if (uart_console(&up->port) && !console_suspend_enabled) + return false; + + return true; +} + +static int bcm2835aux_suspend(struct device *dev) +{ + struct bcm2835aux_data *data = dev_get_drvdata(dev); + + serial8250_suspend_port(data->line); + + if (!bcm2835aux_can_disable_clock(dev)) + return 0; + + clk_disable_unprepare(data->clk); + return 0; +} + +static int bcm2835aux_resume(struct device *dev) +{ + struct bcm2835aux_data *data = dev_get_drvdata(dev); + int ret; + + if (bcm2835aux_can_disable_clock(dev)) { + ret = clk_prepare_enable(data->clk); + if (ret) + return ret; + } + + serial8250_resume_port(data->line); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(bcm2835aux_dev_pm_ops, bcm2835aux_suspend, bcm2835aux_resume); + static struct platform_driver bcm2835aux_serial_driver = { .driver = { .name = "bcm2835-aux-uart", .of_match_table = bcm2835aux_serial_match, .acpi_match_table = bcm2835aux_serial_acpi_match, + .pm = pm_ptr(&bcm2835aux_dev_pm_ops), }, .probe = bcm2835aux_serial_probe, - .remove_new = bcm2835aux_serial_remove, + .remove = bcm2835aux_serial_remove, }; module_platform_driver(bcm2835aux_serial_driver); diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c index 5daa38d9c64e..742004d63c6f 100644 --- a/drivers/tty/serial/8250/8250_bcm7271.c +++ b/drivers/tty/serial/8250/8250_bcm7271.c @@ -413,20 +413,18 @@ static int stop_tx_dma(struct uart_8250_port *p) static int brcmuart_tx_dma(struct uart_8250_port *p) { struct brcmuart_priv *priv = p->port.private_data; - struct circ_buf *xmit = &p->port.state->xmit; + struct tty_port *tport = &p->port.state->port; u32 tx_size; if (uart_tx_stopped(&p->port) || priv->tx_running || - uart_circ_empty(xmit)) { + kfifo_is_empty(&tport->xmit_fifo)) { return 0; } - tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); priv->dma.tx_err = 0; - memcpy(priv->tx_buf, &xmit->buf[xmit->tail], tx_size); - uart_xmit_advance(&p->port, tx_size); + tx_size = uart_fifo_out(&p->port, priv->tx_buf, UART_XMIT_SIZE); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&p->port); udma_writel(priv, REGS_DMA_TX, UDMA_TX_TRANSFER_LEN, tx_size); @@ -540,7 +538,7 @@ static void brcmuart_tx_isr(struct uart_port *up, u32 isr) struct brcmuart_priv *priv = up->private_data; struct device *dev = up->dev; struct uart_8250_port *port_8250 = up_to_u8250p(up); - struct circ_buf *xmit = &port_8250->port.state->xmit; + struct tty_port *tport = &port_8250->port.state->port; if (isr & UDMA_INTR_TX_ABORT) { if (priv->tx_running) @@ -548,7 +546,7 @@ static void brcmuart_tx_isr(struct uart_port *up, u32 isr) return; } priv->tx_running = false; - if (!uart_circ_empty(xmit) && !uart_tx_stopped(up)) + if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(up)) brcmuart_tx_dma(port_8250); } @@ -675,18 +673,46 @@ static void init_real_clk_rates(struct device *dev, struct brcmuart_priv *priv) clk_set_rate(priv->baud_mux_clk, priv->default_mux_rate); } +static u32 find_quot(struct device *dev, u32 freq, u32 baud, u32 *percent) +{ + u32 quot; + u32 rate; + u64 hires_rate; + u64 hires_baud; + u64 hires_err; + + rate = freq / 16; + quot = DIV_ROUND_CLOSEST(rate, baud); + if (!quot) + return 0; + + /* increase resolution to get xx.xx percent */ + hires_rate = div_u64((u64)rate * 10000, (u64)quot); + hires_baud = (u64)baud * 10000; + + /* get the delta */ + if (hires_rate > hires_baud) + hires_err = (hires_rate - hires_baud); + else + hires_err = (hires_baud - hires_rate); + + *percent = (unsigned long)DIV_ROUND_CLOSEST_ULL(hires_err, baud); + + dev_dbg(dev, "Baud rate: %u, MUX Clk: %u, Error: %u.%u%%\n", + baud, freq, *percent / 100, *percent % 100); + + return quot; +} + static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv, u32 baud) { u32 percent; u32 best_percent = UINT_MAX; u32 quot; + u32 freq; u32 best_quot = 1; - u32 rate; - int best_index = -1; - u64 hires_rate; - u64 hires_baud; - u64 hires_err; + u32 best_freq = 0; int rc; int i; int real_baud; @@ -695,44 +721,35 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv, if (priv->baud_mux_clk == NULL) return; - /* Find the closest match for specified baud */ - for (i = 0; i < ARRAY_SIZE(priv->real_rates); i++) { - if (priv->real_rates[i] == 0) - continue; - rate = priv->real_rates[i] / 16; - quot = DIV_ROUND_CLOSEST(rate, baud); - if (!quot) - continue; - - /* increase resolution to get xx.xx percent */ - hires_rate = (u64)rate * 10000; - hires_baud = (u64)baud * 10000; - - hires_err = div_u64(hires_rate, (u64)quot); - - /* get the delta */ - if (hires_err > hires_baud) - hires_err = (hires_err - hires_baud); - else - hires_err = (hires_baud - hires_err); - - percent = (unsigned long)DIV_ROUND_CLOSEST_ULL(hires_err, baud); - dev_dbg(up->dev, - "Baud rate: %u, MUX Clk: %u, Error: %u.%u%%\n", - baud, priv->real_rates[i], percent / 100, - percent % 100); - if (percent < best_percent) { - best_percent = percent; - best_index = i; - best_quot = quot; + /* Try default_mux_rate first */ + quot = find_quot(up->dev, priv->default_mux_rate, baud, &percent); + if (quot) { + best_percent = percent; + best_freq = priv->default_mux_rate; + best_quot = quot; + } + /* If more than 1% error, find the closest match for specified baud */ + if (best_percent > 100) { + for (i = 0; i < ARRAY_SIZE(priv->real_rates); i++) { + freq = priv->real_rates[i]; + if (freq == 0 || freq == priv->default_mux_rate) + continue; + quot = find_quot(up->dev, freq, baud, &percent); + if (!quot) + continue; + + if (percent < best_percent) { + best_percent = percent; + best_freq = freq; + best_quot = quot; + } } } - if (best_index == -1) { + if (!best_freq) { dev_err(up->dev, "Error, %d BAUD rate is too fast.\n", baud); return; } - rate = priv->real_rates[best_index]; - rc = clk_set_rate(priv->baud_mux_clk, rate); + rc = clk_set_rate(priv->baud_mux_clk, best_freq); if (rc) dev_err(up->dev, "Error selecting BAUD MUX clock\n"); @@ -741,8 +758,8 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv, dev_err(up->dev, "Error, baud: %d has %u.%u%% error\n", baud, percent / 100, percent % 100); - real_baud = rate / 16 / best_quot; - dev_dbg(up->dev, "Selecting BAUD MUX rate: %u\n", rate); + real_baud = best_freq / 16 / best_quot; + dev_dbg(up->dev, "Selecting BAUD MUX rate: %u\n", best_freq); dev_dbg(up->dev, "Requested baud: %u, Actual baud: %u\n", baud, real_baud); @@ -751,7 +768,7 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv, i += (i / 2); priv->char_wait = ns_to_ktime(i); - up->uartclk = rate; + up->uartclk = best_freq; } static void brcmstb_set_termios(struct uart_port *up, @@ -795,7 +812,7 @@ static int brcmuart_handle_irq(struct uart_port *p) /* * if Receive Data Interrupt is enabled and * we're uing hardware flow control, deassert - * RTS and wait for any chars in the pipline to + * RTS and wait for any chars in the pipeline to * arrive and then check for DR again. */ if ((ier & UART_IER_RDI) && (up->mcr & UART_MCR_AFE)) { @@ -1039,8 +1056,7 @@ static int brcmuart_probe(struct platform_device *pdev) } /* setup HR timer */ - hrtimer_init(&priv->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); - priv->hrt.function = brcmuart_hrtimer_func; + hrtimer_setup(&priv->hrt, brcmuart_hrtimer_func, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); up.port.shutdown = brcmuart_shutdown; up.port.startup = brcmuart_startup; @@ -1187,7 +1203,7 @@ static struct platform_driver brcmuart_platform_driver = { .of_match_table = brcmuart_dt_ids, }, .probe = brcmuart_probe, - .remove_new = brcmuart_remove, + .remove = brcmuart_remove, }; static int __init brcmuart_init(void) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index b62ad9006780..7a6050f1c094 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -6,11 +6,9 @@ * * Copyright (C) 2001 Russell King. * - * Supports: ISA-compatible 8250/16550 ports - * PNP 8250/16550 ports + * Supports: * early_serial_setup() ports * userspace-configurable "phantom" ports - * "serial8250" platform devices * serial8250_register_8250_port() ports */ @@ -35,52 +33,13 @@ #include <linux/string_helpers.h> #include <linux/uaccess.h> #include <linux/io.h> -#ifdef CONFIG_SPARC -#include <linux/sunserialcore.h> -#endif #include <asm/irq.h> #include "8250.h" -/* - * Configuration: - * share_irqs - whether we pass IRQF_SHARED to request_irq(). This option - * is unsafe when used on edge-triggered interrupts. - */ -static unsigned int share_irqs = SERIAL8250_SHARE_IRQS; - -static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS; - -static struct uart_driver serial8250_reg; - -static unsigned int skip_txen_test; /* force skip of txen test at init time */ - #define PASS_LIMIT 512 -#include <asm/serial.h> -/* - * SERIAL_PORT_DFNS tells us about built-in ports that have no - * standard enumeration mechanism. Platforms that can find all - * serial ports via mechanisms like ACPI or PCI need not supply it. - */ -#ifndef SERIAL_PORT_DFNS -#define SERIAL_PORT_DFNS -#endif - -static const struct old_serial_port old_serial_port[] = { - SERIAL_PORT_DFNS /* defined in asm/serial.h */ -}; - -#define UART_NR CONFIG_SERIAL_8250_NR_UARTS - -#ifdef CONFIG_SERIAL_8250_RSA - -#define PORT_RSA_MAX 4 -static unsigned long probe_rsa[PORT_RSA_MAX]; -static unsigned int probe_rsa_count; -#endif /* CONFIG_SERIAL_8250_RSA */ - struct irq_info { struct hlist_node node; int irq; @@ -247,7 +206,7 @@ static void serial_unlink_irq_chain(struct uart_8250_port *up) */ static void serial8250_timeout(struct timer_list *t) { - struct uart_8250_port *up = from_timer(up, t, timer); + struct uart_8250_port *up = timer_container_of(up, t, timer); up->port.handle_irq(&up->port); mod_timer(&up->timer, jiffies + uart_poll_timeout(&up->port)); @@ -255,7 +214,7 @@ static void serial8250_timeout(struct timer_list *t) static void serial8250_backup_timeout(struct timer_list *t) { - struct uart_8250_port *up = from_timer(up, t, timer); + struct uart_8250_port *up = timer_container_of(up, t, timer); unsigned int iir, ier = 0, lsr; unsigned long flags; @@ -280,7 +239,8 @@ static void serial8250_backup_timeout(struct timer_list *t) */ lsr = serial_lsr_in(up); if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) && - (!uart_circ_empty(&up->port.state->xmit) || up->port.x_char) && + (!kfifo_is_empty(&up->port.state->port.xmit_fifo) || + up->port.x_char) && (lsr & UART_LSR_THRE)) { iir &= ~(UART_IIR_ID | UART_IIR_NO_INT); iir |= UART_IIR_THRI; @@ -338,51 +298,14 @@ static void univ8250_release_irq(struct uart_8250_port *up) { struct uart_port *port = &up->port; - del_timer_sync(&up->timer); + timer_delete_sync(&up->timer); up->timer.function = serial8250_timeout; if (port->irq) serial_unlink_irq_chain(up); } -#ifdef CONFIG_SERIAL_8250_RSA -static int serial8250_request_rsa_resource(struct uart_8250_port *up) -{ - unsigned long start = UART_RSA_BASE << up->port.regshift; - unsigned int size = 8 << up->port.regshift; - struct uart_port *port = &up->port; - int ret = -EINVAL; - - switch (port->iotype) { - case UPIO_HUB6: - case UPIO_PORT: - start += port->iobase; - if (request_region(start, size, "serial-rsa")) - ret = 0; - else - ret = -EBUSY; - break; - } - - return ret; -} - -static void serial8250_release_rsa_resource(struct uart_8250_port *up) -{ - unsigned long offset = UART_RSA_BASE << up->port.regshift; - unsigned int size = 8 << up->port.regshift; - struct uart_port *port = &up->port; - - switch (port->iotype) { - case UPIO_HUB6: - case UPIO_PORT: - release_region(port->iobase + offset, size); - break; - } -} -#endif - -static const struct uart_ops *base_ops; -static struct uart_ops univ8250_port_ops; +const struct uart_ops *univ8250_port_base_ops = NULL; +struct uart_ops univ8250_port_ops; static const struct uart_8250_ops univ8250_driver_ops = { .setup_irq = univ8250_setup_irq, @@ -410,85 +333,12 @@ struct uart_8250_port *serial8250_get_port(int line) } EXPORT_SYMBOL_GPL(serial8250_get_port); -static void (*serial8250_isa_config)(int port, struct uart_port *up, - u32 *capabilities); - -void serial8250_set_isa_configurator( - void (*v)(int port, struct uart_port *up, u32 *capabilities)) -{ - serial8250_isa_config = v; -} -EXPORT_SYMBOL(serial8250_set_isa_configurator); - -#ifdef CONFIG_SERIAL_8250_RSA - -static void univ8250_config_port(struct uart_port *port, int flags) -{ - struct uart_8250_port *up = up_to_u8250p(port); - - up->probe &= ~UART_PROBE_RSA; - if (port->type == PORT_RSA) { - if (serial8250_request_rsa_resource(up) == 0) - up->probe |= UART_PROBE_RSA; - } else if (flags & UART_CONFIG_TYPE) { - int i; - - for (i = 0; i < probe_rsa_count; i++) { - if (probe_rsa[i] == up->port.iobase) { - if (serial8250_request_rsa_resource(up) == 0) - up->probe |= UART_PROBE_RSA; - break; - } - } - } - - base_ops->config_port(port, flags); - - if (port->type != PORT_RSA && up->probe & UART_PROBE_RSA) - serial8250_release_rsa_resource(up); -} - -static int univ8250_request_port(struct uart_port *port) -{ - struct uart_8250_port *up = up_to_u8250p(port); - int ret; - - ret = base_ops->request_port(port); - if (ret == 0 && port->type == PORT_RSA) { - ret = serial8250_request_rsa_resource(up); - if (ret < 0) - base_ops->release_port(port); - } - - return ret; -} - -static void univ8250_release_port(struct uart_port *port) -{ - struct uart_8250_port *up = up_to_u8250p(port); - - if (port->type == PORT_RSA) - serial8250_release_rsa_resource(up); - base_ops->release_port(port); -} - -static void univ8250_rsa_support(struct uart_ops *ops) -{ - ops->config_port = univ8250_config_port; - ops->request_port = univ8250_request_port; - ops->release_port = univ8250_release_port; -} - -#else -#define univ8250_rsa_support(x) do { } while (0) -#endif /* CONFIG_SERIAL_8250_RSA */ - static inline void serial8250_apply_quirks(struct uart_8250_port *up) { up->port.quirks |= skip_txen_test ? UPQ_NO_TXEN_TEST : 0; } -static struct uart_8250_port *serial8250_setup_port(int index) +struct uart_8250_port *serial8250_setup_port(int index) { struct uart_8250_port *up; @@ -500,74 +350,20 @@ static struct uart_8250_port *serial8250_setup_port(int index) up->port.port_id = index; serial8250_init_port(up); - if (!base_ops) - base_ops = up->port.ops; + if (!univ8250_port_base_ops) + univ8250_port_base_ops = up->port.ops; up->port.ops = &univ8250_port_ops; timer_setup(&up->timer, serial8250_timeout, 0); up->ops = &univ8250_driver_ops; - if (IS_ENABLED(CONFIG_ALPHA_JENSEN) || - (IS_ENABLED(CONFIG_ALPHA_GENERIC) && alpha_jensen())) - up->port.set_mctrl = alpha_jensen_set_mctrl; - serial8250_set_defaults(up); return up; } -static void __init serial8250_isa_init_ports(void) -{ - struct uart_8250_port *up; - static int first = 1; - int i, irqflag = 0; - - if (!first) - return; - first = 0; - - if (nr_uarts > UART_NR) - nr_uarts = UART_NR; - - /* - * Set up initial isa ports based on nr_uart module param, or else - * default to CONFIG_SERIAL_8250_RUNTIME_UARTS. Note that we do not - * need to increase nr_uarts when setting up the initial isa ports. - */ - for (i = 0; i < nr_uarts; i++) - serial8250_setup_port(i); - - /* chain base port ops to support Remote Supervisor Adapter */ - univ8250_port_ops = *base_ops; - univ8250_rsa_support(&univ8250_port_ops); - - if (share_irqs) - irqflag = IRQF_SHARED; - - for (i = 0, up = serial8250_ports; - i < ARRAY_SIZE(old_serial_port) && i < nr_uarts; - i++, up++) { - struct uart_port *port = &up->port; - - port->iobase = old_serial_port[i].port; - port->irq = irq_canonicalize(old_serial_port[i].irq); - port->irqflags = 0; - port->uartclk = old_serial_port[i].baud_base * 16; - port->flags = old_serial_port[i].flags; - port->hub6 = 0; - port->membase = old_serial_port[i].iomem_base; - port->iotype = old_serial_port[i].io_type; - port->regshift = old_serial_port[i].iomem_reg_shift; - - port->irqflags |= irqflag; - if (serial8250_isa_config != NULL) - serial8250_isa_config(i, &up->port, &up->capabilities); - } -} - -static void __init -serial8250_register_ports(struct uart_driver *drv, struct device *dev) +void __init serial8250_register_ports(struct uart_driver *drv, struct device *dev) { int i; @@ -627,11 +423,11 @@ static int univ8250_console_setup(struct console *co, char *options) port = &serial8250_ports[co->index].port; /* link port to console */ - port->cons = co; + uart_port_set_cons(port, co); retval = serial8250_console_setup(port, options, false); if (retval != 0) - port->cons = NULL; + uart_port_set_cons(port, NULL); return retval; } @@ -665,7 +461,7 @@ static int univ8250_console_match(struct console *co, char *name, int idx, char *options) { char match[] = "uart"; /* 8250-specific earlycon name */ - unsigned char iotype; + enum uart_iotype iotype; resource_size_t addr; int i; @@ -689,7 +485,7 @@ static int univ8250_console_match(struct console *co, char *name, int idx, continue; co->index = i; - port->cons = co; + uart_port_set_cons(port, co); return serial8250_console_setup(port, options, true); } @@ -724,7 +520,7 @@ console_initcall(univ8250_console_init); #define SERIAL8250_CONSOLE NULL #endif -static struct uart_driver serial8250_reg = { +struct uart_driver serial8250_reg = { .owner = THIS_MODULE, .driver_name = "serial", .dev_name = "ttyS", @@ -826,120 +622,6 @@ void serial8250_resume_port(int line) EXPORT_SYMBOL(serial8250_resume_port); /* - * Register a set of serial devices attached to a platform device. The - * list is terminated with a zero flags entry, which means we expect - * all entries to have at least UPF_BOOT_AUTOCONF set. - */ -static int serial8250_probe(struct platform_device *dev) -{ - struct plat_serial8250_port *p = dev_get_platdata(&dev->dev); - struct uart_8250_port uart; - int ret, i, irqflag = 0; - - memset(&uart, 0, sizeof(uart)); - - if (share_irqs) - irqflag = IRQF_SHARED; - - for (i = 0; p && p->flags != 0; p++, i++) { - uart.port.iobase = p->iobase; - uart.port.membase = p->membase; - uart.port.irq = p->irq; - uart.port.irqflags = p->irqflags; - uart.port.uartclk = p->uartclk; - uart.port.regshift = p->regshift; - uart.port.iotype = p->iotype; - uart.port.flags = p->flags; - uart.port.mapbase = p->mapbase; - uart.port.mapsize = p->mapsize; - uart.port.hub6 = p->hub6; - uart.port.has_sysrq = p->has_sysrq; - uart.port.private_data = p->private_data; - uart.port.type = p->type; - uart.bugs = p->bugs; - uart.port.serial_in = p->serial_in; - uart.port.serial_out = p->serial_out; - uart.dl_read = p->dl_read; - uart.dl_write = p->dl_write; - uart.port.handle_irq = p->handle_irq; - uart.port.handle_break = p->handle_break; - uart.port.set_termios = p->set_termios; - uart.port.set_ldisc = p->set_ldisc; - uart.port.get_mctrl = p->get_mctrl; - uart.port.pm = p->pm; - uart.port.dev = &dev->dev; - uart.port.irqflags |= irqflag; - ret = serial8250_register_8250_port(&uart); - if (ret < 0) { - dev_err(&dev->dev, "unable to register port at index %d " - "(IO%lx MEM%llx IRQ%d): %d\n", i, - p->iobase, (unsigned long long)p->mapbase, - p->irq, ret); - } - } - return 0; -} - -/* - * Remove serial ports registered against a platform device. - */ -static void serial8250_remove(struct platform_device *dev) -{ - int i; - - for (i = 0; i < nr_uarts; i++) { - struct uart_8250_port *up = &serial8250_ports[i]; - - if (up->port.dev == &dev->dev) - serial8250_unregister_port(i); - } -} - -static int serial8250_suspend(struct platform_device *dev, pm_message_t state) -{ - int i; - - for (i = 0; i < UART_NR; i++) { - struct uart_8250_port *up = &serial8250_ports[i]; - - if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) - uart_suspend_port(&serial8250_reg, &up->port); - } - - return 0; -} - -static int serial8250_resume(struct platform_device *dev) -{ - int i; - - for (i = 0; i < UART_NR; i++) { - struct uart_8250_port *up = &serial8250_ports[i]; - - if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) - serial8250_resume_port(i); - } - - return 0; -} - -static struct platform_driver serial8250_isa_driver = { - .probe = serial8250_probe, - .remove_new = serial8250_remove, - .suspend = serial8250_suspend, - .resume = serial8250_resume, - .driver = { - .name = "serial8250", - }, -}; - -/* - * This "device" covers _all_ ISA 8250-compatible serial devices listed - * in the table in include/asm/serial.h - */ -static struct platform_device *serial8250_isa_devs; - -/* * serial8250_register_8250_port and serial8250_unregister_port allows for * 16x50 serial ports to be configured at run-time, to support PCMCIA * modems and PCI multiport cards. @@ -993,7 +675,6 @@ static void serial_8250_overrun_backoff_work(struct work_struct *work) uart_port_lock_irqsave(port, &flags); up->ier |= UART_IER_RLSI | UART_IER_RDI; - up->port.read_status_mask |= UART_LSR_DR; serial_out(up, UART_IER, up->ier); uart_port_unlock_irqrestore(port, flags); } @@ -1130,6 +811,9 @@ int serial8250_register_8250_port(const struct uart_8250_port *up) uart->dl_write = up->dl_write; if (uart->port.type != PORT_8250_CIR) { + if (uart_console_registered(&uart->port)) + pm_runtime_get_sync(uart->port.dev); + if (serial8250_isa_config != NULL) serial8250_isa_config(0, &uart->port, &uart->capabilities); @@ -1215,134 +899,5 @@ void serial8250_unregister_port(int line) } EXPORT_SYMBOL(serial8250_unregister_port); -static int __init serial8250_init(void) -{ - int ret; - - if (nr_uarts == 0) - return -ENODEV; - - serial8250_isa_init_ports(); - - pr_info("Serial: 8250/16550 driver, %d ports, IRQ sharing %s\n", - nr_uarts, str_enabled_disabled(share_irqs)); - -#ifdef CONFIG_SPARC - ret = sunserial_register_minors(&serial8250_reg, UART_NR); -#else - serial8250_reg.nr = UART_NR; - ret = uart_register_driver(&serial8250_reg); -#endif - if (ret) - goto out; - - ret = serial8250_pnp_init(); - if (ret) - goto unreg_uart_drv; - - serial8250_isa_devs = platform_device_alloc("serial8250", - PLAT8250_DEV_LEGACY); - if (!serial8250_isa_devs) { - ret = -ENOMEM; - goto unreg_pnp; - } - - ret = platform_device_add(serial8250_isa_devs); - if (ret) - goto put_dev; - - serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev); - - ret = platform_driver_register(&serial8250_isa_driver); - if (ret == 0) - goto out; - - platform_device_del(serial8250_isa_devs); -put_dev: - platform_device_put(serial8250_isa_devs); -unreg_pnp: - serial8250_pnp_exit(); -unreg_uart_drv: -#ifdef CONFIG_SPARC - sunserial_unregister_minors(&serial8250_reg, UART_NR); -#else - uart_unregister_driver(&serial8250_reg); -#endif -out: - return ret; -} - -static void __exit serial8250_exit(void) -{ - struct platform_device *isa_dev = serial8250_isa_devs; - - /* - * This tells serial8250_unregister_port() not to re-register - * the ports (thereby making serial8250_isa_driver permanently - * in use.) - */ - serial8250_isa_devs = NULL; - - platform_driver_unregister(&serial8250_isa_driver); - platform_device_unregister(isa_dev); - - serial8250_pnp_exit(); - -#ifdef CONFIG_SPARC - sunserial_unregister_minors(&serial8250_reg, UART_NR); -#else - uart_unregister_driver(&serial8250_reg); -#endif -} - -module_init(serial8250_init); -module_exit(serial8250_exit); - MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Generic 8250/16x50 serial driver"); - -module_param_hw(share_irqs, uint, other, 0644); -MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices (unsafe)"); - -module_param(nr_uarts, uint, 0644); -MODULE_PARM_DESC(nr_uarts, "Maximum number of UARTs supported. (1-" __MODULE_STRING(CONFIG_SERIAL_8250_NR_UARTS) ")"); - -module_param(skip_txen_test, uint, 0644); -MODULE_PARM_DESC(skip_txen_test, "Skip checking for the TXEN bug at init time"); - -#ifdef CONFIG_SERIAL_8250_RSA -module_param_hw_array(probe_rsa, ulong, ioport, &probe_rsa_count, 0444); -MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); -#endif -MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR); - -#ifdef CONFIG_SERIAL_8250_DEPRECATED_OPTIONS -#ifndef MODULE -/* This module was renamed to 8250_core in 3.7. Keep the old "8250" name - * working as well for the module options so we don't break people. We - * need to keep the names identical and the convenient macros will happily - * refuse to let us do that by failing the build with redefinition errors - * of global variables. So we stick them inside a dummy function to avoid - * those conflicts. The options still get parsed, and the redefined - * MODULE_PARAM_PREFIX lets us keep the "8250." syntax alive. - * - * This is hacky. I'm sorry. - */ -static void __used s8250_options(void) -{ -#undef MODULE_PARAM_PREFIX -#define MODULE_PARAM_PREFIX "8250_core." - - module_param_cb(share_irqs, ¶m_ops_uint, &share_irqs, 0644); - module_param_cb(nr_uarts, ¶m_ops_uint, &nr_uarts, 0644); - module_param_cb(skip_txen_test, ¶m_ops_uint, &skip_txen_test, 0644); -#ifdef CONFIG_SERIAL_8250_RSA - __module_param_call(MODULE_PARAM_PREFIX, probe_rsa, - ¶m_array_ops, .arr = &__param_arr_probe_rsa, - 0444, -1, 0); -#endif -} -#else -MODULE_ALIAS("8250_core"); -#endif -#endif diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 8b30ca8fdd3f..bdd26c9f34bd 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -15,7 +15,7 @@ static void __dma_tx_complete(void *param) { struct uart_8250_port *p = param; struct uart_8250_dma *dma = p->dma; - struct circ_buf *xmit = &p->port.state->xmit; + struct tty_port *tport = &p->port.state->port; unsigned long flags; int ret; @@ -28,7 +28,7 @@ static void __dma_tx_complete(void *param) uart_xmit_advance(&p->port, dma->tx_size); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&p->port); ret = serial8250_tx_dma(p); @@ -86,9 +86,12 @@ static void dma_rx_complete(void *param) int serial8250_tx_dma(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; - struct circ_buf *xmit = &p->port.state->xmit; + struct tty_port *tport = &p->port.state->port; struct dma_async_tx_descriptor *desc; struct uart_port *up = &p->port; + struct scatterlist *sg; + struct scatterlist sgl[2]; + int i; int ret; if (dma->tx_running) { @@ -102,19 +105,26 @@ int serial8250_tx_dma(struct uart_8250_port *p) uart_xchar_out(up, UART_TX); } - if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) { + if (uart_tx_stopped(&p->port) || kfifo_is_empty(&tport->xmit_fifo)) { /* We have been called from __dma_tx_complete() */ return 0; } - dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); - serial8250_do_prepare_tx_dma(p); - desc = dmaengine_prep_slave_single(dma->txchan, - dma->tx_addr + xmit->tail, - dma->tx_size, DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + sg_init_table(sgl, ARRAY_SIZE(sgl)); + + ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, sgl, ARRAY_SIZE(sgl), + UART_XMIT_SIZE, dma->tx_addr); + + dma->tx_size = 0; + + for_each_sg(sgl, sg, ret, i) + dma->tx_size += sg_dma_len(sg); + + desc = dmaengine_prep_slave_sg(dma->txchan, sgl, ret, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { ret = -EBUSY; goto err; @@ -139,6 +149,22 @@ err: return ret; } +void serial8250_tx_dma_flush(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + + if (!dma->tx_running) + return; + + /* + * kfifo_reset() has been called by the serial core, avoid + * advancing and underflowing in __dma_tx_complete(). + */ + dma->tx_size = 0; + + dmaengine_terminate_async(dma->txchan); +} + int serial8250_rx_dma(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; @@ -253,7 +279,7 @@ int serial8250_request_dma(struct uart_8250_port *p) /* TX buffer */ dma->tx_addr = dma_map_single(dma->txchan->device->dev, - p->port.state->xmit.buf, + p->port.state->port.xmit_buf, UART_XMIT_SIZE, DMA_TO_DEVICE); if (dma_mapping_error(dma->txchan->device->dev, dma->tx_addr)) { diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index a3acbf0f5da1..1902f29444a1 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -55,6 +55,34 @@ #define DW_UART_QUIRK_SKIP_SET_RATE BIT(2) #define DW_UART_QUIRK_IS_DMA_FC BIT(3) #define DW_UART_QUIRK_APMC0D08 BIT(4) +#define DW_UART_QUIRK_CPR_VALUE BIT(5) + +struct dw8250_platform_data { + u8 usr_reg; + u32 cpr_value; + unsigned int quirks; +}; + +struct dw8250_data { + struct dw8250_port_data data; + const struct dw8250_platform_data *pdata; + + int msr_mask_on; + int msr_mask_off; + struct clk *clk; + struct clk *pclk; + struct notifier_block clk_notifier; + struct work_struct clk_work; + struct reset_control *rst; + + unsigned int skip_autocfg:1; + unsigned int uart_16550_compatible:1; +}; + +static inline struct dw8250_data *to_dw8250_data(struct dw8250_port_data *data) +{ + return container_of(data, struct dw8250_data, data); +} static inline struct dw8250_data *clk_to_dw8250_data(struct notifier_block *nb) { @@ -79,11 +107,23 @@ static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value) return value; } +/* + * This function is being called as part of the uart_port::serial_out() + * routine. Hence, it must not call serial_port_out() or serial_out() + * against the modified registers here, i.e. LCR. + */ static void dw8250_force_idle(struct uart_port *p) { struct uart_8250_port *up = up_to_u8250p(p); unsigned int lsr; + /* + * The following call currently performs serial_out() + * against the FCR register. Because it differs to LCR + * there will be no infinite loop, but if it ever gets + * modified, we might need a new custom version of it + * that avoids infinite recursion. + */ serial8250_clear_and_reinit_fifos(up); /* @@ -92,22 +132,31 @@ static void dw8250_force_idle(struct uart_port *p) * enabled. */ if (up->fcr & UART_FCR_ENABLE_FIFO) { - lsr = p->serial_in(p, UART_LSR); + lsr = serial_port_in(p, UART_LSR); if (!(lsr & UART_LSR_DR)) return; } - (void)p->serial_in(p, UART_RX); + serial_port_in(p, UART_RX); } -static void dw8250_check_lcr(struct uart_port *p, int value) +/* + * This function is being called as part of the uart_port::serial_out() + * routine. Hence, it must not call serial_port_out() or serial_out() + * against the modified registers here, i.e. LCR. + */ +static void dw8250_check_lcr(struct uart_port *p, int offset, int value) { - void __iomem *offset = p->membase + (UART_LCR << p->regshift); + struct dw8250_data *d = to_dw8250_data(p->private_data); + void __iomem *addr = p->membase + (offset << p->regshift); int tries = 1000; + if (offset != UART_LCR || d->uart_16550_compatible) + return; + /* Make sure LCR write wasn't ignored */ while (tries--) { - unsigned int lcr = p->serial_in(p, UART_LCR); + unsigned int lcr = serial_port_in(p, offset); if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR)) return; @@ -116,15 +165,15 @@ static void dw8250_check_lcr(struct uart_port *p, int value) #ifdef CONFIG_64BIT if (p->type == PORT_OCTEON) - __raw_writeq(value & 0xff, offset); + __raw_writeq(value & 0xff, addr); else #endif if (p->iotype == UPIO_MEM32) - writel(value, offset); + writel(value, addr); else if (p->iotype == UPIO_MEM32BE) - iowrite32be(value, offset); + iowrite32be(value, addr); else - writeb(value, offset); + writeb(value, addr); } /* * FIXME: this deadlocks if port->lock is already held @@ -158,12 +207,8 @@ static void dw8250_tx_wait_empty(struct uart_port *p) static void dw8250_serial_out(struct uart_port *p, int offset, int value) { - struct dw8250_data *d = to_dw8250_data(p->private_data); - writeb(value, p->membase + (offset << p->regshift)); - - if (offset == UART_LCR && !d->uart_16550_compatible) - dw8250_check_lcr(p, value); + dw8250_check_lcr(p, offset, value); } static void dw8250_serial_out38x(struct uart_port *p, int offset, int value) @@ -185,35 +230,26 @@ static unsigned int dw8250_serial_in(struct uart_port *p, int offset) #ifdef CONFIG_64BIT static unsigned int dw8250_serial_inq(struct uart_port *p, int offset) { - unsigned int value; - - value = (u8)__raw_readq(p->membase + (offset << p->regshift)); + u8 value = __raw_readq(p->membase + (offset << p->regshift)); return dw8250_modify_msr(p, offset, value); } static void dw8250_serial_outq(struct uart_port *p, int offset, int value) { - struct dw8250_data *d = to_dw8250_data(p->private_data); - value &= 0xff; __raw_writeq(value, p->membase + (offset << p->regshift)); /* Read back to ensure register write ordering. */ __raw_readq(p->membase + (UART_LCR << p->regshift)); - if (offset == UART_LCR && !d->uart_16550_compatible) - dw8250_check_lcr(p, value); + dw8250_check_lcr(p, offset, value); } #endif /* CONFIG_64BIT */ static void dw8250_serial_out32(struct uart_port *p, int offset, int value) { - struct dw8250_data *d = to_dw8250_data(p->private_data); - writel(value, p->membase + (offset << p->regshift)); - - if (offset == UART_LCR && !d->uart_16550_compatible) - dw8250_check_lcr(p, value); + dw8250_check_lcr(p, offset, value); } static unsigned int dw8250_serial_in32(struct uart_port *p, int offset) @@ -225,12 +261,8 @@ static unsigned int dw8250_serial_in32(struct uart_port *p, int offset) static void dw8250_serial_out32be(struct uart_port *p, int offset, int value) { - struct dw8250_data *d = to_dw8250_data(p->private_data); - iowrite32be(value, p->membase + (offset << p->regshift)); - - if (offset == UART_LCR && !d->uart_16550_compatible) - dw8250_check_lcr(p, value); + dw8250_check_lcr(p, offset, value); } static unsigned int dw8250_serial_in32be(struct uart_port *p, int offset) @@ -245,7 +277,7 @@ static int dw8250_handle_irq(struct uart_port *p) { struct uart_8250_port *up = up_to_u8250p(p); struct dw8250_data *d = to_dw8250_data(p->private_data); - unsigned int iir = p->serial_in(p, UART_IIR); + unsigned int iir = serial_port_in(p, UART_IIR); bool rx_timeout = (iir & 0x3f) == UART_IIR_RX_TIMEOUT; unsigned int quirks = d->pdata->quirks; unsigned int status; @@ -266,7 +298,7 @@ static int dw8250_handle_irq(struct uart_port *p) status = serial_lsr_in(up); if (!(status & (UART_LSR_DR | UART_LSR_BI))) - (void) p->serial_in(p, UART_RX); + serial_port_in(p, UART_RX); uart_port_unlock_irqrestore(p, flags); } @@ -288,7 +320,7 @@ static int dw8250_handle_irq(struct uart_port *p) if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { /* Clear the USR */ - (void)p->serial_in(p, d->pdata->usr_reg); + serial_port_in(p, d->pdata->usr_reg); return 1; } @@ -356,9 +388,9 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios, long rate; int ret; + clk_disable_unprepare(d->clk); rate = clk_round_rate(d->clk, newrate); - if (rate > 0 && p->uartclk != rate) { - clk_disable_unprepare(d->clk); + if (rate > 0) { /* * Note that any clock-notifer worker will block in * serial8250_update_uartclk() until we are done. @@ -366,8 +398,8 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios, ret = clk_set_rate(d->clk, newrate); if (!ret) p->uartclk = rate; - clk_prepare_enable(d->clk); } + clk_prepare_enable(d->clk); dw8250_do_set_termios(p, termios, old); } @@ -375,7 +407,7 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios, static void dw8250_set_ldisc(struct uart_port *p, struct ktermios *termios) { struct uart_8250_port *up = up_to_u8250p(p); - unsigned int mcr = p->serial_in(p, UART_MCR); + unsigned int mcr = serial_port_in(p, UART_MCR); if (up->capabilities & UART_CAP_IRDA) { if (termios->c_line == N_IRDA) @@ -383,7 +415,7 @@ static void dw8250_set_ldisc(struct uart_port *p, struct ktermios *termios) else mcr &= ~DW_UART_MCR_SIRE; - p->serial_out(p, UART_MCR, mcr); + serial_port_out(p, UART_MCR, mcr); } serial8250_do_set_ldisc(p, termios); } @@ -406,6 +438,18 @@ static bool dw8250_idma_filter(struct dma_chan *chan, void *param) return param == chan->device->dev; } +static void dw8250_setup_dma_filter(struct uart_port *p, struct dw8250_data *data) +{ + /* Platforms with iDMA 64-bit */ + if (platform_get_resource_byname(to_platform_device(p->dev), IORESOURCE_MEM, "lpss_priv")) { + data->data.dma.rx_param = p->dev->parent; + data->data.dma.tx_param = p->dev->parent; + data->data.dma.fn = dw8250_idma_filter; + } else { + data->data.dma.fn = dw8250_fallback_dma_filter; + } +} + static u32 dw8250_rzn1_get_dmacr_burst(int max_burst) { if (max_burst >= 8) @@ -444,7 +488,11 @@ static void dw8250_prepare_rx_dma(struct uart_8250_port *p) static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) { - unsigned int quirks = data->pdata ? data->pdata->quirks : 0; + unsigned int quirks = data->pdata->quirks; + u32 cpr_value = data->pdata->cpr_value; + + if (quirks & DW_UART_QUIRK_CPR_VALUE) + data->data.cpr_value = cpr_value; #ifdef CONFIG_64BIT if (quirks & DW_UART_QUIRK_OCTEON) { @@ -472,14 +520,6 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) p->serial_in = dw8250_serial_in32; data->uart_16550_compatible = true; } - - /* Platforms with iDMA 64-bit */ - if (platform_get_resource_byname(to_platform_device(p->dev), - IORESOURCE_MEM, "lpss_priv")) { - data->data.dma.rx_param = p->dev->parent; - data->data.dma.tx_param = p->dev->parent; - data->data.dma.fn = dw8250_idma_filter; - } } static void dw8250_reset_control_assert(void *data) @@ -501,7 +541,6 @@ static int dw8250_probe(struct platform_device *pdev) return dev_err_probe(dev, -EINVAL, "no registers defined\n"); spin_lock_init(&p->lock); - p->handle_irq = dw8250_handle_irq; p->pm = dw8250_do_pm; p->type = PORT_8250; p->flags = UPF_FIXED_PORT; @@ -513,13 +552,8 @@ static int dw8250_probe(struct platform_device *pdev) if (!data) return -ENOMEM; - data->data.dma.fn = dw8250_fallback_dma_filter; - data->pdata = device_get_match_data(p->dev); p->private_data = &data->data; - data->uart_16550_compatible = device_property_read_bool(dev, - "snps,uart-16550-compatible"); - p->mapbase = regs->start; p->mapsize = resource_size(regs); @@ -597,7 +631,7 @@ static int dw8250_probe(struct platform_device *pdev) if (IS_ERR(data->pclk)) return PTR_ERR(data->pclk); - data->rst = devm_reset_control_get_optional_exclusive(dev, NULL); + data->rst = devm_reset_control_array_get_optional_exclusive(dev); if (IS_ERR(data->rst)) return PTR_ERR(data->rst); @@ -607,11 +641,19 @@ static int dw8250_probe(struct platform_device *pdev) if (err) return err; - dw8250_quirks(p, data); + data->uart_16550_compatible = device_property_read_bool(dev, "snps,uart-16550-compatible"); + + data->pdata = device_get_match_data(p->dev); + if (data->pdata) + dw8250_quirks(p, data); /* If the Busy Functionality is not implemented, don't handle it */ if (data->uart_16550_compatible) p->handle_irq = NULL; + else if (data->pdata) + p->handle_irq = dw8250_handle_irq; + + dw8250_setup_dma_filter(p, data); if (!data->skip_autocfg) dw8250_setup_port(p); @@ -727,11 +769,11 @@ static const struct dw8250_platform_data dw8250_armada_38x_data = { static const struct dw8250_platform_data dw8250_renesas_rzn1_data = { .usr_reg = DW_UART_USR, - .cpr_val = 0x00012f32, - .quirks = DW_UART_QUIRK_IS_DMA_FC, + .cpr_value = 0x00012f32, + .quirks = DW_UART_QUIRK_CPR_VALUE | DW_UART_QUIRK_IS_DMA_FC, }; -static const struct dw8250_platform_data dw8250_starfive_jh7100_data = { +static const struct dw8250_platform_data dw8250_skip_set_rate_data = { .usr_reg = DW_UART_USR, .quirks = DW_UART_QUIRK_SKIP_SET_RATE, }; @@ -741,7 +783,8 @@ static const struct of_device_id dw8250_of_match[] = { { .compatible = "cavium,octeon-3860-uart", .data = &dw8250_octeon_3860_data }, { .compatible = "marvell,armada-38x-uart", .data = &dw8250_armada_38x_data }, { .compatible = "renesas,rzn1-uart", .data = &dw8250_renesas_rzn1_data }, - { .compatible = "starfive,jh7100-uart", .data = &dw8250_starfive_jh7100_data }, + { .compatible = "sophgo,sg2044-uart", .data = &dw8250_skip_set_rate_data }, + { .compatible = "starfive,jh7100-uart", .data = &dw8250_skip_set_rate_data }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, dw8250_of_match); @@ -777,7 +820,7 @@ static struct platform_driver dw8250_platform_driver = { .acpi_match_table = dw8250_acpi_match, }, .probe = dw8250_probe, - .remove_new = dw8250_remove, + .remove = dw8250_remove, }; module_platform_driver(dw8250_platform_driver); diff --git a/drivers/tty/serial/8250/8250_dwlib.c b/drivers/tty/serial/8250/8250_dwlib.c index 3e33ddf7bc80..b055d89cfb39 100644 --- a/drivers/tty/serial/8250/8250_dwlib.c +++ b/drivers/tty/serial/8250/8250_dwlib.c @@ -89,7 +89,7 @@ static void dw8250_set_divisor(struct uart_port *p, unsigned int baud, unsigned int quot, unsigned int quot_frac) { dw8250_writel_ext(p, DW_UART_DLF, quot_frac); - serial8250_do_set_divisor(p, baud, quot, quot_frac); + serial8250_do_set_divisor(p, baud, quot); } void dw8250_do_set_termios(struct uart_port *p, struct ktermios *termios, @@ -242,7 +242,6 @@ static const struct serial_rs485 dw8250_rs485_supported = { void dw8250_setup_port(struct uart_port *p) { struct dw8250_port_data *pd = p->private_data; - struct dw8250_data *data = to_dw8250_data(pd); struct uart_8250_port *up = up_to_u8250p(p); u32 reg, old_dlf; @@ -278,7 +277,7 @@ void dw8250_setup_port(struct uart_port *p) reg = dw8250_readl_ext(p, DW_UART_CPR); if (!reg) { - reg = data->pdata->cpr_val; + reg = pd->cpr_value; dev_dbg(p->dev, "CPR is not available, using 0x%08x instead\n", reg); } if (!reg) diff --git a/drivers/tty/serial/8250/8250_dwlib.h b/drivers/tty/serial/8250/8250_dwlib.h index f13e91f2cace..7dd2a8e7b780 100644 --- a/drivers/tty/serial/8250/8250_dwlib.h +++ b/drivers/tty/serial/8250/8250_dwlib.h @@ -2,15 +2,10 @@ /* Synopsys DesignWare 8250 library header file. */ #include <linux/io.h> -#include <linux/notifier.h> #include <linux/types.h> -#include <linux/workqueue.h> #include "8250.h" -struct clk; -struct reset_control; - struct dw8250_port_data { /* Port properties */ int line; @@ -19,42 +14,16 @@ struct dw8250_port_data { struct uart_8250_dma dma; /* Hardware configuration */ + u32 cpr_value; u8 dlf_size; /* RS485 variables */ bool hw_rs485_support; }; -struct dw8250_platform_data { - u8 usr_reg; - u32 cpr_val; - unsigned int quirks; -}; - -struct dw8250_data { - struct dw8250_port_data data; - const struct dw8250_platform_data *pdata; - - int msr_mask_on; - int msr_mask_off; - struct clk *clk; - struct clk *pclk; - struct notifier_block clk_notifier; - struct work_struct clk_work; - struct reset_control *rst; - - unsigned int skip_autocfg:1; - unsigned int uart_16550_compatible:1; -}; - void dw8250_do_set_termios(struct uart_port *p, struct ktermios *termios, const struct ktermios *old); void dw8250_setup_port(struct uart_port *p); -static inline struct dw8250_data *to_dw8250_data(struct dw8250_port_data *data) -{ - return container_of(data, struct dw8250_data, data); -} - static inline u32 dw8250_readl_ext(struct uart_port *p, int offset) { if (p->iotype == UPIO_MEM32BE) diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index e3f482fd3de4..dc0371857ecb 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -46,8 +46,10 @@ static unsigned int serial8250_early_in(struct uart_port *port, int offset) return readl(port->membase + offset); case UPIO_MEM32BE: return ioread32be(port->membase + offset); +#ifdef CONFIG_HAS_IOPORT case UPIO_PORT: return inb(port->iobase + offset); +#endif default: return 0; } @@ -70,9 +72,13 @@ static void serial8250_early_out(struct uart_port *port, int offset, int value) case UPIO_MEM32BE: iowrite32be(value, port->membase + offset); break; +#ifdef CONFIG_HAS_IOPORT case UPIO_PORT: outb(value, port->iobase + offset); break; +#endif + default: + break; } } @@ -171,6 +177,17 @@ OF_EARLYCON_DECLARE(ns16550a, "ns16550a", early_serial8250_setup); OF_EARLYCON_DECLARE(uart, "nvidia,tegra20-uart", early_serial8250_setup); OF_EARLYCON_DECLARE(uart, "snps,dw-apb-uart", early_serial8250_setup); +static int __init early_serial8250_rs2_setup(struct earlycon_device *device, + const char *options) +{ + device->port.regshift = 2; + + return early_serial8250_setup(device, options); +} +OF_EARLYCON_DECLARE(uart, "intel,xscale-uart", early_serial8250_rs2_setup); +OF_EARLYCON_DECLARE(uart, "mrvl,mmp-uart", early_serial8250_rs2_setup); +OF_EARLYCON_DECLARE(uart, "mrvl,pxa-uart", early_serial8250_rs2_setup); + #ifdef CONFIG_SERIAL_8250_OMAP static int __init early_omap8250_setup(struct earlycon_device *device, diff --git a/drivers/tty/serial/8250/8250_em.c b/drivers/tty/serial/8250/8250_em.c index a754755100ff..35094f884492 100644 --- a/drivers/tty/serial/8250/8250_em.c +++ b/drivers/tty/serial/8250/8250_em.c @@ -219,7 +219,7 @@ static struct platform_driver serial8250_em_platform_driver = { .of_match_table = serial8250_em_dt_ids, }, .probe = serial8250_em_probe, - .remove_new = serial8250_em_remove, + .remove = serial8250_em_remove, }; module_platform_driver(serial8250_em_platform_driver); diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 0440df7de1ed..04a0cbab02c2 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -6,10 +6,12 @@ * * Copyright (C) 2017 Sudip Mukherjee, All Rights Reserved. */ +#include <linux/bitfield.h> #include <linux/bits.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/dmi.h> +#include <linux/eeprom_93cx6.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/math.h> @@ -46,8 +48,50 @@ #define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021 #define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022 +#define PCI_VENDOR_ID_CONNECT_TECH 0x12c4 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_SP_OPTO 0x0340 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_A 0x0341 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_B 0x0342 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS 0x0350 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_A 0x0351 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_B 0x0352 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS 0x0353 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_A 0x0354 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_B 0x0355 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS_OPTO 0x0360 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_A 0x0361 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_B 0x0362 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP 0x0370 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232 0x0371 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485 0x0372 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP 0x0373 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP 0x0374 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP 0x0375 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232_NS 0x0376 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT 0x0380 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT 0x0381 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO 0x0382 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO 0x0392 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP 0x03A0 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232 0x03A1 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485 0x03A2 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232_NS 0x03A3 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XEG001 0x0602 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_BASE 0x1000 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_2 0x1002 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_4 0x1004 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_8 0x1008 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_12 0x100C +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_16 0x1010 +#define PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG00X 0x110c +#define PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG01X 0x110d +#define PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_16 0x1110 + #define PCI_DEVICE_ID_EXAR_XR17V4358 0x4358 #define PCI_DEVICE_ID_EXAR_XR17V8358 0x8358 +#define PCI_DEVICE_ID_EXAR_XR17V252 0x0252 +#define PCI_DEVICE_ID_EXAR_XR17V254 0x0254 +#define PCI_DEVICE_ID_EXAR_XR17V258 0x0258 #define PCI_SUBDEVICE_ID_USR_2980 0x0128 #define PCI_SUBDEVICE_ID_USR_2981 0x0129 @@ -86,12 +130,23 @@ #define UART_EXAR_DLD 0x02 /* Divisor Fractional */ #define UART_EXAR_DLD_485_POLARITY 0x80 /* RS-485 Enable Signal Polarity */ +/* EEPROM registers */ +#define UART_EXAR_REGB 0x8e +#define UART_EXAR_REGB_EECK BIT(4) +#define UART_EXAR_REGB_EECS BIT(5) +#define UART_EXAR_REGB_EEDI BIT(6) +#define UART_EXAR_REGB_EEDO BIT(7) + +#define UART_EXAR_XR17C15X_PORT_OFFSET 0x200 +#define UART_EXAR_XR17V25X_PORT_OFFSET 0x200 +#define UART_EXAR_XR17V35X_PORT_OFFSET 0x400 + /* * IOT2040 MPIO wiring semantics: * * MPIO Port Function * ---- ---- -------- - * 0 2 Mode bit 0 + * 0 2 Mode bit 0 * 1 2 Mode bit 1 * 2 2 Terminate bus * 3 - <reserved> @@ -121,38 +176,247 @@ #define IOT2040_UARTS_ENABLE 0x03 #define IOT2040_UARTS_GPIO_HI_MODE 0xF8 /* enable & LED as outputs */ +/* CTI EEPROM offsets */ +#define CTI_EE_OFF_XR17C15X_OSC_FREQ 0x04 /* 2 words */ +#define CTI_EE_OFF_XR17C15X_PART_NUM 0x0A /* 4 words */ +#define CTI_EE_OFF_XR17C15X_SERIAL_NUM 0x0E /* 1 word */ + +#define CTI_EE_OFF_XR17V25X_OSC_FREQ 0x08 /* 2 words */ +#define CTI_EE_OFF_XR17V25X_PART_NUM 0x0E /* 4 words */ +#define CTI_EE_OFF_XR17V25X_SERIAL_NUM 0x12 /* 1 word */ + +#define CTI_EE_OFF_XR17V35X_SERIAL_NUM 0x11 /* 2 word */ +#define CTI_EE_OFF_XR17V35X_BRD_FLAGS 0x13 /* 1 word */ +#define CTI_EE_OFF_XR17V35X_PORT_FLAGS 0x14 /* 1 word */ + +#define CTI_EE_MASK_PORT_FLAGS_TYPE GENMASK(7, 0) +#define CTI_EE_MASK_OSC_FREQ GENMASK(31, 0) + +#define CTI_FPGA_RS485_IO_REG 0x2008 +#define CTI_FPGA_CFG_INT_EN_REG 0x48 +#define CTI_FPGA_CFG_INT_EN_EXT_BIT BIT(15) /* External int enable bit */ + +#define CTI_DEFAULT_PCI_OSC_FREQ 29491200 +#define CTI_DEFAULT_PCIE_OSC_FREQ 125000000 +#define CTI_DEFAULT_FPGA_OSC_FREQ 33333333 + +/* + * CTI Serial port line types. These match the values stored in the first + * nibble of the CTI EEPROM port_flags word. + */ +enum cti_port_type { + CTI_PORT_TYPE_NONE = 0, + CTI_PORT_TYPE_RS232, // RS232 ONLY + CTI_PORT_TYPE_RS422_485, // RS422/RS485 ONLY + CTI_PORT_TYPE_RS232_422_485_HW, // RS232/422/485 HW ONLY Switchable + CTI_PORT_TYPE_RS232_422_485_SW, // RS232/422/485 SW ONLY Switchable + CTI_PORT_TYPE_RS232_422_485_4B, // RS232/422/485 HW/SW (4bit ex. BCG004) + CTI_PORT_TYPE_RS232_422_485_2B, // RS232/422/485 HW/SW (2bit ex. BBG008) + CTI_PORT_TYPE_MAX, +}; + +#define CTI_PORT_TYPE_VALID(_port_type) \ + (((_port_type) > CTI_PORT_TYPE_NONE) && \ + ((_port_type) < CTI_PORT_TYPE_MAX)) + +#define CTI_PORT_TYPE_RS485(_port_type) \ + (((_port_type) > CTI_PORT_TYPE_RS232) && \ + ((_port_type) < CTI_PORT_TYPE_MAX)) + struct exar8250; struct exar8250_platform { int (*rs485_config)(struct uart_port *port, struct ktermios *termios, struct serial_rs485 *rs485); const struct serial_rs485 *rs485_supported; - int (*register_gpio)(struct pci_dev *, struct uart_8250_port *); - void (*unregister_gpio)(struct uart_8250_port *); + int (*register_gpio)(struct pci_dev *pcidev, struct uart_8250_port *port); + void (*unregister_gpio)(struct uart_8250_port *port); }; /** * struct exar8250_board - board information * @num_ports: number of serial ports * @reg_shift: describes UART register mapping in PCI memory - * @setup: quirk run at ->probe() stage + * @setup: quirk run at ->probe() stage for each port * @exit: quirk run at ->remove() stage */ struct exar8250_board { unsigned int num_ports; unsigned int reg_shift; - int (*setup)(struct exar8250 *, struct pci_dev *, - struct uart_8250_port *, int); + int (*setup)(struct exar8250 *priv, struct pci_dev *pcidev, + struct uart_8250_port *port, int idx); void (*exit)(struct pci_dev *pcidev); }; struct exar8250 { unsigned int nr; + unsigned int osc_freq; struct exar8250_board *board; + struct eeprom_93cx6 eeprom; void __iomem *virt; int line[]; }; +static inline void exar_write_reg(struct exar8250 *priv, + unsigned int reg, u8 value) +{ + writeb(value, priv->virt + reg); +} + +static inline u8 exar_read_reg(struct exar8250 *priv, unsigned int reg) +{ + return readb(priv->virt + reg); +} + +static void exar_eeprom_93cx6_reg_read(struct eeprom_93cx6 *eeprom) +{ + struct exar8250 *priv = eeprom->data; + u8 regb = exar_read_reg(priv, UART_EXAR_REGB); + + /* EECK and EECS always read 0 from REGB so only set EEDO */ + eeprom->reg_data_out = regb & UART_EXAR_REGB_EEDO; +} + +static void exar_eeprom_93cx6_reg_write(struct eeprom_93cx6 *eeprom) +{ + struct exar8250 *priv = eeprom->data; + u8 regb = 0; + + if (eeprom->reg_data_in) + regb |= UART_EXAR_REGB_EEDI; + if (eeprom->reg_data_clock) + regb |= UART_EXAR_REGB_EECK; + if (eeprom->reg_chip_select) + regb |= UART_EXAR_REGB_EECS; + + exar_write_reg(priv, UART_EXAR_REGB, regb); +} + +static void exar_eeprom_init(struct exar8250 *priv) +{ + priv->eeprom.data = priv; + priv->eeprom.register_read = exar_eeprom_93cx6_reg_read; + priv->eeprom.register_write = exar_eeprom_93cx6_reg_write; + priv->eeprom.width = PCI_EEPROM_WIDTH_93C46; + priv->eeprom.quirks |= PCI_EEPROM_QUIRK_EXTRA_READ_CYCLE; +} + +/** + * exar_mpio_config_output() - Configure an Exar MPIO as an output + * @priv: Device's private structure + * @mpio_num: MPIO number/offset to configure + * + * Configure a single MPIO as an output and disable tristate. It is recommended + * to set the level with exar_mpio_set_high()/exar_mpio_set_low() prior to + * calling this function to ensure default MPIO pin state. + * + * Return: 0 on success, negative error code on failure + */ +static int exar_mpio_config_output(struct exar8250 *priv, + unsigned int mpio_num) +{ + unsigned int mpio_offset; + u8 sel_reg; // MPIO Select register (input/output) + u8 tri_reg; // MPIO Tristate register + u8 value; + + if (mpio_num < 8) { + sel_reg = UART_EXAR_MPIOSEL_7_0; + tri_reg = UART_EXAR_MPIO3T_7_0; + mpio_offset = mpio_num; + } else if (mpio_num >= 8 && mpio_num < 16) { + sel_reg = UART_EXAR_MPIOSEL_15_8; + tri_reg = UART_EXAR_MPIO3T_15_8; + mpio_offset = mpio_num - 8; + } else { + return -EINVAL; + } + + // Disable MPIO pin tri-state + value = exar_read_reg(priv, tri_reg); + value &= ~BIT(mpio_offset); + exar_write_reg(priv, tri_reg, value); + + value = exar_read_reg(priv, sel_reg); + value &= ~BIT(mpio_offset); + exar_write_reg(priv, sel_reg, value); + + return 0; +} + +/** + * _exar_mpio_set() - Set an Exar MPIO output high or low + * @priv: Device's private structure + * @mpio_num: MPIO number/offset to set + * @high: Set MPIO high if true, low if false + * + * Set a single MPIO high or low. exar_mpio_config_output() must also be called + * to configure the pin as an output. + * + * Return: 0 on success, negative error code on failure + */ +static int _exar_mpio_set(struct exar8250 *priv, + unsigned int mpio_num, bool high) +{ + unsigned int mpio_offset; + u8 lvl_reg; + u8 value; + + if (mpio_num < 8) { + lvl_reg = UART_EXAR_MPIOLVL_7_0; + mpio_offset = mpio_num; + } else if (mpio_num >= 8 && mpio_num < 16) { + lvl_reg = UART_EXAR_MPIOLVL_15_8; + mpio_offset = mpio_num - 8; + } else { + return -EINVAL; + } + + value = exar_read_reg(priv, lvl_reg); + if (high) + value |= BIT(mpio_offset); + else + value &= ~BIT(mpio_offset); + exar_write_reg(priv, lvl_reg, value); + + return 0; +} + +static int exar_mpio_set_low(struct exar8250 *priv, unsigned int mpio_num) +{ + return _exar_mpio_set(priv, mpio_num, false); +} + +static int exar_mpio_set_high(struct exar8250 *priv, unsigned int mpio_num) +{ + return _exar_mpio_set(priv, mpio_num, true); +} + +static int generic_rs485_config(struct uart_port *port, struct ktermios *termios, + struct serial_rs485 *rs485) +{ + bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED); + u8 __iomem *p = port->membase; + u8 value; + + value = readb(p + UART_EXAR_FCTR); + if (is_rs485) + value |= UART_FCTR_EXAR_485; + else + value &= ~UART_FCTR_EXAR_485; + + writeb(value, p + UART_EXAR_FCTR); + + if (is_rs485) + writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR); + + return 0; +} + +static const struct serial_rs485 generic_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND, +}; + static void exar_pm(struct uart_port *port, unsigned int state, unsigned int old) { /* @@ -182,7 +446,7 @@ static unsigned int xr17v35x_get_divisor(struct uart_port *p, unsigned int baud, static void xr17v35x_set_divisor(struct uart_port *p, unsigned int baud, unsigned int quot, unsigned int quot_frac) { - serial8250_do_set_divisor(p, baud, quot, quot_frac); + serial8250_do_set_divisor(p, baud, quot); /* Preserve bits not related to baudrate; DLD[7:4]. */ quot_frac |= serial_port_in(p, 0x2) & 0xf0; @@ -198,7 +462,7 @@ static int xr17v35x_startup(struct uart_port *port) serial_port_out(port, UART_XR_EFR, UART_EFR_ECB); /* - * Make sure all interrups are masked until initialization is + * Make sure all interrupts are masked until initialization is * complete and the FIFOs are cleared * * Synchronize UART_IER access against the console. @@ -214,7 +478,7 @@ static void exar_shutdown(struct uart_port *port) { bool tx_complete = false; struct uart_8250_port *up = up_to_u8250p(port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; int i = 0; u16 lsr; @@ -225,7 +489,8 @@ static void exar_shutdown(struct uart_port *port) else tx_complete = false; usleep_range(1000, 1100); - } while (!uart_circ_empty(xmit) && !tx_complete && i++ < 1000); + } while (!kfifo_is_empty(&tport->xmit_fifo) && + !tx_complete && i++ < 1000); serial8250_do_shutdown(port); } @@ -289,41 +554,542 @@ pci_fastcom335_setup(struct exar8250 *priv, struct pci_dev *pcidev, writeb(32, p + UART_EXAR_TXTRG); writeb(32, p + UART_EXAR_RXTRG); + /* Skip the initial (per device) setup */ + if (idx) + return 0; + /* * Setup Multipurpose Input/Output pins. */ + switch (pcidev->device) { + case PCI_DEVICE_ID_COMMTECH_4222PCI335: + case PCI_DEVICE_ID_COMMTECH_4224PCI335: + writeb(0x78, p + UART_EXAR_MPIOLVL_7_0); + writeb(0x00, p + UART_EXAR_MPIOINV_7_0); + writeb(0x00, p + UART_EXAR_MPIOSEL_7_0); + break; + case PCI_DEVICE_ID_COMMTECH_2324PCI335: + case PCI_DEVICE_ID_COMMTECH_2328PCI335: + writeb(0x00, p + UART_EXAR_MPIOLVL_7_0); + writeb(0xc0, p + UART_EXAR_MPIOINV_7_0); + writeb(0xc0, p + UART_EXAR_MPIOSEL_7_0); + break; + default: + break; + } + writeb(0x00, p + UART_EXAR_MPIOINT_7_0); + writeb(0x00, p + UART_EXAR_MPIO3T_7_0); + writeb(0x00, p + UART_EXAR_MPIOOD_7_0); + + return 0; +} + +/** + * cti_tristate_disable() - Disable RS485 transciever tristate + * @priv: Device's private structure + * @port_num: Port number to set tristate off + * + * Most RS485 capable cards have a power on tristate jumper/switch that ensures + * the RS422/RS485 transceiver does not drive a multi-drop RS485 bus when it is + * not the master. When this jumper is installed the user must set the RS485 + * mode to Full or Half duplex to disable tristate prior to using the port. + * + * Some Exar UARTs have an auto-tristate feature while others require setting + * an MPIO to disable the tristate. + * + * Return: 0 on success, negative error code on failure + */ +static int cti_tristate_disable(struct exar8250 *priv, unsigned int port_num) +{ + int ret; + + ret = exar_mpio_set_high(priv, port_num); + if (ret) + return ret; + + return exar_mpio_config_output(priv, port_num); +} + +/** + * cti_plx_int_enable() - Enable UART interrupts to PLX bridge + * @priv: Device's private structure + * + * Some older CTI cards require MPIO_0 to be set low to enable the + * interrupts from the UART to the PLX PCI->PCIe bridge. + * + * Return: 0 on success, negative error code on failure + */ +static int cti_plx_int_enable(struct exar8250 *priv) +{ + int ret; + + ret = exar_mpio_set_low(priv, 0); + if (ret) + return ret; + + return exar_mpio_config_output(priv, 0); +} + +/** + * cti_read_osc_freq() - Read the UART oscillator frequency from EEPROM + * @priv: Device's private structure + * @eeprom_offset: Offset where the oscillator frequency is stored + * + * CTI XR17x15X and XR17V25X cards have the serial boards oscillator frequency + * stored in the EEPROM. FPGA and XR17V35X based cards use the PCI/PCIe clock. + * + * Return: frequency on success, negative error code on failure + */ +static int cti_read_osc_freq(struct exar8250 *priv, u8 eeprom_offset) +{ + __le16 ee_words[2]; + u32 osc_freq; + + eeprom_93cx6_multiread(&priv->eeprom, eeprom_offset, ee_words, ARRAY_SIZE(ee_words)); + + osc_freq = le16_to_cpu(ee_words[0]) | (le16_to_cpu(ee_words[1]) << 16); + if (osc_freq == CTI_EE_MASK_OSC_FREQ) + return -EIO; + + return osc_freq; +} + +/** + * cti_get_port_type_xr17c15x_xr17v25x() - Get port type of xr17c15x/xr17v25x + * @priv: Device's private structure + * @pcidev: Pointer to the PCI device for this port + * @port_num: Port to get type of + * + * CTI xr17c15x and xr17v25x based cards port types are based on PCI IDs. + * + * Return: port type on success, CTI_PORT_TYPE_NONE on failure + */ +static enum cti_port_type cti_get_port_type_xr17c15x_xr17v25x(struct exar8250 *priv, + struct pci_dev *pcidev, + unsigned int port_num) +{ + switch (pcidev->subsystem_device) { + // RS232 only cards + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_232: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_232: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232_NS: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232_NS: + return CTI_PORT_TYPE_RS232; + // 1x RS232, 1x RS422/RS485 + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1: + return (port_num == 0) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; + // 2x RS232, 2x RS422/RS485 + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2: + return (port_num < 2) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; + // 4x RS232, 4x RS422/RS485 + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP: + return (port_num < 4) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; + // RS232/RS422/RS485 HW (jumper) selectable + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_SP_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_B: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_B: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_B: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_B: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP: + return CTI_PORT_TYPE_RS232_422_485_HW; + // RS422/RS485 HW (jumper) selectable + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485: + return CTI_PORT_TYPE_RS422_485; + // 6x RS232, 2x RS422/RS485 + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP: + return (port_num < 6) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; + // 2x RS232, 6x RS422/RS485 + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP: + return (port_num < 2) ? CTI_PORT_TYPE_RS232 : CTI_PORT_TYPE_RS422_485; + default: + dev_err(&pcidev->dev, "unknown/unsupported device\n"); + return CTI_PORT_TYPE_NONE; + } +} + +/** + * cti_get_port_type_fpga() - Get the port type of a CTI FPGA card + * @priv: Device's private structure + * @pcidev: Pointer to the PCI device for this port + * @port_num: Port to get type of + * + * FPGA based cards port types are based on PCI IDs. + * + * Return: port type on success, CTI_PORT_TYPE_NONE on failure + */ +static enum cti_port_type cti_get_port_type_fpga(struct exar8250 *priv, + struct pci_dev *pcidev, + unsigned int port_num) +{ + switch (pcidev->device) { + case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG00X: + case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG01X: + case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_16: + return CTI_PORT_TYPE_RS232_422_485_HW; + default: + dev_err(&pcidev->dev, "unknown/unsupported device\n"); + return CTI_PORT_TYPE_NONE; + } +} + +/** + * cti_get_port_type_xr17v35x() - Read port type from the EEPROM + * @priv: Device's private structure + * @pcidev: Pointer to the PCI device for this port + * @port_num: port offset + * + * CTI XR17V35X based cards have the port types stored in the EEPROM. + * This function reads the port type for a single port. + * + * Return: port type on success, CTI_PORT_TYPE_NONE on failure + */ +static enum cti_port_type cti_get_port_type_xr17v35x(struct exar8250 *priv, + struct pci_dev *pcidev, + unsigned int port_num) +{ + enum cti_port_type port_type; + u16 port_flags; + u8 offset; + + offset = CTI_EE_OFF_XR17V35X_PORT_FLAGS + port_num; + eeprom_93cx6_read(&priv->eeprom, offset, &port_flags); + + port_type = FIELD_GET(CTI_EE_MASK_PORT_FLAGS_TYPE, port_flags); + if (CTI_PORT_TYPE_VALID(port_type)) + return port_type; + + /* + * If the port type is missing the card assume it is a + * RS232/RS422/RS485 card to be safe. + * + * There is one known board (BEG013) that only has 3 of 4 port types + * written to the EEPROM so this acts as a work around. + */ + dev_warn(&pcidev->dev, "failed to get port %d type from EEPROM\n", port_num); + + return CTI_PORT_TYPE_RS232_422_485_HW; +} + +static int cti_rs485_config_mpio_tristate(struct uart_port *port, + struct ktermios *termios, + struct serial_rs485 *rs485) +{ + struct exar8250 *priv = (struct exar8250 *)port->private_data; + int ret; + + ret = generic_rs485_config(port, termios, rs485); + if (ret) + return ret; + + // Disable power-on RS485 tri-state via MPIO + return cti_tristate_disable(priv, port->port_id); +} + +static void cti_board_init_osc_freq(struct exar8250 *priv, struct pci_dev *pcidev, u8 eeprom_offset) +{ + int osc_freq; + + osc_freq = cti_read_osc_freq(priv, eeprom_offset); + if (osc_freq <= 0) { + dev_warn(&pcidev->dev, "failed to read OSC freq from EEPROM, using default\n"); + osc_freq = CTI_DEFAULT_PCI_OSC_FREQ; + } + + priv->osc_freq = osc_freq; +} + +static int cti_port_setup_common(struct exar8250 *priv, + struct pci_dev *pcidev, + int idx, unsigned int offset, + struct uart_8250_port *port) +{ + int ret; + + port->port.port_id = idx; + port->port.uartclk = priv->osc_freq; + + ret = serial8250_pci_setup_port(pcidev, port, 0, offset, 0); + if (ret) + return ret; + + port->port.private_data = (void *)priv; + port->port.pm = exar_pm; + port->port.shutdown = exar_shutdown; + + return 0; +} + +static int cti_board_init_fpga(struct exar8250 *priv, struct pci_dev *pcidev) +{ + int ret; + u16 cfg_val; + + // FPGA OSC is fixed to the 33MHz PCI clock + priv->osc_freq = CTI_DEFAULT_FPGA_OSC_FREQ; + + // Enable external interrupts in special cfg space register + ret = pci_read_config_word(pcidev, CTI_FPGA_CFG_INT_EN_REG, &cfg_val); + if (ret) + return pcibios_err_to_errno(ret); + + cfg_val |= CTI_FPGA_CFG_INT_EN_EXT_BIT; + ret = pci_write_config_word(pcidev, CTI_FPGA_CFG_INT_EN_REG, cfg_val); + if (ret) + return pcibios_err_to_errno(ret); + + // RS485 gate needs to be enabled; otherwise RTS/CTS will not work + exar_write_reg(priv, CTI_FPGA_RS485_IO_REG, 0x01); + + return 0; +} + +static int cti_port_setup_fpga(struct exar8250 *priv, + struct pci_dev *pcidev, + struct uart_8250_port *port, + int idx) +{ + enum cti_port_type port_type; + unsigned int offset; + int ret; + if (idx == 0) { - switch (pcidev->device) { - case PCI_DEVICE_ID_COMMTECH_4222PCI335: - case PCI_DEVICE_ID_COMMTECH_4224PCI335: - writeb(0x78, p + UART_EXAR_MPIOLVL_7_0); - writeb(0x00, p + UART_EXAR_MPIOINV_7_0); - writeb(0x00, p + UART_EXAR_MPIOSEL_7_0); + ret = cti_board_init_fpga(priv, pcidev); + if (ret) + return ret; + } + + port_type = cti_get_port_type_fpga(priv, pcidev, idx); + + // FPGA shares port offsets with XR17C15X + offset = idx * UART_EXAR_XR17C15X_PORT_OFFSET; + port->port.type = PORT_XR17D15X; + + port->port.get_divisor = xr17v35x_get_divisor; + port->port.set_divisor = xr17v35x_set_divisor; + port->port.startup = xr17v35x_startup; + + if (CTI_PORT_TYPE_RS485(port_type)) { + port->port.rs485_config = generic_rs485_config; + port->port.rs485_supported = generic_rs485_supported; + } + + return cti_port_setup_common(priv, pcidev, idx, offset, port); +} + +static void cti_board_init_xr17v35x(struct exar8250 *priv, struct pci_dev *pcidev) +{ + // XR17V35X uses the PCIe clock rather than an oscillator + priv->osc_freq = CTI_DEFAULT_PCIE_OSC_FREQ; +} + +static int cti_port_setup_xr17v35x(struct exar8250 *priv, + struct pci_dev *pcidev, + struct uart_8250_port *port, + int idx) +{ + enum cti_port_type port_type; + unsigned int offset; + int ret; + + if (idx == 0) + cti_board_init_xr17v35x(priv, pcidev); + + port_type = cti_get_port_type_xr17v35x(priv, pcidev, idx); + + offset = idx * UART_EXAR_XR17V35X_PORT_OFFSET; + port->port.type = PORT_XR17V35X; + + port->port.get_divisor = xr17v35x_get_divisor; + port->port.set_divisor = xr17v35x_set_divisor; + port->port.startup = xr17v35x_startup; + + switch (port_type) { + case CTI_PORT_TYPE_RS422_485: + case CTI_PORT_TYPE_RS232_422_485_HW: + port->port.rs485_config = cti_rs485_config_mpio_tristate; + port->port.rs485_supported = generic_rs485_supported; + break; + case CTI_PORT_TYPE_RS232_422_485_SW: + case CTI_PORT_TYPE_RS232_422_485_4B: + case CTI_PORT_TYPE_RS232_422_485_2B: + port->port.rs485_config = generic_rs485_config; + port->port.rs485_supported = generic_rs485_supported; + break; + default: + break; + } + + ret = cti_port_setup_common(priv, pcidev, idx, offset, port); + if (ret) + return ret; + + exar_write_reg(priv, (offset + UART_EXAR_8XMODE), 0x00); + exar_write_reg(priv, (offset + UART_EXAR_FCTR), UART_FCTR_EXAR_TRGD); + exar_write_reg(priv, (offset + UART_EXAR_TXTRG), 128); + exar_write_reg(priv, (offset + UART_EXAR_RXTRG), 128); + + return 0; +} + +static void cti_board_init_xr17v25x(struct exar8250 *priv, struct pci_dev *pcidev) +{ + cti_board_init_osc_freq(priv, pcidev, CTI_EE_OFF_XR17V25X_OSC_FREQ); + + /* enable interrupts on cards that need the "PLX fix" */ + switch (pcidev->subsystem_device) { + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_B: + cti_plx_int_enable(priv); + break; + default: + break; + } +} + +static int cti_port_setup_xr17v25x(struct exar8250 *priv, + struct pci_dev *pcidev, + struct uart_8250_port *port, + int idx) +{ + enum cti_port_type port_type; + unsigned int offset; + int ret; + + if (idx == 0) + cti_board_init_xr17v25x(priv, pcidev); + + port_type = cti_get_port_type_xr17c15x_xr17v25x(priv, pcidev, idx); + + offset = idx * UART_EXAR_XR17V25X_PORT_OFFSET; + port->port.type = PORT_XR17D15X; + + // XR17V25X supports fractional baudrates + port->port.get_divisor = xr17v35x_get_divisor; + port->port.set_divisor = xr17v35x_set_divisor; + port->port.startup = xr17v35x_startup; + + if (CTI_PORT_TYPE_RS485(port_type)) { + switch (pcidev->subsystem_device) { + // These cards support power on 485 tri-state via MPIO + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485: + port->port.rs485_config = cti_rs485_config_mpio_tristate; break; - case PCI_DEVICE_ID_COMMTECH_2324PCI335: - case PCI_DEVICE_ID_COMMTECH_2328PCI335: - writeb(0x00, p + UART_EXAR_MPIOLVL_7_0); - writeb(0xc0, p + UART_EXAR_MPIOINV_7_0); - writeb(0xc0, p + UART_EXAR_MPIOSEL_7_0); + // Otherwise auto or no power on 485 tri-state support + default: + port->port.rs485_config = generic_rs485_config; break; } - writeb(0x00, p + UART_EXAR_MPIOINT_7_0); - writeb(0x00, p + UART_EXAR_MPIO3T_7_0); - writeb(0x00, p + UART_EXAR_MPIOOD_7_0); + + port->port.rs485_supported = generic_rs485_supported; } + ret = cti_port_setup_common(priv, pcidev, idx, offset, port); + if (ret) + return ret; + + exar_write_reg(priv, (offset + UART_EXAR_8XMODE), 0x00); + exar_write_reg(priv, (offset + UART_EXAR_FCTR), UART_FCTR_EXAR_TRGD); + exar_write_reg(priv, (offset + UART_EXAR_TXTRG), 32); + exar_write_reg(priv, (offset + UART_EXAR_RXTRG), 32); + return 0; } -static int -pci_connect_tech_setup(struct exar8250 *priv, struct pci_dev *pcidev, - struct uart_8250_port *port, int idx) +static void cti_board_init_xr17c15x(struct exar8250 *priv, struct pci_dev *pcidev) { - unsigned int offset = idx * 0x200; - unsigned int baud = 1843200; + cti_board_init_osc_freq(priv, pcidev, CTI_EE_OFF_XR17C15X_OSC_FREQ); + + /* enable interrupts on cards that need the "PLX fix" */ + switch (pcidev->subsystem_device) { + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_B: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_A: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_B: + cti_plx_int_enable(priv); + break; + default: + break; + } +} - port->port.uartclk = baud * 16; - return default_setup(priv, pcidev, idx, offset, port); +static int cti_port_setup_xr17c15x(struct exar8250 *priv, + struct pci_dev *pcidev, + struct uart_8250_port *port, + int idx) +{ + enum cti_port_type port_type; + unsigned int offset; + + if (idx == 0) + cti_board_init_xr17c15x(priv, pcidev); + + port_type = cti_get_port_type_xr17c15x_xr17v25x(priv, pcidev, idx); + + offset = idx * UART_EXAR_XR17C15X_PORT_OFFSET; + port->port.type = PORT_XR17D15X; + + if (CTI_PORT_TYPE_RS485(port_type)) { + switch (pcidev->subsystem_device) { + // These cards support power on 485 tri-state via MPIO + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP: + case PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485: + port->port.rs485_config = cti_rs485_config_mpio_tristate; + break; + // Otherwise auto or no power on 485 tri-state support + default: + port->port.rs485_config = generic_rs485_config; + break; + } + + port->port.rs485_supported = generic_rs485_supported; + } + + return cti_port_setup_common(priv, pcidev, idx, offset, port); } static int @@ -344,11 +1110,10 @@ static void setup_gpio(struct pci_dev *pcidev, u8 __iomem *p) * devices will export them as GPIOs, so we pre-configure them safely * as inputs. */ - u8 dir = 0x00; if ((pcidev->vendor == PCI_VENDOR_ID_EXAR) && - (pcidev->subsystem_vendor != PCI_VENDOR_ID_SEALEVEL)) { + (pcidev->subsystem_vendor != PCI_VENDOR_ID_SEALEVEL)) { // Configure GPIO as inputs for Commtech adapters dir = 0xff; } else { @@ -425,27 +1190,6 @@ static void xr17v35x_unregister_gpio(struct uart_8250_port *port) port->port.private_data = NULL; } -static int generic_rs485_config(struct uart_port *port, struct ktermios *termios, - struct serial_rs485 *rs485) -{ - bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED); - u8 __iomem *p = port->membase; - u8 value; - - value = readb(p + UART_EXAR_FCTR); - if (is_rs485) - value |= UART_FCTR_EXAR_485; - else - value &= ~UART_FCTR_EXAR_485; - - writeb(value, p + UART_EXAR_FCTR); - - if (is_rs485) - writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR); - - return 0; -} - static int sealevel_rs485_config(struct uart_port *port, struct ktermios *termios, struct serial_rs485 *rs485) { @@ -459,35 +1203,32 @@ static int sealevel_rs485_config(struct uart_port *port, struct ktermios *termio if (ret) return ret; - if (rs485->flags & SER_RS485_ENABLED) { - old_lcr = readb(p + UART_LCR); + if (!(rs485->flags & SER_RS485_ENABLED)) + return 0; - /* Set EFR[4]=1 to enable enhanced feature registers */ - efr = readb(p + UART_XR_EFR); - efr |= UART_EFR_ECB; - writeb(efr, p + UART_XR_EFR); + old_lcr = readb(p + UART_LCR); - /* Set MCR to use DTR as Auto-RS485 Enable signal */ - writeb(UART_MCR_OUT1, p + UART_MCR); + /* Set EFR[4]=1 to enable enhanced feature registers */ + efr = readb(p + UART_XR_EFR); + efr |= UART_EFR_ECB; + writeb(efr, p + UART_XR_EFR); - /* Set LCR[7]=1 to enable access to DLD register */ - writeb(old_lcr | UART_LCR_DLAB, p + UART_LCR); + /* Set MCR to use DTR as Auto-RS485 Enable signal */ + writeb(UART_MCR_OUT1, p + UART_MCR); - /* Set DLD[7]=1 for inverted RS485 Enable logic */ - dld = readb(p + UART_EXAR_DLD); - dld |= UART_EXAR_DLD_485_POLARITY; - writeb(dld, p + UART_EXAR_DLD); + /* Set LCR[7]=1 to enable access to DLD register */ + writeb(old_lcr | UART_LCR_DLAB, p + UART_LCR); - writeb(old_lcr, p + UART_LCR); - } + /* Set DLD[7]=1 for inverted RS485 Enable logic */ + dld = readb(p + UART_EXAR_DLD); + dld |= UART_EXAR_DLD_485_POLARITY; + writeb(dld, p + UART_EXAR_DLD); + + writeb(old_lcr, p + UART_LCR); return 0; } -static const struct serial_rs485 generic_rs485_supported = { - .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND, -}; - static const struct exar8250_platform exar8250_default_platform = { .register_gpio = xr17v35x_register_gpio, .unregister_gpio = xr17v35x_unregister_gpio, @@ -672,6 +1413,35 @@ static irqreturn_t exar_misc_handler(int irq, void *data) return IRQ_HANDLED; } +static unsigned int exar_get_nr_ports(struct exar8250_board *board, struct pci_dev *pcidev) +{ + if (pcidev->vendor == PCI_VENDOR_ID_ACCESSIO) + return BIT(((pcidev->device & 0x38) >> 3) - 1); + + // Check if board struct overrides number of ports + if (board->num_ports > 0) + return board->num_ports; + + // Exar encodes # ports in last nibble of PCI Device ID ex. 0358 + if (pcidev->vendor == PCI_VENDOR_ID_EXAR) + return pcidev->device & 0x0f; + + // Handle CTI FPGA cards + if (pcidev->vendor == PCI_VENDOR_ID_CONNECT_TECH) { + switch (pcidev->device) { + case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG00X: + case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG01X: + return 12; + case PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_16: + return 16; + default: + return 0; + } + } + + return 0; +} + static int exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) { @@ -691,12 +1461,9 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) maxnr = pci_resource_len(pcidev, bar) >> (board->reg_shift + 3); - if (pcidev->vendor == PCI_VENDOR_ID_ACCESSIO) - nr_ports = BIT(((pcidev->device & 0x38) >> 3) - 1); - else if (board->num_ports) - nr_ports = board->num_ports; - else - nr_ports = pcidev->device & 0x0f; + nr_ports = exar_get_nr_ports(board, pcidev); + if (nr_ports == 0) + return dev_err_probe(&pcidev->dev, -ENODEV, "failed to get number of ports\n"); priv = devm_kzalloc(&pcidev->dev, struct_size(priv, line, nr_ports), GFP_KERNEL); if (!priv) @@ -726,10 +1493,12 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) if (rc) return rc; + exar_eeprom_init(priv); + for (i = 0; i < nr_ports && i < maxnr; i++) { rc = board->setup(priv, pcidev, &uart, i); if (rc) { - dev_err(&pcidev->dev, "Failed to setup port %u\n", i); + dev_err_probe(&pcidev->dev, rc, "Failed to setup port %u\n", i); break; } @@ -738,10 +1507,9 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) priv->line[i] = serial8250_register_8250_port(&uart); if (priv->line[i] < 0) { - dev_err(&pcidev->dev, - "Couldn't register serial port %lx, irq %d, type %d, error %d\n", - uart.port.iobase, uart.port.irq, - uart.port.iotype, priv->line[i]); + dev_err_probe(&pcidev->dev, priv->line[i], + "Couldn't register serial port %lx, type %d, irq %d\n", + uart.port.iobase, uart.port.iotype, uart.port.irq); break; } } @@ -806,8 +1574,20 @@ static const struct exar8250_board pbn_fastcom335_8 = { .setup = pci_fastcom335_setup, }; -static const struct exar8250_board pbn_connect = { - .setup = pci_connect_tech_setup, +static const struct exar8250_board pbn_cti_xr17c15x = { + .setup = cti_port_setup_xr17c15x, +}; + +static const struct exar8250_board pbn_cti_xr17v25x = { + .setup = cti_port_setup_xr17v25x, +}; + +static const struct exar8250_board pbn_cti_xr17v35x = { + .setup = cti_port_setup_xr17v35x, +}; + +static const struct exar8250_board pbn_cti_fpga = { + .setup = cti_port_setup_fpga, }; static const struct exar8250_board pbn_exar_ibm_saturn = { @@ -854,13 +1634,13 @@ static const struct exar8250_board pbn_exar_XR17V8358 = { .exit = pci_xr17v35x_exit, }; -#define CONNECT_DEVICE(devid, sdevid, bd) { \ - PCI_DEVICE_SUB( \ - PCI_VENDOR_ID_EXAR, \ - PCI_DEVICE_ID_EXAR_##devid, \ - PCI_SUBVENDOR_ID_CONNECT_TECH, \ - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_##sdevid), 0, 0, \ - (kernel_ulong_t)&bd \ +#define CTI_EXAR_DEVICE(devid, bd) { \ + PCI_DEVICE_SUB( \ + PCI_VENDOR_ID_EXAR, \ + PCI_DEVICE_ID_EXAR_##devid, \ + PCI_SUBVENDOR_ID_CONNECT_TECH, \ + PCI_ANY_ID), 0, 0, \ + (kernel_ulong_t)&bd \ } #define EXAR_DEVICE(vend, devid, bd) { PCI_DEVICE_DATA(vend, devid, &bd) } @@ -869,7 +1649,7 @@ static const struct exar8250_board pbn_exar_XR17V8358 = { PCI_DEVICE_SUB( \ PCI_VENDOR_ID_EXAR, \ PCI_DEVICE_ID_EXAR_##devid, \ - PCI_VENDOR_ID_IBM, \ + PCI_SUBVENDOR_ID_IBM, \ PCI_SUBDEVICE_ID_IBM_##sdevid), 0, 0, \ (kernel_ulong_t)&bd \ } @@ -892,18 +1672,23 @@ static const struct pci_device_id exar_pci_tbl[] = { EXAR_DEVICE(ACCESSIO, COM_4SM, pbn_exar_XR17C15x), EXAR_DEVICE(ACCESSIO, COM_8SM, pbn_exar_XR17C15x), - CONNECT_DEVICE(XR17C152, UART_2_232, pbn_connect), - CONNECT_DEVICE(XR17C154, UART_4_232, pbn_connect), - CONNECT_DEVICE(XR17C158, UART_8_232, pbn_connect), - CONNECT_DEVICE(XR17C152, UART_1_1, pbn_connect), - CONNECT_DEVICE(XR17C154, UART_2_2, pbn_connect), - CONNECT_DEVICE(XR17C158, UART_4_4, pbn_connect), - CONNECT_DEVICE(XR17C152, UART_2, pbn_connect), - CONNECT_DEVICE(XR17C154, UART_4, pbn_connect), - CONNECT_DEVICE(XR17C158, UART_8, pbn_connect), - CONNECT_DEVICE(XR17C152, UART_2_485, pbn_connect), - CONNECT_DEVICE(XR17C154, UART_4_485, pbn_connect), - CONNECT_DEVICE(XR17C158, UART_8_485, pbn_connect), + /* Connect Tech cards with Exar vendor/device PCI IDs */ + CTI_EXAR_DEVICE(XR17C152, pbn_cti_xr17c15x), + CTI_EXAR_DEVICE(XR17C154, pbn_cti_xr17c15x), + CTI_EXAR_DEVICE(XR17C158, pbn_cti_xr17c15x), + + CTI_EXAR_DEVICE(XR17V252, pbn_cti_xr17v25x), + CTI_EXAR_DEVICE(XR17V254, pbn_cti_xr17v25x), + CTI_EXAR_DEVICE(XR17V258, pbn_cti_xr17v25x), + + CTI_EXAR_DEVICE(XR17V352, pbn_cti_xr17v35x), + CTI_EXAR_DEVICE(XR17V354, pbn_cti_xr17v35x), + CTI_EXAR_DEVICE(XR17V358, pbn_cti_xr17v35x), + + /* Connect Tech cards with Connect Tech vendor/device PCI IDs (FPGA based) */ + EXAR_DEVICE(CONNECT_TECH, PCI_XR79X_12_XIG00X, pbn_cti_fpga), + EXAR_DEVICE(CONNECT_TECH, PCI_XR79X_12_XIG01X, pbn_cti_fpga), + EXAR_DEVICE(CONNECT_TECH, PCI_XR79X_16, pbn_cti_fpga), IBM_DEVICE(XR17C152, SATURN_SERIAL_ONE_PORT, pbn_exar_ibm_saturn), @@ -945,7 +1730,7 @@ static struct pci_driver exar_pci_driver = { }; module_pci_driver(exar_pci_driver); -MODULE_IMPORT_NS(SERIAL_8250_PCI); +MODULE_IMPORT_NS("SERIAL_8250_PCI"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Exar Serial Driver"); MODULE_AUTHOR("Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>"); diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index e2aa2a1a02dd..b4461a89b8d0 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -21,6 +21,7 @@ #define CHIP_ID_F81866 0x1010 #define CHIP_ID_F81966 0x0215 #define CHIP_ID_F81216AD 0x1602 +#define CHIP_ID_F81216E 0x1617 #define CHIP_ID_F81216H 0x0501 #define CHIP_ID_F81216 0x0802 #define VENDOR_ID1 0x23 @@ -125,7 +126,7 @@ static int fintek_8250_enter_key(u16 base_port, u8 key) if (!request_muxed_region(base_port, 2, "8250_fintek")) return -EBUSY; - /* Force to deactive all SuperIO in this base_port */ + /* Force to deactivate all SuperIO in this base_port */ outb(EXIT_KEY, base_port + ADDR_PORT); outb(key, base_port + ADDR_PORT); @@ -158,6 +159,7 @@ static int fintek_8250_check_id(struct fintek_8250 *pdata) case CHIP_ID_F81866: case CHIP_ID_F81966: case CHIP_ID_F81216AD: + case CHIP_ID_F81216E: case CHIP_ID_F81216H: case CHIP_ID_F81216: break; @@ -181,6 +183,7 @@ static int fintek_8250_get_ldn_range(struct fintek_8250 *pdata, int *min, return 0; case CHIP_ID_F81216AD: + case CHIP_ID_F81216E: case CHIP_ID_F81216H: case CHIP_ID_F81216: *min = F81216_LDN_LOW; @@ -250,6 +253,7 @@ static void fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool is_level) break; case CHIP_ID_F81216AD: + case CHIP_ID_F81216E: case CHIP_ID_F81216H: case CHIP_ID_F81216: sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_SHARE, @@ -263,7 +267,8 @@ static void fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool is_level) static void fintek_8250_set_max_fifo(struct fintek_8250 *pdata) { switch (pdata->pid) { - case CHIP_ID_F81216H: /* 128Bytes FIFO */ + case CHIP_ID_F81216E: /* 128Bytes FIFO */ + case CHIP_ID_F81216H: case CHIP_ID_F81966: case CHIP_ID_F81866: sio_write_mask_reg(pdata, FIFO_CTRL, @@ -297,6 +302,7 @@ static void fintek_8250_set_termios(struct uart_port *port, goto exit; switch (pdata->pid) { + case CHIP_ID_F81216E: case CHIP_ID_F81216H: reg = RS485; break; @@ -346,6 +352,7 @@ static void fintek_8250_set_termios_handler(struct uart_8250_port *uart) struct fintek_8250 *pdata = uart->port.private_data; switch (pdata->pid) { + case CHIP_ID_F81216E: case CHIP_ID_F81216H: case CHIP_ID_F81966: case CHIP_ID_F81866: @@ -438,6 +445,11 @@ static void fintek_8250_set_rs485_handler(struct uart_8250_port *uart) uart->port.rs485_supported = fintek_8250_rs485_supported; break; + case CHIP_ID_F81216E: /* F81216E does not support RS485 delays */ + uart->port.rs485_config = fintek_8250_rs485_config; + uart->port.rs485_supported = fintek_8250_rs485_supported; + break; + default: /* No RS485 Auto direction functional */ break; } diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c index b4ed442082a8..649ae5c8304d 100644 --- a/drivers/tty/serial/8250/8250_fsl.c +++ b/drivers/tty/serial/8250/8250_fsl.c @@ -32,7 +32,7 @@ int fsl8250_handle_irq(struct uart_port *port) uart_port_lock_irqsave(&up->port, &flags); - iir = port->serial_in(port, UART_IIR); + iir = serial_port_in(port, UART_IIR); if (iir & UART_IIR_NO_INT) { uart_port_unlock_irqrestore(&up->port, flags); return 0; @@ -54,12 +54,12 @@ int fsl8250_handle_irq(struct uart_port *port) if (unlikely((iir & UART_IIR_ID) == UART_IIR_RLSI && (up->lsr_saved_flags & UART_LSR_BI))) { up->lsr_saved_flags &= ~UART_LSR_BI; - port->serial_in(port, UART_RX); + serial_port_in(port, UART_RX); uart_port_unlock_irqrestore(&up->port, flags); return 1; } - lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR); + lsr = orig_lsr = serial_port_in(port, UART_LSR); /* Process incoming characters first */ if ((lsr & (UART_LSR_DR | UART_LSR_BI)) && @@ -71,7 +71,7 @@ int fsl8250_handle_irq(struct uart_port *port) if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) { unsigned long delay; - up->ier = port->serial_in(port, UART_IER); + up->ier = serial_port_in(port, UART_IER); if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) { port->ops->stop_rx(port); } else { @@ -179,7 +179,7 @@ static struct platform_driver fsl8250_platform_driver = { .acpi_match_table = ACPI_PTR(fsl_8250_acpi_id), }, .probe = fsl8250_acpi_probe, - .remove_new = fsl8250_acpi_remove, + .remove = fsl8250_acpi_remove, }; module_platform_driver(fsl8250_platform_driver); diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c index a2783e38a2e3..a73dd3773640 100644 --- a/drivers/tty/serial/8250/8250_ingenic.c +++ b/drivers/tty/serial/8250/8250_ingenic.c @@ -361,7 +361,7 @@ static struct platform_driver ingenic_uart_platform_driver = { .of_match_table = of_match, }, .probe = ingenic_uart_probe, - .remove_new = ingenic_uart_remove, + .remove = ingenic_uart_remove, }; module_platform_driver(ingenic_uart_platform_driver); diff --git a/drivers/tty/serial/8250/8250_ioc3.c b/drivers/tty/serial/8250/8250_ioc3.c index 50c77c3dacf2..499e80aa4cf9 100644 --- a/drivers/tty/serial/8250/8250_ioc3.c +++ b/drivers/tty/serial/8250/8250_ioc3.c @@ -84,7 +84,7 @@ static void serial8250_ioc3_remove(struct platform_device *pdev) static struct platform_driver serial8250_ioc3_driver = { .probe = serial8250_ioc3_probe, - .remove_new = serial8250_ioc3_remove, + .remove = serial8250_ioc3_remove, .driver = { .name = "ioc3-serial8250", } diff --git a/drivers/tty/serial/8250/8250_lpc18xx.c b/drivers/tty/serial/8250/8250_lpc18xx.c index 7984ee05af1d..d52445948da0 100644 --- a/drivers/tty/serial/8250/8250_lpc18xx.c +++ b/drivers/tty/serial/8250/8250_lpc18xx.c @@ -151,7 +151,7 @@ static int lpc18xx_serial_probe(struct platform_device *pdev) ret = uart_read_port_properties(&uart.port); if (ret) - return ret; + goto dis_uart_clk; uart.port.iotype = UPIO_MEM32; uart.port.regshift = 2; @@ -195,7 +195,7 @@ MODULE_DEVICE_TABLE(of, lpc18xx_serial_match); static struct platform_driver lpc18xx_serial_driver = { .probe = lpc18xx_serial_probe, - .remove_new = lpc18xx_serial_remove, + .remove = lpc18xx_serial_remove, .driver = { .name = "lpc18xx-uart", .of_match_table = lpc18xx_serial_match, diff --git a/drivers/tty/serial/8250/8250_men_mcb.c b/drivers/tty/serial/8250/8250_men_mcb.c index dc9e093b1cb3..a78ef35c8187 100644 --- a/drivers/tty/serial/8250/8250_men_mcb.c +++ b/drivers/tty/serial/8250/8250_men_mcb.c @@ -271,4 +271,4 @@ MODULE_AUTHOR("Michael Moese <michael.moese@men.de"); MODULE_ALIAS("mcb:16z125"); MODULE_ALIAS("mcb:16z025"); MODULE_ALIAS("mcb:16z057"); -MODULE_IMPORT_NS(MCB); +MODULE_IMPORT_NS("MCB"); diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index 9ff6bbe9c086..b44de2ed7413 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -199,7 +199,7 @@ static int mtk8250_startup(struct uart_port *port) if (up->dma) { data->rx_status = DMA_RX_START; - uart_circ_clear(&port->state->xmit); + kfifo_reset(&port->state->port.xmit_fifo); } #endif memset(&port->icount, 0, sizeof(port->icount)); @@ -209,15 +209,19 @@ static int mtk8250_startup(struct uart_port *port) static void mtk8250_shutdown(struct uart_port *port) { -#ifdef CONFIG_SERIAL_8250_DMA struct uart_8250_port *up = up_to_u8250p(port); struct mtk8250_data *data = port->private_data; + int irq = data->rx_wakeup_irq; +#ifdef CONFIG_SERIAL_8250_DMA if (up->dma) data->rx_status = DMA_RX_SHUTDOWN; #endif - return serial8250_do_shutdown(port); + serial8250_do_shutdown(port); + + if (irq >= 0) + serial8250_do_set_mctrl(&up->port, TIOCM_RTS); } static void mtk8250_disable_intrs(struct uart_8250_port *up, int mask) @@ -342,8 +346,8 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, /* * Mediatek UARTs use an extra highspeed register (MTK_UART_HIGHS) * - * We need to recalcualte the quot register, as the claculation depends - * on the vaule in the highspeed register. + * We need to recalculate the quot register, as the calculation depends + * on the value in the highspeed register. * * Some baudrates are not supported by the chip, so we use the next * lower rate supported and update termios c_flag. @@ -650,7 +654,7 @@ static struct platform_driver mtk8250_platform_driver = { .of_match_table = mtk8250_of_match, }, .probe = mtk8250_probe, - .remove_new = mtk8250_remove, + .remove = mtk8250_remove, }; module_platform_driver(mtk8250_platform_driver); diff --git a/drivers/tty/serial/8250/8250_ni.c b/drivers/tty/serial/8250/8250_ni.c new file mode 100644 index 000000000000..b0e44fb00b3a --- /dev/null +++ b/drivers/tty/serial/8250/8250_ni.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NI 16550 UART Driver + * + * The National Instruments (NI) 16550 is a UART that is compatible with the + * TL16C550C and OX16C950B register interfaces, but has additional functions + * for RS-485 transceiver control. This driver implements support for the + * additional functionality on top of the standard serial8250 core. + * + * Copyright 2012-2023 National Instruments Corporation + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/init.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/serial_core.h> +#include <linux/types.h> + +#include "8250.h" + +/* Extra bits in UART_ACR */ +#define NI16550_ACR_AUTO_DTR_EN BIT(4) + +/* TFS - TX FIFO Size */ +#define NI16550_TFS_OFFSET 0x0C +/* RFS - RX FIFO Size */ +#define NI16550_RFS_OFFSET 0x0D + +/* PMR - Port Mode Register */ +#define NI16550_PMR_OFFSET 0x0E +/* PMR[1:0] - Port Capabilities */ +#define NI16550_PMR_CAP_MASK GENMASK(1, 0) +#define NI16550_PMR_NOT_IMPL FIELD_PREP(NI16550_PMR_CAP_MASK, 0) /* not implemented */ +#define NI16550_PMR_CAP_RS232 FIELD_PREP(NI16550_PMR_CAP_MASK, 1) /* RS-232 capable */ +#define NI16550_PMR_CAP_RS485 FIELD_PREP(NI16550_PMR_CAP_MASK, 2) /* RS-485 capable */ +#define NI16550_PMR_CAP_DUAL FIELD_PREP(NI16550_PMR_CAP_MASK, 3) /* dual-port */ +/* PMR[4] - Interface Mode */ +#define NI16550_PMR_MODE_MASK GENMASK(4, 4) +#define NI16550_PMR_MODE_RS232 FIELD_PREP(NI16550_PMR_MODE_MASK, 0) /* currently 232 */ +#define NI16550_PMR_MODE_RS485 FIELD_PREP(NI16550_PMR_MODE_MASK, 1) /* currently 485 */ + +/* PCR - Port Control Register */ +/* + * Wire Mode | Tx enabled? | Rx enabled? + * ---------------|----------------------|-------------------------- + * PCR_RS422 | Always | Always + * PCR_ECHO_RS485 | When DTR asserted | Always + * PCR_DTR_RS485 | When DTR asserted | Disabled when TX enabled + * PCR_AUTO_RS485 | When data in TX FIFO | Disabled when TX enabled + */ +#define NI16550_PCR_OFFSET 0x0F +#define NI16550_PCR_WIRE_MODE_MASK GENMASK(1, 0) +#define NI16550_PCR_RS422 FIELD_PREP(NI16550_PCR_WIRE_MODE_MASK, 0) +#define NI16550_PCR_ECHO_RS485 FIELD_PREP(NI16550_PCR_WIRE_MODE_MASK, 1) +#define NI16550_PCR_DTR_RS485 FIELD_PREP(NI16550_PCR_WIRE_MODE_MASK, 2) +#define NI16550_PCR_AUTO_RS485 FIELD_PREP(NI16550_PCR_WIRE_MODE_MASK, 3) +#define NI16550_PCR_TXVR_ENABLE_BIT BIT(3) +#define NI16550_PCR_RS485_TERMINATION_BIT BIT(6) + +/* flags for ni16550_device_info */ +#define NI_HAS_PMR BIT(0) + +struct ni16550_device_info { + u32 uartclk; + u8 prescaler; + u8 flags; +}; + +struct ni16550_data { + int line; + struct clk *clk; +}; + +static int ni16550_enable_transceivers(struct uart_port *port) +{ + u8 pcr; + + pcr = port->serial_in(port, NI16550_PCR_OFFSET); + pcr |= NI16550_PCR_TXVR_ENABLE_BIT; + dev_dbg(port->dev, "enable transceivers: write pcr: 0x%02x\n", pcr); + port->serial_out(port, NI16550_PCR_OFFSET, pcr); + + return 0; +} + +static int ni16550_disable_transceivers(struct uart_port *port) +{ + u8 pcr; + + pcr = serial_port_in(port, NI16550_PCR_OFFSET); + pcr &= ~NI16550_PCR_TXVR_ENABLE_BIT; + dev_dbg(port->dev, "disable transceivers: write pcr: 0x%02x\n", pcr); + serial_port_out(port, NI16550_PCR_OFFSET, pcr); + + return 0; +} + +static int ni16550_rs485_config(struct uart_port *port, + struct ktermios *termios, + struct serial_rs485 *rs485) +{ + struct uart_8250_port *up = container_of(port, struct uart_8250_port, port); + u8 pcr; + + pcr = serial_port_in(port, NI16550_PCR_OFFSET); + pcr &= ~NI16550_PCR_WIRE_MODE_MASK; + + if ((rs485->flags & SER_RS485_MODE_RS422) || + !(rs485->flags & SER_RS485_ENABLED)) { + /* RS-422 */ + pcr |= NI16550_PCR_RS422; + up->acr &= ~NI16550_ACR_AUTO_DTR_EN; + } else { + /* RS-485 2-wire Auto */ + pcr |= NI16550_PCR_AUTO_RS485; + up->acr |= NI16550_ACR_AUTO_DTR_EN; + } + + dev_dbg(port->dev, "config rs485: write pcr: 0x%02x, acr: %02x\n", pcr, up->acr); + serial_port_out(port, NI16550_PCR_OFFSET, pcr); + serial_icr_write(up, UART_ACR, up->acr); + + return 0; +} + +static bool is_pmr_rs232_mode(struct uart_8250_port *up) +{ + u8 pmr = serial_in(up, NI16550_PMR_OFFSET); + u8 pmr_mode = pmr & NI16550_PMR_MODE_MASK; + u8 pmr_cap = pmr & NI16550_PMR_CAP_MASK; + + /* + * If the PMR is not implemented, then by default NI UARTs are + * connected to RS-485 transceivers + */ + if (pmr_cap == NI16550_PMR_NOT_IMPL) + return false; + + if (pmr_cap == NI16550_PMR_CAP_DUAL) + /* + * If the port is dual-mode capable, then read the mode bit + * to know the current mode + */ + return pmr_mode == NI16550_PMR_MODE_RS232; + /* + * If it is not dual-mode capable, then decide based on the + * capability + */ + return pmr_cap == NI16550_PMR_CAP_RS232; +} + +static void ni16550_config_prescaler(struct uart_8250_port *up, + u8 prescaler) +{ + /* + * Page in the Enhanced Mode Registers + * Sets EFR[4] for Enhanced Mode. + */ + u8 lcr_value; + u8 efr_value; + + lcr_value = serial_in(up, UART_LCR); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + efr_value = serial_in(up, UART_EFR); + efr_value |= UART_EFR_ECB; + + serial_out(up, UART_EFR, efr_value); + + /* Page out the Enhanced Mode Registers */ + serial_out(up, UART_LCR, lcr_value); + + /* Set prescaler to CPR register. */ + serial_out(up, UART_SCR, UART_CPR); + serial_out(up, UART_ICR, prescaler); +} + +static const struct serial_rs485 ni16550_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_MODE_RS422 | SER_RS485_RTS_ON_SEND | + SER_RS485_RTS_AFTER_SEND, + /* + * delay_rts_* and RX_DURING_TX are not supported. + * + * RTS_{ON,AFTER}_SEND are supported, but ignored; the transceiver + * is connected in only one way and we don't need userspace to tell + * us, but want to retain compatibility with applications that do. + */ +}; + +static void ni16550_rs485_setup(struct uart_port *port) +{ + port->rs485_config = ni16550_rs485_config; + port->rs485_supported = ni16550_rs485_supported; + /* + * The hardware comes up by default in 2-wire auto mode and we + * set the flags to represent that + */ + port->rs485.flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND; +} + +static int ni16550_port_startup(struct uart_port *port) +{ + int ret; + + ret = serial8250_do_startup(port); + if (ret) + return ret; + + return ni16550_enable_transceivers(port); +} + +static void ni16550_port_shutdown(struct uart_port *port) +{ + ni16550_disable_transceivers(port); + + serial8250_do_shutdown(port); +} + +static int ni16550_get_regs(struct platform_device *pdev, + struct uart_port *port) +{ + struct resource *regs; + + regs = platform_get_mem_or_io(pdev, 0); + if (!regs) + return dev_err_probe(&pdev->dev, -EINVAL, "no registers defined\n"); + + switch (resource_type(regs)) { + case IORESOURCE_IO: + port->iotype = UPIO_PORT; + port->iobase = regs->start; + + return 0; + case IORESOURCE_MEM: + port->iotype = UPIO_MEM; + port->mapbase = regs->start; + port->mapsize = resource_size(regs); + port->flags |= UPF_IOREMAP; + + return 0; + default: + return -EINVAL; + } +} + +/* + * Very old implementations don't have the TFS or RFS registers + * defined, so we may read all-0s or all-1s. For such devices, + * assume a FIFO size of 128. + */ +static u8 ni16550_read_fifo_size(struct uart_8250_port *uart, int reg) +{ + u8 value = serial_in(uart, reg); + + if (value == 0x00 || value == 0xFF) + return 128; + + return value; +} + +static void ni16550_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + up->mcr |= UART_MCR_CLKSEL; + serial8250_do_set_mctrl(port, mctrl); +} + +static int ni16550_probe(struct platform_device *pdev) +{ + const struct ni16550_device_info *info; + struct device *dev = &pdev->dev; + struct uart_8250_port uart = {}; + unsigned int txfifosz, rxfifosz; + unsigned int prescaler; + struct ni16550_data *data; + const char *portmode; + bool rs232_property; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + spin_lock_init(&uart.port.lock); + + ret = ni16550_get_regs(pdev, &uart.port); + if (ret < 0) + return ret; + + /* early setup so that serial_in()/serial_out() work */ + serial8250_set_defaults(&uart); + + info = device_get_match_data(dev); + + uart.port.dev = dev; + uart.port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_FIXED_TYPE; + uart.port.startup = ni16550_port_startup; + uart.port.shutdown = ni16550_port_shutdown; + + /* + * Hardware instantiation of FIFO sizes are held in registers. + */ + txfifosz = ni16550_read_fifo_size(&uart, NI16550_TFS_OFFSET); + rxfifosz = ni16550_read_fifo_size(&uart, NI16550_RFS_OFFSET); + + dev_dbg(dev, "NI 16550 has TX FIFO size %u, RX FIFO size %u\n", + txfifosz, rxfifosz); + + uart.port.type = PORT_16550A; + uart.port.fifosize = txfifosz; + uart.tx_loadsz = txfifosz; + uart.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10; + uart.capabilities = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR; + + /* + * Declaration of the base clock frequency can come from one of: + * - static declaration in this driver (for older ACPI IDs) + * - a "clock-frequency" ACPI + */ + uart.port.uartclk = info->uartclk; + + ret = uart_read_port_properties(&uart.port); + if (ret) + return ret; + + if (!uart.port.uartclk) { + data->clk = devm_clk_get_enabled(dev, NULL); + if (!IS_ERR(data->clk)) + uart.port.uartclk = clk_get_rate(data->clk); + } + + if (!uart.port.uartclk) + return dev_err_probe(dev, -ENODEV, "unable to determine clock frequency!\n"); + + prescaler = info->prescaler; + device_property_read_u32(dev, "clock-prescaler", &prescaler); + if (prescaler) { + uart.port.set_mctrl = ni16550_set_mctrl; + ni16550_config_prescaler(&uart, (u8)prescaler); + } + + /* + * The determination of whether or not this is an RS-485 or RS-232 port + * can come from the PMR (if present), otherwise we're solely an RS-485 + * port. + * + * This is a device-specific property, and there are old devices in the + * field using "transceiver" as an ACPI property, so we have to check + * for that as well. + */ + if (!device_property_read_string(dev, "transceiver", &portmode)) { + rs232_property = strncmp(portmode, "RS-232", 6) == 0; + + dev_dbg(dev, "port is in %s mode (via device property)\n", + rs232_property ? "RS-232" : "RS-485"); + } else if (info->flags & NI_HAS_PMR) { + rs232_property = is_pmr_rs232_mode(&uart); + + dev_dbg(dev, "port is in %s mode (via PMR)\n", + rs232_property ? "RS-232" : "RS-485"); + } else { + rs232_property = 0; + + dev_dbg(dev, "port is fixed as RS-485\n"); + } + + if (!rs232_property) { + /* + * Neither the 'transceiver' property nor the PMR indicate + * that this is an RS-232 port, so it must be an RS-485 one. + */ + ni16550_rs485_setup(&uart.port); + } + + ret = serial8250_register_8250_port(&uart); + if (ret < 0) + return ret; + data->line = ret; + + platform_set_drvdata(pdev, data); + return 0; +} + +static void ni16550_remove(struct platform_device *pdev) +{ + struct ni16550_data *data = platform_get_drvdata(pdev); + + serial8250_unregister_port(data->line); +} + +/* NI 16550 RS-485 Interface */ +static const struct ni16550_device_info nic7750 = { + .uartclk = 33333333, +}; + +/* NI CVS-145x RS-485 Interface */ +static const struct ni16550_device_info nic7772 = { + .uartclk = 1843200, + .flags = NI_HAS_PMR, +}; + +/* NI cRIO-904x RS-485 Interface */ +static const struct ni16550_device_info nic792b = { + /* Sets UART clock rate to 22.222 MHz with 1.125 prescale */ + .uartclk = 22222222, + .prescaler = 0x09, +}; + +/* NI sbRIO 96x8 RS-232/485 Interfaces */ +static const struct ni16550_device_info nic7a69 = { + /* Set UART clock rate to 29.629 MHz with 1.125 prescale */ + .uartclk = 29629629, + .prescaler = 0x09, +}; + +static const struct acpi_device_id ni16550_acpi_match[] = { + { "NIC7750", (kernel_ulong_t)&nic7750 }, + { "NIC7772", (kernel_ulong_t)&nic7772 }, + { "NIC792B", (kernel_ulong_t)&nic792b }, + { "NIC7A69", (kernel_ulong_t)&nic7a69 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, ni16550_acpi_match); + +static struct platform_driver ni16550_driver = { + .driver = { + .name = "ni16550", + .acpi_match_table = ni16550_acpi_match, + }, + .probe = ni16550_probe, + .remove = ni16550_remove, +}; + +module_platform_driver(ni16550_driver); + +MODULE_AUTHOR("Emerson Electric Co."); +MODULE_DESCRIPTION("NI 16550 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 5d1dd992d8fb..d178b6c54ea1 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -18,14 +18,17 @@ #include <linux/pm_runtime.h> #include <linux/clk.h> #include <linux/reset.h> +#include <linux/notifier.h> #include "8250.h" struct of_serial_info { struct clk *clk; + struct clk *bus_clk; struct reset_control *rst; int type; int line; + struct notifier_block clk_notifier; }; /* Nuvoton NPCM timeout register */ @@ -58,6 +61,26 @@ static int npcm_setup(struct uart_port *port) return 0; } +static inline struct of_serial_info *clk_nb_to_info(struct notifier_block *nb) +{ + return container_of(nb, struct of_serial_info, clk_notifier); +} + +static int of_platform_serial_clk_notifier_cb(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct of_serial_info *info = clk_nb_to_info(nb); + struct uart_8250_port *port8250 = serial8250_get_port(info->line); + struct clk_notifier_data *ndata = data; + + if (event == POST_RATE_CHANGE) { + serial8250_update_uartclk(&port8250->port, ndata->new_rate); + return NOTIFY_OK; + } + + return NOTIFY_DONE; +} + /* * Fill a struct uart_port for a given device node */ @@ -88,7 +111,6 @@ static int of_platform_serial_setup(struct platform_device *ofdev, spin_lock_init(&port->lock); if (resource_type(&resource) == IORESOURCE_IO) { - port->iotype = UPIO_PORT; port->iobase = resource.start; } else { port->mapbase = resource.start; @@ -102,12 +124,22 @@ static int of_platform_serial_setup(struct platform_device *ofdev, /* Get clk rate through clk driver if present */ if (!port->uartclk) { - info->clk = devm_clk_get_enabled(dev, NULL); + struct clk *bus_clk; + + bus_clk = devm_clk_get_optional_enabled(dev, "bus"); + if (IS_ERR(bus_clk)) { + ret = dev_err_probe(dev, PTR_ERR(bus_clk), "failed to get bus clock\n"); + goto err_pmruntime; + } + + /* If the bus clock is required, core clock must be named */ + info->clk = devm_clk_get_enabled(dev, bus_clk ? "core" : NULL); if (IS_ERR(info->clk)) { ret = dev_err_probe(dev, PTR_ERR(info->clk), "failed to get clock\n"); goto err_pmruntime; } + info->bus_clk = bus_clk; port->uartclk = clk_get_rate(info->clk); } /* If current-speed was set, then try not to change it. */ @@ -218,7 +250,19 @@ static int of_platform_serial_probe(struct platform_device *ofdev) info->type = port_type; info->line = ret; platform_set_drvdata(ofdev, info); + + if (info->clk) { + info->clk_notifier.notifier_call = of_platform_serial_clk_notifier_cb; + ret = clk_notifier_register(info->clk, &info->clk_notifier); + if (ret) { + dev_err_probe(port8250.port.dev, ret, "Failed to set the clock notifier\n"); + goto err_unregister; + } + } + return 0; +err_unregister: + serial8250_unregister_port(info->line); err_dispose: pm_runtime_put_sync(&ofdev->dev); pm_runtime_disable(&ofdev->dev); @@ -234,6 +278,9 @@ static void of_platform_serial_remove(struct platform_device *ofdev) { struct of_serial_info *info = platform_get_drvdata(ofdev); + if (info->clk) + clk_notifier_unregister(info->clk, &info->clk_notifier); + serial8250_unregister_port(info->line); reset_control_assert(info->rst); @@ -254,6 +301,7 @@ static int of_serial_suspend(struct device *dev) if (!uart_console(port) || console_suspend_enabled) { pm_runtime_put_sync(dev); clk_disable_unprepare(info->clk); + clk_disable_unprepare(info->bus_clk); } return 0; } @@ -266,6 +314,7 @@ static int of_serial_resume(struct device *dev) if (!uart_console(port) || console_suspend_enabled) { pm_runtime_get_sync(dev); + clk_prepare_enable(info->bus_clk); clk_prepare_enable(info->clk); } @@ -315,7 +364,7 @@ static struct platform_driver of_platform_serial_driver = { .pm = &of_serial_pm_ops, }, .probe = of_platform_serial_probe, - .remove_new = of_platform_serial_remove, + .remove = of_platform_serial_remove, }; module_platform_driver(of_platform_serial_driver); diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 66901d93089a..72ae08d6204f 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -19,7 +19,6 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/of_irq.h> #include <linux/delay.h> #include <linux/pm_runtime.h> @@ -28,7 +27,6 @@ #include <linux/pm_wakeirq.h> #include <linux/dma-mapping.h> #include <linux/sys_soc.h> -#include <linux/pm_domain.h> #include "8250.h" @@ -116,11 +114,9 @@ /* RX FIFO occupancy indicator */ #define UART_OMAP_RX_LVL 0x19 -/* - * Copy of the genpd flags for the console. - * Only used if console suspend is disabled - */ -static unsigned int genpd_flags_console; +/* Timeout low and High */ +#define UART_OMAP_TO_L 0x26 +#define UART_OMAP_TO_H 0x27 struct omap8250_priv { void __iomem *membase; @@ -141,7 +137,6 @@ struct omap8250_priv { atomic_t active; bool is_suspending; int wakeirq; - int wakeups_enabled; u32 latency; u32 calc_latency; struct pm_qos_request pm_qos_request; @@ -370,7 +365,7 @@ static void omap8250_restore_regs(struct uart_8250_port *up) if (up->port.rs485.flags & SER_RS485_ENABLED && up->port.rs485_config == serial8250_em485_config) - serial8250_em485_stop_tx(up); + serial8250_em485_stop_tx(up, true); } /* @@ -417,7 +412,13 @@ static void omap_8250_set_termios(struct uart_port *port, */ uart_update_timeout(port, termios->c_cflag, baud); - up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + /* + * Specify which conditions may be considered for error + * handling and the ignoring of characters. The actual + * ignoring of characters only occurs if the bit is set + * in @ignore_status_mask as well. + */ + up->port.read_status_mask = UART_LSR_OE | UART_LSR_DR; if (termios->c_iflag & INPCK) up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; if (termios->c_iflag & (IGNBRK | PARMRK)) @@ -664,13 +665,25 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id) /* * On K3 SoCs, it is observed that RX TIMEOUT is signalled after - * FIFO has been drained, in which case a dummy read of RX FIFO - * is required to clear RX TIMEOUT condition. + * FIFO has been drained or erroneously. + * So apply solution of Errata i2310 as mentioned in + * https://www.ti.com/lit/pdf/sprz536 */ if (priv->habit & UART_RX_TIMEOUT_QUIRK && (iir & UART_IIR_RX_TIMEOUT) == UART_IIR_RX_TIMEOUT && serial_port_in(port, UART_OMAP_RX_LVL) == 0) { - serial_port_in(port, UART_RX); + unsigned char efr2, timeout_h, timeout_l; + + efr2 = serial_in(up, UART_OMAP_EFR2); + timeout_h = serial_in(up, UART_OMAP_TO_H); + timeout_l = serial_in(up, UART_OMAP_TO_L); + serial_out(up, UART_OMAP_TO_H, 0xFF); + serial_out(up, UART_OMAP_TO_L, 0xFF); + serial_out(up, UART_OMAP_EFR2, UART_OMAP_EFR2_TIMEOUT_BEHAVE); + serial_in(up, UART_IIR); + serial_out(up, UART_OMAP_EFR2, efr2); + serial_out(up, UART_OMAP_TO_H, timeout_h); + serial_out(up, UART_OMAP_TO_L, timeout_l); } /* Stop processing interrupts on input overrun */ @@ -679,7 +692,7 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id) /* Synchronize UART_IER access against the console. */ uart_port_lock(port); - up->ier = port->serial_in(port, UART_IER); + up->ier = serial_port_in(port, UART_IER); if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) { port->ops->stop_rx(port); } else { @@ -769,12 +782,12 @@ static void omap_8250_shutdown(struct uart_port *port) struct uart_8250_port *up = up_to_u8250p(port); struct omap8250_priv *priv = port->private_data; + pm_runtime_get_sync(port->dev); + flush_work(&priv->qos_work); if (up->dma) omap_8250_rx_dma_flush(up); - pm_runtime_get_sync(port->dev); - serial_out(up, UART_OMAP_WER, 0); if (priv->habit & UART_HAS_EFR2) serial_out(up, UART_OMAP_EFR2, 0x0); @@ -831,7 +844,6 @@ static void omap_8250_unthrottle(struct uart_port *port) if (up->dma) up->dma->rx_dma(up); up->ier |= UART_IER_RLSI | UART_IER_RDI; - port->read_status_mask |= UART_LSR_DR; serial_out(up, UART_IER, up->ier); uart_port_unlock_irqrestore(port, flags); @@ -1094,7 +1106,7 @@ static void omap_8250_dma_tx_complete(void *param) { struct uart_8250_port *p = param; struct uart_8250_dma *dma = p->dma; - struct circ_buf *xmit = &p->port.state->xmit; + struct tty_port *tport = &p->port.state->port; unsigned long flags; bool en_thri = false; struct omap8250_priv *priv = p->port.private_data; @@ -1113,10 +1125,10 @@ static void omap_8250_dma_tx_complete(void *param) omap8250_restore_regs(p); } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&p->port); - if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) { + if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(&p->port)) { int ret; ret = omap_8250_tx_dma(p); @@ -1138,14 +1150,15 @@ static int omap_8250_tx_dma(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; struct omap8250_priv *priv = p->port.private_data; - struct circ_buf *xmit = &p->port.state->xmit; + struct tty_port *tport = &p->port.state->port; struct dma_async_tx_descriptor *desc; - unsigned int skip_byte = 0; + struct scatterlist sg; + int skip_byte = -1; int ret; if (dma->tx_running) return 0; - if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) { + if (uart_tx_stopped(&p->port) || kfifo_is_empty(&tport->xmit_fifo)) { /* * Even if no data, we need to return an error for the two cases @@ -1160,8 +1173,8 @@ static int omap_8250_tx_dma(struct uart_8250_port *p) return 0; } - dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); if (priv->habit & OMAP_DMA_TX_KICK) { + unsigned char c; u8 tx_lvl; /* @@ -1184,22 +1197,32 @@ static int omap_8250_tx_dma(struct uart_8250_port *p) ret = -EBUSY; goto err; } - if (dma->tx_size < 4) { + if (kfifo_len(&tport->xmit_fifo) < 4) { ret = -EINVAL; goto err; } - skip_byte = 1; + if (!uart_fifo_out(&p->port, &c, 1)) { + ret = -EINVAL; + goto err; + } + skip_byte = c; + } + + sg_init_table(&sg, 1); + ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1, UART_XMIT_SIZE, dma->tx_addr); + if (ret != 1) { + ret = -EINVAL; + goto err; } - desc = dmaengine_prep_slave_single(dma->txchan, - dma->tx_addr + xmit->tail + skip_byte, - dma->tx_size - skip_byte, DMA_MEM_TO_DEV, + desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { ret = -EBUSY; goto err; } + dma->tx_size = sg_dma_len(&sg); dma->tx_running = 1; desc->callback = omap_8250_dma_tx_complete; @@ -1215,11 +1238,13 @@ static int omap_8250_tx_dma(struct uart_8250_port *p) dma->tx_err = 0; serial8250_clear_THRI(p); - if (skip_byte) - serial_out(p, UART_TX, xmit->buf[xmit->tail]); - return 0; + ret = 0; + goto out_skip; err: dma->tx_err = 1; +out_skip: + if (skip_byte >= 0) + serial_out(p, UART_TX, skip_byte); return ret; } @@ -1279,7 +1304,7 @@ static void am654_8250_handle_rx_dma(struct uart_8250_port *up, u8 iir, /* * This is mostly serial8250_handle_irq(). We have a slightly different DMA - * hoook for RX/TX and need different logic for them in the ISR. Therefore we + * hook for RX/TX and need different logic for them in the ISR. Therefore we * use the default routine in the non-DMA case and this one for with DMA. */ static int omap_8250_dma_handle_irq(struct uart_port *port) @@ -1308,12 +1333,12 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) serial8250_modem_status(up); if (status & UART_LSR_THRE && up->dma->tx_err) { if (uart_tx_stopped(&up->port) || - uart_circ_empty(&up->port.state->xmit)) { + kfifo_is_empty(&up->port.state->port.xmit_fifo)) { up->dma->tx_err = 0; serial8250_tx_chars(up); } else { /* - * try again due to an earlier failer which + * try again due to an earlier failure which * might have been resolved by now. */ if (omap_8250_tx_dma(up)) @@ -1497,7 +1522,10 @@ static int omap8250_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); - device_init_wakeup(&pdev->dev, true); + device_set_wakeup_capable(&pdev->dev, true); + if (of_property_read_bool(np, "wakeup-source")) + device_set_wakeup_enable(&pdev->dev, true); + pm_runtime_enable(&pdev->dev); pm_runtime_use_autosuspend(&pdev->dev); @@ -1555,7 +1583,7 @@ static int omap8250_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, up.port.irq, omap8250_irq, 0, dev_name(&pdev->dev), priv); if (ret < 0) - return ret; + goto err; priv->wakeirq = irq_of_parse_and_map(np, 1); @@ -1596,7 +1624,7 @@ static void omap8250_remove(struct platform_device *pdev) flush_work(&priv->qos_work); pm_runtime_disable(&pdev->dev); cpu_latency_qos_remove_request(&priv->pm_qos_request); - device_init_wakeup(&pdev->dev, false); + device_set_wakeup_capable(&pdev->dev, false); } static int omap8250_prepare(struct device *dev) @@ -1622,7 +1650,6 @@ static int omap8250_suspend(struct device *dev) { struct omap8250_priv *priv = dev_get_drvdata(dev); struct uart_8250_port *up = serial8250_get_port(priv->line); - struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain); int err = 0; serial8250_suspend_port(priv->line); @@ -1633,19 +1660,8 @@ static int omap8250_suspend(struct device *dev) if (!device_may_wakeup(dev)) priv->wer = 0; serial_out(up, UART_OMAP_WER, priv->wer); - if (uart_console(&up->port)) { - if (console_suspend_enabled) - err = pm_runtime_force_suspend(dev); - else { - /* - * The pd shall not be powered-off (no console suspend). - * Make copy of genpd flags before to set it always on. - * The original value is restored during the resume. - */ - genpd_flags_console = genpd->flags; - genpd->flags |= GENPD_FLAG_ALWAYS_ON; - } - } + if (uart_console(&up->port) && console_suspend_enabled) + err = pm_runtime_force_suspend(dev); flush_work(&priv->qos_work); return err; @@ -1655,16 +1671,12 @@ static int omap8250_resume(struct device *dev) { struct omap8250_priv *priv = dev_get_drvdata(dev); struct uart_8250_port *up = serial8250_get_port(priv->line); - struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain); int err; if (uart_console(&up->port) && console_suspend_enabled) { - if (console_suspend_enabled) { - err = pm_runtime_force_resume(dev); - if (err) - return err; - } else - genpd->flags = genpd_flags_console; + err = pm_runtime_force_resume(dev); + if (err) + return err; } serial8250_resume_port(priv->line); @@ -1855,7 +1867,7 @@ static struct platform_driver omap8250_platform_driver = { .of_match_table = omap8250_dt_ids, }, .probe = omap8250_probe, - .remove_new = omap8250_remove, + .remove = omap8250_remove, }; module_platform_driver(omap8250_platform_driver); diff --git a/drivers/tty/serial/8250/8250_parisc.c b/drivers/tty/serial/8250/8250_parisc.c index 948d0a1c6ae8..4ba05a98791c 100644 --- a/drivers/tty/serial/8250/8250_parisc.c +++ b/drivers/tty/serial/8250/8250_parisc.c @@ -127,4 +127,5 @@ static int __init probe_serial_gsc(void) module_init(probe_serial_gsc); +MODULE_DESCRIPTION("Serial Device Initialisation for Lasi/Asp/Wax/Dino"); MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 0d35c77fad9e..73c200127b08 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -64,23 +64,17 @@ #define PCIE_DEVICE_ID_NEO_2_OX_IBM 0x00F6 #define PCI_DEVICE_ID_PLX_CRONYX_OMEGA 0xc001 #define PCI_DEVICE_ID_INTEL_PATSBURG_KT 0x1d3d -#define PCI_VENDOR_ID_WCH 0x4348 -#define PCI_DEVICE_ID_WCH_CH352_2S 0x3253 -#define PCI_DEVICE_ID_WCH_CH353_4S 0x3453 -#define PCI_DEVICE_ID_WCH_CH353_2S1PF 0x5046 -#define PCI_DEVICE_ID_WCH_CH353_1S1P 0x5053 -#define PCI_DEVICE_ID_WCH_CH353_2S1P 0x7053 -#define PCI_DEVICE_ID_WCH_CH355_4S 0x7173 + +#define PCI_DEVICE_ID_WCHCN_CH352_2S 0x3253 +#define PCI_DEVICE_ID_WCHCN_CH355_4S 0x7173 + #define PCI_VENDOR_ID_AGESTAR 0x5372 #define PCI_DEVICE_ID_AGESTAR_9375 0x6872 #define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a #define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e -#define PCIE_VENDOR_ID_WCH 0x1c00 -#define PCIE_DEVICE_ID_WCH_CH382_2S1P 0x3250 -#define PCIE_DEVICE_ID_WCH_CH384_4S 0x3470 -#define PCIE_DEVICE_ID_WCH_CH384_8S 0x3853 -#define PCIE_DEVICE_ID_WCH_CH382_2S 0x3253 +#define PCI_DEVICE_ID_WCHIC_CH384_4S 0x3470 +#define PCI_DEVICE_ID_WCHIC_CH384_8S 0x3853 #define PCI_DEVICE_ID_MOXA_CP102E 0x1024 #define PCI_DEVICE_ID_MOXA_CP102EL 0x1025 @@ -964,6 +958,9 @@ static int pci_ite887x_init(struct pci_dev *dev) struct resource *iobase = NULL; u32 miscr, uartbar, ioport; + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(dev); + /* search for the base-ioport */ for (i = 0; i < ARRAY_SIZE(inta_addr); i++) { iobase = request_region(inta_addr[i], ITE_887x_IOSIZE, @@ -1277,7 +1274,7 @@ static void pci_oxsemi_tornado_set_divisor(struct uart_port *port, serial_icr_write(up, UART_TCR, tcr); serial_icr_write(up, UART_CPR, cpr); serial_icr_write(up, UART_CKS, cpr2); - serial8250_do_set_divisor(port, baud, quot, 0); + serial8250_do_set_divisor(port, baud, quot); } /* @@ -1514,6 +1511,9 @@ static int pci_quatech_init(struct pci_dev *dev) const struct pci_device_id *match; bool amcc = false; + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(dev); + match = pci_match_id(quatech_cards, dev); if (match) amcc = match->driver_data; @@ -1538,6 +1538,9 @@ static int pci_quatech_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_8250_port *port, int idx) { + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(priv->dev); + /* Needed by pci_quatech calls below */ port->port.iobase = pci_resource_start(priv->dev, FL_GET_BASE(board->flags)); /* Set up the clocking */ @@ -1655,6 +1658,9 @@ static int pci_fintek_setup(struct serial_private *priv, u8 config_base; u16 iobase; + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(pdev); + config_base = 0x40 + 0x08 * idx; /* Get the io address from configuration space */ @@ -1686,6 +1692,9 @@ static int pci_fintek_init(struct pci_dev *dev) u8 config_base; struct serial_private *priv = pci_get_drvdata(dev); + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(dev); + if (!(pci_resource_flags(dev, 5) & IORESOURCE_IO) || !(pci_resource_flags(dev, 4) & IORESOURCE_IO) || !(pci_resource_flags(dev, 3) & IORESOURCE_IO)) @@ -1864,6 +1873,9 @@ static int kt_serial_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_8250_port *port, int idx) { + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(priv->dev); + port->port.flags |= UPF_BUG_THRE; port->port.serial_in = kt_serial_in; port->port.handle_break = kt_handle_break; @@ -1884,6 +1896,9 @@ pci_wch_ch353_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_8250_port *port, int idx) { + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(priv->dev); + port->port.flags |= UPF_FIXED_TYPE; port->port.type = PORT_16550A; return pci_default_setup(priv, board, port, idx); @@ -1894,6 +1909,9 @@ pci_wch_ch355_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_8250_port *port, int idx) { + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(priv->dev); + port->port.flags |= UPF_FIXED_TYPE; port->port.type = PORT_16550A; return pci_default_setup(priv, board, port, idx); @@ -1904,6 +1922,9 @@ pci_wch_ch38x_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_8250_port *port, int idx) { + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(priv->dev); + port->port.flags |= UPF_FIXED_TYPE; port->port.type = PORT_16850; return pci_default_setup(priv, board, port, idx); @@ -1918,6 +1939,8 @@ static int pci_wch_ch38x_init(struct pci_dev *dev) int max_port; unsigned long iobase; + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(dev); switch (dev->device) { case 0x3853: /* 8 ports */ @@ -1937,6 +1960,11 @@ static void pci_wch_ch38x_exit(struct pci_dev *dev) { unsigned long iobase; + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) { + serial_8250_warn_need_ioport(dev); + return; + } + iobase = pci_resource_start(dev, 0); outb(0x0, iobase + CH384_XINT_ENABLE_REG); } @@ -1985,6 +2013,17 @@ enum { MOXA_SUPP_RS485 = BIT(2), }; +static unsigned short moxa_get_nports(unsigned short device) +{ + switch (device) { + case PCI_DEVICE_ID_MOXA_CP116E_A_A: + case PCI_DEVICE_ID_MOXA_CP116E_A_B: + return 8; + } + + return FIELD_GET(0x00F0, device); +} + static bool pci_moxa_is_mini_pcie(unsigned short device) { if (device == PCI_DEVICE_ID_MOXA_CP102N || @@ -2038,9 +2077,12 @@ static int pci_moxa_init(struct pci_dev *dev) { unsigned short device = dev->device; resource_size_t iobar_addr = pci_resource_start(dev, 2); - unsigned int num_ports = (device & 0x00F0) >> 4, i; + unsigned int i, num_ports = moxa_get_nports(device); u8 val, init_mode = MOXA_RS232; + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(dev); + if (!(pci_moxa_supported_rs(dev) & MOXA_SUPP_RS232)) { init_mode = MOXA_RS422; } @@ -2073,6 +2115,9 @@ pci_moxa_setup(struct serial_private *priv, unsigned int bar = FL_GET_BASE(board->flags); int offset; + if (!IS_ENABLED(CONFIG_HAS_IOPORT)) + return serial_8250_warn_need_ioport(priv->dev); + if (board->num_ports == 4 && idx == 3) offset = 7 * board->uart_offset; else @@ -2683,6 +2728,22 @@ static struct pci_serial_quirk pci_serial_quirks[] = { .setup = pci_oxsemi_tornado_setup, }, { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x4026, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTASHIELD, + .device = 0x4021, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_oxsemi_tornado_setup, + }, + { .vendor = PCI_VENDOR_ID_INTEL, .device = 0x8811, .subvendor = PCI_ANY_ID, @@ -2766,80 +2827,80 @@ static struct pci_serial_quirk pci_serial_quirks[] = { }, /* WCH CH353 1S1P card (16550 clone) */ { - .vendor = PCI_VENDOR_ID_WCH, - .device = PCI_DEVICE_ID_WCH_CH353_1S1P, + .vendor = PCI_VENDOR_ID_WCHCN, + .device = PCI_DEVICE_ID_WCHCN_CH353_1S1P, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .setup = pci_wch_ch353_setup, }, /* WCH CH353 2S1P card (16550 clone) */ { - .vendor = PCI_VENDOR_ID_WCH, - .device = PCI_DEVICE_ID_WCH_CH353_2S1P, + .vendor = PCI_VENDOR_ID_WCHCN, + .device = PCI_DEVICE_ID_WCHCN_CH353_2S1P, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .setup = pci_wch_ch353_setup, }, /* WCH CH353 4S card (16550 clone) */ { - .vendor = PCI_VENDOR_ID_WCH, - .device = PCI_DEVICE_ID_WCH_CH353_4S, + .vendor = PCI_VENDOR_ID_WCHCN, + .device = PCI_DEVICE_ID_WCHCN_CH353_4S, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .setup = pci_wch_ch353_setup, }, /* WCH CH353 2S1PF card (16550 clone) */ { - .vendor = PCI_VENDOR_ID_WCH, - .device = PCI_DEVICE_ID_WCH_CH353_2S1PF, + .vendor = PCI_VENDOR_ID_WCHCN, + .device = PCI_DEVICE_ID_WCHCN_CH353_2S1PF, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .setup = pci_wch_ch353_setup, }, /* WCH CH352 2S card (16550 clone) */ { - .vendor = PCI_VENDOR_ID_WCH, - .device = PCI_DEVICE_ID_WCH_CH352_2S, + .vendor = PCI_VENDOR_ID_WCHCN, + .device = PCI_DEVICE_ID_WCHCN_CH352_2S, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .setup = pci_wch_ch353_setup, }, /* WCH CH355 4S card (16550 clone) */ { - .vendor = PCI_VENDOR_ID_WCH, - .device = PCI_DEVICE_ID_WCH_CH355_4S, + .vendor = PCI_VENDOR_ID_WCHCN, + .device = PCI_DEVICE_ID_WCHCN_CH355_4S, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .setup = pci_wch_ch355_setup, }, /* WCH CH382 2S card (16850 clone) */ { - .vendor = PCIE_VENDOR_ID_WCH, - .device = PCIE_DEVICE_ID_WCH_CH382_2S, + .vendor = PCI_VENDOR_ID_WCHIC, + .device = PCI_DEVICE_ID_WCHIC_CH382_2S, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .setup = pci_wch_ch38x_setup, }, /* WCH CH382 2S1P card (16850 clone) */ { - .vendor = PCIE_VENDOR_ID_WCH, - .device = PCIE_DEVICE_ID_WCH_CH382_2S1P, + .vendor = PCI_VENDOR_ID_WCHIC, + .device = PCI_DEVICE_ID_WCHIC_CH382_2S1P, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .setup = pci_wch_ch38x_setup, }, /* WCH CH384 4S card (16850 clone) */ { - .vendor = PCIE_VENDOR_ID_WCH, - .device = PCIE_DEVICE_ID_WCH_CH384_4S, + .vendor = PCI_VENDOR_ID_WCHIC, + .device = PCI_DEVICE_ID_WCHIC_CH384_4S, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .setup = pci_wch_ch38x_setup, }, /* WCH CH384 8S card (16850 clone) */ { - .vendor = PCIE_VENDOR_ID_WCH, - .device = PCIE_DEVICE_ID_WCH_CH384_8S, + .vendor = PCI_VENDOR_ID_WCHIC, + .device = PCI_DEVICE_ID_WCHIC_CH384_8S, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .init = pci_wch_ch38x_init, @@ -3916,11 +3977,11 @@ static const struct pci_device_id blacklist[] = { /* multi-io cards handled by parport_serial */ /* WCH CH353 2S1P */ - { PCI_DEVICE(0x4348, 0x7053), 0, 0, REPORT_CONFIG(PARPORT_SERIAL), }, + { PCI_VDEVICE(WCHCN, 0x7053), REPORT_CONFIG(PARPORT_SERIAL), }, /* WCH CH353 1S1P */ - { PCI_DEVICE(0x4348, 0x5053), 0, 0, REPORT_CONFIG(PARPORT_SERIAL), }, + { PCI_VDEVICE(WCHCN, 0x5053), REPORT_CONFIG(PARPORT_SERIAL), }, /* WCH CH382 2S1P */ - { PCI_DEVICE(0x1c00, 0x3250), 0, 0, REPORT_CONFIG(PARPORT_SERIAL), }, + { PCI_VDEVICE(WCHIC, 0x3250), REPORT_CONFIG(PARPORT_SERIAL), }, /* Intel platforms with MID UART */ { PCI_VDEVICE(INTEL, 0x081b), REPORT_8250_CONFIG(MID), }, @@ -4108,7 +4169,7 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) rc = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_ALL_TYPES); } else { pci_dbg(dev, "Using legacy interrupts\n"); - rc = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY); + rc = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_INTX); } if (rc < 0) { kfree(priv); @@ -5010,12 +5071,6 @@ static const struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_4_460800 }, @@ -5214,6 +5269,14 @@ static const struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_2_115200 }, + { PCI_VENDOR_ID_INTASHIELD, 0x0BA2, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + pbn_b2_2_115200 }, + { PCI_VENDOR_ID_INTASHIELD, 0x0BA3, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + pbn_b2_2_115200 }, /* * Brainboxes UC-235/246 */ @@ -5334,6 +5397,14 @@ static const struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_4_115200 }, + { PCI_VENDOR_ID_INTASHIELD, 0x0C42, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + pbn_b2_4_115200 }, + { PCI_VENDOR_ID_INTASHIELD, 0x0C43, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + pbn_b2_4_115200 }, /* * Brainboxes UC-420 */ @@ -5560,6 +5631,20 @@ static const struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_oxsemi_1_15625000 }, + /* + * Brainboxes XC-235 + */ + { PCI_VENDOR_ID_INTASHIELD, 0x4026, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + pbn_oxsemi_1_15625000 }, + /* + * Brainboxes XC-475 + */ + { PCI_VENDOR_ID_INTASHIELD, 0x4021, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + pbn_oxsemi_1_15625000 }, /* * Perle PCI-RAS cards @@ -5999,27 +6084,27 @@ static const struct pci_device_id serial_pci_tbl[] = { * WCH CH353 series devices: The 2S1P is handled by parport_serial * so not listed here. */ - { PCI_VENDOR_ID_WCH, PCI_DEVICE_ID_WCH_CH353_4S, + { PCI_VENDOR_ID_WCHCN, PCI_DEVICE_ID_WCHCN_CH353_4S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_4_115200 }, - { PCI_VENDOR_ID_WCH, PCI_DEVICE_ID_WCH_CH353_2S1PF, + { PCI_VENDOR_ID_WCHCN, PCI_DEVICE_ID_WCHCN_CH353_2S1PF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_WCH, PCI_DEVICE_ID_WCH_CH355_4S, + { PCI_VENDOR_ID_WCHCN, PCI_DEVICE_ID_WCHCN_CH355_4S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_4_115200 }, - { PCIE_VENDOR_ID_WCH, PCIE_DEVICE_ID_WCH_CH382_2S, + { PCI_VENDOR_ID_WCHIC, PCI_DEVICE_ID_WCHIC_CH382_2S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_wch382_2 }, - { PCIE_VENDOR_ID_WCH, PCIE_DEVICE_ID_WCH_CH384_4S, + { PCI_VENDOR_ID_WCHIC, PCI_DEVICE_ID_WCHIC_CH384_4S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_wch384_4 }, - { PCIE_VENDOR_ID_WCH, PCIE_DEVICE_ID_WCH_CH384_8S, + { PCI_VENDOR_ID_WCHIC, PCI_DEVICE_ID_WCHIC_CH384_8S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_wch384_8 }, /* @@ -6135,4 +6220,4 @@ module_pci_driver(serial_pci_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module"); MODULE_DEVICE_TABLE(pci, serial_pci_tbl); -MODULE_IMPORT_NS(SERIAL_8250_PCI); +MODULE_IMPORT_NS("SERIAL_8250_PCI"); diff --git a/drivers/tty/serial/8250/8250_pci1xxxx.c b/drivers/tty/serial/8250/8250_pci1xxxx.c index 2fbb5851f788..4c149db84692 100644 --- a/drivers/tty/serial/8250/8250_pci1xxxx.c +++ b/drivers/tty/serial/8250/8250_pci1xxxx.c @@ -78,6 +78,12 @@ #define UART_TX_BYTE_FIFO 0x00 #define UART_FIFO_CTL 0x02 +#define UART_MODEM_CTL_REG 0x04 +#define UART_MODEM_CTL_RTS_SET BIT(1) + +#define UART_LINE_STAT_REG 0x05 +#define UART_LINE_XMIT_CHECK_MASK GENMASK(6, 5) + #define UART_ACTV_REG 0x11 #define UART_BLOCK_SET_ACTIVE BIT(0) @@ -94,6 +100,7 @@ #define UART_BIT_SAMPLE_CNT_16 16 #define BAUD_CLOCK_DIV_INT_MSK GENMASK(31, 8) #define ADCL_CFG_RTS_DELAY_MASK GENMASK(11, 8) +#define FRAC_DIV_TX_END_POINT_MASK GENMASK(23, 20) #define UART_WAKE_REG 0x8C #define UART_WAKE_MASK_REG 0x90 @@ -108,6 +115,7 @@ #define UART_RESET_REG 0x94 #define UART_RESET_D3_RESET_DISABLE BIT(16) +#define UART_RESET_HOT_RESET_DISABLE BIT(17) #define UART_BURST_STATUS_REG 0x9C #define UART_TX_BURST_FIFO 0xA0 @@ -134,6 +142,11 @@ #define UART_BST_STAT_LSR_FRAME_ERR 0x8000000 #define UART_BST_STAT_LSR_THRE 0x20000000 +#define GET_MODEM_CTL_RTS_STATUS(reg) ((reg) & UART_MODEM_CTL_RTS_SET) +#define GET_RTS_PIN_STATUS(val) (((val) & TIOCM_RTS) >> 1) +#define RTS_TOGGLE_STATUS_MASK(val, reg) (GET_MODEM_CTL_RTS_STATUS(reg) \ + != GET_RTS_PIN_STATUS(val)) + struct pci1xxxx_8250 { unsigned int nr; u8 dev_rev; @@ -254,6 +267,47 @@ static void pci1xxxx_set_divisor(struct uart_port *port, unsigned int baud, port->membase + UART_BAUD_CLK_DIVISOR_REG); } +static void pci1xxxx_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + u32 fract_div_cfg_reg; + u32 line_stat_reg; + u32 modem_ctl_reg; + u32 adcl_cfg_reg; + + adcl_cfg_reg = readl(port->membase + ADCL_CFG_REG); + + /* HW is responsible in ADCL_EN case */ + if ((adcl_cfg_reg & (ADCL_CFG_EN | ADCL_CFG_PIN_SEL))) + return; + + modem_ctl_reg = readl(port->membase + UART_MODEM_CTL_REG); + + serial8250_do_set_mctrl(port, mctrl); + + if (RTS_TOGGLE_STATUS_MASK(mctrl, modem_ctl_reg)) { + line_stat_reg = readl(port->membase + UART_LINE_STAT_REG); + if (line_stat_reg & UART_LINE_XMIT_CHECK_MASK) { + fract_div_cfg_reg = readl(port->membase + + FRAC_DIV_CFG_REG); + + writel((fract_div_cfg_reg & + ~(FRAC_DIV_TX_END_POINT_MASK)), + port->membase + FRAC_DIV_CFG_REG); + + /* Enable ADC and set the nRTS pin */ + writel((adcl_cfg_reg | (ADCL_CFG_EN | + ADCL_CFG_PIN_SEL)), + port->membase + ADCL_CFG_REG); + + /* Revert to the original settings */ + writel(adcl_cfg_reg, port->membase + ADCL_CFG_REG); + + writel(fract_div_cfg_reg, port->membase + + FRAC_DIV_CFG_REG); + } + } +} + static int pci1xxxx_rs485_config(struct uart_port *port, struct ktermios *termios, struct serial_rs485 *rs485) @@ -382,10 +436,10 @@ static void pci1xxxx_rx_burst(struct uart_port *port, u32 uart_status) } static void pci1xxxx_process_write_data(struct uart_port *port, - struct circ_buf *xmit, int *data_empty_count, u32 *valid_byte_count) { + struct tty_port *tport = &port->state->port; u32 valid_burst_count = *valid_byte_count / UART_BURST_SIZE; /* @@ -395,41 +449,36 @@ static void pci1xxxx_process_write_data(struct uart_port *port, * one byte at a time. */ while (valid_burst_count) { + u32 c; + if (*data_empty_count - UART_BURST_SIZE < 0) break; - if (xmit->tail > (UART_XMIT_SIZE - UART_BURST_SIZE)) + if (kfifo_len(&tport->xmit_fifo) < UART_BURST_SIZE) + break; + if (WARN_ON(kfifo_out(&tport->xmit_fifo, (u8 *)&c, sizeof(c)) != + sizeof(c))) break; - writel(*(unsigned int *)&xmit->buf[xmit->tail], - port->membase + UART_TX_BURST_FIFO); + writel(c, port->membase + UART_TX_BURST_FIFO); *valid_byte_count -= UART_BURST_SIZE; *data_empty_count -= UART_BURST_SIZE; valid_burst_count -= UART_BYTE_SIZE; - - xmit->tail = (xmit->tail + UART_BURST_SIZE) & - (UART_XMIT_SIZE - 1); } while (*valid_byte_count) { - if (*data_empty_count - UART_BYTE_SIZE < 0) + u8 c; + + if (!kfifo_get(&tport->xmit_fifo, &c)) break; - writeb(xmit->buf[xmit->tail], port->membase + - UART_TX_BYTE_FIFO); + writeb(c, port->membase + UART_TX_BYTE_FIFO); *data_empty_count -= UART_BYTE_SIZE; *valid_byte_count -= UART_BYTE_SIZE; /* - * When the tail of the circular buffer is reached, the next - * byte is transferred to the beginning of the buffer. - */ - xmit->tail = (xmit->tail + UART_BYTE_SIZE) & - (UART_XMIT_SIZE - 1); - - /* * If there are any pending burst count, data is handled by * transmitting DWORDs at a time. */ - if (valid_burst_count && (xmit->tail < - (UART_XMIT_SIZE - UART_BURST_SIZE))) + if (valid_burst_count && + kfifo_len(&tport->xmit_fifo) >= UART_BURST_SIZE) break; } } @@ -437,11 +486,9 @@ static void pci1xxxx_process_write_data(struct uart_port *port, static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status) { struct uart_8250_port *up = up_to_u8250p(port); + struct tty_port *tport = &port->state->port; u32 valid_byte_count; int data_empty_count; - struct circ_buf *xmit; - - xmit = &port->state->xmit; if (port->x_char) { writeb(port->x_char, port->membase + UART_TX); @@ -450,25 +497,25 @@ static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status) return; } - if ((uart_tx_stopped(port)) || (uart_circ_empty(xmit))) { + if ((uart_tx_stopped(port)) || kfifo_is_empty(&tport->xmit_fifo)) { port->ops->stop_tx(port); } else { data_empty_count = (pci1xxxx_read_burst_status(port) & UART_BST_STAT_TX_COUNT_MASK) >> 8; do { - valid_byte_count = uart_circ_chars_pending(xmit); + valid_byte_count = kfifo_len(&tport->xmit_fifo); - pci1xxxx_process_write_data(port, xmit, + pci1xxxx_process_write_data(port, &data_empty_count, &valid_byte_count); port->icount.tx++; - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) break; } while (data_empty_count && valid_byte_count); } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); /* @@ -476,7 +523,8 @@ static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status) * the HW can go idle. So we get here once again with empty FIFO and * disable the interrupt and RPM in __stop_tx() */ - if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM)) + if (kfifo_is_empty(&tport->xmit_fifo) && + !(up->capabilities & UART_CAP_RPM)) port->ops->stop_tx(port); } @@ -573,6 +621,10 @@ static int pci1xxxx_suspend(struct device *dev) } data = readl(p + UART_RESET_REG); + + if (priv->dev_rev >= 0xC0) + data |= UART_RESET_HOT_RESET_DISABLE; + writel(data | UART_RESET_D3_RESET_DISABLE, p + UART_RESET_REG); if (wakeup) @@ -600,7 +652,12 @@ static int pci1xxxx_resume(struct device *dev) } data = readl(p + UART_RESET_REG); + + if (priv->dev_rev >= 0xC0) + data &= ~UART_RESET_HOT_RESET_DISABLE; + writel(data & ~UART_RESET_D3_RESET_DISABLE, p + UART_RESET_REG); + iounmap(p); for (i = 0; i < priv->nr; i++) { @@ -637,9 +694,14 @@ static int pci1xxxx_setup(struct pci_dev *pdev, port->port.rs485_config = pci1xxxx_rs485_config; port->port.rs485_supported = pci1xxxx_rs485_supported; - /* From C0 rev Burst operation is supported */ + /* + * C0 and later revisions support Burst operation. + * RTS workaround in mctrl is applicable only to B0. + */ if (rev >= 0xC0) port->port.handle_irq = pci1xxxx_handle_irq; + else if (rev == 0xB0) + port->port.set_mctrl = pci1xxxx_set_mctrl; ret = serial8250_pci_setup_port(pdev, port, 0, PORT_OFFSET * port_idx, 0); if (ret < 0) @@ -818,7 +880,7 @@ module_pci_driver(pci1xxxx_pci_driver); static_assert((ARRAY_SIZE(logical_to_physical_port_idx) == PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p3 + 1)); -MODULE_IMPORT_NS(SERIAL_8250_PCI); +MODULE_IMPORT_NS("SERIAL_8250_PCI"); MODULE_DESCRIPTION("Microchip Technology Inc. PCIe to UART module"); MODULE_AUTHOR("Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>"); MODULE_AUTHOR("Tharun Kumar P <tharunkumar.pasumarthi@microchip.com>"); diff --git a/drivers/tty/serial/8250/8250_pcilib.c b/drivers/tty/serial/8250/8250_pcilib.c index d234e9194feb..d8d0ae0d7238 100644 --- a/drivers/tty/serial/8250/8250_pcilib.c +++ b/drivers/tty/serial/8250/8250_pcilib.c @@ -12,6 +12,15 @@ #include "8250.h" #include "8250_pcilib.h" +int serial_8250_warn_need_ioport(struct pci_dev *dev) +{ + dev_warn(&dev->dev, + "Serial port not supported because of missing I/O resource\n"); + + return -ENXIO; +} +EXPORT_SYMBOL_NS_GPL(serial_8250_warn_need_ioport, "SERIAL_8250_PCI"); + int serial8250_pci_setup_port(struct pci_dev *dev, struct uart_8250_port *port, u8 bar, unsigned int offset, int regshift) { @@ -27,14 +36,17 @@ int serial8250_pci_setup_port(struct pci_dev *dev, struct uart_8250_port *port, port->port.mapbase = pci_resource_start(dev, bar) + offset; port->port.membase = pcim_iomap_table(dev)[bar] + offset; port->port.regshift = regshift; - } else { + } else if (IS_ENABLED(CONFIG_HAS_IOPORT)) { port->port.iotype = UPIO_PORT; port->port.iobase = pci_resource_start(dev, bar) + offset; port->port.mapbase = 0; port->port.membase = NULL; port->port.regshift = 0; + } else { + return serial_8250_warn_need_ioport(dev); } return 0; } -EXPORT_SYMBOL_NS_GPL(serial8250_pci_setup_port, SERIAL_8250_PCI); +EXPORT_SYMBOL_NS_GPL(serial8250_pci_setup_port, "SERIAL_8250_PCI"); +MODULE_DESCRIPTION("8250 PCI library"); MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_pcilib.h b/drivers/tty/serial/8250/8250_pcilib.h index 1aaf1b50ce9c..16a274574cde 100644 --- a/drivers/tty/serial/8250/8250_pcilib.h +++ b/drivers/tty/serial/8250/8250_pcilib.h @@ -13,3 +13,5 @@ struct uart_8250_port; int serial8250_pci_setup_port(struct pci_dev *dev, struct uart_8250_port *port, u8 bar, unsigned int offset, int regshift); + +int serial_8250_warn_need_ioport(struct pci_dev *dev); diff --git a/drivers/tty/serial/8250/8250_platform.c b/drivers/tty/serial/8250/8250_platform.c new file mode 100644 index 000000000000..c0343bfb8064 --- /dev/null +++ b/drivers/tty/serial/8250/8250_platform.c @@ -0,0 +1,414 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Universal/legacy platform driver for 8250/16550-type serial ports + * + * Supports: + * ISA-compatible 8250/16550 ports + * ACPI 8250/16550 ports + * PNP 8250/16550 ports + * "serial8250" platform devices + */ +#include <linux/acpi.h> +#include <linux/array_size.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/once.h> +#include <linux/platform_device.h> + +#include <linux/serial_8250.h> + +#ifdef CONFIG_SPARC +#include <linux/sunserialcore.h> +#endif + +#include "8250.h" + +/* + * Configuration: + * share_irqs: Whether we pass IRQF_SHARED to request_irq(). + * This option is unsafe when used on edge-triggered interrupts. + * skip_txen_test: Force skip of txen test at init time. + */ +unsigned int share_irqs = SERIAL8250_SHARE_IRQS; +unsigned int skip_txen_test; + +unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS; + +#include <asm/serial.h> + +/* + * SERIAL_PORT_DFNS tells us about built-in ports that have no + * standard enumeration mechanism. Platforms that can find all + * serial ports via mechanisms like ACPI or PCI need not supply it. + */ +#ifndef SERIAL_PORT_DFNS +#define SERIAL_PORT_DFNS +#endif + +static const struct old_serial_port old_serial_port[] = { + SERIAL_PORT_DFNS /* defined in asm/serial.h */ +}; + +serial8250_isa_config_fn serial8250_isa_config; +void serial8250_set_isa_configurator(serial8250_isa_config_fn v) +{ + serial8250_isa_config = v; +} +EXPORT_SYMBOL(serial8250_set_isa_configurator); + +static void __init __serial8250_isa_init_ports(void) +{ + int i, irqflag = 0; + + if (nr_uarts > UART_NR) + nr_uarts = UART_NR; + + /* + * Set up initial ISA ports based on nr_uart module param, or else + * default to CONFIG_SERIAL_8250_RUNTIME_UARTS. Note that we do not + * need to increase nr_uarts when setting up the initial ISA ports. + */ + for (i = 0; i < nr_uarts; i++) + serial8250_setup_port(i); + + /* chain base port ops to support Remote Supervisor Adapter */ + univ8250_port_ops = *univ8250_port_base_ops; + univ8250_rsa_support(&univ8250_port_ops); + + if (share_irqs) + irqflag = IRQF_SHARED; + + for (i = 0; i < ARRAY_SIZE(old_serial_port) && i < nr_uarts; i++) { + struct uart_8250_port *up = serial8250_get_port(i); + struct uart_port *port = &up->port; + + port->iobase = old_serial_port[i].port; + port->irq = irq_canonicalize(old_serial_port[i].irq); + port->irqflags = 0; + port->uartclk = old_serial_port[i].baud_base * 16; + port->flags = old_serial_port[i].flags; + port->hub6 = 0; + port->membase = old_serial_port[i].iomem_base; + port->iotype = old_serial_port[i].io_type; + port->regshift = old_serial_port[i].iomem_reg_shift; + + port->irqflags |= irqflag; + if (serial8250_isa_config != NULL) + serial8250_isa_config(i, &up->port, &up->capabilities); + } +} + +void __init serial8250_isa_init_ports(void) +{ + DO_ONCE(__serial8250_isa_init_ports); +} + +/* + * Generic 16550A platform devices + */ +static int serial8250_probe_acpi(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct uart_8250_port uart = { }; + struct resource *regs; + int ret, line; + + regs = platform_get_mem_or_io(pdev, 0); + if (!regs) + return dev_err_probe(dev, -EINVAL, "no registers defined\n"); + + switch (resource_type(regs)) { + case IORESOURCE_IO: + uart.port.iobase = regs->start; + break; + case IORESOURCE_MEM: + uart.port.mapbase = regs->start; + uart.port.mapsize = resource_size(regs); + uart.port.flags = UPF_IOREMAP; + break; + default: + return -EINVAL; + } + + /* default clock frequency */ + uart.port.uartclk = 1843200; + uart.port.type = PORT_16550A; + uart.port.dev = &pdev->dev; + uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; + + ret = uart_read_and_validate_port_properties(&uart.port); + /* no interrupt -> fall back to polling */ + if (ret == -ENXIO) + ret = 0; + if (ret) + return ret; + + line = serial8250_register_8250_port(&uart); + if (line < 0) + return line; + + return 0; +} + +static int serial8250_probe_platform(struct platform_device *dev, struct plat_serial8250_port *p) +{ + struct uart_8250_port uart; + int ret, i, irqflag = 0; + + memset(&uart, 0, sizeof(uart)); + + if (share_irqs) + irqflag = IRQF_SHARED; + + for (i = 0; p && p->flags != 0; p++, i++) { + uart.port.iobase = p->iobase; + uart.port.membase = p->membase; + uart.port.irq = p->irq; + uart.port.irqflags = p->irqflags; + uart.port.uartclk = p->uartclk; + uart.port.regshift = p->regshift; + uart.port.iotype = p->iotype; + uart.port.flags = p->flags; + uart.port.mapbase = p->mapbase; + uart.port.mapsize = p->mapsize; + uart.port.hub6 = p->hub6; + uart.port.has_sysrq = p->has_sysrq; + uart.port.private_data = p->private_data; + uart.port.type = p->type; + uart.bugs = p->bugs; + uart.port.serial_in = p->serial_in; + uart.port.serial_out = p->serial_out; + uart.dl_read = p->dl_read; + uart.dl_write = p->dl_write; + uart.port.handle_irq = p->handle_irq; + uart.port.handle_break = p->handle_break; + uart.port.set_termios = p->set_termios; + uart.port.set_ldisc = p->set_ldisc; + uart.port.get_mctrl = p->get_mctrl; + uart.port.pm = p->pm; + uart.port.dev = &dev->dev; + uart.port.irqflags |= irqflag; + ret = serial8250_register_8250_port(&uart); + if (ret < 0) { + dev_err(&dev->dev, "unable to register port at index %d " + "(IO%lx MEM%llx IRQ%d): %d\n", i, + p->iobase, (unsigned long long)p->mapbase, + p->irq, ret); + } + } + return 0; +} + +/* + * Register a set of serial devices attached to a platform device. + * The list is terminated with a zero flags entry, which means we expect + * all entries to have at least UPF_BOOT_AUTOCONF set. + */ +static int serial8250_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct plat_serial8250_port *p; + + p = dev_get_platdata(dev); + if (p) + return serial8250_probe_platform(pdev, p); + + /* + * Probe platform UART devices defined using standard hardware + * discovery mechanism like ACPI or DT. Support only ACPI based + * serial device for now. + */ + if (has_acpi_companion(dev)) + return serial8250_probe_acpi(pdev); + + return 0; +} + +/* + * Remove serial ports registered against a platform device. + */ +static void serial8250_remove(struct platform_device *dev) +{ + int i; + + for (i = 0; i < nr_uarts; i++) { + struct uart_8250_port *up = serial8250_get_port(i); + + if (up->port.dev == &dev->dev) + serial8250_unregister_port(i); + } +} + +static int serial8250_suspend(struct platform_device *dev, pm_message_t state) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + struct uart_8250_port *up = serial8250_get_port(i); + + if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) + uart_suspend_port(&serial8250_reg, &up->port); + } + + return 0; +} + +static int serial8250_resume(struct platform_device *dev) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + struct uart_8250_port *up = serial8250_get_port(i); + + if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) + serial8250_resume_port(i); + } + + return 0; +} + +static const struct acpi_device_id acpi_platform_serial_table[] = { + { "RSCV0003" }, /* RISC-V Generic 16550A UART */ + { } +}; +MODULE_DEVICE_TABLE(acpi, acpi_platform_serial_table); + +static struct platform_driver serial8250_isa_driver = { + .probe = serial8250_probe, + .remove = serial8250_remove, + .suspend = serial8250_suspend, + .resume = serial8250_resume, + .driver = { + .name = "serial8250", + .acpi_match_table = acpi_platform_serial_table, + }, +}; + +/* + * This "device" covers _all_ ISA 8250-compatible serial devices listed + * in the table in include/asm/serial.h. + */ +struct platform_device *serial8250_isa_devs; + +static int __init serial8250_init(void) +{ + int ret; + + if (nr_uarts == 0) + return -ENODEV; + + serial8250_isa_init_ports(); + + pr_info("Serial: 8250/16550 driver, %d ports, IRQ sharing %s\n", + nr_uarts, str_enabled_disabled(share_irqs)); + +#ifdef CONFIG_SPARC + ret = sunserial_register_minors(&serial8250_reg, UART_NR); +#else + serial8250_reg.nr = UART_NR; + ret = uart_register_driver(&serial8250_reg); +#endif + if (ret) + goto out; + + ret = serial8250_pnp_init(); + if (ret) + goto unreg_uart_drv; + + serial8250_isa_devs = platform_device_alloc("serial8250", PLAT8250_DEV_LEGACY); + if (!serial8250_isa_devs) { + ret = -ENOMEM; + goto unreg_pnp; + } + + ret = platform_device_add(serial8250_isa_devs); + if (ret) + goto put_dev; + + serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev); + + ret = platform_driver_register(&serial8250_isa_driver); + if (ret == 0) + goto out; + + platform_device_del(serial8250_isa_devs); +put_dev: + platform_device_put(serial8250_isa_devs); +unreg_pnp: + serial8250_pnp_exit(); +unreg_uart_drv: +#ifdef CONFIG_SPARC + sunserial_unregister_minors(&serial8250_reg, UART_NR); +#else + uart_unregister_driver(&serial8250_reg); +#endif +out: + return ret; +} +module_init(serial8250_init); + +static void __exit serial8250_exit(void) +{ + struct platform_device *isa_dev = serial8250_isa_devs; + + /* + * This tells serial8250_unregister_port() not to re-register + * the ports (thereby making serial8250_isa_driver permanently + * in use). + */ + serial8250_isa_devs = NULL; + + platform_driver_unregister(&serial8250_isa_driver); + platform_device_unregister(isa_dev); + + serial8250_pnp_exit(); + +#ifdef CONFIG_SPARC + sunserial_unregister_minors(&serial8250_reg, UART_NR); +#else + uart_unregister_driver(&serial8250_reg); +#endif +} +module_exit(serial8250_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 serial platform driver"); + +module_param_hw(share_irqs, uint, other, 0644); +MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices (unsafe)"); + +module_param(nr_uarts, uint, 0644); +MODULE_PARM_DESC(nr_uarts, "Maximum number of UARTs supported. (1-" __MODULE_STRING(CONFIG_SERIAL_8250_NR_UARTS) ")"); + +module_param(skip_txen_test, uint, 0644); +MODULE_PARM_DESC(skip_txen_test, "Skip checking for the TXEN bug at init time"); + +MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR); + +#ifdef CONFIG_SERIAL_8250_DEPRECATED_OPTIONS +#ifndef MODULE +/* + * This module was renamed to 8250_core in 3.7. Keep the old "8250" name + * working as well for the module options so we don't break people. We + * need to keep the names identical and the convenient macros will happily + * refuse to let us do that by failing the build with redefinition errors + * of global variables. So we stick them inside a dummy function to avoid + * those conflicts. The options still get parsed, and the redefined + * MODULE_PARAM_PREFIX lets us keep the "8250." syntax alive. + * + * This is hacky. I'm sorry. + */ +static void __used s8250_options(void) +{ +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "8250_core." + + module_param_cb(share_irqs, ¶m_ops_uint, &share_irqs, 0644); + module_param_cb(nr_uarts, ¶m_ops_uint, &nr_uarts, 0644); + module_param_cb(skip_txen_test, ¶m_ops_uint, &skip_txen_test, 0644); +} +#else +MODULE_ALIAS("8250_core"); +#endif +#endif diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 1974bbadc975..7a837fdf9df1 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -10,6 +10,7 @@ */ #include <linux/module.h> #include <linux/pci.h> +#include <linux/pm.h> #include <linux/pnp.h> #include <linux/string.h> #include <linux/kernel.h> @@ -434,7 +435,8 @@ static int serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) { struct uart_8250_port uart, *port; - int ret, line, flags = dev_id->driver_data; + int ret, flags = dev_id->driver_data; + long line; if (flags & UNKNOWN_DEV) { ret = serial_pnp_guess_board(dev); @@ -443,37 +445,37 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) } memset(&uart, 0, sizeof(uart)); - if (pnp_irq_valid(dev, 0)) - uart.port.irq = pnp_irq(dev, 0); if ((flags & CIR_PORT) && pnp_port_valid(dev, 2)) { uart.port.iobase = pnp_port_start(dev, 2); - uart.port.iotype = UPIO_PORT; } else if (pnp_port_valid(dev, 0)) { uart.port.iobase = pnp_port_start(dev, 0); - uart.port.iotype = UPIO_PORT; } else if (pnp_mem_valid(dev, 0)) { uart.port.mapbase = pnp_mem_start(dev, 0); - uart.port.iotype = UPIO_MEM; + uart.port.mapsize = pnp_mem_len(dev, 0); uart.port.flags = UPF_IOREMAP; } else return -ENODEV; - dev_dbg(&dev->dev, - "Setup PNP port: port %#lx, mem %#llx, irq %u, type %u\n", - uart.port.iobase, (unsigned long long)uart.port.mapbase, - uart.port.irq, uart.port.iotype); + uart.port.uartclk = 1843200; + uart.port.dev = &dev->dev; + uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; + + ret = uart_read_port_properties(&uart.port); + /* no interrupt -> fall back to polling */ + if (ret == -ENXIO) + ret = 0; + if (ret) + return ret; if (flags & CIR_PORT) { uart.port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE; uart.port.type = PORT_8250_CIR; } - uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; - if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE) - uart.port.flags |= UPF_SHARE_IRQ; - uart.port.uartclk = 1843200; - device_property_read_u32(&dev->dev, "clock-frequency", &uart.port.uartclk); - uart.port.dev = &dev->dev; + dev_dbg(&dev->dev, + "Setup PNP port: port %#lx, mem %#llx, size %#llx, irq %u, type %u\n", + uart.port.iobase, (unsigned long long)uart.port.mapbase, + (unsigned long long)uart.port.mapsize, uart.port.irq, uart.port.iotype); line = serial8250_register_8250_port(&uart); if (line < 0 || (flags & CIR_PORT)) @@ -483,7 +485,7 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) if (uart_console(&port->port)) dev->capabilities |= PNP_CONSOLE; - pnp_set_drvdata(dev, (void *)((long)line + 1)); + pnp_set_drvdata(dev, (void *)line); return 0; } @@ -492,38 +494,33 @@ static void serial_pnp_remove(struct pnp_dev *dev) long line = (long)pnp_get_drvdata(dev); dev->capabilities &= ~PNP_CONSOLE; - if (line) - serial8250_unregister_port(line - 1); + serial8250_unregister_port(line); } -static int __maybe_unused serial_pnp_suspend(struct device *dev) +static int serial_pnp_suspend(struct device *dev) { long line = (long)dev_get_drvdata(dev); - if (!line) - return -ENODEV; - serial8250_suspend_port(line - 1); + serial8250_suspend_port(line); return 0; } -static int __maybe_unused serial_pnp_resume(struct device *dev) +static int serial_pnp_resume(struct device *dev) { long line = (long)dev_get_drvdata(dev); - if (!line) - return -ENODEV; - serial8250_resume_port(line - 1); + serial8250_resume_port(line); return 0; } -static SIMPLE_DEV_PM_OPS(serial_pnp_pm_ops, serial_pnp_suspend, serial_pnp_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(serial_pnp_pm_ops, serial_pnp_suspend, serial_pnp_resume); static struct pnp_driver serial_pnp_driver = { .name = "serial", .probe = serial_pnp_probe, .remove = serial_pnp_remove, .driver = { - .pm = &serial_pnp_pm_ops, + .pm = pm_sleep_ptr(&serial_pnp_pm_ops), }, .id_table = pnp_dev_table, }; diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index fc9dd5d45295..6d7b8c4667c9 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -338,6 +338,7 @@ static void default_serial_dl_write(struct uart_8250_port *up, u32 value) serial_out(up, UART_DLM, value >> 8 & 0xff); } +#ifdef CONFIG_HAS_IOPORT static unsigned int hub6_serial_in(struct uart_port *p, int offset) { offset = offset << p->regshift; @@ -351,6 +352,7 @@ static void hub6_serial_out(struct uart_port *p, int offset, int value) outb(p->hub6 - 1 + offset, p->iobase); outb(value, p->iobase + 1); } +#endif /* CONFIG_HAS_IOPORT */ static unsigned int mem_serial_in(struct uart_port *p, int offset) { @@ -400,6 +402,7 @@ static unsigned int mem32be_serial_in(struct uart_port *p, int offset) return ioread32be(p->membase + offset); } +#ifdef CONFIG_HAS_IOPORT static unsigned int io_serial_in(struct uart_port *p, int offset) { offset = offset << p->regshift; @@ -411,6 +414,15 @@ static void io_serial_out(struct uart_port *p, int offset, int value) offset = offset << p->regshift; outb(value, p->iobase + offset); } +#endif +static unsigned int no_serial_in(struct uart_port *p, int offset) +{ + return (unsigned int)-1; +} + +static void no_serial_out(struct uart_port *p, int offset, int value) +{ +} static int serial8250_default_handle_irq(struct uart_port *port); @@ -422,10 +434,12 @@ static void set_io_from_upio(struct uart_port *p) up->dl_write = default_serial_dl_write; switch (p->iotype) { +#ifdef CONFIG_HAS_IOPORT case UPIO_HUB6: p->serial_in = hub6_serial_in; p->serial_out = hub6_serial_out; break; +#endif case UPIO_MEM: p->serial_in = mem_serial_in; @@ -446,11 +460,17 @@ static void set_io_from_upio(struct uart_port *p) p->serial_in = mem32be_serial_in; p->serial_out = mem32be_serial_out; break; - - default: +#ifdef CONFIG_HAS_IOPORT + case UPIO_PORT: p->serial_in = io_serial_in; p->serial_out = io_serial_out; break; +#endif + default: + WARN(p->iotype != UPIO_PORT || p->iobase, + "Unsupported UART type %x\n", p->iotype); + p->serial_in = no_serial_in; + p->serial_out = no_serial_out; } /* Remember loaded iotype */ up->cur_iotype = p->iotype; @@ -497,22 +517,20 @@ void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p) } EXPORT_SYMBOL_GPL(serial8250_clear_and_reinit_fifos); -void serial8250_rpm_get(struct uart_8250_port *p) +static void serial8250_rpm_get(struct uart_8250_port *p) { if (!(p->capabilities & UART_CAP_RPM)) return; pm_runtime_get_sync(p->port.dev); } -EXPORT_SYMBOL_GPL(serial8250_rpm_get); -void serial8250_rpm_put(struct uart_8250_port *p) +static void serial8250_rpm_put(struct uart_8250_port *p) { if (!(p->capabilities & UART_CAP_RPM)) return; pm_runtime_mark_last_busy(p->port.dev); pm_runtime_put_autosuspend(p->port.dev); } -EXPORT_SYMBOL_GPL(serial8250_rpm_put); /** * serial8250_em485_init() - put uart_8250_port into rs485 emulating @@ -546,19 +564,17 @@ static int serial8250_em485_init(struct uart_8250_port *p) if (!p->em485) return -ENOMEM; - hrtimer_init(&p->em485->stop_tx_timer, CLOCK_MONOTONIC, - HRTIMER_MODE_REL); - hrtimer_init(&p->em485->start_tx_timer, CLOCK_MONOTONIC, - HRTIMER_MODE_REL); - p->em485->stop_tx_timer.function = &serial8250_em485_handle_stop_tx; - p->em485->start_tx_timer.function = &serial8250_em485_handle_start_tx; + hrtimer_setup(&p->em485->stop_tx_timer, &serial8250_em485_handle_stop_tx, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + hrtimer_setup(&p->em485->start_tx_timer, &serial8250_em485_handle_start_tx, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); p->em485->port = p; p->em485->active_timer = NULL; p->em485->tx_stopped = true; deassert_rts: if (p->em485->tx_stopped) - p->rs485_stop_tx(p); + p->rs485_stop_tx(p, true); return 0; } @@ -612,13 +628,6 @@ int serial8250_em485_config(struct uart_port *port, struct ktermios *termios, { struct uart_8250_port *up = up_to_u8250p(port); - /* pick sane settings if the user hasn't */ - if (!!(rs485->flags & SER_RS485_RTS_ON_SEND) == - !!(rs485->flags & SER_RS485_RTS_AFTER_SEND)) { - rs485->flags |= SER_RS485_RTS_ON_SEND; - rs485->flags &= ~SER_RS485_RTS_AFTER_SEND; - } - /* * Both serial8250_em485_init() and serial8250_em485_destroy() * are idempotent. @@ -636,7 +645,7 @@ EXPORT_SYMBOL_GPL(serial8250_em485_config); * once and disable_runtime_pm_tx() will still disable RPM because the fifo is * empty and the HW can idle again. */ -void serial8250_rpm_get_tx(struct uart_8250_port *p) +static void serial8250_rpm_get_tx(struct uart_8250_port *p) { unsigned char rpm_active; @@ -648,9 +657,8 @@ void serial8250_rpm_get_tx(struct uart_8250_port *p) return; pm_runtime_get_sync(p->port.dev); } -EXPORT_SYMBOL_GPL(serial8250_rpm_get_tx); -void serial8250_rpm_put_tx(struct uart_8250_port *p) +static void serial8250_rpm_put_tx(struct uart_8250_port *p) { unsigned char rpm_active; @@ -663,7 +671,6 @@ void serial8250_rpm_put_tx(struct uart_8250_port *p) pm_runtime_mark_last_busy(p->port.dev); pm_runtime_put_autosuspend(p->port.dev); } -EXPORT_SYMBOL_GPL(serial8250_rpm_put_tx); /* * IER sleep support. UARTs which have EFRs need the "extended @@ -1181,7 +1188,7 @@ static void autoconfig(struct uart_8250_port *up) */ scratch = serial_in(up, UART_IER); serial_out(up, UART_IER, 0); -#ifdef __i386__ +#if defined(__i386__) && defined(CONFIG_HAS_IOPORT) outb(0xff, 0x080); #endif /* @@ -1190,7 +1197,7 @@ static void autoconfig(struct uart_8250_port *up) */ scratch2 = serial_in(up, UART_IER) & UART_IER_ALL_INTR; serial_out(up, UART_IER, UART_IER_ALL_INTR); -#ifdef __i386__ +#if defined(__i386__) && defined(CONFIG_HAS_IOPORT) outb(0, 0x080); #endif scratch3 = serial_in(up, UART_IER) & UART_IER_ALL_INTR; @@ -1377,7 +1384,6 @@ static void serial8250_stop_rx(struct uart_port *port) serial8250_rpm_get(up); up->ier &= ~(UART_IER_RLSI | UART_IER_RDI); - up->port.read_status_mask &= ~UART_LSR_DR; serial_port_out(port, UART_IER, up->ier); serial8250_rpm_put(up); @@ -1386,10 +1392,11 @@ static void serial8250_stop_rx(struct uart_port *port) /** * serial8250_em485_stop_tx() - generic ->rs485_stop_tx() callback * @p: uart 8250 port + * @toggle_ier: true to allow enabling receive interrupts * * Generic callback usable by 8250 uart drivers to stop rs485 transmission. */ -void serial8250_em485_stop_tx(struct uart_8250_port *p) +void serial8250_em485_stop_tx(struct uart_8250_port *p, bool toggle_ier) { unsigned char mcr = serial8250_in_MCR(p); @@ -1410,8 +1417,10 @@ void serial8250_em485_stop_tx(struct uart_8250_port *p) if (!(p->port.rs485.flags & SER_RS485_RX_DURING_TX)) { serial8250_clear_and_reinit_fifos(p); - p->ier |= UART_IER_RLSI | UART_IER_RDI; - serial_port_out(&p->port, UART_IER, p->ier); + if (toggle_ier) { + p->ier |= UART_IER_RLSI | UART_IER_RDI; + serial_port_out(&p->port, UART_IER, p->ier); + } } } EXPORT_SYMBOL_GPL(serial8250_em485_stop_tx); @@ -1426,7 +1435,7 @@ static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t) serial8250_rpm_get(p); uart_port_lock_irqsave(&p->port, &flags); if (em485->active_timer == &em485->stop_tx_timer) { - p->rs485_stop_tx(p); + p->rs485_stop_tx(p, true); em485->active_timer = NULL; em485->tx_stopped = true; } @@ -1458,7 +1467,7 @@ static void __stop_tx_rs485(struct uart_8250_port *p, u64 stop_delay) em485->active_timer = &em485->stop_tx_timer; hrtimer_start(&em485->stop_tx_timer, ns_to_ktime(stop_delay), HRTIMER_MODE_REL); } else { - p->rs485_stop_tx(p); + p->rs485_stop_tx(p, true); em485->active_timer = NULL; em485->tx_stopped = true; } @@ -1547,6 +1556,7 @@ static inline void __start_tx(struct uart_port *port) /** * serial8250_em485_start_tx() - generic ->rs485_start_tx() callback * @up: uart 8250 port + * @toggle_ier: true to allow disabling receive interrupts * * Generic callback usable by 8250 uart drivers to start rs485 transmission. * Assumes that setting the RTS bit in the MCR register means RTS is high. @@ -1554,11 +1564,11 @@ static inline void __start_tx(struct uart_port *port) * stoppable by disabling the UART_IER_RDI interrupt. (Some chips set the * UART_LSR_DR bit even when UART_IER_RDI is disabled, foiling this approach.) */ -void serial8250_em485_start_tx(struct uart_8250_port *up) +void serial8250_em485_start_tx(struct uart_8250_port *up, bool toggle_ier) { unsigned char mcr = serial8250_in_MCR(up); - if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) + if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX) && toggle_ier) serial8250_stop_rx(&up->port); if (up->port.rs485.flags & SER_RS485_RTS_ON_SEND) @@ -1592,7 +1602,7 @@ static bool start_tx_rs485(struct uart_port *port) if (em485->tx_stopped) { em485->tx_stopped = false; - up->rs485_start_tx(up); + up->rs485_start_tx(up, true); if (up->port.rs485.delay_rts_before_send > 0) { em485->active_timer = &em485->start_tx_timer; @@ -1630,7 +1640,7 @@ static void serial8250_start_tx(struct uart_port *port) /* Port locked to synchronize UART_IER access against the console. */ lockdep_assert_held_once(&port->lock); - if (!port->x_char && uart_circ_empty(&port->state->xmit)) + if (!port->x_char && kfifo_is_empty(&port->state->port.xmit_fifo)) return; serial8250_rpm_get_tx(up); @@ -1664,7 +1674,7 @@ static void serial8250_disable_ms(struct uart_port *port) if (up->bugs & UART_BUG_NOMSR) return; - mctrl_gpio_disable_ms(up->gpios); + mctrl_gpio_disable_ms_no_sync(up->gpios); up->ier &= ~UART_IER_MSI; serial_port_out(port, UART_IER, up->ier); @@ -1778,7 +1788,7 @@ EXPORT_SYMBOL_GPL(serial8250_rx_chars); void serial8250_tx_chars(struct uart_8250_port *up) { struct uart_port *port = &up->port; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; int count; if (port->x_char) { @@ -1789,14 +1799,19 @@ void serial8250_tx_chars(struct uart_8250_port *up) serial8250_stop_tx(port); return; } - if (uart_circ_empty(xmit)) { + if (kfifo_is_empty(&tport->xmit_fifo)) { __stop_tx(up); return; } count = up->tx_loadsz; do { - serial_out(up, UART_TX, xmit->buf[xmit->tail]); + unsigned char c; + + if (!uart_fifo_get(port, &c)) + break; + + serial_out(up, UART_TX, c); if (up->bugs & UART_BUG_TXRACE) { /* * The Aspeed BMC virtual UARTs have a bug where data @@ -1809,9 +1824,7 @@ void serial8250_tx_chars(struct uart_8250_port *up) */ serial_in(up, UART_SCR); } - uart_xmit_advance(port, 1); - if (uart_circ_empty(xmit)) - break; + if ((up->capabilities & UART_CAP_HFIFO) && !uart_lsr_tx_empty(serial_in(up, UART_LSR))) break; @@ -1821,7 +1834,7 @@ void serial8250_tx_chars(struct uart_8250_port *up) break; } while (--count > 0); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); /* @@ -1829,7 +1842,8 @@ void serial8250_tx_chars(struct uart_8250_port *up) * HW can go idle. So we get here once again with empty FIFO and disable * the interrupt and RPM in __stop_tx() */ - if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM)) + if (kfifo_is_empty(&tport->xmit_fifo) && + !(up->capabilities & UART_CAP_RPM)) __stop_tx(up); } EXPORT_SYMBOL_GPL(serial8250_tx_chars); @@ -1914,7 +1928,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) */ if (!(status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)) && (port->status & (UPSTAT_AUTOCTS | UPSTAT_AUTORTS)) && - !(port->read_status_mask & UART_LSR_DR)) + !(up->ier & (UART_IER_RLSI | UART_IER_RDI))) skip_rx = true; if (status & (UART_LSR_DR | UART_LSR_BI) && !skip_rx) { @@ -2062,11 +2076,20 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state) serial8250_rpm_put(up); } -static void wait_for_lsr(struct uart_8250_port *up, int bits) +/* Returns true if @bits were set, false on timeout */ +static bool wait_for_lsr(struct uart_8250_port *up, int bits) { - unsigned int status, tmout = 10000; + unsigned int status, tmout; + + /* + * Wait for a character to be sent. Fallback to a safe default + * timeout value if @frame_time is not available. + */ + if (up->port.frame_time) + tmout = up->port.frame_time * 2 / NSEC_PER_USEC; + else + tmout = 10000; - /* Wait up to 10ms for the character(s) to be sent. */ for (;;) { status = serial_lsr_in(up); @@ -2077,11 +2100,11 @@ static void wait_for_lsr(struct uart_8250_port *up, int bits) udelay(1); touch_nmi_watchdog(); } + + return (tmout != 0); } -/* - * Wait for transmitter & holding register to empty - */ +/* Wait for transmitter and holding register to empty with timeout */ static void wait_for_xmitr(struct uart_8250_port *up, int bits) { unsigned int tmout; @@ -2379,28 +2402,26 @@ int serial8250_do_startup(struct uart_port *port) * test if we receive TX irq. This way, we'll never enable * UART_BUG_TXEN. */ - if (up->port.quirks & UPQ_NO_TXEN_TEST) - goto dont_test_tx_en; - - /* - * Do a quick test to see if we receive an interrupt when we enable - * the TX irq. - */ - serial_port_out(port, UART_IER, UART_IER_THRI); - lsr = serial_port_in(port, UART_LSR); - iir = serial_port_in(port, UART_IIR); - serial_port_out(port, UART_IER, 0); + if (!(up->port.quirks & UPQ_NO_TXEN_TEST)) { + /* + * Do a quick test to see if we receive an interrupt when we + * enable the TX irq. + */ + serial_port_out(port, UART_IER, UART_IER_THRI); + lsr = serial_port_in(port, UART_LSR); + iir = serial_port_in(port, UART_IIR); + serial_port_out(port, UART_IER, 0); - if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) { - if (!(up->bugs & UART_BUG_TXEN)) { - up->bugs |= UART_BUG_TXEN; - dev_dbg(port->dev, "enabling bad tx status workarounds\n"); + if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) { + if (!(up->bugs & UART_BUG_TXEN)) { + up->bugs |= UART_BUG_TXEN; + dev_dbg(port->dev, "enabling bad tx status workarounds\n"); + } + } else { + up->bugs &= ~UART_BUG_TXEN; } - } else { - up->bugs &= ~UART_BUG_TXEN; } -dont_test_tx_en: uart_port_unlock_irqrestore(port, flags); /* @@ -2526,6 +2547,14 @@ static void serial8250_shutdown(struct uart_port *port) serial8250_do_shutdown(port); } +static void serial8250_flush_buffer(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + if (up->dma) + serial8250_tx_dma_flush(up); +} + static unsigned int serial8250_do_get_divisor(struct uart_port *port, unsigned int baud, unsigned int *frac) @@ -2612,7 +2641,7 @@ static unsigned char serial8250_compute_lcr(struct uart_8250_port *up, } void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud, - unsigned int quot, unsigned int quot_frac) + unsigned int quot) { struct uart_8250_port *up = up_to_u8250p(port); @@ -2644,7 +2673,7 @@ static void serial8250_set_divisor(struct uart_port *port, unsigned int baud, if (port->set_divisor) port->set_divisor(port, baud, quot, quot_frac); else - serial8250_do_set_divisor(port, baud, quot, quot_frac); + serial8250_do_set_divisor(port, baud, quot); } static unsigned int serial8250_get_baud_rate(struct uart_port *port, @@ -2769,7 +2798,13 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, */ uart_update_timeout(port, termios->c_cflag, baud); - port->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + /* + * Specify which conditions may be considered for error + * handling and the ignoring of characters. The actual + * ignoring of characters only occurs if the bit is set + * in @ignore_status_mask as well. + */ + port->read_status_mask = UART_LSR_OE | UART_LSR_DR; if (termios->c_iflag & INPCK) port->read_status_mask |= UART_LSR_FE | UART_LSR_PE; if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) @@ -2927,7 +2962,6 @@ static int serial8250_request_std_resource(struct uart_8250_port *up) { unsigned int size = serial8250_port_size(up); struct uart_port *port = &up->port; - int ret = 0; switch (port->iotype) { case UPIO_AU: @@ -2936,32 +2970,30 @@ static int serial8250_request_std_resource(struct uart_8250_port *up) case UPIO_MEM32BE: case UPIO_MEM16: case UPIO_MEM: - if (!port->mapbase) { - ret = -EINVAL; - break; - } + if (!port->mapbase) + return -EINVAL; - if (!request_mem_region(port->mapbase, size, "serial")) { - ret = -EBUSY; - break; - } + if (!request_mem_region(port->mapbase, size, "serial")) + return -EBUSY; if (port->flags & UPF_IOREMAP) { port->membase = ioremap(port->mapbase, size); if (!port->membase) { release_mem_region(port->mapbase, size); - ret = -ENOMEM; + return -ENOMEM; } } - break; - + return 0; case UPIO_HUB6: case UPIO_PORT: if (!request_region(port->iobase, size, "serial")) - ret = -EBUSY; + return -EBUSY; + return 0; + case UPIO_UNKNOWN: break; } - return ret; + + return 0; } static void serial8250_release_std_resource(struct uart_8250_port *up) @@ -2991,6 +3023,8 @@ static void serial8250_release_std_resource(struct uart_8250_port *up) case UPIO_PORT: release_region(port->iobase, size); break; + case UPIO_UNKNOWN: + break; } } @@ -3179,7 +3213,7 @@ static void serial8250_config_port(struct uart_port *port, int flags) static int serial8250_verify_port(struct uart_port *port, struct serial_struct *ser) { - if (ser->irq >= nr_irqs || ser->irq < 0 || + if (ser->irq >= irq_get_nr_irqs() || ser->irq < 0 || ser->baud_base < 9600 || ser->type < PORT_UNKNOWN || ser->type >= ARRAY_SIZE(uart_config) || ser->type == PORT_CIRRUS || ser->type == PORT_STARTECH) @@ -3209,6 +3243,7 @@ static const struct uart_ops serial8250_pops = { .break_ctl = serial8250_break_ctl, .startup = serial8250_startup, .shutdown = serial8250_shutdown, + .flush_buffer = serial8250_flush_buffer, .set_termios = serial8250_set_termios, .set_ldisc = serial8250_set_ldisc, .pm = serial8250_pm, @@ -3233,7 +3268,7 @@ void serial8250_init_port(struct uart_8250_port *up) port->ops = &serial8250_pops; port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE); - up->cur_iotype = 0xFF; + up->cur_iotype = UPIO_UNKNOWN; } EXPORT_SYMBOL_GPL(serial8250_init_port); @@ -3268,10 +3303,15 @@ EXPORT_SYMBOL_GPL(serial8250_set_defaults); static void serial8250_console_putchar(struct uart_port *port, unsigned char ch) { + serial_port_out(port, UART_TX, ch); +} + +static void serial8250_console_wait_putchar(struct uart_port *port, unsigned char ch) +{ struct uart_8250_port *up = up_to_u8250p(port); wait_for_xmitr(up, UART_LSR_THRE); - serial_port_out(port, UART_TX, ch); + serial8250_console_putchar(port, ch); } /* @@ -3300,6 +3340,16 @@ static void serial8250_console_restore(struct uart_8250_port *up) serial8250_out_MCR(up, up->mcr | UART_MCR_DTR | UART_MCR_RTS); } +static void fifo_wait_for_lsr(struct uart_8250_port *up, unsigned int count) +{ + unsigned int i; + + for (i = 0; i < count; i++) { + if (wait_for_lsr(up, UART_LSR_THRE)) + return; + } +} + /* * Print a string to the serial port using the device FIFO * @@ -3309,24 +3359,34 @@ static void serial8250_console_restore(struct uart_8250_port *up) static void serial8250_console_fifo_write(struct uart_8250_port *up, const char *s, unsigned int count) { - int i; const char *end = s + count; unsigned int fifosize = up->tx_loadsz; + struct uart_port *port = &up->port; + unsigned int tx_count = 0; bool cr_sent = false; + unsigned int i; while (s != end) { - wait_for_lsr(up, UART_LSR_THRE); + /* Allow timeout for each byte of a possibly full FIFO */ + fifo_wait_for_lsr(up, fifosize); for (i = 0; i < fifosize && s != end; ++i) { if (*s == '\n' && !cr_sent) { - serial_out(up, UART_TX, '\r'); + serial8250_console_putchar(port, '\r'); cr_sent = true; } else { - serial_out(up, UART_TX, *s++); + serial8250_console_putchar(port, *s++); cr_sent = false; } } + tx_count = i; } + + /* + * Allow timeout for each byte written since the caller will only wait + * for UART_LSR_BOTH_EMPTY using the timeout of a single character + */ + fifo_wait_for_lsr(up, tx_count); } /* @@ -3368,7 +3428,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, if (em485) { if (em485->tx_stopped) - up->rs485_start_tx(up); + up->rs485_start_tx(up, false); mdelay(port->rs485.delay_rts_before_send); } @@ -3395,7 +3455,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, if (likely(use_fifo)) serial8250_console_fifo_write(up, s, count); else - uart_console_write(port, s, count, serial8250_console_putchar); + uart_console_write(port, s, count, serial8250_console_wait_putchar); /* * Finally, wait for transmitter to become empty @@ -3406,7 +3466,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, if (em485) { mdelay(port->rs485.delay_rts_after_send); if (em485->tx_stopped) - up->rs485_stop_tx(up); + up->rs485_stop_tx(up, false); } serial_port_out(port, UART_IER, ier); @@ -3476,4 +3536,5 @@ int serial8250_console_exit(struct uart_port *port) #endif /* CONFIG_SERIAL_8250_CONSOLE */ +MODULE_DESCRIPTION("Base port operations for 8250/16550-type serial ports"); MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250/8250_pxa.c b/drivers/tty/serial/8250/8250_pxa.c index f1a51b00b1b9..6dd0190b4843 100644 --- a/drivers/tty/serial/8250/8250_pxa.c +++ b/drivers/tty/serial/8250/8250_pxa.c @@ -125,6 +125,7 @@ static int serial_pxa_probe(struct platform_device *pdev) uart.port.iotype = UPIO_MEM32; uart.port.regshift = 2; uart.port.fifosize = 64; + uart.tx_loadsz = 32; uart.dl_write = serial_pxa_dl_write; ret = serial8250_register_8250_port(&uart); @@ -153,7 +154,7 @@ static void serial_pxa_remove(struct platform_device *pdev) static struct platform_driver serial_pxa_driver = { .probe = serial_pxa_probe, - .remove_new = serial_pxa_remove, + .remove = serial_pxa_remove, .driver = { .name = "pxa2xx-uart", @@ -164,22 +165,7 @@ static struct platform_driver serial_pxa_driver = { module_platform_driver(serial_pxa_driver); -#ifdef CONFIG_SERIAL_8250_CONSOLE -static int __init early_serial_pxa_setup(struct earlycon_device *device, - const char *options) -{ - struct uart_port *port = &device->port; - - if (!(device->port.membase || device->port.iobase)) - return -ENODEV; - - port->regshift = 2; - return early_serial8250_setup(device, NULL); -} -OF_EARLYCON_DECLARE(early_pxa, "mrvl,pxa-uart", early_serial_pxa_setup); -OF_EARLYCON_DECLARE(mmp, "mrvl,mmp-uart", early_serial_pxa_setup); -#endif - MODULE_AUTHOR("Sergei Ianovich"); +MODULE_DESCRIPTION("driver for PXA on-board UARTS"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:pxa2xx-uart"); diff --git a/drivers/tty/serial/8250/8250_rsa.c b/drivers/tty/serial/8250/8250_rsa.c new file mode 100644 index 000000000000..4c8b9671bd41 --- /dev/null +++ b/drivers/tty/serial/8250/8250_rsa.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/moduleparam.h> + +#include <linux/serial.h> +#include <linux/serial_8250.h> + +#include "8250.h" + +#define PORT_RSA_MAX 4 +static unsigned long probe_rsa[PORT_RSA_MAX]; +static unsigned int probe_rsa_count; + +static int rsa8250_request_resource(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + unsigned long start = UART_RSA_BASE << port->regshift; + unsigned int size = 8 << port->regshift; + + switch (port->iotype) { + case UPIO_HUB6: + case UPIO_PORT: + start += port->iobase; + if (!request_region(start, size, "serial-rsa")) + return -EBUSY; + return 0; + default: + return -EINVAL; + } +} + +static void rsa8250_release_resource(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + unsigned long offset = UART_RSA_BASE << port->regshift; + unsigned int size = 8 << port->regshift; + + switch (port->iotype) { + case UPIO_HUB6: + case UPIO_PORT: + release_region(port->iobase + offset, size); + break; + default: + break; + } +} + +static void univ8250_config_port(struct uart_port *port, int flags) +{ + struct uart_8250_port *up = up_to_u8250p(port); + unsigned int i; + + up->probe &= ~UART_PROBE_RSA; + if (port->type == PORT_RSA) { + if (rsa8250_request_resource(up) == 0) + up->probe |= UART_PROBE_RSA; + } else if (flags & UART_CONFIG_TYPE) { + for (i = 0; i < probe_rsa_count; i++) { + if (probe_rsa[i] == up->port.iobase) { + if (rsa8250_request_resource(up) == 0) + up->probe |= UART_PROBE_RSA; + break; + } + } + } + + univ8250_port_base_ops->config_port(port, flags); + + if (port->type != PORT_RSA && up->probe & UART_PROBE_RSA) + rsa8250_release_resource(up); +} + +static int univ8250_request_port(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + int ret; + + ret = univ8250_port_base_ops->request_port(port); + if (ret == 0 && port->type == PORT_RSA) { + ret = rsa8250_request_resource(up); + if (ret < 0) + univ8250_port_base_ops->release_port(port); + } + + return ret; +} + +static void univ8250_release_port(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + + if (port->type == PORT_RSA) + rsa8250_release_resource(up); + univ8250_port_base_ops->release_port(port); +} + +void univ8250_rsa_support(struct uart_ops *ops) +{ + ops->config_port = univ8250_config_port; + ops->request_port = univ8250_request_port; + ops->release_port = univ8250_release_port; +} + +module_param_hw_array(probe_rsa, ulong, ioport, &probe_rsa_count, 0444); +MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); + +#ifdef CONFIG_SERIAL_8250_DEPRECATED_OPTIONS +#ifndef MODULE +/* + * Keep the old "8250" name working as well for the module options so we don't + * break people. We need to keep the names identical and the convenient macros + * will happily refuse to let us do that by failing the build with redefinition + * errors of global variables. So we stick them inside a dummy function to + * avoid those conflicts. The options still get parsed, and the redefined + * MODULE_PARAM_PREFIX lets us keep the "8250." syntax alive. + * + * This is hacky. I'm sorry. + */ +static void __used rsa8250_options(void) +{ +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "8250_core." + + __module_param_call(MODULE_PARAM_PREFIX, probe_rsa, + ¶m_array_ops, .arr = &__param_arr_probe_rsa, + 0444, -1, 0); +} +#endif +#endif diff --git a/drivers/tty/serial/8250/8250_tegra.c b/drivers/tty/serial/8250/8250_tegra.c index 60a80d00d251..2f3b0075763f 100644 --- a/drivers/tty/serial/8250/8250_tegra.c +++ b/drivers/tty/serial/8250/8250_tegra.c @@ -182,7 +182,7 @@ static struct platform_driver tegra_uart_driver = { .acpi_match_table = ACPI_PTR(tegra_uart_acpi_match), }, .probe = tegra_uart_probe, - .remove_new = tegra_uart_remove, + .remove = tegra_uart_remove, }; module_platform_driver(tegra_uart_driver); diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index 670d2ca0f757..4874a9632db3 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -282,7 +282,7 @@ MODULE_DEVICE_TABLE(of, uniphier_uart_match); static struct platform_driver uniphier_uart_platform_driver = { .probe = uniphier_uart_probe, - .remove_new = uniphier_uart_remove, + .remove = uniphier_uart_remove, .driver = { .name = "uniphier-uart", .of_match_table = uniphier_uart_match, diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 47ff50763c04..f64ef0819cd4 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -72,7 +72,7 @@ config SERIAL_8250_16550A_VARIANTS config SERIAL_8250_FINTEK bool "Support for Fintek variants" - depends on SERIAL_8250 + depends on SERIAL_8250 && HAS_IOPORT help Selecting this option will add support for the RS232 and RS485 capabilities of the Fintek F81216A LPC to 4 UART as well similar @@ -150,6 +150,7 @@ config SERIAL_8250_EXAR tristate "8250/16550 Exar/Commtech PCI/PCIe device support" depends on SERIAL_8250 && PCI select SERIAL_8250_PCILIB + select EEPROM_93CX6 default SERIAL_8250 help This builds support for XR17C1xx, XR17V3xx and some Commtech @@ -163,7 +164,7 @@ config SERIAL_8250_HP300 config SERIAL_8250_CS tristate "8250/16550 PCMCIA device support" - depends on PCMCIA && SERIAL_8250 + depends on PCMCIA && SERIAL_8250 && HAS_IOPORT help Say Y here to enable support for 16-bit PCMCIA serial devices, including serial port cards, modems, and the modem functions of @@ -568,6 +569,19 @@ config SERIAL_8250_BCM7271 including DMA support and high accuracy BAUD rates, say Y to this option. If unsure, say N. +config SERIAL_8250_NI + tristate "NI 16550 based serial port" + depends on SERIAL_8250 + depends on X86 || COMPILE_TEST + help + This driver supports the integrated serial ports on National + Instruments (NI) controller hardware. This is required for all NI + controller models with onboard RS-485 or dual-mode RS-485/RS-232 + ports. + + To compile this driver as a module, choose M here: the module + will be called 8250_ni. + config SERIAL_OF_PLATFORM tristate "Devicetree based probing for 8250 ports" depends on SERIAL_8250 && OF diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index ea2e81f58eac..b04eeda03b23 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -3,11 +3,13 @@ # Makefile for the 8250 serial device drivers. # -obj-$(CONFIG_SERIAL_8250) += 8250.o 8250_base.o +obj-$(CONFIG_SERIAL_8250) += 8250.o 8250-y := 8250_core.o -8250-$(CONFIG_ALPHA_GENERIC) += 8250_alpha.o -8250-$(CONFIG_ALPHA_JENSEN) += 8250_alpha.o +8250-y += 8250_platform.o 8250-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o +8250-$(CONFIG_SERIAL_8250_RSA) += 8250_rsa.o + +obj-$(CONFIG_SERIAL_8250) += 8250_base.o 8250_base-y := 8250_port.o 8250_base-$(CONFIG_SERIAL_8250_DMA) += 8250_dma.o 8250_base-$(CONFIG_SERIAL_8250_DWLIB) += 8250_dwlib.o @@ -38,6 +40,7 @@ obj-$(CONFIG_SERIAL_8250_LPSS) += 8250_lpss.o obj-$(CONFIG_SERIAL_8250_MEN_MCB) += 8250_men_mcb.o obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o +obj-$(CONFIG_SERIAL_8250_NI) += 8250_ni.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += 8250_of.o obj-$(CONFIG_SERIAL_8250_OMAP) += 8250_omap.o obj-$(CONFIG_SERIAL_8250_PARISC) += 8250_parisc.o diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c index 2056aed46688..58e279ea7ee0 100644 --- a/drivers/tty/serial/8250/serial_cs.c +++ b/drivers/tty/serial/8250/serial_cs.c @@ -864,4 +864,5 @@ static struct pcmcia_driver serial_cs_driver = { }; module_pcmcia_driver(serial_cs_driver); +MODULE_DESCRIPTION("driver for PCMCIA serial devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index ffcf4882b25f..79a8186d3361 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -128,7 +128,7 @@ config SERIAL_SB1250_DUART_CONSOLE config SERIAL_ATMEL bool "AT91 on-chip serial port support" depends on COMMON_CLK - depends on ARCH_AT91 || COMPILE_TEST + depends on ARCH_AT91 || ARCH_LAN969X || COMPILE_TEST select SERIAL_CORE select SERIAL_MCTRL_GPIO if GPIOLIB select MFD_AT91_USART @@ -179,25 +179,6 @@ config SERIAL_ATMEL_TTYAT Say Y if you have an external 8250/16C550 UART. If unsure, say N. -config SERIAL_KGDB_NMI - bool "Serial console over KGDB NMI debugger port" - depends on KGDB_SERIAL_CONSOLE - help - This special driver allows you to temporary use NMI debugger port - as a normal console (assuming that the port is attached to KGDB). - - Unlike KDB's disable_nmi command, with this driver you are always - able to go back to the debugger using KGDB escape sequence ($3#33). - This is because this console driver processes the input in NMI - context, and thus is able to intercept the magic sequence. - - Note that since the console interprets input and uses polling - communication methods, for things like PPP you still must fully - detach debugger port from the KGDB NMI (i.e. disable_nmi), and - use raw console. - - If unsure, say N. - config SERIAL_MESON tristate "Meson serial port support" depends on ARCH_MESON || COMPILE_TEST @@ -306,12 +287,38 @@ config SERIAL_TEGRA_TCU_CONSOLE If unsure, say Y. +config SERIAL_TEGRA_UTC + tristate "NVIDIA Tegra UART Trace Controller" + depends on ARCH_TEGRA || COMPILE_TEST + select SERIAL_CORE + help + Support for Tegra UTC (UART Trace controller) client serial port. + + UTC is a HW based serial port that allows multiplexing multiple data + streams of up to 16 UTC clients into a single hardware serial port. + +config SERIAL_TEGRA_UTC_CONSOLE + bool "Support for console on a Tegra UTC serial port" + depends on SERIAL_TEGRA_UTC + select SERIAL_CORE_CONSOLE + default SERIAL_TEGRA_UTC + help + If you say Y here, it will be possible to use a Tegra UTC client as + the system console (the system console is the device which receives + all kernel messages and warnings and which allows logins in single + user mode). + + If unsure, say Y. + config SERIAL_MAX3100 - tristate "MAX3100 support" + tristate "MAX3100/3110/3111/3222 support" depends on SPI select SERIAL_CORE help - MAX3100 chip support + This selects support for an advanced UART from Maxim. + Supported ICs are MAX3100, MAX3110, MAX3111, MAX3222. + + Say Y here if you want to support these ICs. config SERIAL_MAX310X tristate "MAX310X support" @@ -874,7 +881,7 @@ config SERIAL_TXX9_STDSERIAL config SERIAL_JSM tristate "Digi International NEO and Classic PCI Support" - depends on PCI + depends on PCI && HAS_IOPORT select SERIAL_CORE help This is a driver for Digi International's Neo and Classic series @@ -1020,42 +1027,32 @@ config SERIAL_SCCNXP_CONSOLE help Support for console on SCCNXP serial ports. -config SERIAL_SC16IS7XX_CORE - tristate - config SERIAL_SC16IS7XX - tristate "SC16IS7xx serial support" + tristate "NXP SC16IS7xx UART support" + depends on SPI_MASTER || I2C select SERIAL_CORE - depends on (SPI_MASTER && !I2C) || I2C + select SERIAL_SC16IS7XX_SPI if SPI_MASTER + select SERIAL_SC16IS7XX_I2C if I2C help - This selects support for SC16IS7xx serial ports. - Supported ICs are SC16IS740, SC16IS741, SC16IS750, SC16IS752, - SC16IS760 and SC16IS762. Select supported buses using options below. + Core driver for NXP SC16IS7xx UARTs. + Supported ICs are: + + SC16IS740 + SC16IS741 + SC16IS750 + SC16IS752 + SC16IS760 + SC16IS762 + + The driver supports both I2C and SPI interfaces. config SERIAL_SC16IS7XX_I2C - bool "SC16IS7xx for I2C interface" - depends on SERIAL_SC16IS7XX - depends on I2C - select SERIAL_SC16IS7XX_CORE if SERIAL_SC16IS7XX - select REGMAP_I2C if I2C - default y - help - Enable SC16IS7xx driver on I2C bus, - If required say y, and say n to i2c if not required, - Enabled by default to support oldconfig. - You must select at least one bus for the driver to be built. + tristate + select REGMAP_I2C config SERIAL_SC16IS7XX_SPI - bool "SC16IS7xx for spi interface" - depends on SERIAL_SC16IS7XX - depends on SPI_MASTER - select SERIAL_SC16IS7XX_CORE if SERIAL_SC16IS7XX - select REGMAP_SPI if SPI_MASTER - help - Enable SC16IS7xx driver on SPI bus, - If required say y, and say n to spi if not required, - This is additional support to existing driver. - You must select at least one bus for the driver to be built. + tristate + select REGMAP_SPI config SERIAL_TIMBERDALE tristate "Support for timberdale UART" diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index b25e9b54a660..d58d9f719889 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -75,7 +75,9 @@ obj-$(CONFIG_SERIAL_SA1100) += sa1100.o obj-$(CONFIG_SERIAL_SAMSUNG) += samsung_tty.o obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o -obj-$(CONFIG_SERIAL_SC16IS7XX_CORE) += sc16is7xx.o +obj-$(CONFIG_SERIAL_SC16IS7XX) += sc16is7xx.o +obj-$(CONFIG_SERIAL_SC16IS7XX_SPI) += sc16is7xx_spi.o +obj-$(CONFIG_SERIAL_SC16IS7XX_I2C) += sc16is7xx_i2c.o obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o obj-$(CONFIG_SERIAL_SIFIVE) += sifive.o obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o @@ -84,6 +86,7 @@ obj-$(CONFIG_SERIAL_STM32) += stm32-usart.o obj-$(CONFIG_SERIAL_SUNPLUS) += sunplus-uart.o obj-$(CONFIG_SERIAL_TEGRA) += serial-tegra.o obj-$(CONFIG_SERIAL_TEGRA_TCU) += tegra-tcu.o +obj-$(CONFIG_SERIAL_TEGRA_UTC) += tegra-utc.o obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o @@ -94,6 +97,5 @@ obj-$(CONFIG_SERIAL_ZS) += zs.o # GPIOLIB helpers for modem control lines obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o -obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o obj-$(CONFIG_SERIAL_NUVOTON_MA35D1) += ma35d1_serial.o diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index effcba71ea77..d47a62d1c9f7 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -24,8 +24,6 @@ #include <linux/io.h> #include <linux/altera_jtaguart.h> -#define DRV_NAME "altera_jtaguart" - /* * Altera JTAG UART register definitions according to the Altera JTAG UART * datasheet: https://www.altera.com/literature/hb/nios2/n2cpu_nii51009.pdf @@ -173,10 +171,10 @@ static int altera_jtaguart_startup(struct uart_port *port) int ret; ret = request_irq(port->irq, altera_jtaguart_interrupt, 0, - DRV_NAME, port); + dev_name(port->dev), port); if (ret) { - pr_err(DRV_NAME ": unable to attach Altera JTAG UART %d " - "interrupt vector=%d\n", port->line, port->irq); + dev_err(port->dev, "unable to attach Altera JTAG UART %d interrupt vector=%d\n", + port->line, port->irq); return ret; } @@ -365,7 +363,7 @@ OF_EARLYCON_DECLARE(juart, "altr,juart-1.0", altera_jtaguart_earlycon_setup); static struct uart_driver altera_jtaguart_driver = { .owner = THIS_MODULE, - .driver_name = "altera_jtaguart", + .driver_name = KBUILD_MODNAME, .dev_name = "ttyJ", .major = ALTERA_JTAGUART_MAJOR, .minor = ALTERA_JTAGUART_MINOR, @@ -449,9 +447,9 @@ MODULE_DEVICE_TABLE(of, altera_jtaguart_match); static struct platform_driver altera_jtaguart_platform_driver = { .probe = altera_jtaguart_probe, - .remove_new = altera_jtaguart_remove, + .remove = altera_jtaguart_remove, .driver = { - .name = DRV_NAME, + .name = KBUILD_MODNAME, .of_match_table = of_match_ptr(altera_jtaguart_match), }, }; @@ -481,4 +479,4 @@ module_exit(altera_jtaguart_exit); MODULE_DESCRIPTION("Altera JTAG UART driver"); MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); +MODULE_ALIAS("platform:" KBUILD_MODNAME); diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 897f0995b2fe..837991dc4db9 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -24,7 +24,6 @@ #include <linux/io.h> #include <linux/altera_uart.h> -#define DRV_NAME "altera_uart" #define SERIAL_ALTERA_MAJOR 204 #define SERIAL_ALTERA_MINOR 213 @@ -276,7 +275,7 @@ static irqreturn_t altera_uart_interrupt(int irq, void *data) static void altera_uart_timer(struct timer_list *t) { - struct altera_uart *pp = from_timer(pp, t, tmr); + struct altera_uart *pp = timer_container_of(pp, t, tmr); struct uart_port *port = &pp->port; altera_uart_interrupt(0, port); @@ -307,8 +306,8 @@ static int altera_uart_startup(struct uart_port *port) ret = request_irq(port->irq, altera_uart_interrupt, 0, dev_name(port->dev), port); if (ret) { - pr_err(DRV_NAME ": unable to attach Altera UART %d " - "interrupt vector=%d\n", port->line, port->irq); + dev_err(port->dev, "unable to attach Altera UART %d interrupt vector=%d\n", + port->line, port->irq); return ret; } } @@ -340,7 +339,7 @@ static void altera_uart_shutdown(struct uart_port *port) if (port->irq) free_irq(port->irq, port); else - del_timer_sync(&pp->tmr); + timer_delete_sync(&pp->tmr); } static const char *altera_uart_type(struct uart_port *port) @@ -518,7 +517,7 @@ OF_EARLYCON_DECLARE(uart, "altr,uart-1.0", altera_uart_earlycon_setup); */ static struct uart_driver altera_uart_driver = { .owner = THIS_MODULE, - .driver_name = DRV_NAME, + .driver_name = KBUILD_MODNAME, .dev_name = "ttyAL", .major = SERIAL_ALTERA_MAJOR, .minor = SERIAL_ALTERA_MINOR, @@ -617,9 +616,9 @@ MODULE_DEVICE_TABLE(of, altera_uart_match); static struct platform_driver altera_uart_platform_driver = { .probe = altera_uart_probe, - .remove_new = altera_uart_remove, + .remove = altera_uart_remove, .driver = { - .name = DRV_NAME, + .name = KBUILD_MODNAME, .of_match_table = of_match_ptr(altera_uart_match), }, }; @@ -649,5 +648,5 @@ module_exit(altera_uart_exit); MODULE_DESCRIPTION("Altera UART driver"); MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); +MODULE_ALIAS("platform:" KBUILD_MODNAME); MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_ALTERA_MAJOR); diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c index eabbf8afc9b5..c3a7fad02ac9 100644 --- a/drivers/tty/serial/amba-pl010.c +++ b/drivers/tty/serial/amba-pl010.c @@ -499,7 +499,7 @@ static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser) int ret = 0; if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA) ret = -EINVAL; - if (ser->irq < 0 || ser->irq >= nr_irqs) + if (ser->irq < 0 || ser->irq >= irq_get_nr_irqs()) ret = -EINVAL; if (ser->baud_base < 9600) ret = -EINVAL; diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 2fa3fb30dc6c..22939841b1de 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -248,6 +248,13 @@ struct pl011_dmatx_data { bool queued; }; +enum pl011_rs485_tx_state { + OFF, + WAIT_AFTER_RTS, + SEND, + WAIT_AFTER_SEND, +}; + /* * We wrap our port structure around the generic uart_port. */ @@ -256,16 +263,19 @@ struct uart_amba_port { const u16 *reg_offset; struct clk *clk; const struct vendor_data *vendor; - unsigned int dmacr; /* dma control reg */ unsigned int im; /* interrupt mask */ unsigned int old_status; unsigned int fifosize; /* vendor-specific */ unsigned int fixed_baud; /* vendor-set fixed baud rate */ char type[12]; - bool rs485_tx_started; - unsigned int rs485_tx_drain_interval; /* usecs */ + ktime_t rs485_tx_drain_interval; /* nano */ + enum pl011_rs485_tx_state rs485_tx_state; + struct hrtimer trigger_start_tx; + struct hrtimer trigger_stop_tx; + bool console_line_ended; #ifdef CONFIG_DMA_ENGINE /* DMA stuff */ + unsigned int dmacr; /* dma control reg */ bool using_tx_dma; bool using_rx_dma; struct pl011_dmarx_data dmarx; @@ -535,6 +545,7 @@ static void pl011_start_tx_pio(struct uart_amba_port *uap); static void pl011_dma_tx_callback(void *data) { struct uart_amba_port *uap = data; + struct tty_port *tport = &uap->port.state->port; struct pl011_dmatx_data *dmatx = &uap->dmatx; unsigned long flags; u16 dmacr; @@ -558,7 +569,7 @@ static void pl011_dma_tx_callback(void *data) * get further refills (hence we check dmacr). */ if (!(dmacr & UART011_TXDMAE) || uart_tx_stopped(&uap->port) || - uart_circ_empty(&uap->port.state->xmit)) { + kfifo_is_empty(&tport->xmit_fifo)) { uap->dmatx.queued = false; uart_port_unlock_irqrestore(&uap->port, flags); return; @@ -588,7 +599,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap) struct dma_chan *chan = dmatx->chan; struct dma_device *dma_dev = chan->device; struct dma_async_tx_descriptor *desc; - struct circ_buf *xmit = &uap->port.state->xmit; + struct tty_port *tport = &uap->port.state->port; unsigned int count; /* @@ -597,7 +608,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap) * the standard interrupt handling. This ensures that we * issue a uart_write_wakeup() at the appropriate time. */ - count = uart_circ_chars_pending(xmit); + count = kfifo_len(&tport->xmit_fifo); if (count < (uap->fifosize >> 1)) { uap->dmatx.queued = false; return 0; @@ -613,21 +624,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap) if (count > PL011_DMA_BUFFER_SIZE) count = PL011_DMA_BUFFER_SIZE; - if (xmit->tail < xmit->head) { - memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], count); - } else { - size_t first = UART_XMIT_SIZE - xmit->tail; - size_t second; - - if (first > count) - first = count; - second = count - first; - - memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], first); - if (second) - memcpy(&dmatx->buf[first], &xmit->buf[0], second); - } - + count = kfifo_out_peek(&tport->xmit_fifo, dmatx->buf, count); dmatx->len = count; dmatx->dma = dma_map_single(dma_dev->dev, dmatx->buf, count, DMA_TO_DEVICE); @@ -670,7 +667,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap) */ uart_xmit_advance(&uap->port, count); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&uap->port); return 1; @@ -1049,7 +1046,7 @@ static inline void pl011_dma_rx_stop(struct uart_amba_port *uap) */ static void pl011_dma_rx_poll(struct timer_list *t) { - struct uart_amba_port *uap = from_timer(uap, t, dmarx.timer); + struct uart_amba_port *uap = timer_container_of(uap, t, dmarx.timer); struct tty_port *port = &uap->port.state->port; struct pl011_dmarx_data *dmarx = &uap->dmarx; struct dma_chan *rxchan = uap->dmarx.chan; @@ -1087,7 +1084,7 @@ static void pl011_dma_rx_poll(struct timer_list *t) uap->dmarx.running = false; dmaengine_terminate_all(rxchan); - del_timer(&uap->dmarx.timer); + timer_delete(&uap->dmarx.timer); } else { mod_timer(&uap->dmarx.timer, jiffies + msecs_to_jiffies(uap->dmarx.poll_rate)); @@ -1202,7 +1199,7 @@ static void pl011_dma_shutdown(struct uart_amba_port *uap) pl011_dmabuf_free(uap->dmarx.chan, &uap->dmarx.dbuf_a, DMA_FROM_DEVICE); pl011_dmabuf_free(uap->dmarx.chan, &uap->dmarx.dbuf_b, DMA_FROM_DEVICE); if (uap->dmarx.poll_rate) - del_timer_sync(&uap->dmarx.timer); + timer_delete_sync(&uap->dmarx.timer); uap->using_rx_dma = false; } } @@ -1273,30 +1270,31 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap) static void pl011_rs485_tx_stop(struct uart_amba_port *uap) { - /* - * To be on the safe side only time out after twice as many iterations - * as fifo size. - */ - const int MAX_TX_DRAIN_ITERS = uap->port.fifosize * 2; struct uart_port *port = &uap->port; - int i = 0; u32 cr; - /* Wait until hardware tx queue is empty */ - while (!pl011_tx_empty(port)) { - if (i > MAX_TX_DRAIN_ITERS) { - dev_warn(port->dev, - "timeout while draining hardware tx queue\n"); - break; - } + if (uap->rs485_tx_state == SEND) + uap->rs485_tx_state = WAIT_AFTER_SEND; - udelay(uap->rs485_tx_drain_interval); - i++; + if (uap->rs485_tx_state == WAIT_AFTER_SEND) { + /* Schedule hrtimer if tx queue not empty */ + if (!pl011_tx_empty(port)) { + hrtimer_start(&uap->trigger_stop_tx, + uap->rs485_tx_drain_interval, + HRTIMER_MODE_REL); + return; + } + if (port->rs485.delay_rts_after_send > 0) { + hrtimer_start(&uap->trigger_stop_tx, + ms_to_ktime(port->rs485.delay_rts_after_send), + HRTIMER_MODE_REL); + return; + } + /* Continue without any delay */ + } else if (uap->rs485_tx_state == WAIT_AFTER_RTS) { + hrtimer_try_to_cancel(&uap->trigger_start_tx); } - if (port->rs485.delay_rts_after_send) - mdelay(port->rs485.delay_rts_after_send); - cr = pl011_read(uap, REG_CR); if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) @@ -1309,7 +1307,7 @@ static void pl011_rs485_tx_stop(struct uart_amba_port *uap) cr |= UART011_CR_RXE; pl011_write(cr, uap, REG_CR); - uap->rs485_tx_started = false; + uap->rs485_tx_state = OFF; } static void pl011_stop_tx(struct uart_port *port) @@ -1317,11 +1315,18 @@ static void pl011_stop_tx(struct uart_port *port) struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); + if (port->rs485.flags & SER_RS485_ENABLED && + uap->rs485_tx_state == WAIT_AFTER_RTS) { + pl011_rs485_tx_stop(uap); + return; + } + uap->im &= ~UART011_TXIM; pl011_write(uap->im, uap, REG_IMSC); pl011_dma_tx_stop(uap); - if ((port->rs485.flags & SER_RS485_ENABLED) && uap->rs485_tx_started) + if (port->rs485.flags & SER_RS485_ENABLED && + uap->rs485_tx_state != OFF) pl011_rs485_tx_stop(uap); } @@ -1341,10 +1346,19 @@ static void pl011_rs485_tx_start(struct uart_amba_port *uap) struct uart_port *port = &uap->port; u32 cr; + if (uap->rs485_tx_state == WAIT_AFTER_RTS) { + uap->rs485_tx_state = SEND; + return; + } + if (uap->rs485_tx_state == WAIT_AFTER_SEND) { + hrtimer_try_to_cancel(&uap->trigger_stop_tx); + uap->rs485_tx_state = SEND; + return; + } + /* uap->rs485_tx_state == OFF */ /* Enable transmitter */ cr = pl011_read(uap, REG_CR); cr |= UART011_CR_TXE; - /* Disable receiver if half-duplex */ if (!(port->rs485.flags & SER_RS485_RX_DURING_TX)) cr &= ~UART011_CR_RXE; @@ -1356,10 +1370,14 @@ static void pl011_rs485_tx_start(struct uart_amba_port *uap) pl011_write(cr, uap, REG_CR); - if (port->rs485.delay_rts_before_send) - mdelay(port->rs485.delay_rts_before_send); - - uap->rs485_tx_started = true; + if (port->rs485.delay_rts_before_send > 0) { + uap->rs485_tx_state = WAIT_AFTER_RTS; + hrtimer_start(&uap->trigger_start_tx, + ms_to_ktime(port->rs485.delay_rts_before_send), + HRTIMER_MODE_REL); + } else { + uap->rs485_tx_state = SEND; + } } static void pl011_start_tx(struct uart_port *port) @@ -1368,13 +1386,44 @@ static void pl011_start_tx(struct uart_port *port) container_of(port, struct uart_amba_port, port); if ((uap->port.rs485.flags & SER_RS485_ENABLED) && - !uap->rs485_tx_started) + uap->rs485_tx_state != SEND) { pl011_rs485_tx_start(uap); + if (uap->rs485_tx_state == WAIT_AFTER_RTS) + return; + } if (!pl011_dma_tx_start(uap)) pl011_start_tx_pio(uap); } +static enum hrtimer_restart pl011_trigger_start_tx(struct hrtimer *t) +{ + struct uart_amba_port *uap = + container_of(t, struct uart_amba_port, trigger_start_tx); + unsigned long flags; + + uart_port_lock_irqsave(&uap->port, &flags); + if (uap->rs485_tx_state == WAIT_AFTER_RTS) + pl011_start_tx(&uap->port); + uart_port_unlock_irqrestore(&uap->port, flags); + + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart pl011_trigger_stop_tx(struct hrtimer *t) +{ + struct uart_amba_port *uap = + container_of(t, struct uart_amba_port, trigger_stop_tx); + unsigned long flags; + + uart_port_lock_irqsave(&uap->port, &flags); + if (uap->rs485_tx_state == WAIT_AFTER_SEND) + pl011_rs485_tx_stop(uap); + uart_port_unlock_irqrestore(&uap->port, flags); + + return HRTIMER_NORESTART; +} + static void pl011_stop_rx(struct uart_port *port) { struct uart_amba_port *uap = @@ -1454,7 +1503,7 @@ static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c, /* Returns true if tx interrupts have to be (kept) enabled */ static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq) { - struct circ_buf *xmit = &uap->port.state->xmit; + struct tty_port *tport = &uap->port.state->port; int count = uap->fifosize >> 1; if (uap->port.x_char) { @@ -1463,7 +1512,7 @@ static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq) uap->port.x_char = 0; --count; } - if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(&uap->port)) { pl011_stop_tx(&uap->port); return false; } @@ -1472,20 +1521,25 @@ static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq) if (pl011_dma_tx_irq(uap)) return true; - do { + while (1) { + unsigned char c; + if (likely(from_irq) && count-- == 0) break; - if (!pl011_tx_char(uap, xmit->buf[xmit->tail], from_irq)) + if (!kfifo_peek(&tport->xmit_fifo, &c)) break; - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - } while (!uart_circ_empty(xmit)); + if (!pl011_tx_char(uap, c, from_irq)) + break; + + kfifo_skip(&tport->xmit_fifo); + } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&uap->port); - if (uart_circ_empty(xmit)) { + if (kfifo_is_empty(&tport->xmit_fifo)) { pl011_stop_tx(&uap->port); return false; } @@ -1827,6 +1881,13 @@ static void pl011_unthrottle_rx(struct uart_port *port) pl011_write(uap->im, uap, REG_IMSC); +#ifdef CONFIG_DMA_ENGINE + if (uap->using_rx_dma) { + uap->dmacr |= UART011_RXDMAE; + pl011_write(uap->dmacr, uap, REG_DMACR); + } +#endif + uart_port_unlock_irqrestore(&uap->port, flags); } @@ -1954,7 +2015,7 @@ static void pl011_shutdown(struct uart_port *port) pl011_dma_shutdown(uap); - if ((port->rs485.flags & SER_RS485_ENABLED) && uap->rs485_tx_started) + if ((port->rs485.flags & SER_RS485_ENABLED && uap->rs485_tx_state != OFF)) pl011_rs485_tx_stop(uap); free_irq(uap->port.irq, uap); @@ -2099,7 +2160,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, * with the given baud rate. We use this as the poll interval when we * wait for the tx queue to empty. */ - uap->rs485_tx_drain_interval = DIV_ROUND_UP(bits * 1000 * 1000, baud); + uap->rs485_tx_drain_interval = ns_to_ktime(DIV_ROUND_UP(bits * NSEC_PER_SEC, baud)); pl011_setup_status_masks(port, termios); @@ -2210,7 +2271,7 @@ static int pl011_verify_port(struct uart_port *port, struct serial_struct *ser) if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA) ret = -EINVAL; - if (ser->irq < 0 || ser->irq >= nr_irqs) + if (ser->irq < 0 || ser->irq >= irq_get_nr_irqs()) ret = -EINVAL; if (ser->baud_base < 9600) ret = -EINVAL; @@ -2306,50 +2367,7 @@ static void pl011_console_putchar(struct uart_port *port, unsigned char ch) while (pl011_read(uap, REG_FR) & UART01x_FR_TXFF) cpu_relax(); pl011_write(ch, uap, REG_DR); -} - -static void -pl011_console_write(struct console *co, const char *s, unsigned int count) -{ - struct uart_amba_port *uap = amba_ports[co->index]; - unsigned int old_cr = 0, new_cr; - unsigned long flags; - int locked = 1; - - clk_enable(uap->clk); - - if (oops_in_progress) - locked = uart_port_trylock_irqsave(&uap->port, &flags); - else - uart_port_lock_irqsave(&uap->port, &flags); - - /* - * First save the CR then disable the interrupts - */ - if (!uap->vendor->always_enabled) { - old_cr = pl011_read(uap, REG_CR); - new_cr = old_cr & ~UART011_CR_CTSEN; - new_cr |= UART01x_CR_UARTEN | UART011_CR_TXE; - pl011_write(new_cr, uap, REG_CR); - } - - uart_console_write(&uap->port, s, count, pl011_console_putchar); - - /* - * Finally, wait for transmitter to become empty and restore the - * TCR. Allow feature register bits to be inverted to work around - * errata. - */ - while ((pl011_read(uap, REG_FR) ^ uap->vendor->inv_fr) - & uap->vendor->fr_busy) - cpu_relax(); - if (!uap->vendor->always_enabled) - pl011_write(old_cr, uap, REG_CR); - - if (locked) - uart_port_unlock_irqrestore(&uap->port, flags); - - clk_disable(uap->clk); + uap->console_line_ended = (ch == '\n'); } static void pl011_console_get_options(struct uart_amba_port *uap, int *baud, @@ -2412,6 +2430,8 @@ static int pl011_console_setup(struct console *co, char *options) if (ret) return ret; + uap->console_line_ended = true; + if (dev_get_platdata(uap->port.dev)) { struct amba_pl011_data *plat; @@ -2456,7 +2476,7 @@ static int pl011_console_setup(struct console *co, char *options) static int pl011_console_match(struct console *co, char *name, int idx, char *options) { - unsigned char iotype; + enum uart_iotype iotype; resource_size_t addr; int i; @@ -2488,21 +2508,112 @@ static int pl011_console_match(struct console *co, char *name, int idx, continue; co->index = i; - port->cons = co; + uart_port_set_cons(port, co); return pl011_console_setup(co, options); } return -ENODEV; } +static void +pl011_console_write_atomic(struct console *co, struct nbcon_write_context *wctxt) +{ + struct uart_amba_port *uap = amba_ports[co->index]; + unsigned int old_cr = 0; + + if (!nbcon_enter_unsafe(wctxt)) + return; + + clk_enable(uap->clk); + + if (!uap->vendor->always_enabled) { + old_cr = pl011_read(uap, REG_CR); + pl011_write((old_cr & ~UART011_CR_CTSEN) | (UART01x_CR_UARTEN | UART011_CR_TXE), + uap, REG_CR); + } + + if (!uap->console_line_ended) + uart_console_write(&uap->port, "\n", 1, pl011_console_putchar); + uart_console_write(&uap->port, wctxt->outbuf, wctxt->len, pl011_console_putchar); + + while ((pl011_read(uap, REG_FR) ^ uap->vendor->inv_fr) & uap->vendor->fr_busy) + cpu_relax(); + + if (!uap->vendor->always_enabled) + pl011_write(old_cr, uap, REG_CR); + + clk_disable(uap->clk); + + nbcon_exit_unsafe(wctxt); +} + +static void +pl011_console_write_thread(struct console *co, struct nbcon_write_context *wctxt) +{ + struct uart_amba_port *uap = amba_ports[co->index]; + unsigned int old_cr = 0; + + if (!nbcon_enter_unsafe(wctxt)) + return; + + clk_enable(uap->clk); + + if (!uap->vendor->always_enabled) { + old_cr = pl011_read(uap, REG_CR); + pl011_write((old_cr & ~UART011_CR_CTSEN) | (UART01x_CR_UARTEN | UART011_CR_TXE), + uap, REG_CR); + } + + if (nbcon_exit_unsafe(wctxt)) { + int i; + unsigned int len = READ_ONCE(wctxt->len); + + for (i = 0; i < len; i++) { + if (!nbcon_enter_unsafe(wctxt)) + break; + uart_console_write(&uap->port, wctxt->outbuf + i, 1, pl011_console_putchar); + if (!nbcon_exit_unsafe(wctxt)) + break; + } + } + + while (!nbcon_enter_unsafe(wctxt)) + nbcon_reacquire_nobuf(wctxt); + + while ((pl011_read(uap, REG_FR) ^ uap->vendor->inv_fr) & uap->vendor->fr_busy) + cpu_relax(); + + if (!uap->vendor->always_enabled) + pl011_write(old_cr, uap, REG_CR); + + clk_disable(uap->clk); + + nbcon_exit_unsafe(wctxt); +} + +static void +pl011_console_device_lock(struct console *co, unsigned long *flags) +{ + __uart_port_lock_irqsave(&amba_ports[co->index]->port, flags); +} + +static void +pl011_console_device_unlock(struct console *co, unsigned long flags) +{ + __uart_port_unlock_irqrestore(&amba_ports[co->index]->port, flags); +} + static struct uart_driver amba_reg; static struct console amba_console = { .name = "ttyAMA", - .write = pl011_console_write, .device = uart_console_device, .setup = pl011_console_setup, .match = pl011_console_match, - .flags = CON_PRINTBUFFER | CON_ANYTIME, + .write_atomic = pl011_console_write_atomic, + .write_thread = pl011_console_write_thread, + .device_lock = pl011_console_device_lock, + .device_unlock = pl011_console_device_unlock, + .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_NBCON, .index = -1, .data = &amba_reg, }; @@ -2700,18 +2811,6 @@ static int pl011_find_free_port(void) return -EBUSY; } -static int pl011_get_rs485_mode(struct uart_amba_port *uap) -{ - struct uart_port *port = &uap->port; - int ret; - - ret = uart_get_rs485_mode(port); - if (ret) - return ret; - - return 0; -} - static int pl011_setup_port(struct device *dev, struct uart_amba_port *uap, struct resource *mmiobase, int index) { @@ -2732,7 +2831,7 @@ static int pl011_setup_port(struct device *dev, struct uart_amba_port *uap, uap->port.flags = UPF_BOOT_AUTOCONF; uap->port.line = index; - ret = pl011_get_rs485_mode(uap); + ret = uart_get_rs485_mode(&uap->port); if (ret) return ret; @@ -2819,6 +2918,10 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) return -EINVAL; } } + hrtimer_setup(&uap->trigger_start_tx, pl011_trigger_start_tx, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + hrtimer_setup(&uap->trigger_stop_tx, pl011_trigger_stop_tx, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); ret = pl011_setup_port(&dev->dev, uap, &dev->res, portnr); if (ret) @@ -2948,7 +3051,7 @@ static const struct of_device_id sbsa_uart_of_match[] = { }; MODULE_DEVICE_TABLE(of, sbsa_uart_of_match); -static const struct acpi_device_id __maybe_unused sbsa_uart_acpi_match[] = { +static const struct acpi_device_id sbsa_uart_acpi_match[] = { { "ARMH0011", 0 }, { "ARMHB000", 0 }, {}, @@ -2957,12 +3060,12 @@ MODULE_DEVICE_TABLE(acpi, sbsa_uart_acpi_match); static struct platform_driver arm_sbsa_uart_platform_driver = { .probe = sbsa_uart_probe, - .remove_new = sbsa_uart_remove, + .remove = sbsa_uart_remove, .driver = { .name = "sbsa-uart", .pm = &pl011_dev_pm_ops, - .of_match_table = of_match_ptr(sbsa_uart_of_match), - .acpi_match_table = ACPI_PTR(sbsa_uart_acpi_match), + .of_match_table = sbsa_uart_of_match, + .acpi_match_table = sbsa_uart_acpi_match, .suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_AMBA_PL011), }, }; diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index 7790cbc57391..8bb33556b312 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -390,7 +390,7 @@ static void ar933x_uart_rx_chars(struct ar933x_uart_port *up) static void ar933x_uart_tx_chars(struct ar933x_uart_port *up) { - struct circ_buf *xmit = &up->port.state->xmit; + struct tty_port *tport = &up->port.state->port; struct serial_rs485 *rs485conf = &up->port.rs485; int count; bool half_duplex_send = false; @@ -399,7 +399,7 @@ static void ar933x_uart_tx_chars(struct ar933x_uart_port *up) return; if ((rs485conf->flags & SER_RS485_ENABLED) && - (up->port.x_char || !uart_circ_empty(xmit))) { + (up->port.x_char || !kfifo_is_empty(&tport->xmit_fifo))) { ar933x_uart_stop_rx_interrupt(up); gpiod_set_value(up->rts_gpiod, !!(rs485conf->flags & SER_RS485_RTS_ON_SEND)); half_duplex_send = true; @@ -408,6 +408,7 @@ static void ar933x_uart_tx_chars(struct ar933x_uart_port *up) count = up->port.fifosize; do { unsigned int rdata; + unsigned char c; rdata = ar933x_uart_read(up, AR933X_UART_DATA_REG); if ((rdata & AR933X_UART_DATA_TX_CSR) == 0) @@ -420,18 +421,16 @@ static void ar933x_uart_tx_chars(struct ar933x_uart_port *up) continue; } - if (uart_circ_empty(xmit)) + if (!uart_fifo_get(&up->port, &c)) break; - ar933x_uart_putc(up, xmit->buf[xmit->tail]); - - uart_xmit_advance(&up->port, 1); + ar933x_uart_putc(up, c); } while (--count > 0); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&up->port); - if (!uart_circ_empty(xmit)) { + if (!kfifo_is_empty(&tport->xmit_fifo)) { ar933x_uart_start_tx_interrupt(up); } else if (half_duplex_send) { ar933x_uart_wait_tx_complete(up); @@ -693,7 +692,6 @@ static struct uart_driver ar933x_uart_driver = { .cons = NULL, /* filled in runtime */ }; -static const struct serial_rs485 ar933x_no_rs485 = {}; static const struct serial_rs485 ar933x_rs485_supported = { .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, }; @@ -789,7 +787,7 @@ static int ar933x_uart_probe(struct platform_device *pdev) up->rts_gpiod = mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS); if (!up->rts_gpiod) { - port->rs485_supported = ar933x_no_rs485; + port->rs485_supported.flags &= ~SER_RS485_ENABLED; if (port->rs485.flags & SER_RS485_ENABLED) { dev_err(&pdev->dev, "lacking rts-gpio, disabling RS485\n"); port->rs485.flags &= ~SER_RS485_ENABLED; @@ -834,7 +832,7 @@ MODULE_DEVICE_TABLE(of, ar933x_uart_of_ids); static struct platform_driver ar933x_uart_platform_driver = { .probe = ar933x_uart_probe, - .remove_new = ar933x_uart_remove, + .remove = ar933x_uart_remove, .driver = { .name = DRIVER_NAME, .of_match_table = of_match_ptr(ar933x_uart_of_ids), diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index 1aa5b2b49c26..5c4895d154c0 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -155,7 +155,7 @@ static unsigned int arc_serial_tx_empty(struct uart_port *port) */ static void arc_serial_tx_chars(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; int sent = 0; unsigned char ch; @@ -164,9 +164,7 @@ static void arc_serial_tx_chars(struct uart_port *port) port->icount.tx++; port->x_char = 0; sent = 1; - } else if (!uart_circ_empty(xmit)) { - ch = xmit->buf[xmit->tail]; - uart_xmit_advance(port, 1); + } else if (uart_fifo_get(port, &ch)) { while (!(UART_GET_STATUS(port) & TXEMPTY)) cpu_relax(); UART_SET_DATA(port, ch); @@ -177,7 +175,7 @@ static void arc_serial_tx_chars(struct uart_port *port) * If num chars in xmit buffer are too few, ask tty layer for more. * By Hard ISR to schedule processing in software interrupt part */ - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); if (sent) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 85667f709515..08dd8f887956 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -96,7 +96,9 @@ struct atmel_uart_char { * can contain up to 1024 characters in PIO mode and up to 4096 characters in * DMA mode. */ -#define ATMEL_SERIAL_RINGSIZE 1024 +#define ATMEL_SERIAL_RINGSIZE 1024 +#define ATMEL_SERIAL_RX_SIZE array_size(sizeof(struct atmel_uart_char), \ + ATMEL_SERIAL_RINGSIZE) /* * at91: 6 USARTs and one DBGU port (SAM9260) @@ -132,8 +134,8 @@ struct atmel_uart_port { 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; + dma_addr_t tx_phys; + dma_addr_t rx_phys; struct tasklet_struct tasklet_rx; struct tasklet_struct tasklet_tx; atomic_t tasklet_shutdown; @@ -698,7 +700,7 @@ static void atmel_disable_ms(struct uart_port *port) atmel_port->ms_irq_enabled = false; - mctrl_gpio_disable_ms(atmel_port->gpios); + mctrl_gpio_disable_ms_no_sync(atmel_port->gpios); if (!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_CTS)) idr |= ATMEL_US_CTSIC; @@ -857,7 +859,7 @@ 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 tty_port *tport = &port->state->port; struct dma_chan *chan = atmel_port->chan_tx; unsigned long flags; @@ -873,15 +875,15 @@ static void atmel_complete_tx_dma(void *arg) atmel_port->desc_tx = NULL; spin_unlock(&atmel_port->lock_tx); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); /* - * xmit is a circular buffer so, if we have just send data from - * xmit->tail to the end of xmit->buf, now we have to transmit the - * remaining data from the beginning of xmit->buf to xmit->head. + * xmit is a circular buffer so, if we have just send data from the + * tail to the end, now we have to transmit the remaining data from the + * beginning to the head. */ - if (!uart_circ_empty(xmit)) + if (!kfifo_is_empty(&tport->xmit_fifo)) atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx); else if (atmel_uart_is_half_duplex(port)) { /* @@ -904,8 +906,8 @@ static void atmel_release_tx_dma(struct uart_port *port) if (chan) { dmaengine_terminate_all(chan); dma_release_channel(chan); - dma_unmap_sg(port->dev, &atmel_port->sg_tx, 1, - DMA_TO_DEVICE); + dma_unmap_single(port->dev, atmel_port->tx_phys, + UART_XMIT_SIZE, DMA_TO_DEVICE); } atmel_port->desc_tx = NULL; @@ -919,18 +921,18 @@ static void atmel_release_tx_dma(struct uart_port *port) 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 tty_port *tport = &port->state->port; struct dma_chan *chan = atmel_port->chan_tx; struct dma_async_tx_descriptor *desc; - struct scatterlist sgl[2], *sg, *sg_tx = &atmel_port->sg_tx; - unsigned int tx_len, part1_len, part2_len, sg_len; + struct scatterlist sgl[2], *sg; + unsigned int tx_len, tail, part1_len, part2_len, sg_len; dma_addr_t phys_addr; /* Make sure we have an idle channel */ if (atmel_port->desc_tx != NULL) return; - if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) { + if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(port)) { /* * DMA is idle now. * Port xmit buffer is already mapped, @@ -940,9 +942,8 @@ static void atmel_tx_dma(struct uart_port *port) * Take the port lock to get a * consistent xmit buffer state. */ - tx_len = CIRC_CNT_TO_END(xmit->head, - xmit->tail, - UART_XMIT_SIZE); + tx_len = kfifo_out_linear(&tport->xmit_fifo, &tail, + UART_XMIT_SIZE); if (atmel_port->fifo_size) { /* multi data mode */ @@ -956,7 +957,7 @@ static void atmel_tx_dma(struct uart_port *port) sg_init_table(sgl, 2); sg_len = 0; - phys_addr = sg_dma_address(sg_tx) + xmit->tail; + phys_addr = atmel_port->tx_phys + tail; if (part1_len) { sg = &sgl[sg_len++]; sg_dma_address(sg) = phys_addr; @@ -973,7 +974,7 @@ static void atmel_tx_dma(struct uart_port *port) /* * save tx_len so atmel_complete_tx_dma() will increase - * xmit->tail correctly + * tail correctly */ atmel_port->tx_len = tx_len; @@ -988,7 +989,8 @@ static void atmel_tx_dma(struct uart_port *port) return; } - dma_sync_sg_for_device(port->dev, sg_tx, 1, DMA_TO_DEVICE); + dma_sync_single_for_device(port->dev, atmel_port->tx_phys, + UART_XMIT_SIZE, DMA_TO_DEVICE); atmel_port->desc_tx = desc; desc->callback = atmel_complete_tx_dma; @@ -1003,18 +1005,19 @@ static void atmel_tx_dma(struct uart_port *port) dma_async_issue_pending(chan); } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < 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); + struct tty_port *tport = &port->state->port; struct device *mfd_dev = port->dev->parent; dma_cap_mask_t mask; struct dma_slave_config config; struct dma_chan *chan; - int ret, nent; + int ret; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); @@ -1029,26 +1032,18 @@ static int atmel_prepare_tx_dma(struct uart_port *port) 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(!PAGE_ALIGNED(port->state->xmit.buf)); - sg_set_page(&atmel_port->sg_tx, - virt_to_page(port->state->xmit.buf), - UART_XMIT_SIZE, - offset_in_page(port->state->xmit.buf)); - nent = dma_map_sg(port->dev, - &atmel_port->sg_tx, - 1, - DMA_TO_DEVICE); - - if (!nent) { + BUG_ON(!PAGE_ALIGNED(tport->xmit_buf)); + atmel_port->tx_phys = dma_map_single(port->dev, tport->xmit_buf, + UART_XMIT_SIZE, DMA_TO_DEVICE); + + if (dma_mapping_error(port->dev, atmel_port->tx_phys)) { dev_dbg(port->dev, "need to release resource of dma\n"); goto chan_err; } else { - dev_dbg(port->dev, "%s: mapped %d@%p to %pad\n", __func__, - sg_dma_len(&atmel_port->sg_tx), - port->state->xmit.buf, - &sg_dma_address(&atmel_port->sg_tx)); + dev_dbg(port->dev, "%s: mapped %lu@%p to %pad\n", __func__, + UART_XMIT_SIZE, tport->xmit_buf, + &atmel_port->tx_phys); } /* Configure the slave DMA */ @@ -1093,8 +1088,8 @@ static void atmel_release_rx_dma(struct uart_port *port) if (chan) { dmaengine_terminate_all(chan); dma_release_channel(chan); - dma_unmap_sg(port->dev, &atmel_port->sg_rx, 1, - DMA_FROM_DEVICE); + dma_unmap_single(port->dev, atmel_port->rx_phys, + ATMEL_SERIAL_RX_SIZE, DMA_FROM_DEVICE); } atmel_port->desc_rx = NULL; @@ -1127,10 +1122,8 @@ static void atmel_rx_from_dma(struct uart_port *port) } /* CPU claims ownership of RX DMA buffer */ - dma_sync_sg_for_cpu(port->dev, - &atmel_port->sg_rx, - 1, - DMA_FROM_DEVICE); + dma_sync_single_for_cpu(port->dev, atmel_port->rx_phys, + ATMEL_SERIAL_RX_SIZE, DMA_FROM_DEVICE); /* * ring->head points to the end of data already written by the DMA. @@ -1139,8 +1132,8 @@ static void atmel_rx_from_dma(struct uart_port *port) * The current transfer size should not be larger than the dma buffer * length. */ - ring->head = sg_dma_len(&atmel_port->sg_rx) - state.residue; - BUG_ON(ring->head > sg_dma_len(&atmel_port->sg_rx)); + ring->head = ATMEL_SERIAL_RX_SIZE - state.residue; + BUG_ON(ring->head > ATMEL_SERIAL_RX_SIZE); /* * At this point ring->head may point to the first byte right after the * last byte of the dma buffer: @@ -1154,7 +1147,7 @@ static void atmel_rx_from_dma(struct uart_port *port) * tail to the end of the buffer then reset tail. */ if (ring->head < ring->tail) { - count = sg_dma_len(&atmel_port->sg_rx) - ring->tail; + count = ATMEL_SERIAL_RX_SIZE - ring->tail; tty_insert_flip_string(tport, ring->buf + ring->tail, count); ring->tail = 0; @@ -1167,17 +1160,15 @@ static void atmel_rx_from_dma(struct uart_port *port) tty_insert_flip_string(tport, ring->buf + ring->tail, count); /* Wrap ring->head if needed */ - if (ring->head >= sg_dma_len(&atmel_port->sg_rx)) + if (ring->head >= ATMEL_SERIAL_RX_SIZE) ring->head = 0; ring->tail = ring->head; port->icount.rx += count; } - /* USART retreives ownership of RX DMA buffer */ - dma_sync_sg_for_device(port->dev, - &atmel_port->sg_rx, - 1, - DMA_FROM_DEVICE); + /* USART retrieves ownership of RX DMA buffer */ + dma_sync_single_for_device(port->dev, atmel_port->rx_phys, + ATMEL_SERIAL_RX_SIZE, DMA_FROM_DEVICE); tty_flip_buffer_push(tport); @@ -1193,7 +1184,7 @@ static int atmel_prepare_rx_dma(struct uart_port *port) struct dma_slave_config config; struct circ_buf *ring; struct dma_chan *chan; - int ret, nent; + int ret; ring = &atmel_port->rx_ring; @@ -1210,26 +1201,18 @@ static int atmel_prepare_rx_dma(struct uart_port *port) 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(!PAGE_ALIGNED(ring->buf)); - sg_set_page(&atmel_port->sg_rx, - virt_to_page(ring->buf), - sizeof(struct atmel_uart_char) * ATMEL_SERIAL_RINGSIZE, - offset_in_page(ring->buf)); - nent = dma_map_sg(port->dev, - &atmel_port->sg_rx, - 1, - DMA_FROM_DEVICE); - - if (!nent) { + atmel_port->rx_phys = dma_map_single(port->dev, ring->buf, + ATMEL_SERIAL_RX_SIZE, + DMA_FROM_DEVICE); + + if (dma_mapping_error(port->dev, atmel_port->rx_phys)) { dev_dbg(port->dev, "need to release resource of dma\n"); goto chan_err; } else { - dev_dbg(port->dev, "%s: mapped %d@%p to %pad\n", __func__, - sg_dma_len(&atmel_port->sg_rx), - ring->buf, - &sg_dma_address(&atmel_port->sg_rx)); + dev_dbg(port->dev, "%s: mapped %zu@%p to %pad\n", __func__, + ATMEL_SERIAL_RX_SIZE, ring->buf, &atmel_port->rx_phys); } /* Configure the slave DMA */ @@ -1250,9 +1233,9 @@ static int atmel_prepare_rx_dma(struct uart_port *port) * 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, + atmel_port->rx_phys, + ATMEL_SERIAL_RX_SIZE, + ATMEL_SERIAL_RX_SIZE / 2, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); if (!desc) { @@ -1283,8 +1266,8 @@ chan_err: static void atmel_uart_timer_callback(struct timer_list *t) { - struct atmel_uart_port *atmel_port = from_timer(atmel_port, t, - uart_timer); + struct atmel_uart_port *atmel_port = timer_container_of(atmel_port, t, + uart_timer); struct uart_port *port = &atmel_port->uart; if (!atomic_read(&atmel_port->tasklet_shutdown)) { @@ -1459,9 +1442,8 @@ static void atmel_release_tx_pdc(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; + struct tty_port *tport = &port->state->port; struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; - int count; /* nothing left to transmit? */ if (atmel_uart_readl(port, ATMEL_PDC_TCR)) @@ -1474,17 +1456,19 @@ static void atmel_tx_pdc(struct uart_port *port) /* disable PDC transmit */ atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS); - if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) { + if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(port)) { + unsigned int count, tail; + dma_sync_single_for_device(port->dev, pdc->dma_addr, pdc->dma_size, DMA_TO_DEVICE); - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + count = kfifo_out_linear(&tport->xmit_fifo, &tail, + UART_XMIT_SIZE); pdc->ofs = count; - atmel_uart_writel(port, ATMEL_PDC_TPR, - pdc->dma_addr + xmit->tail); + atmel_uart_writel(port, ATMEL_PDC_TPR, pdc->dma_addr + tail); atmel_uart_writel(port, ATMEL_PDC_TCR, count); /* re-enable PDC transmit */ atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN); @@ -1498,7 +1482,7 @@ static void atmel_tx_pdc(struct uart_port *port) } } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); } @@ -1506,9 +1490,9 @@ 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; + struct tty_port *tport = &port->state->port; - pdc->buf = xmit->buf; + pdc->buf = tport->xmit_buf; pdc->dma_addr = dma_map_single(port->dev, pdc->buf, UART_XMIT_SIZE, @@ -1743,26 +1727,16 @@ static void atmel_init_property(struct atmel_uart_port *atmel_port, /* DMA/PDC usage specification */ if (of_property_read_bool(np, "atmel,use-dma-rx")) { - if (of_property_read_bool(np, "dmas")) { - 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; - } + atmel_port->use_dma_rx = of_property_present(np, "dmas"); + atmel_port->use_pdc_rx = !atmel_port->use_dma_rx; } else { atmel_port->use_dma_rx = false; atmel_port->use_pdc_rx = false; } if (of_property_read_bool(np, "atmel,use-dma-tx")) { - if (of_property_read_bool(np, "dmas")) { - 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; - } + atmel_port->use_dma_tx = of_property_present(np, "dmas"); + atmel_port->use_pdc_tx = !atmel_port->use_dma_tx; } else { atmel_port->use_dma_tx = false; atmel_port->use_pdc_tx = false; @@ -2043,7 +2017,7 @@ static void atmel_shutdown(struct uart_port *port) * Prevent any tasklets being scheduled during * cleanup */ - del_timer_sync(&atmel_port->uart_timer); + timer_delete_sync(&atmel_port->uart_timer); /* Make sure that no interrupt is on the fly */ synchronize_irq(port->irq); @@ -2435,17 +2409,11 @@ static void atmel_release_port(struct uart_port *port) static int atmel_request_port(struct uart_port *port) { struct platform_device *mpdev = to_platform_device(port->dev->parent); - int size = resource_size(mpdev->resource); - - if (!request_mem_region(port->mapbase, size, "atmel_serial")) - return -EBUSY; if (port->flags & UPF_IOREMAP) { - port->membase = ioremap(port->mapbase, size); - if (port->membase == NULL) { - release_mem_region(port->mapbase, size); - return -ENOMEM; - } + port->membase = devm_platform_ioremap_resource(mpdev, 0); + if (IS_ERR(port->membase)) + return PTR_ERR(port->membase); } return 0; @@ -2530,7 +2498,7 @@ static const struct uart_ops atmel_pops = { }; static const struct serial_rs485 atmel_rs485_supported = { - .flags = SER_RS485_ENABLED | SER_RS485_RTS_AFTER_SEND | SER_RS485_RX_DURING_TX, + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX, .delay_rts_before_send = 1, .delay_rts_after_send = 1, }; @@ -2953,9 +2921,7 @@ static int atmel_serial_probe(struct platform_device *pdev) if (!atmel_use_pdc_rx(&atmel_port->uart)) { ret = -ENOMEM; - data = kmalloc_array(ATMEL_SERIAL_RINGSIZE, - sizeof(struct atmel_uart_char), - GFP_KERNEL); + data = kmalloc(ATMEL_SERIAL_RX_SIZE, GFP_KERNEL); if (!data) goto err_clk_disable_unprepare; atmel_port->rx_ring.buf = data; @@ -3035,7 +3001,7 @@ static SIMPLE_DEV_PM_OPS(atmel_serial_pm_ops, atmel_serial_suspend, static struct platform_driver atmel_serial_driver = { .probe = atmel_serial_probe, - .remove_new = atmel_serial_remove, + .remove = atmel_serial_remove, .driver = { .name = "atmel_usart_serial", .of_match_table = of_match_ptr(atmel_serial_dt_ids), diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index 34801a6f300b..51df9d2d8bfc 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -308,8 +308,8 @@ static void bcm_uart_do_tx(struct uart_port *port) val = bcm_uart_readl(port, UART_MCTL_REG); val = (val & UART_MCTL_TXFIFOFILL_MASK) >> UART_MCTL_TXFIFOFILL_SHIFT; - - pending = uart_port_tx_limited(port, ch, port->fifosize - val, + pending = uart_port_tx_limited_flags(port, ch, UART_TX_NOSTOP, + port->fifosize - val, true, bcm_uart_writel(port, ch, UART_FIFO_REG), ({})); @@ -320,6 +320,9 @@ static void bcm_uart_do_tx(struct uart_port *port) val = bcm_uart_readl(port, UART_IR_REG); val &= ~UART_TX_INT_MASK; bcm_uart_writel(port, val, UART_IR_REG); + + if (uart_tx_stopped(port)) + bcm_uart_stop_tx(port); } /* @@ -881,7 +884,7 @@ MODULE_DEVICE_TABLE(of, bcm63xx_of_match); */ static struct platform_driver bcm_uart_platform_driver = { .probe = bcm_uart_probe, - .remove_new = bcm_uart_remove, + .remove = bcm_uart_remove, .driver = { .name = "bcm63xx_uart", .of_match_table = bcm63xx_of_match, diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 7927725b8957..83186bf50002 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -146,7 +146,8 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id) { struct uart_port *port = dev_id; struct clps711x_port *s = dev_get_drvdata(port->dev); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; + unsigned char c; if (port->x_char) { writew(port->x_char, port->membase + UARTDR_OFFSET); @@ -155,7 +156,7 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id) return IRQ_HANDLED; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { if (s->tx_enabled) { disable_irq_nosync(port->irq); s->tx_enabled = 0; @@ -163,18 +164,17 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id) return IRQ_HANDLED; } - while (!uart_circ_empty(xmit)) { + while (uart_fifo_get(port, &c)) { u32 sysflg = 0; - writew(xmit->buf[xmit->tail], port->membase + UARTDR_OFFSET); - uart_xmit_advance(port, 1); + writew(c, port->membase + UARTDR_OFFSET); regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); if (sysflg & SYSFLG_UTXFF) break; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); return IRQ_HANDLED; @@ -529,7 +529,7 @@ static struct platform_driver clps711x_uart_platform = { .of_match_table = of_match_ptr(clps711x_uart_dt_ids), }, .probe = uart_clps711x_probe, - .remove_new = uart_clps711x_remove, + .remove = uart_clps711x_remove, }; static int __init uart_clps711x_init(void) diff --git a/drivers/tty/serial/cpm_uart.c b/drivers/tty/serial/cpm_uart.c index df56c6c5afd0..b778a20ec9b1 100644 --- a/drivers/tty/serial/cpm_uart.c +++ b/drivers/tty/serial/cpm_uart.c @@ -631,7 +631,7 @@ static int cpm_uart_verify_port(struct uart_port *port, if (ser->type != PORT_UNKNOWN && ser->type != PORT_CPM) ret = -EINVAL; - if (ser->irq < 0 || ser->irq >= nr_irqs) + if (ser->irq < 0 || ser->irq >= irq_get_nr_irqs()) ret = -EINVAL; if (ser->baud_base < 9600) ret = -EINVAL; @@ -648,7 +648,7 @@ static int cpm_uart_tx_pump(struct uart_port *port) int count; struct uart_cpm_port *pinfo = container_of(port, struct uart_cpm_port, port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; /* Handle xon/xoff */ if (port->x_char) { @@ -673,7 +673,7 @@ static int cpm_uart_tx_pump(struct uart_port *port) return 1; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { cpm_uart_stop_tx(port); return 0; } @@ -681,16 +681,10 @@ static int cpm_uart_tx_pump(struct uart_port *port) /* Pick next descriptor and fill from buffer */ bdp = pinfo->tx_cur; - while (!(in_be16(&bdp->cbd_sc) & BD_SC_READY) && !uart_circ_empty(xmit)) { - count = 0; + while (!(in_be16(&bdp->cbd_sc) & BD_SC_READY) && + !kfifo_is_empty(&tport->xmit_fifo)) { p = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), pinfo); - while (count < pinfo->tx_fifosize) { - *p++ = xmit->buf[xmit->tail]; - uart_xmit_advance(port, 1); - count++; - if (uart_circ_empty(xmit)) - break; - } + count = uart_fifo_out(port, p, pinfo->tx_fifosize); out_be16(&bdp->cbd_datlen, count); setbits16(&bdp->cbd_sc, BD_SC_READY); /* Get next BD. */ @@ -701,10 +695,10 @@ static int cpm_uart_tx_pump(struct uart_port *port) } pinfo->tx_cur = bdp; - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) { + if (kfifo_is_empty(&tport->xmit_fifo)) { cpm_uart_stop_tx(port); return 0; } @@ -1579,7 +1573,7 @@ static struct platform_driver cpm_uart_driver = { .of_match_table = cpm_uart_match, }, .probe = cpm_uart_probe, - .remove_new = cpm_uart_remove, + .remove = cpm_uart_remove, }; static int __init cpm_uart_init(void) diff --git a/drivers/tty/serial/digicolor-usart.c b/drivers/tty/serial/digicolor-usart.c index e419c4bde8b7..d2482df5cb9b 100644 --- a/drivers/tty/serial/digicolor-usart.c +++ b/drivers/tty/serial/digicolor-usart.c @@ -179,8 +179,9 @@ static void digicolor_uart_rx(struct uart_port *port) static void digicolor_uart_tx(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; unsigned long flags; + unsigned char c; if (digicolor_uart_tx_full(port)) return; @@ -194,20 +195,19 @@ static void digicolor_uart_tx(struct uart_port *port) goto out; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { digicolor_uart_stop_tx(port); goto out; } - while (!uart_circ_empty(xmit)) { - writeb(xmit->buf[xmit->tail], port->membase + UA_EMI_REC); - uart_xmit_advance(port, 1); + while (uart_fifo_get(port, &c)) { + writeb(c, port->membase + UA_EMI_REC); if (digicolor_uart_tx_full(port)) break; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); out: @@ -522,7 +522,7 @@ static struct platform_driver digicolor_uart_platform = { .of_match_table = of_match_ptr(digicolor_uart_dt_ids), }, .probe = digicolor_uart_probe, - .remove_new = digicolor_uart_remove, + .remove = digicolor_uart_remove, }; static int __init digicolor_uart_init(void) diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c index 6df7af9edc1c..eba91daedef8 100644 --- a/drivers/tty/serial/dz.c +++ b/drivers/tty/serial/dz.c @@ -252,13 +252,13 @@ static inline void dz_receive_chars(struct dz_mux *mux) static inline void dz_transmit_chars(struct dz_mux *mux) { struct dz_port *dport = &mux->dport[0]; - struct circ_buf *xmit; + struct tty_port *tport; unsigned char tmp; u16 status; status = dz_in(dport, DZ_CSR); dport = &mux->dport[LINE(status)]; - xmit = &dport->port.state->xmit; + tport = &dport->port.state->port; if (dport->port.x_char) { /* XON/XOFF chars */ dz_out(dport, DZ_TDR, dport->port.x_char); @@ -267,7 +267,8 @@ static inline void dz_transmit_chars(struct dz_mux *mux) return; } /* If nothing to do or stopped or hardware stopped. */ - if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) { + if (uart_tx_stopped(&dport->port) || + !uart_fifo_get(&dport->port, &tmp)) { uart_port_lock(&dport->port); dz_stop_tx(&dport->port); uart_port_unlock(&dport->port); @@ -278,15 +279,13 @@ static inline void dz_transmit_chars(struct dz_mux *mux) * If something to do... (remember the dz has no output fifo, * so we go one char at a time) :-< */ - tmp = xmit->buf[xmit->tail]; dz_out(dport, DZ_TDR, tmp); - uart_xmit_advance(&dport->port, 1); - if (uart_circ_chars_pending(xmit) < DZ_WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < DZ_WAKEUP_CHARS) uart_write_wakeup(&dport->port); /* Are we are done. */ - if (uart_circ_empty(xmit)) { + if (kfifo_is_empty(&tport->xmit_fifo)) { uart_port_lock(&dport->port); dz_stop_tx(&dport->port); uart_port_unlock(&dport->port); diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index a5fbb6ed38ae..ab9af37f6cda 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -248,6 +248,29 @@ static int __init param_setup_earlycon(char *buf) } early_param("earlycon", param_setup_earlycon); +/* + * The `console` parameter is overloaded. It's handled here as an early param + * and in `printk.c` as a late param. It's possible to specify an early + * `bootconsole` using `earlycon=uartXXXX` (handled above), or via + * the `console=uartXXX` alias. See the comment in `8250_early.c`. + */ +static int __init param_setup_earlycon_console_alias(char *buf) +{ + /* + * A plain `console` parameter must not enable the SPCR `bootconsole` + * like a plain `earlycon` does. + * + * A `console=` parameter that specifies an empty value is used to + * disable the `console`, not the `earlycon` `bootconsole`. The + * disabling of the `console` is handled by `printk.c`. + */ + if (!buf || !buf[0]) + return 0; + + return param_setup_earlycon(buf); +} +early_param("console", param_setup_earlycon_console_alias); + #ifdef CONFIG_OF_EARLY_FLATTREE int __init of_setup_earlycon(const struct earlycon_id *match, diff --git a/drivers/tty/serial/esp32_acm.c b/drivers/tty/serial/esp32_acm.c index d4e8bdb1cdef..bb7cc65427f0 100644 --- a/drivers/tty/serial/esp32_acm.c +++ b/drivers/tty/serial/esp32_acm.c @@ -423,7 +423,7 @@ static void esp32s3_acm_remove(struct platform_device *pdev) static struct platform_driver esp32s3_acm_driver = { .probe = esp32s3_acm_probe, - .remove_new = esp32s3_acm_remove, + .remove = esp32s3_acm_remove, .driver = { .name = DRIVER_NAME, .of_match_table = esp32s3_acm_dt_ids, @@ -455,4 +455,5 @@ module_init(esp32s3_acm_init); module_exit(esp32s3_acm_exit); MODULE_AUTHOR("Max Filippov <jcmvbkbc@gmail.com>"); +MODULE_DESCRIPTION("Espressif ESP32 USB ACM gadget support"); MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/esp32_uart.c b/drivers/tty/serial/esp32_uart.c index 6fc61f323355..667c2198a03a 100644 --- a/drivers/tty/serial/esp32_uart.c +++ b/drivers/tty/serial/esp32_uart.c @@ -743,7 +743,7 @@ static void esp32_uart_remove(struct platform_device *pdev) static struct platform_driver esp32_uart_driver = { .probe = esp32_uart_probe, - .remove_new = esp32_uart_remove, + .remove = esp32_uart_remove, .driver = { .name = DRIVER_NAME, .of_match_table = esp32_uart_dt_ids, @@ -775,4 +775,5 @@ module_init(esp32_uart_init); module_exit(esp32_uart_exit); MODULE_AUTHOR("Max Filippov <jcmvbkbc@gmail.com>"); +MODULE_DESCRIPTION("Espressif ESP32 UART support"); MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index 5426322b5f0c..e70a56de1fce 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -174,17 +174,18 @@ static void linflex_put_char(struct uart_port *sport, unsigned char c) static inline void linflex_transmit_buffer(struct uart_port *sport) { - struct circ_buf *xmit = &sport->state->xmit; + struct tty_port *tport = &sport->state->port; + unsigned char c; - while (!uart_circ_empty(xmit)) { - linflex_put_char(sport, xmit->buf[xmit->tail]); - uart_xmit_advance(sport, 1); + while (uart_fifo_get(sport, &c)) { + linflex_put_char(sport, c); + sport->icount.tx++; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(sport); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) linflex_stop_tx(sport); } @@ -200,7 +201,7 @@ static void linflex_start_tx(struct uart_port *port) static irqreturn_t linflex_txint(int irq, void *dev_id) { struct uart_port *sport = dev_id; - struct circ_buf *xmit = &sport->state->xmit; + struct tty_port *tport = &sport->state->port; unsigned long flags; uart_port_lock_irqsave(sport, &flags); @@ -210,7 +211,7 @@ static irqreturn_t linflex_txint(int irq, void *dev_id) goto out; } - if (uart_circ_empty(xmit) || uart_tx_stopped(sport)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(sport)) { linflex_stop_tx(sport); goto out; } @@ -881,7 +882,7 @@ static SIMPLE_DEV_PM_OPS(linflex_pm_ops, linflex_suspend, linflex_resume); static struct platform_driver linflex_driver = { .probe = linflex_probe, - .remove_new = linflex_remove, + .remove = linflex_remove, .driver = { .name = DRIVER_NAME, .of_match_table = linflex_dt_ids, diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index bbcbc91482af..2790b4078e7e 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -7,6 +7,7 @@ #include <linux/bitfield.h> #include <linux/bits.h> +#include <linux/circ_buf.h> #include <linux/clk.h> #include <linux/console.h> #include <linux/delay.h> @@ -183,6 +184,7 @@ #define UARTCTRL_SBK 0x00010000 #define UARTCTRL_MA1IE 0x00008000 #define UARTCTRL_MA2IE 0x00004000 +#define UARTCTRL_M7 0x00000800 #define UARTCTRL_IDLECFG GENMASK(10, 8) #define UARTCTRL_LOOPS 0x00000080 #define UARTCTRL_DOZEEN 0x00000040 @@ -243,7 +245,7 @@ #define DRIVER_NAME "fsl-lpuart" #define DEV_NAME "ttyLP" -#define UART_NR 8 +#define UART_NR 12 /* IMX lpuart has four extra unused regs located at the beginning */ #define IMX_REG_OFF 0x10 @@ -401,6 +403,8 @@ static inline void lpuart32_write(struct uart_port *port, u32 val, case UPIO_MEM32BE: iowrite32be(val, port->membase + off); break; + default: + break; } } @@ -439,41 +443,41 @@ static unsigned int lpuart_get_baud_clk_rate(struct lpuart_port *sport) static void lpuart_stop_tx(struct uart_port *port) { - unsigned char temp; + u8 cr2; - temp = readb(port->membase + UARTCR2); - temp &= ~(UARTCR2_TIE | UARTCR2_TCIE); - writeb(temp, port->membase + UARTCR2); + cr2 = readb(port->membase + UARTCR2); + cr2 &= ~(UARTCR2_TIE | UARTCR2_TCIE); + writeb(cr2, port->membase + UARTCR2); } static void lpuart32_stop_tx(struct uart_port *port) { - unsigned long temp; + u32 ctrl; - temp = lpuart32_read(port, UARTCTRL); - temp &= ~(UARTCTRL_TIE | UARTCTRL_TCIE); - lpuart32_write(port, temp, UARTCTRL); + ctrl = lpuart32_read(port, UARTCTRL); + ctrl &= ~(UARTCTRL_TIE | UARTCTRL_TCIE); + lpuart32_write(port, ctrl, UARTCTRL); } static void lpuart_stop_rx(struct uart_port *port) { - unsigned char temp; + u8 cr2; - temp = readb(port->membase + UARTCR2); - writeb(temp & ~UARTCR2_RE, port->membase + UARTCR2); + cr2 = readb(port->membase + UARTCR2); + writeb(cr2 & ~UARTCR2_RE, port->membase + UARTCR2); } static void lpuart32_stop_rx(struct uart_port *port) { - unsigned long temp; + u32 ctrl; - temp = lpuart32_read(port, UARTCTRL); - lpuart32_write(port, temp & ~UARTCTRL_RE, UARTCTRL); + ctrl = lpuart32_read(port, UARTCTRL); + lpuart32_write(port, ctrl & ~UARTCTRL_RE, UARTCTRL); } static void lpuart_dma_tx(struct lpuart_port *sport) { - struct circ_buf *xmit = &sport->port.state->xmit; + struct tty_port *tport = &sport->port.state->port; struct scatterlist *sgl = sport->tx_sgl; struct device *dev = sport->port.dev; struct dma_chan *chan = sport->dma_tx_chan; @@ -482,18 +486,10 @@ static void lpuart_dma_tx(struct lpuart_port *sport) if (sport->dma_tx_in_progress) return; - sport->dma_tx_bytes = uart_circ_chars_pending(xmit); - - if (xmit->tail < xmit->head || xmit->head == 0) { - sport->dma_tx_nents = 1; - sg_init_one(sgl, xmit->buf + xmit->tail, sport->dma_tx_bytes); - } else { - 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); - } + sg_init_table(sgl, ARRAY_SIZE(sport->tx_sgl)); + sport->dma_tx_bytes = kfifo_len(&tport->xmit_fifo); + sport->dma_tx_nents = kfifo_dma_out_prepare(&tport->xmit_fifo, sgl, + ARRAY_SIZE(sport->tx_sgl), sport->dma_tx_bytes); ret = dma_map_sg(chan->device->dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE); @@ -521,14 +517,15 @@ static void lpuart_dma_tx(struct lpuart_port *sport) static bool lpuart_stopped_or_empty(struct uart_port *port) { - return uart_circ_empty(&port->state->xmit) || uart_tx_stopped(port); + return kfifo_is_empty(&port->state->port.xmit_fifo) || + uart_tx_stopped(port); } static void lpuart_dma_tx_complete(void *arg) { struct lpuart_port *sport = arg; struct scatterlist *sgl = &sport->tx_sgl[0]; - struct circ_buf *xmit = &sport->port.state->xmit; + struct tty_port *tport = &sport->port.state->port; struct dma_chan *chan = sport->dma_tx_chan; unsigned long flags; @@ -545,7 +542,7 @@ static void lpuart_dma_tx_complete(void *arg) sport->dma_tx_in_progress = false; uart_port_unlock_irqrestore(&sport->port, flags); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&sport->port); if (waitqueue_active(&sport->dma_wait)) { @@ -568,8 +565,9 @@ static dma_addr_t lpuart_dma_datareg_addr(struct lpuart_port *sport) return sport->port.mapbase + UARTDATA; case UPIO_MEM32BE: return sport->port.mapbase + UARTDATA + sizeof(u32) - 1; + default: + return sport->port.mapbase + UARTDR; } - return sport->port.mapbase + UARTDR; } static int lpuart_dma_tx_request(struct uart_port *port) @@ -586,7 +584,7 @@ static int lpuart_dma_tx_request(struct uart_port *port) ret = dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig); if (ret) { - dev_err(sport->port.dev, + dev_err(port->dev, "DMA slave config failed, err = %d\n", ret); return ret; } @@ -604,7 +602,7 @@ static void lpuart_flush_buffer(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); struct dma_chan *chan = sport->dma_tx_chan; - u32 val; + u32 fifo; if (sport->lpuart_dma_tx_use) { if (sport->dma_tx_in_progress) { @@ -616,13 +614,13 @@ static void lpuart_flush_buffer(struct uart_port *port) } if (lpuart_is_32(sport)) { - val = lpuart32_read(&sport->port, UARTFIFO); - val |= UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH; - lpuart32_write(&sport->port, val, UARTFIFO); + fifo = lpuart32_read(port, UARTFIFO); + fifo |= UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH; + lpuart32_write(port, fifo, UARTFIFO); } else { - val = readb(sport->port.membase + UARTCFIFO); - val |= UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH; - writeb(val, sport->port.membase + UARTCFIFO); + fifo = readb(port->membase + UARTCFIFO); + fifo |= UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH; + writeb(fifo, port->membase + UARTCFIFO); } } @@ -644,38 +642,36 @@ static void lpuart32_wait_bit_set(struct uart_port *port, unsigned int offset, static int lpuart_poll_init(struct uart_port *port) { - struct lpuart_port *sport = container_of(port, - struct lpuart_port, port); unsigned long flags; - unsigned char temp; + u8 fifo; - sport->port.fifosize = 0; + port->fifosize = 0; - uart_port_lock_irqsave(&sport->port, &flags); + uart_port_lock_irqsave(port, &flags); /* Disable Rx & Tx */ - writeb(0, sport->port.membase + UARTCR2); + writeb(0, port->membase + UARTCR2); - temp = readb(sport->port.membase + UARTPFIFO); + fifo = readb(port->membase + UARTPFIFO); /* Enable Rx and Tx FIFO */ - writeb(temp | UARTPFIFO_RXFE | UARTPFIFO_TXFE, - sport->port.membase + UARTPFIFO); + writeb(fifo | UARTPFIFO_RXFE | UARTPFIFO_TXFE, + port->membase + UARTPFIFO); /* flush Tx and Rx FIFO */ writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH, - sport->port.membase + UARTCFIFO); + port->membase + UARTCFIFO); /* explicitly clear RDRF */ - if (readb(sport->port.membase + UARTSR1) & UARTSR1_RDRF) { - readb(sport->port.membase + UARTDR); - writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO); + if (readb(port->membase + UARTSR1) & UARTSR1_RDRF) { + readb(port->membase + UARTDR); + writeb(UARTSFIFO_RXUF, port->membase + UARTSFIFO); } - writeb(0, sport->port.membase + UARTTWFIFO); - writeb(1, sport->port.membase + UARTRWFIFO); + writeb(0, port->membase + UARTTWFIFO); + writeb(1, port->membase + UARTRWFIFO); /* Enable Rx and Tx */ - writeb(UARTCR2_RE | UARTCR2_TE, sport->port.membase + UARTCR2); - uart_port_unlock_irqrestore(&sport->port, flags); + writeb(UARTCR2_RE | UARTCR2_TE, port->membase + UARTCR2); + uart_port_unlock_irqrestore(port, flags); return 0; } @@ -698,33 +694,32 @@ static int lpuart_poll_get_char(struct uart_port *port) static int lpuart32_poll_init(struct uart_port *port) { unsigned long flags; - struct lpuart_port *sport = container_of(port, struct lpuart_port, port); - u32 temp; + u32 fifo; - sport->port.fifosize = 0; + port->fifosize = 0; - uart_port_lock_irqsave(&sport->port, &flags); + uart_port_lock_irqsave(port, &flags); /* Disable Rx & Tx */ - lpuart32_write(&sport->port, 0, UARTCTRL); + lpuart32_write(port, 0, UARTCTRL); - temp = lpuart32_read(&sport->port, UARTFIFO); + fifo = lpuart32_read(port, UARTFIFO); /* Enable Rx and Tx FIFO */ - lpuart32_write(&sport->port, temp | UARTFIFO_RXFE | UARTFIFO_TXFE, UARTFIFO); + lpuart32_write(port, fifo | UARTFIFO_RXFE | UARTFIFO_TXFE, UARTFIFO); /* flush Tx and Rx FIFO */ - lpuart32_write(&sport->port, UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH, UARTFIFO); + lpuart32_write(port, UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH, UARTFIFO); /* explicitly clear RDRF */ - if (lpuart32_read(&sport->port, UARTSTAT) & UARTSTAT_RDRF) { - lpuart32_read(&sport->port, UARTDATA); - lpuart32_write(&sport->port, UARTFIFO_RXUF, UARTFIFO); + if (lpuart32_read(port, UARTSTAT) & UARTSTAT_RDRF) { + lpuart32_read(port, UARTDATA); + lpuart32_write(port, UARTFIFO_RXUF, UARTFIFO); } /* Enable Rx and Tx */ - lpuart32_write(&sport->port, UARTCTRL_RE | UARTCTRL_TE, UARTCTRL); - uart_port_unlock_irqrestore(&sport->port, flags); + lpuart32_write(port, UARTCTRL_RE | UARTCTRL_TE, UARTCTRL); + uart_port_unlock_irqrestore(port, flags); return 0; } @@ -756,8 +751,9 @@ static inline void lpuart_transmit_buffer(struct lpuart_port *sport) static inline void lpuart32_transmit_buffer(struct lpuart_port *sport) { - struct circ_buf *xmit = &sport->port.state->xmit; - unsigned long txcnt; + struct tty_port *tport = &sport->port.state->port; + u32 txcnt; + unsigned char c; if (sport->port.x_char) { lpuart32_write(&sport->port, sport->port.x_char, UARTDATA); @@ -774,18 +770,18 @@ static inline void lpuart32_transmit_buffer(struct lpuart_port *sport) txcnt = lpuart32_read(&sport->port, UARTWATER); txcnt = txcnt >> UARTWATER_TXCNT_OFF; txcnt &= UARTWATER_COUNT_MASK; - while (!uart_circ_empty(xmit) && (txcnt < sport->txfifo_size)) { - lpuart32_write(&sport->port, xmit->buf[xmit->tail], UARTDATA); - uart_xmit_advance(&sport->port, 1); + while (txcnt < sport->txfifo_size && + uart_fifo_get(&sport->port, &c)) { + lpuart32_write(&sport->port, c, UARTDATA); txcnt = lpuart32_read(&sport->port, UARTWATER); txcnt = txcnt >> UARTWATER_TXCNT_OFF; txcnt &= UARTWATER_COUNT_MASK; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&sport->port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) lpuart32_stop_tx(&sport->port); } @@ -793,10 +789,10 @@ static void lpuart_start_tx(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); - unsigned char temp; + u8 cr2; - temp = readb(port->membase + UARTCR2); - writeb(temp | UARTCR2_TIE, port->membase + UARTCR2); + cr2 = readb(port->membase + UARTCR2); + writeb(cr2 | UARTCR2_TIE, port->membase + UARTCR2); if (sport->lpuart_dma_tx_use) { if (!lpuart_stopped_or_empty(port)) @@ -810,14 +806,14 @@ static void lpuart_start_tx(struct uart_port *port) static void lpuart32_start_tx(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); - unsigned long temp; + u32 ctrl; if (sport->lpuart_dma_tx_use) { if (!lpuart_stopped_or_empty(port)) lpuart_dma_tx(sport); } else { - temp = lpuart32_read(port, UARTCTRL); - lpuart32_write(port, temp | UARTCTRL_TIE, UARTCTRL); + ctrl = lpuart32_read(port, UARTCTRL); + lpuart32_write(port, ctrl | UARTCTRL_TIE, UARTCTRL); if (lpuart32_read(port, UARTSTAT) & UARTSTAT_TDRE) lpuart32_transmit_buffer(sport); @@ -843,8 +839,8 @@ static unsigned int lpuart_tx_empty(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); - unsigned char sr1 = readb(port->membase + UARTSR1); - unsigned char sfifo = readb(port->membase + UARTSFIFO); + u8 sr1 = readb(port->membase + UARTSR1); + u8 sfifo = readb(port->membase + UARTSFIFO); if (sport->dma_tx_in_progress) return 0; @@ -859,9 +855,9 @@ static unsigned int lpuart32_tx_empty(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); - unsigned long stat = lpuart32_read(port, UARTSTAT); - unsigned long sfifo = lpuart32_read(port, UARTFIFO); - unsigned long ctrl = lpuart32_read(port, UARTCTRL); + u32 stat = lpuart32_read(port, UARTSTAT); + u32 sfifo = lpuart32_read(port, UARTFIFO); + u32 ctrl = lpuart32_read(port, UARTCTRL); if (sport->dma_tx_in_progress) return 0; @@ -888,7 +884,7 @@ static void lpuart_rxint(struct lpuart_port *sport) { unsigned int flg, ignored = 0, overrun = 0; struct tty_port *port = &sport->port.state->port; - unsigned char rx, sr; + u8 rx, sr; uart_port_lock(&sport->port); @@ -965,7 +961,7 @@ static void lpuart32_rxint(struct lpuart_port *sport) { unsigned int flg, ignored = 0; struct tty_port *port = &sport->port.state->port; - unsigned long rx, sr; + u32 rx, sr; bool is_break; uart_port_lock(&sport->port); @@ -1043,7 +1039,7 @@ out: static irqreturn_t lpuart_int(int irq, void *dev_id) { struct lpuart_port *sport = dev_id; - unsigned char sts; + u8 sts; sts = readb(sport->port.membase + UARTSR1); @@ -1117,7 +1113,7 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport) int count, copied; if (lpuart_is_32(sport)) { - unsigned long sr = lpuart32_read(&sport->port, UARTSTAT); + u32 sr = lpuart32_read(&sport->port, UARTSTAT); if (sr & (UARTSTAT_PE | UARTSTAT_FE)) { /* Clear the error flags */ @@ -1129,10 +1125,10 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport) sport->port.icount.frame++; } } else { - unsigned char sr = readb(sport->port.membase + UARTSR1); + u8 sr = readb(sport->port.membase + UARTSR1); if (sr & (UARTSR1_PE | UARTSR1_FE)) { - unsigned char cr2; + u8 cr2; /* Disable receiver during this operation... */ cr2 = readb(sport->port.membase + UARTCR2); @@ -1283,7 +1279,7 @@ static void lpuart32_dma_idleint(struct lpuart_port *sport) static irqreturn_t lpuart32_int(int irq, void *dev_id) { struct lpuart_port *sport = dev_id; - unsigned long sts, rxcount; + u32 sts, rxcount; sts = lpuart32_read(&sport->port, UARTSTAT); rxcount = lpuart32_read(&sport->port, UARTWATER); @@ -1311,7 +1307,7 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id) */ static void lpuart_timer_func(struct timer_list *t) { - struct lpuart_port *sport = from_timer(sport, t, lpuart_timer); + struct lpuart_port *sport = timer_container_of(sport, t, lpuart_timer); enum dma_status dmastat; struct dma_chan *chan = sport->dma_rx_chan; struct circ_buf *ring = &sport->rx_ring; @@ -1415,12 +1411,12 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) dma_async_issue_pending(chan); if (lpuart_is_32(sport)) { - unsigned long temp = lpuart32_read(&sport->port, UARTBAUD); + u32 baud = lpuart32_read(&sport->port, UARTBAUD); - lpuart32_write(&sport->port, temp | UARTBAUD_RDMAE, UARTBAUD); + lpuart32_write(&sport->port, baud | UARTBAUD_RDMAE, UARTBAUD); if (sport->dma_idle_int) { - unsigned long ctrl = lpuart32_read(&sport->port, UARTCTRL); + u32 ctrl = lpuart32_read(&sport->port, UARTCTRL); lpuart32_write(&sport->port, ctrl | UARTCTRL_ILIE, UARTCTRL); } @@ -1440,7 +1436,7 @@ static void lpuart_dma_rx_free(struct uart_port *port) dmaengine_terminate_sync(chan); if (!sport->dma_idle_int) - del_timer_sync(&sport->lpuart_timer); + timer_delete_sync(&sport->lpuart_timer); dma_unmap_sg(chan->device->dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE); kfree(sport->rx_ring.buf); @@ -1453,12 +1449,9 @@ static void lpuart_dma_rx_free(struct uart_port *port) static int lpuart_config_rs485(struct uart_port *port, struct ktermios *termios, struct serial_rs485 *rs485) { - struct lpuart_port *sport = container_of(port, - struct lpuart_port, port); - - u8 modem = readb(sport->port.membase + UARTMODEM) & + u8 modem = readb(port->membase + UARTMODEM) & ~(UARTMODEM_TXRTSPOL | UARTMODEM_TXRTSE); - writeb(modem, sport->port.membase + UARTMODEM); + writeb(modem, port->membase + UARTMODEM); if (rs485->flags & SER_RS485_ENABLED) { /* Enable auto RS-485 RTS mode */ @@ -1476,19 +1469,29 @@ static int lpuart_config_rs485(struct uart_port *port, struct ktermios *termios, modem &= ~UARTMODEM_TXRTSPOL; } - writeb(modem, sport->port.membase + UARTMODEM); + writeb(modem, port->membase + UARTMODEM); return 0; } static int lpuart32_config_rs485(struct uart_port *port, struct ktermios *termios, struct serial_rs485 *rs485) { - struct lpuart_port *sport = container_of(port, - struct lpuart_port, port); - - unsigned long modem = lpuart32_read(&sport->port, UARTMODIR) + u32 modem = lpuart32_read(port, UARTMODIR) & ~(UARTMODIR_TXRTSPOL | UARTMODIR_TXRTSE); - lpuart32_write(&sport->port, modem, UARTMODIR); + u32 ctrl; + + /* TXRTSE and TXRTSPOL only can be changed when transmitter is disabled. */ + ctrl = lpuart32_read(port, UARTCTRL); + if (ctrl & UARTCTRL_TE) { + /* wait for the transmit engine to complete */ + lpuart32_wait_bit_set(port, UARTSTAT, UARTSTAT_TC); + lpuart32_write(port, ctrl & ~UARTCTRL_TE, UARTCTRL); + + while (lpuart32_read(port, UARTCTRL) & UARTCTRL_TE) + cpu_relax(); + } + + lpuart32_write(port, modem, UARTMODIR); if (rs485->flags & SER_RS485_ENABLED) { /* Enable auto RS-485 RTS mode */ @@ -1506,17 +1509,21 @@ static int lpuart32_config_rs485(struct uart_port *port, struct ktermios *termio modem &= ~UARTMODIR_TXRTSPOL; } - lpuart32_write(&sport->port, modem, UARTMODIR); + lpuart32_write(port, modem, UARTMODIR); + + if (ctrl & UARTCTRL_TE) + lpuart32_write(port, ctrl, UARTCTRL); + return 0; } static unsigned int lpuart_get_mctrl(struct uart_port *port) { unsigned int mctrl = 0; - u8 reg; + u8 cr1; - reg = readb(port->membase + UARTCR1); - if (reg & UARTCR1_LOOPS) + cr1 = readb(port->membase + UARTCR1); + if (cr1 & UARTCR1_LOOPS) mctrl |= TIOCM_LOOP; return mctrl; @@ -1525,10 +1532,10 @@ static unsigned int lpuart_get_mctrl(struct uart_port *port) static unsigned int lpuart32_get_mctrl(struct uart_port *port) { unsigned int mctrl = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; - u32 reg; + u32 ctrl; - reg = lpuart32_read(port, UARTCTRL); - if (reg & UARTCTRL_LOOPS) + ctrl = lpuart32_read(port, UARTCTRL); + if (ctrl & UARTCTRL_LOOPS) mctrl |= TIOCM_LOOP; return mctrl; @@ -1536,49 +1543,49 @@ static unsigned int lpuart32_get_mctrl(struct uart_port *port) static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl) { - u8 reg; + u8 cr1; - reg = readb(port->membase + UARTCR1); + cr1 = readb(port->membase + UARTCR1); /* for internal loopback we need LOOPS=1 and RSRC=0 */ - reg &= ~(UARTCR1_LOOPS | UARTCR1_RSRC); + cr1 &= ~(UARTCR1_LOOPS | UARTCR1_RSRC); if (mctrl & TIOCM_LOOP) - reg |= UARTCR1_LOOPS; + cr1 |= UARTCR1_LOOPS; - writeb(reg, port->membase + UARTCR1); + writeb(cr1, port->membase + UARTCR1); } static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl) { - u32 reg; + u32 ctrl; - reg = lpuart32_read(port, UARTCTRL); + ctrl = lpuart32_read(port, UARTCTRL); /* for internal loopback we need LOOPS=1 and RSRC=0 */ - reg &= ~(UARTCTRL_LOOPS | UARTCTRL_RSRC); + ctrl &= ~(UARTCTRL_LOOPS | UARTCTRL_RSRC); if (mctrl & TIOCM_LOOP) - reg |= UARTCTRL_LOOPS; + ctrl |= UARTCTRL_LOOPS; - lpuart32_write(port, reg, UARTCTRL); + lpuart32_write(port, ctrl, UARTCTRL); } static void lpuart_break_ctl(struct uart_port *port, int break_state) { - unsigned char temp; + u8 cr2; - temp = readb(port->membase + UARTCR2) & ~UARTCR2_SBK; + cr2 = readb(port->membase + UARTCR2) & ~UARTCR2_SBK; if (break_state != 0) - temp |= UARTCR2_SBK; + cr2 |= UARTCR2_SBK; - writeb(temp, port->membase + UARTCR2); + writeb(cr2, port->membase + UARTCR2); } static void lpuart32_break_ctl(struct uart_port *port, int break_state) { - unsigned long temp; + u32 ctrl; - temp = lpuart32_read(port, UARTCTRL); + ctrl = lpuart32_read(port, UARTCTRL); /* * LPUART IP now has two known bugs, one is CTS has higher priority than the @@ -1595,23 +1602,22 @@ static void lpuart32_break_ctl(struct uart_port *port, int break_state) * Disable the transmitter to prevent any data from being sent out * during break, then invert the TX line to send break. */ - temp &= ~UARTCTRL_TE; - lpuart32_write(port, temp, UARTCTRL); - temp |= UARTCTRL_TXINV; - lpuart32_write(port, temp, UARTCTRL); + ctrl &= ~UARTCTRL_TE; + lpuart32_write(port, ctrl, UARTCTRL); + ctrl |= UARTCTRL_TXINV; + lpuart32_write(port, ctrl, UARTCTRL); } else { /* Disable the TXINV to turn off break and re-enable transmitter. */ - temp &= ~UARTCTRL_TXINV; - lpuart32_write(port, temp, UARTCTRL); - temp |= UARTCTRL_TE; - lpuart32_write(port, temp, UARTCTRL); + ctrl &= ~UARTCTRL_TXINV; + lpuart32_write(port, ctrl, UARTCTRL); + ctrl |= UARTCTRL_TE; + lpuart32_write(port, ctrl, UARTCTRL); } } static void lpuart_setup_watermark(struct lpuart_port *sport) { - unsigned char val, cr2; - unsigned char cr2_saved; + u8 fifo, cr2, cr2_saved; cr2 = readb(sport->port.membase + UARTCR2); cr2_saved = cr2; @@ -1619,8 +1625,8 @@ static void lpuart_setup_watermark(struct lpuart_port *sport) UARTCR2_RIE | UARTCR2_RE); writeb(cr2, sport->port.membase + UARTCR2); - val = readb(sport->port.membase + UARTPFIFO); - writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE, + fifo = readb(sport->port.membase + UARTPFIFO); + writeb(fifo | UARTPFIFO_TXFE | UARTPFIFO_RXFE, sport->port.membase + UARTPFIFO); /* flush Tx and Rx FIFO */ @@ -1644,7 +1650,7 @@ static void lpuart_setup_watermark(struct lpuart_port *sport) static void lpuart_setup_watermark_enable(struct lpuart_port *sport) { - unsigned char cr2; + u8 cr2; lpuart_setup_watermark(sport); @@ -1655,8 +1661,7 @@ static void lpuart_setup_watermark_enable(struct lpuart_port *sport) static void lpuart32_setup_watermark(struct lpuart_port *sport) { - unsigned long val, ctrl; - unsigned long ctrl_saved; + u32 val, ctrl, ctrl_saved; ctrl = lpuart32_read(&sport->port, UARTCTRL); ctrl_saved = ctrl; @@ -1691,14 +1696,14 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport) static void lpuart32_setup_watermark_enable(struct lpuart_port *sport) { - u32 temp; + u32 ctrl; lpuart32_setup_watermark(sport); - temp = lpuart32_read(&sport->port, UARTCTRL); - temp |= UARTCTRL_RE | UARTCTRL_TE; - temp |= FIELD_PREP(UARTCTRL_IDLECFG, 0x7); - lpuart32_write(&sport->port, temp, UARTCTRL); + ctrl = lpuart32_read(&sport->port, UARTCTRL); + ctrl |= UARTCTRL_RE | UARTCTRL_TE; + ctrl |= FIELD_PREP(UARTCTRL_IDLECFG, 0x7); + lpuart32_write(&sport->port, ctrl, UARTCTRL); } static void rx_dma_timer_init(struct lpuart_port *sport) @@ -1765,7 +1770,7 @@ err: static void lpuart_rx_dma_startup(struct lpuart_port *sport) { int ret; - unsigned char cr3; + u8 cr3; if (uart_console(&sport->port)) goto err; @@ -1815,16 +1820,16 @@ static void lpuart_hw_setup(struct lpuart_port *sport) static int lpuart_startup(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); - unsigned char temp; + u8 fifo; /* determine FIFO size and enable FIFO mode */ - temp = readb(sport->port.membase + UARTPFIFO); + fifo = readb(port->membase + UARTPFIFO); - sport->txfifo_size = UARTFIFO_DEPTH((temp >> UARTPFIFO_TXSIZE_OFF) & + sport->txfifo_size = UARTFIFO_DEPTH((fifo >> UARTPFIFO_TXSIZE_OFF) & UARTPFIFO_FIFOSIZE_MASK); - sport->port.fifosize = sport->txfifo_size; + port->fifosize = sport->txfifo_size; - sport->rxfifo_size = UARTFIFO_DEPTH((temp >> UARTPFIFO_RXSIZE_OFF) & + sport->rxfifo_size = UARTFIFO_DEPTH((fifo >> UARTPFIFO_RXSIZE_OFF) & UARTPFIFO_FIFOSIZE_MASK); lpuart_request_dma(sport); @@ -1835,24 +1840,24 @@ static int lpuart_startup(struct uart_port *port) static void lpuart32_hw_disable(struct lpuart_port *sport) { - unsigned long temp; + u32 ctrl; - temp = lpuart32_read(&sport->port, UARTCTRL); - temp &= ~(UARTCTRL_RIE | UARTCTRL_ILIE | UARTCTRL_RE | + ctrl = lpuart32_read(&sport->port, UARTCTRL); + ctrl &= ~(UARTCTRL_RIE | UARTCTRL_ILIE | UARTCTRL_RE | UARTCTRL_TIE | UARTCTRL_TE); - lpuart32_write(&sport->port, temp, UARTCTRL); + lpuart32_write(&sport->port, ctrl, UARTCTRL); } static void lpuart32_configure(struct lpuart_port *sport) { - unsigned long temp; + u32 ctrl; - temp = lpuart32_read(&sport->port, UARTCTRL); + ctrl = lpuart32_read(&sport->port, UARTCTRL); if (!sport->lpuart_dma_rx_use) - temp |= UARTCTRL_RIE | UARTCTRL_ILIE; + ctrl |= UARTCTRL_RIE | UARTCTRL_ILIE; if (!sport->lpuart_dma_tx_use) - temp |= UARTCTRL_TIE; - lpuart32_write(&sport->port, temp, UARTCTRL); + ctrl |= UARTCTRL_TIE; + lpuart32_write(&sport->port, ctrl, UARTCTRL); } static void lpuart32_hw_setup(struct lpuart_port *sport) @@ -1875,16 +1880,16 @@ static void lpuart32_hw_setup(struct lpuart_port *sport) static int lpuart32_startup(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); - unsigned long temp; + u32 fifo; /* determine FIFO size */ - temp = lpuart32_read(&sport->port, UARTFIFO); + fifo = lpuart32_read(port, UARTFIFO); - sport->txfifo_size = UARTFIFO_DEPTH((temp >> UARTFIFO_TXSIZE_OFF) & + sport->txfifo_size = UARTFIFO_DEPTH((fifo >> UARTFIFO_TXSIZE_OFF) & UARTFIFO_FIFOSIZE_MASK); - sport->port.fifosize = sport->txfifo_size; + port->fifosize = sport->txfifo_size; - sport->rxfifo_size = UARTFIFO_DEPTH((temp >> UARTFIFO_RXSIZE_OFF) & + sport->rxfifo_size = UARTFIFO_DEPTH((fifo >> UARTFIFO_RXSIZE_OFF) & UARTFIFO_FIFOSIZE_MASK); /* @@ -1895,7 +1900,7 @@ static int lpuart32_startup(struct uart_port *port) if (is_layerscape_lpuart(sport)) { sport->rxfifo_size = 16; sport->txfifo_size = 16; - sport->port.fifosize = sport->txfifo_size; + port->fifosize = sport->txfifo_size; } lpuart_request_dma(sport); @@ -1929,16 +1934,16 @@ static void lpuart_dma_shutdown(struct lpuart_port *sport) static void lpuart_shutdown(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); - unsigned char temp; + u8 cr2; unsigned long flags; uart_port_lock_irqsave(port, &flags); /* disable Rx/Tx and interrupts */ - temp = readb(port->membase + UARTCR2); - temp &= ~(UARTCR2_TE | UARTCR2_RE | + cr2 = readb(port->membase + UARTCR2); + cr2 &= ~(UARTCR2_TE | UARTCR2_RE | UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_RIE); - writeb(temp, port->membase + UARTCR2); + writeb(cr2, port->membase + UARTCR2); uart_port_unlock_irqrestore(port, flags); @@ -1949,14 +1954,14 @@ static void lpuart32_shutdown(struct uart_port *port) { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); - unsigned long temp; + u32 temp; unsigned long flags; uart_port_lock_irqsave(port, &flags); /* clear status */ - temp = lpuart32_read(&sport->port, UARTSTAT); - lpuart32_write(&sport->port, temp, UARTSTAT); + temp = lpuart32_read(port, UARTSTAT); + lpuart32_write(port, temp, UARTSTAT); /* disable Rx/Tx DMA */ temp = lpuart32_read(port, UARTBAUD); @@ -1969,6 +1974,11 @@ static void lpuart32_shutdown(struct uart_port *port) UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE | UARTCTRL_SBK); lpuart32_write(port, temp, UARTCTRL); + /* flush Rx/Tx FIFO */ + temp = lpuart32_read(port, UARTFIFO); + temp |= UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH; + lpuart32_write(port, temp, UARTFIFO); + uart_port_unlock_irqrestore(port, flags); lpuart_dma_shutdown(sport); @@ -1980,17 +1990,17 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); unsigned long flags; - unsigned char cr1, old_cr1, old_cr2, cr3, cr4, bdh, modem; + u8 cr1, old_cr1, old_cr2, cr3, 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); - cr3 = readb(sport->port.membase + UARTCR3); - cr4 = readb(sport->port.membase + UARTCR4); - bdh = readb(sport->port.membase + UARTBDH); - modem = readb(sport->port.membase + UARTMODEM); + cr1 = old_cr1 = readb(port->membase + UARTCR1); + old_cr2 = readb(port->membase + UARTCR2); + cr3 = readb(port->membase + UARTCR3); + cr4 = readb(port->membase + UARTCR4); + bdh = readb(port->membase + UARTBDH); + modem = readb(port->membase + UARTMODEM); /* * only support CS8 and CS7, and for CS7 must enable PE. * supported mode: @@ -2022,7 +2032,7 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, * When auto RS-485 RTS mode is enabled, * hardware flow control need to be disabled. */ - if (sport->port.rs485.flags & SER_RS485_ENABLED) + if (port->rs485.flags & SER_RS485_ENABLED) termios->c_cflag &= ~CRTSCTS; if (termios->c_cflag & CRTSCTS) @@ -2063,59 +2073,59 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, * Need to update the Ring buffer length according to the selected * baud rate and restart Rx DMA path. * - * Since timer function acqures sport->port.lock, need to stop before - * acquring same lock because otherwise del_timer_sync() can deadlock. + * Since timer function acqures port->lock, need to stop before + * acquring same lock because otherwise timer_delete_sync() can deadlock. */ if (old && sport->lpuart_dma_rx_use) - lpuart_dma_rx_free(&sport->port); + lpuart_dma_rx_free(port); - uart_port_lock_irqsave(&sport->port, &flags); + uart_port_lock_irqsave(port, &flags); - sport->port.read_status_mask = 0; + port->read_status_mask = 0; if (termios->c_iflag & INPCK) - sport->port.read_status_mask |= UARTSR1_FE | UARTSR1_PE; + port->read_status_mask |= UARTSR1_FE | UARTSR1_PE; if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) - sport->port.read_status_mask |= UARTSR1_FE; + port->read_status_mask |= UARTSR1_FE; /* characters to ignore */ - sport->port.ignore_status_mask = 0; + port->ignore_status_mask = 0; if (termios->c_iflag & IGNPAR) - sport->port.ignore_status_mask |= UARTSR1_PE; + port->ignore_status_mask |= UARTSR1_PE; if (termios->c_iflag & IGNBRK) { - sport->port.ignore_status_mask |= UARTSR1_FE; + 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; + port->ignore_status_mask |= UARTSR1_OR; } /* update the per-port timeout */ uart_update_timeout(port, termios->c_cflag, baud); /* wait transmit engin complete */ - lpuart_wait_bit_set(&sport->port, UARTSR1, UARTSR1_TC); + lpuart_wait_bit_set(port, UARTSR1, UARTSR1_TC); /* disable transmit and receive */ writeb(old_cr2 & ~(UARTCR2_TE | UARTCR2_RE), - sport->port.membase + UARTCR2); + port->membase + UARTCR2); - sbr = sport->port.uartclk / (16 * baud); - brfa = ((sport->port.uartclk - (16 * sbr * baud)) * 2) / baud; + sbr = port->uartclk / (16 * baud); + brfa = ((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(cr3, sport->port.membase + UARTCR3); - writeb(cr1, sport->port.membase + UARTCR1); - writeb(modem, sport->port.membase + UARTMODEM); + writeb(cr4 | brfa, port->membase + UARTCR4); + writeb(bdh, port->membase + UARTBDH); + writeb(sbr & 0xFF, port->membase + UARTBDL); + writeb(cr3, port->membase + UARTCR3); + writeb(cr1, port->membase + UARTCR1); + writeb(modem, port->membase + UARTMODEM); /* restore control register */ - writeb(old_cr2, sport->port.membase + UARTCR2); + writeb(old_cr2, port->membase + UARTCR2); if (old && sport->lpuart_dma_rx_use) { if (!lpuart_start_rx_dma(sport)) @@ -2124,14 +2134,14 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, sport->lpuart_dma_rx_use = false; } - uart_port_unlock_irqrestore(&sport->port, flags); + uart_port_unlock_irqrestore(port, flags); } static void __lpuart32_serial_setbrg(struct uart_port *port, unsigned int baudrate, bool use_rx_dma, bool use_tx_dma) { - u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp; + u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, baud; u32 clk = port->uartclk; /* @@ -2160,9 +2170,9 @@ static void __lpuart32_serial_setbrg(struct uart_port *port, tmp_diff = clk / (tmp_osr * tmp_sbr) - baudrate; /* select best values between sbr and sbr+1 */ - tmp = clk / (tmp_osr * (tmp_sbr + 1)); - if (tmp_diff > (baudrate - tmp)) { - tmp_diff = baudrate - tmp; + baud = clk / (tmp_osr * (tmp_sbr + 1)); + if (tmp_diff > (baudrate - baud)) { + tmp_diff = baudrate - baud; tmp_sbr++; } @@ -2184,23 +2194,23 @@ static void __lpuart32_serial_setbrg(struct uart_port *port, dev_warn(port->dev, "unacceptable baud rate difference of more than 3%%\n"); - tmp = lpuart32_read(port, UARTBAUD); + baud = lpuart32_read(port, UARTBAUD); if ((osr > 3) && (osr < 8)) - tmp |= UARTBAUD_BOTHEDGE; + baud |= UARTBAUD_BOTHEDGE; - tmp &= ~(UARTBAUD_OSR_MASK << UARTBAUD_OSR_SHIFT); - tmp |= ((osr-1) & UARTBAUD_OSR_MASK) << UARTBAUD_OSR_SHIFT; + baud &= ~(UARTBAUD_OSR_MASK << UARTBAUD_OSR_SHIFT); + baud |= ((osr-1) & UARTBAUD_OSR_MASK) << UARTBAUD_OSR_SHIFT; - tmp &= ~UARTBAUD_SBR_MASK; - tmp |= sbr & UARTBAUD_SBR_MASK; + baud &= ~UARTBAUD_SBR_MASK; + baud |= sbr & UARTBAUD_SBR_MASK; if (!use_rx_dma) - tmp &= ~UARTBAUD_RDMAE; + baud &= ~UARTBAUD_RDMAE; if (!use_tx_dma) - tmp &= ~UARTBAUD_TDMAE; + baud &= ~UARTBAUD_TDMAE; - lpuart32_write(port, tmp, UARTBAUD); + lpuart32_write(port, baud, UARTBAUD); } static void lpuart32_serial_setbrg(struct lpuart_port *sport, @@ -2218,17 +2228,18 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, { struct lpuart_port *sport = container_of(port, struct lpuart_port, port); unsigned long flags; - unsigned long ctrl, old_ctrl, bd, modem; + u32 ctrl, old_ctrl, bd, modem; unsigned int baud; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; - ctrl = old_ctrl = lpuart32_read(&sport->port, UARTCTRL); - bd = lpuart32_read(&sport->port, UARTBAUD); - modem = lpuart32_read(&sport->port, UARTMODIR); + ctrl = old_ctrl = lpuart32_read(port, UARTCTRL); + bd = lpuart32_read(port, UARTBAUD); + modem = lpuart32_read(port, UARTMODIR); sport->is_cs7 = false; /* - * only support CS8 and CS7, and for CS7 must enable PE. + * only support CS8 and CS7 * supported mode: + * - (7,n,1) (imx only) * - (7,e/o,1) * - (8,n,1) * - (8,m/s,1) @@ -2243,7 +2254,7 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, if ((termios->c_cflag & CSIZE) == CS8 || (termios->c_cflag & CSIZE) == CS7) - ctrl = old_ctrl & ~UARTCTRL_M; + ctrl = old_ctrl & ~(UARTCTRL_M | UARTCTRL_M7); if (termios->c_cflag & CMSPAR) { if ((termios->c_cflag & CSIZE) != CS8) { @@ -2257,7 +2268,7 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, * When auto RS-485 RTS mode is enabled, * hardware flow control need to be disabled. */ - if (sport->port.rs485.flags & SER_RS485_ENABLED) + if (port->rs485.flags & SER_RS485_ENABLED) termios->c_cflag &= ~CRTSCTS; if (termios->c_cflag & CRTSCTS) @@ -2270,9 +2281,18 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, else bd &= ~UARTBAUD_SBNS; - /* parity must be enabled when CS7 to match 8-bits format */ - if ((termios->c_cflag & CSIZE) == CS7) - termios->c_cflag |= PARENB; + /* + * imx support 7-bits format, no limitation on parity when CS7 + * for layerscape, parity must be enabled when CS7 to match 8-bits format + */ + if ((termios->c_cflag & CSIZE) == CS7 && !(termios->c_cflag & PARENB)) { + if (is_imx7ulp_lpuart(sport) || + is_imx8ulp_lpuart(sport) || + is_imx8qxp_lpuart(sport)) + ctrl |= UARTCTRL_M7; + else + termios->c_cflag |= PARENB; + } if ((termios->c_cflag & PARENB)) { if (termios->c_cflag & CMSPAR) { @@ -2298,59 +2318,61 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, * Need to update the Ring buffer length according to the selected * baud rate and restart Rx DMA path. * - * Since timer function acqures sport->port.lock, need to stop before - * acquring same lock because otherwise del_timer_sync() can deadlock. + * Since timer function acqures port->lock, need to stop before + * acquring same lock because otherwise timer_delete_sync() can deadlock. */ if (old && sport->lpuart_dma_rx_use) - lpuart_dma_rx_free(&sport->port); + lpuart_dma_rx_free(port); - uart_port_lock_irqsave(&sport->port, &flags); + uart_port_lock_irqsave(port, &flags); - sport->port.read_status_mask = 0; + port->read_status_mask = 0; if (termios->c_iflag & INPCK) - sport->port.read_status_mask |= UARTSTAT_FE | UARTSTAT_PE; + port->read_status_mask |= UARTSTAT_FE | UARTSTAT_PE; if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) - sport->port.read_status_mask |= UARTSTAT_FE; + port->read_status_mask |= UARTSTAT_FE; /* characters to ignore */ - sport->port.ignore_status_mask = 0; + port->ignore_status_mask = 0; if (termios->c_iflag & IGNPAR) - sport->port.ignore_status_mask |= UARTSTAT_PE; + port->ignore_status_mask |= UARTSTAT_PE; if (termios->c_iflag & IGNBRK) { - sport->port.ignore_status_mask |= UARTSTAT_FE; + port->ignore_status_mask |= UARTSTAT_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 |= UARTSTAT_OR; + port->ignore_status_mask |= UARTSTAT_OR; } /* update the per-port timeout */ uart_update_timeout(port, termios->c_cflag, baud); /* + * disable CTS to ensure the transmit engine is not blocked by the flow + * control when there is dirty data in TX FIFO + */ + lpuart32_write(port, modem & ~UARTMODIR_TXCTSE, UARTMODIR); + + /* * LPUART Transmission Complete Flag may never be set while queuing a break * character, so skip waiting for transmission complete when UARTCTRL_SBK is * asserted. */ - if (!(old_ctrl & UARTCTRL_SBK)) { - lpuart32_write(&sport->port, 0, UARTMODIR); - lpuart32_wait_bit_set(&sport->port, UARTSTAT, UARTSTAT_TC); - } + if (!(old_ctrl & UARTCTRL_SBK)) + lpuart32_wait_bit_set(port, UARTSTAT, UARTSTAT_TC); /* disable transmit and receive */ - lpuart32_write(&sport->port, old_ctrl & ~(UARTCTRL_TE | UARTCTRL_RE), + lpuart32_write(port, old_ctrl & ~(UARTCTRL_TE | UARTCTRL_RE), UARTCTRL); - lpuart32_write(&sport->port, bd, UARTBAUD); + lpuart32_write(port, bd, UARTBAUD); lpuart32_serial_setbrg(sport, baud); - /* disable CTS before enabling UARTCTRL_TE to avoid pending idle preamble */ - lpuart32_write(&sport->port, modem & ~UARTMODIR_TXCTSE, UARTMODIR); /* restore control register */ - lpuart32_write(&sport->port, ctrl, UARTCTRL); + lpuart32_write(port, ctrl, UARTCTRL); /* re-enable the CTS if needed */ - lpuart32_write(&sport->port, modem, UARTMODIR); + lpuart32_write(port, modem, UARTMODIR); if ((ctrl & (UARTCTRL_PE | UARTCTRL_M)) == UARTCTRL_PE) sport->is_cs7 = true; @@ -2362,7 +2384,7 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, sport->lpuart_dma_rx_use = false; } - uart_port_unlock_irqrestore(&sport->port, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *lpuart_type(struct uart_port *port) @@ -2475,7 +2497,7 @@ 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; + u8 old_cr2, cr2; unsigned long flags; int locked = 1; @@ -2505,7 +2527,7 @@ static void lpuart32_console_write(struct console *co, const char *s, unsigned int count) { struct lpuart_port *sport = lpuart_ports[co->index]; - unsigned long old_cr, cr; + u32 old_cr, cr; unsigned long flags; int locked = 1; @@ -2539,7 +2561,7 @@ static void __init lpuart_console_get_options(struct lpuart_port *sport, int *baud, int *parity, int *bits) { - unsigned char cr, bdh, bdl, brfa; + u8 cr, bdh, bdl, brfa; unsigned int sbr, uartclk, baud_raw; cr = readb(sport->port.membase + UARTCR2); @@ -2588,7 +2610,7 @@ static void __init lpuart32_console_get_options(struct lpuart_port *sport, int *baud, int *parity, int *bits) { - unsigned long cr, bd; + u32 cr, bd; unsigned int sbr, uartclk, baud_raw; cr = lpuart32_read(&sport->port, UARTCTRL); @@ -2794,13 +2816,13 @@ static int lpuart_global_reset(struct lpuart_port *sport) { struct uart_port *port = &sport->port; void __iomem *global_addr; - unsigned long ctrl, bd; + u32 ctrl, bd; unsigned int val = 0; int ret; ret = clk_prepare_enable(sport->ipg_clk); if (ret) { - dev_err(sport->port.dev, "failed to enable uart ipg clk: %d\n", ret); + dev_err(port->dev, "failed to enable uart ipg clk: %d\n", ret); return ret; } @@ -2811,10 +2833,10 @@ static int lpuart_global_reset(struct lpuart_port *sport) */ ctrl = lpuart32_read(port, UARTCTRL); if (ctrl & UARTCTRL_TE) { - bd = lpuart32_read(&sport->port, UARTBAUD); + bd = lpuart32_read(port, UARTBAUD); if (read_poll_timeout(lpuart32_tx_empty, val, val, 1, 100000, false, port)) { - dev_warn(sport->port.dev, + dev_warn(port->dev, "timeout waiting for transmit engine to complete\n"); clk_disable_unprepare(sport->ipg_clk); return 0; @@ -2884,8 +2906,7 @@ static int lpuart_probe(struct platform_device *pdev) sport->ipg_clk = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(sport->ipg_clk)) { ret = PTR_ERR(sport->ipg_clk); - dev_err(&pdev->dev, "failed to get uart ipg clk: %d\n", ret); - return ret; + return dev_err_probe(&pdev->dev, ret, "failed to get uart ipg clk\n"); } sport->baud_clk = NULL; @@ -2893,8 +2914,7 @@ static int lpuart_probe(struct platform_device *pdev) sport->baud_clk = devm_clk_get(&pdev->dev, "baud"); if (IS_ERR(sport->baud_clk)) { ret = PTR_ERR(sport->baud_clk); - dev_err(&pdev->dev, "failed to get uart baud clk: %d\n", ret); - return ret; + return dev_err_probe(&pdev->dev, ret, "failed to get uart baud clk\n"); } } @@ -2930,6 +2950,7 @@ static int lpuart_probe(struct platform_device *pdev) pm_runtime_set_autosuspend_delay(&pdev->dev, UART_AUTOSUSPEND_TIMEOUT); pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); + pm_runtime_mark_last_busy(&pdev->dev); ret = lpuart_global_reset(sport); if (ret) @@ -2944,7 +2965,7 @@ static int lpuart_probe(struct platform_device *pdev) goto failed_attach_port; ret = devm_request_irq(&pdev->dev, sport->port.irq, handler, 0, - DRIVER_NAME, sport); + dev_name(&pdev->dev), sport); if (ret) goto failed_irq_request; @@ -3001,7 +3022,7 @@ static int lpuart_runtime_resume(struct device *dev) static void serial_lpuart_enable_wakeup(struct lpuart_port *sport, bool on) { - unsigned int val, baud; + u32 val, baud; if (lpuart_is_32(sport)) { val = lpuart32_read(&sport->port, UARTCTRL); @@ -3066,7 +3087,7 @@ static int lpuart_suspend_noirq(struct device *dev) static int lpuart_resume_noirq(struct device *dev) { struct lpuart_port *sport = dev_get_drvdata(dev); - unsigned int val; + u32 stat; pinctrl_pm_select_default_state(dev); @@ -3075,8 +3096,8 @@ static int lpuart_resume_noirq(struct device *dev) /* clear the wakeup flags */ if (lpuart_is_32(sport)) { - val = lpuart32_read(&sport->port, UARTSTAT); - lpuart32_write(&sport->port, val, UARTSTAT); + stat = lpuart32_read(&sport->port, UARTSTAT); + lpuart32_write(&sport->port, stat, UARTSTAT); } } @@ -3086,7 +3107,8 @@ static int lpuart_resume_noirq(struct device *dev) static int lpuart_suspend(struct device *dev) { struct lpuart_port *sport = dev_get_drvdata(dev); - unsigned long temp, flags; + u32 temp; + unsigned long flags; uart_suspend_port(&lpuart_reg, &sport->port); @@ -3166,7 +3188,7 @@ static void lpuart_console_fixup(struct lpuart_port *sport) * in VLLS mode, or restore console setting here. */ if (is_imx7ulp_lpuart(sport) && lpuart_uport_is_active(sport) && - console_suspend_enabled && uart_console(&sport->port)) { + console_suspend_enabled && uart_console(uport)) { mutex_lock(&port->mutex); memset(&termios, 0, sizeof(struct ktermios)); @@ -3212,7 +3234,7 @@ static const struct dev_pm_ops lpuart_pm_ops = { static struct platform_driver lpuart_driver = { .probe = lpuart_probe, - .remove_new = lpuart_remove, + .remove = lpuart_remove, .driver = { .name = "fsl-lpuart", .of_match_table = lpuart_dt_ids, diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index a75eafbcbea3..7fb995a8490e 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -877,10 +877,10 @@ unlock: static int icom_write(struct uart_port *port) { struct icom_port *icom_port = to_icom_port(port); + struct tty_port *tport = &port->state->port; unsigned long data_count; unsigned char cmdReg; unsigned long offset; - int temp_tail = port->state->xmit.tail; trace(icom_port, "WRITE", 0); @@ -890,16 +890,8 @@ static int icom_write(struct uart_port *port) return 0; } - data_count = 0; - while ((port->state->xmit.head != temp_tail) && - (data_count <= XMIT_BUFF_SZ)) { - - icom_port->xmit_buf[data_count++] = - port->state->xmit.buf[temp_tail]; - - temp_tail++; - temp_tail &= (UART_XMIT_SIZE - 1); - } + data_count = kfifo_out_peek(&tport->xmit_fifo, icom_port->xmit_buf, + XMIT_BUFF_SZ); if (data_count) { icom_port->statStg->xmit[0].flags = @@ -956,7 +948,8 @@ static inline void check_modem_status(struct icom_port *icom_port) static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port) { - u16 count, i; + struct tty_port *tport = &icom_port->uart_port.state->port; + u16 count; if (port_int_reg & (INT_XMIT_COMPLETED)) { trace(icom_port, "XMIT_COMPLETE", 0); @@ -968,13 +961,7 @@ static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port) count = le16_to_cpu(icom_port->statStg->xmit[0].leLength); icom_port->uart_port.icount.tx += count; - for (i=0; i<count && - !uart_circ_empty(&icom_port->uart_port.state->xmit); i++) { - - icom_port->uart_port.state->xmit.tail++; - icom_port->uart_port.state->xmit.tail &= - (UART_XMIT_SIZE - 1); - } + kfifo_skip_count(&tport->xmit_fifo, count); if (!icom_write(&icom_port->uart_port)) /* activate write queue */ @@ -1777,11 +1764,10 @@ static int icom_probe(struct pci_dev *dev, goto probe_exit1; } - /* save off irq and request irq line */ - retval = request_irq(dev->irq, icom_interrupt, IRQF_SHARED, ICOM_DRIVER_NAME, (void *)icom_adapter); - if (retval) { - goto probe_exit2; - } + /* save off irq and request irq line */ + retval = request_irq(dev->irq, icom_interrupt, IRQF_SHARED, ICOM_DRIVER_NAME, icom_adapter); + if (retval) + goto probe_exit2; retval = icom_load_ports(icom_adapter); diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index e14813250616..500dfc009d03 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -8,6 +8,7 @@ * Copyright (C) 2004 Pengutronix */ +#include <linux/circ_buf.h> #include <linux/module.h> #include <linux/ioport.h> #include <linux/init.h> @@ -26,6 +27,7 @@ #include <linux/slab.h> #include <linux/of.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/dma-mapping.h> #include <asm/irq.h> @@ -118,6 +120,7 @@ #define UCR4_OREN (1<<1) /* Receiver overrun interrupt enable */ #define UCR4_DREN (1<<0) /* Recv data ready interrupt enable */ #define UFCR_RXTL_SHF 0 /* Receiver trigger level shift */ +#define UFCR_RXTL_MASK 0x3F /* Receiver trigger 6 bits wide */ #define UFCR_DCEDTE (1<<6) /* DCE/DTE mode select */ #define UFCR_RFDIV (7<<7) /* Reference freq divider mask */ #define UFCR_RFDIV_REG(x) (((x) < 7 ? 6 - (x) : 6) << 7) @@ -227,9 +230,12 @@ struct imx_port { unsigned int saved_reg[10]; bool context_saved; + bool last_putchar_was_newline; + enum imx_tx_state tx_state; struct hrtimer trigger_start_tx; struct hrtimer trigger_stop_tx; + unsigned int rxtl; }; struct imx_port_ucrs { @@ -262,6 +268,11 @@ static const struct of_device_id imx_uart_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, imx_uart_dt_ids); +static inline struct imx_port *to_imx_port(struct uart_port *port) +{ + return container_of(port, struct imx_port, port); +} + static inline void imx_uart_writel(struct imx_port *sport, u32 val, u32 offset) { writel(val, sport->port.membase + offset); @@ -362,6 +373,7 @@ static void imx_uart_soft_reset(struct imx_port *sport) sport->idle_counter = 0; } +/* called with port.lock taken and irqs off */ static void imx_uart_disable_loopback_rs485(struct imx_port *sport) { unsigned int uts; @@ -375,7 +387,7 @@ static void imx_uart_disable_loopback_rs485(struct imx_port *sport) /* called with port.lock taken and irqs off */ static void imx_uart_start_rx(struct uart_port *port) { - struct imx_port *sport = (struct imx_port *)port; + struct imx_port *sport = to_imx_port(port); unsigned int ucr1, ucr2; ucr1 = imx_uart_readl(sport, UCR1); @@ -399,7 +411,7 @@ static void imx_uart_start_rx(struct uart_port *port) /* called with port.lock taken and irqs off */ static void imx_uart_stop_tx(struct uart_port *port) { - struct imx_port *sport = (struct imx_port *)port; + struct imx_port *sport = to_imx_port(port); u32 ucr1, ucr4, usr2; if (sport->tx_state == OFF) @@ -462,9 +474,10 @@ static void imx_uart_stop_tx(struct uart_port *port) } } +/* called with port.lock taken and irqs off */ static void imx_uart_stop_rx_with_loopback_ctrl(struct uart_port *port, bool loopback) { - struct imx_port *sport = (struct imx_port *)port; + struct imx_port *sport = to_imx_port(port); u32 ucr1, ucr2, ucr4, uts; ucr1 = imx_uart_readl(sport, UCR1); @@ -509,7 +522,7 @@ static void imx_uart_stop_rx(struct uart_port *port) /* called with port.lock taken and irqs off */ static void imx_uart_enable_ms(struct uart_port *port) { - struct imx_port *sport = (struct imx_port *)port; + struct imx_port *sport = to_imx_port(port); mod_timer(&sport->timer, jiffies); @@ -521,7 +534,8 @@ static void imx_uart_dma_tx(struct imx_port *sport); /* called with port.lock taken and irqs off */ static inline void imx_uart_transmit_buffer(struct imx_port *sport) { - struct circ_buf *xmit = &sport->port.state->xmit; + struct tty_port *tport = &sport->port.state->port; + unsigned char c; if (sport->port.x_char) { /* Send next char */ @@ -531,7 +545,8 @@ static inline void imx_uart_transmit_buffer(struct imx_port *sport) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || + uart_tx_stopped(&sport->port)) { imx_uart_stop_tx(&sport->port); return; } @@ -555,26 +570,22 @@ static inline void imx_uart_transmit_buffer(struct imx_port *sport) return; } - while (!uart_circ_empty(xmit) && - !(imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL)) { - /* send xmit->buf[xmit->tail] - * out the port here */ - imx_uart_writel(sport, xmit->buf[xmit->tail], URTX0); - uart_xmit_advance(&sport->port, 1); - } + while (!(imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL) && + uart_fifo_get(&sport->port, &c)) + imx_uart_writel(sport, c, URTX0); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&sport->port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) imx_uart_stop_tx(&sport->port); } static void imx_uart_dma_tx_callback(void *data) { struct imx_port *sport = data; + struct tty_port *tport = &sport->port.state->port; struct scatterlist *sgl = &sport->tx_sgl[0]; - struct circ_buf *xmit = &sport->port.state->xmit; unsigned long flags; u32 ucr1; @@ -592,10 +603,11 @@ static void imx_uart_dma_tx_callback(void *data) sport->dma_is_txing = 0; - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&sport->port); - if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port)) + if (!kfifo_is_empty(&tport->xmit_fifo) && + !uart_tx_stopped(&sport->port)) imx_uart_dma_tx(sport); else if (sport->port.rs485.flags & SER_RS485_ENABLED) { u32 ucr4 = imx_uart_readl(sport, UCR4); @@ -609,7 +621,7 @@ static void imx_uart_dma_tx_callback(void *data) /* called with port.lock taken and irqs off */ static void imx_uart_dma_tx(struct imx_port *sport) { - struct circ_buf *xmit = &sport->port.state->xmit; + struct tty_port *tport = &sport->port.state->port; struct scatterlist *sgl = sport->tx_sgl; struct dma_async_tx_descriptor *desc; struct dma_chan *chan = sport->dma_chan_tx; @@ -624,18 +636,10 @@ static void imx_uart_dma_tx(struct imx_port *sport) ucr4 &= ~UCR4_TCEN; imx_uart_writel(sport, ucr4, UCR4); - sport->tx_bytes = uart_circ_chars_pending(xmit); - - if (xmit->tail < xmit->head || xmit->head == 0) { - sport->dma_tx_nents = 1; - sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes); - } else { - 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); - } + sg_init_table(sgl, ARRAY_SIZE(sport->tx_sgl)); + sport->tx_bytes = kfifo_len(&tport->xmit_fifo); + sport->dma_tx_nents = kfifo_dma_out_prepare(&tport->xmit_fifo, sgl, + ARRAY_SIZE(sport->tx_sgl), sport->tx_bytes); ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE); if (ret == 0) { @@ -653,8 +657,7 @@ static void imx_uart_dma_tx(struct imx_port *sport) desc->callback = imx_uart_dma_tx_callback; desc->callback_param = sport; - dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n", - uart_circ_chars_pending(xmit)); + dev_dbg(dev, "TX: prepare to send %u bytes by DMA.\n", sport->tx_bytes); ucr1 = imx_uart_readl(sport, UCR1); ucr1 |= UCR1_TXDMAEN; @@ -670,10 +673,11 @@ static void imx_uart_dma_tx(struct imx_port *sport) /* called with port.lock taken and irqs off */ static void imx_uart_start_tx(struct uart_port *port) { - struct imx_port *sport = (struct imx_port *)port; + struct imx_port *sport = to_imx_port(port); + struct tty_port *tport = &sport->port.state->port; u32 ucr1; - if (!sport->port.x_char && uart_circ_empty(&port->state->xmit)) + if (!sport->port.x_char && kfifo_is_empty(&tport->xmit_fifo)) return; /* @@ -749,7 +753,7 @@ static void imx_uart_start_tx(struct uart_port *port) return; } - if (!uart_circ_empty(&port->state->xmit) && + if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(port)) imx_uart_dma_tx(sport); return; @@ -763,6 +767,21 @@ static irqreturn_t __imx_uart_rtsint(int irq, void *dev_id) imx_uart_writel(sport, USR1_RTSD, USR1); usr1 = imx_uart_readl(sport, USR1) & USR1_RTSS; + /* + * Update sport->old_status here, so any follow-up calls to + * imx_uart_mctrl_check() will be able to recognize that RTS + * state changed since last imx_uart_mctrl_check() call. + * + * In case RTS has been detected as asserted here and later on + * deasserted by the time imx_uart_mctrl_check() was called, + * imx_uart_mctrl_check() can detect the RTS state change and + * trigger uart_handle_cts_change() to unblock the port for + * further TX transfers. + */ + if (usr1 & USR1_RTSS) + sport->old_status |= TIOCM_CTS; + else + sport->old_status &= ~TIOCM_CTS; uart_handle_cts_change(&sport->port, usr1); wake_up_interruptible(&sport->port.state->port.delta_msr_wait); @@ -804,6 +823,8 @@ static irqreturn_t imx_uart_txint(int irq, void *dev_id) * issuing soft reset to the UART (just stop/start of RX does not help). Note * that what we do here is sending isolated start bit about 2.4 times shorter * than it is to be on UART configured baud rate. + * + * Called with port.lock taken and irqs off. */ static void imx_uart_check_flood(struct imx_port *sport, u32 usr2) { @@ -839,6 +860,7 @@ static void imx_uart_check_flood(struct imx_port *sport, u32 usr2) } } +/* called with port.lock taken and irqs off */ static irqreturn_t __imx_uart_rxint(int irq, void *dev_id) { struct imx_port *sport = dev_id; @@ -917,6 +939,7 @@ static void imx_uart_clear_rx_errors(struct imx_port *sport); /* * We have a modem side uart, so the meanings of RTS and CTS are inverted. */ +/* called with port.lock taken and irqs off */ static unsigned int imx_uart_get_hwmctrl(struct imx_port *sport) { unsigned int tmp = TIOCM_DSR; @@ -939,6 +962,8 @@ static unsigned int imx_uart_get_hwmctrl(struct imx_port *sport) /* * Handle any change of modem status signal since we were last called. + * + * Called with port.lock taken and irqs off. */ static void imx_uart_mctrl_check(struct imx_port *sport) { @@ -1050,7 +1075,7 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id) */ static unsigned int imx_uart_tx_empty(struct uart_port *port) { - struct imx_port *sport = (struct imx_port *)port; + struct imx_port *sport = to_imx_port(port); unsigned int ret; ret = (imx_uart_readl(sport, USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0; @@ -1065,7 +1090,7 @@ static unsigned int imx_uart_tx_empty(struct uart_port *port) /* called with port.lock taken and irqs off */ static unsigned int imx_uart_get_mctrl(struct uart_port *port) { - struct imx_port *sport = (struct imx_port *)port; + struct imx_port *sport = to_imx_port(port); unsigned int ret = imx_uart_get_hwmctrl(sport); mctrl_gpio_get(sport->gpios, &ret); @@ -1076,7 +1101,7 @@ static unsigned int imx_uart_get_mctrl(struct uart_port *port) /* called with port.lock taken and irqs off */ static void imx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct imx_port *sport = (struct imx_port *)port; + struct imx_port *sport = to_imx_port(port); u32 ucr3, uts; if (!(port->rs485.flags & SER_RS485_ENABLED)) { @@ -1119,7 +1144,7 @@ static void imx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) */ static void imx_uart_break_ctl(struct uart_port *port, int break_state) { - struct imx_port *sport = (struct imx_port *)port; + struct imx_port *sport = to_imx_port(port); unsigned long flags; u32 ucr1; @@ -1141,7 +1166,7 @@ static void imx_uart_break_ctl(struct uart_port *port, int break_state) */ static void imx_uart_timeout(struct timer_list *t) { - struct imx_port *sport = from_timer(sport, t, timer); + struct imx_port *sport = timer_container_of(sport, t, timer); unsigned long flags; if (sport->port.state) { @@ -1278,6 +1303,7 @@ static int imx_uart_start_rx_dma(struct imx_port *sport) return 0; } +/* called with port.lock taken and irqs off */ static void imx_uart_clear_rx_errors(struct imx_port *sport) { struct tty_port *port = &sport->port.state->port; @@ -1312,8 +1338,9 @@ static void imx_uart_clear_rx_errors(struct imx_port *sport) } -#define TXTL_DEFAULT 2 /* reset default */ +#define TXTL_DEFAULT 8 #define RXTL_DEFAULT 8 /* 8 characters or aging timer */ +#define RXTL_CONSOLE_DEFAULT 1 #define TXTL_DMA 8 /* DMA burst setting */ #define RXTL_DMA 9 /* DMA burst setting */ @@ -1408,6 +1435,7 @@ err: return ret; } +/* called with port.lock taken and irqs off */ static void imx_uart_enable_dma(struct imx_port *sport) { u32 ucr1; @@ -1431,7 +1459,7 @@ static void imx_uart_disable_dma(struct imx_port *sport) ucr1 &= ~(UCR1_RXDMAEN | UCR1_TXDMAEN | UCR1_ATDMAEN); imx_uart_writel(sport, ucr1, UCR1); - imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); + imx_uart_setup_ufcr(sport, TXTL_DEFAULT, sport->rxtl); sport->dma_is_enabled = 0; } @@ -1441,7 +1469,7 @@ static void imx_uart_disable_dma(struct imx_port *sport) static int imx_uart_startup(struct uart_port *port) { - struct imx_port *sport = (struct imx_port *)port; + struct imx_port *sport = to_imx_port(port); int retval; unsigned long flags; int dma_is_inited = 0; @@ -1456,7 +1484,12 @@ static int imx_uart_startup(struct uart_port *port) return retval; } - imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); + if (uart_console(&sport->port)) + sport->rxtl = RXTL_CONSOLE_DEFAULT; + else + sport->rxtl = RXTL_DEFAULT; + + imx_uart_setup_ufcr(sport, TXTL_DEFAULT, sport->rxtl); /* disable the DREN bit (Data Ready interrupt enable) before * requesting IRQs @@ -1555,9 +1588,10 @@ static int imx_uart_startup(struct uart_port *port) static void imx_uart_shutdown(struct uart_port *port) { - struct imx_port *sport = (struct imx_port *)port; + struct imx_port *sport = to_imx_port(port); unsigned long flags; u32 ucr1, ucr2, ucr4, uts; + int loops; if (sport->dma_is_enabled) { dmaengine_terminate_sync(sport->dma_chan_tx); @@ -1581,7 +1615,7 @@ static void imx_uart_shutdown(struct uart_port *port) imx_uart_dma_exit(sport); } - mctrl_gpio_disable_ms(sport->gpios); + mctrl_gpio_disable_ms_sync(sport->gpios); uart_port_lock_irqsave(&sport->port, &flags); ucr2 = imx_uart_readl(sport, UCR2); @@ -1592,7 +1626,7 @@ static void imx_uart_shutdown(struct uart_port *port) /* * Stop our timer. */ - del_timer_sync(&sport->timer); + timer_delete_sync(&sport->timer); /* * Disable all interrupts, port and break condition. @@ -1620,6 +1654,56 @@ static void imx_uart_shutdown(struct uart_port *port) ucr4 &= ~UCR4_TCEN; imx_uart_writel(sport, ucr4, UCR4); + /* + * We have to ensure the tx state machine ends up in OFF. This + * is especially important for rs485 where we must not leave + * the RTS signal high, blocking the bus indefinitely. + * + * All interrupts are now disabled, so imx_uart_stop_tx() will + * no longer be called from imx_uart_transmit_buffer(). It may + * still be called via the hrtimers, and if those are in play, + * we have to honour the delays. + */ + if (sport->tx_state == WAIT_AFTER_RTS || sport->tx_state == SEND) + imx_uart_stop_tx(port); + + /* + * In many cases (rs232 mode, or if tx_state was + * WAIT_AFTER_RTS, or if tx_state was SEND and there is no + * delay_rts_after_send), this will have moved directly to + * OFF. In rs485 mode, tx_state might already have been + * WAIT_AFTER_SEND and the hrtimer thus already started, or + * the above imx_uart_stop_tx() call could have started it. In + * those cases, we have to wait for the hrtimer to fire and + * complete the transition to OFF. + */ + loops = port->rs485.flags & SER_RS485_ENABLED ? + port->rs485.delay_rts_after_send : 0; + while (sport->tx_state != OFF && loops--) { + uart_port_unlock_irqrestore(&sport->port, flags); + msleep(1); + uart_port_lock_irqsave(&sport->port, &flags); + } + + if (sport->tx_state != OFF) { + dev_warn(sport->port.dev, "unexpected tx_state %d\n", + sport->tx_state); + /* + * This machine may be busted, but ensure the RTS + * signal is inactive in order not to block other + * devices. + */ + if (port->rs485.flags & SER_RS485_ENABLED) { + ucr2 = imx_uart_readl(sport, UCR2); + if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) + imx_uart_rts_active(sport, &ucr2); + else + imx_uart_rts_inactive(sport, &ucr2); + imx_uart_writel(sport, ucr2, UCR2); + } + sport->tx_state = OFF; + } + uart_port_unlock_irqrestore(&sport->port, flags); clk_disable_unprepare(sport->clk_per); @@ -1629,7 +1713,7 @@ static void imx_uart_shutdown(struct uart_port *port) /* called with port.lock taken and irqs off */ static void imx_uart_flush_buffer(struct uart_port *port) { - struct imx_port *sport = (struct imx_port *)port; + struct imx_port *sport = to_imx_port(port); struct scatterlist *sgl = &sport->tx_sgl[0]; if (!sport->dma_chan_tx) @@ -1656,7 +1740,7 @@ static void imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, const struct ktermios *old) { - struct imx_port *sport = (struct imx_port *)port; + struct imx_port *sport = to_imx_port(port); unsigned long flags; u32 ucr2, old_ucr2, ufcr; unsigned int baud, quot; @@ -1675,7 +1759,7 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, old_csize = CS8; } - del_timer_sync(&sport->timer); + timer_delete_sync(&sport->timer); /* * Ask the core to calculate the divisor for us. @@ -1859,7 +1943,7 @@ imx_uart_verify_port(struct uart_port *port, struct serial_struct *ser) static int imx_uart_poll_init(struct uart_port *port) { - struct imx_port *sport = (struct imx_port *)port; + struct imx_port *sport = to_imx_port(port); unsigned long flags; u32 ucr1, ucr2; int retval; @@ -1871,7 +1955,7 @@ static int imx_uart_poll_init(struct uart_port *port) if (retval) clk_disable_unprepare(sport->clk_ipg); - imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); + imx_uart_setup_ufcr(sport, TXTL_DEFAULT, sport->rxtl); uart_port_lock_irqsave(&sport->port, &flags); @@ -1908,7 +1992,7 @@ static int imx_uart_poll_init(struct uart_port *port) static int imx_uart_poll_get_char(struct uart_port *port) { - struct imx_port *sport = (struct imx_port *)port; + struct imx_port *sport = to_imx_port(port); if (!(imx_uart_readl(sport, USR2) & USR2_RDR)) return NO_POLL_CHAR; @@ -1917,7 +2001,7 @@ static int imx_uart_poll_get_char(struct uart_port *port) static void imx_uart_poll_put_char(struct uart_port *port, unsigned char c) { - struct imx_port *sport = (struct imx_port *)port; + struct imx_port *sport = to_imx_port(port); unsigned int status; /* drain */ @@ -1939,8 +2023,8 @@ static void imx_uart_poll_put_char(struct uart_port *port, unsigned char c) static int imx_uart_rs485_config(struct uart_port *port, struct ktermios *termios, struct serial_rs485 *rs485conf) { - struct imx_port *sport = (struct imx_port *)port; - u32 ucr2; + struct imx_port *sport = to_imx_port(port); + u32 ucr2, ufcr; if (rs485conf->flags & SER_RS485_ENABLED) { /* Enable receiver if low-active RTS signal is requested */ @@ -1959,8 +2043,13 @@ static int imx_uart_rs485_config(struct uart_port *port, struct ktermios *termio /* Make sure Rx is enabled in case Tx is active with Rx disabled */ if (!(rs485conf->flags & SER_RS485_ENABLED) || - rs485conf->flags & SER_RS485_RX_DURING_TX) + rs485conf->flags & SER_RS485_RX_DURING_TX) { + /* If the receiver trigger is 0, set it to a default value */ + ufcr = imx_uart_readl(sport, UFCR); + if ((ufcr & UFCR_RXTL_MASK) == 0) + imx_uart_setup_ufcr(sport, TXTL_DEFAULT, sport->rxtl); imx_uart_start_rx(port); + } return 0; } @@ -1993,32 +2082,40 @@ static struct imx_port *imx_uart_ports[UART_NR]; #if IS_ENABLED(CONFIG_SERIAL_IMX_CONSOLE) static void imx_uart_console_putchar(struct uart_port *port, unsigned char ch) { - struct imx_port *sport = (struct imx_port *)port; + struct imx_port *sport = to_imx_port(port); while (imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL) barrier(); imx_uart_writel(sport, ch, URTX0); + + sport->last_putchar_was_newline = (ch == '\n'); } -/* - * Interrupts are disabled on entering - */ -static void -imx_uart_console_write(struct console *co, const char *s, unsigned int count) +static void imx_uart_console_device_lock(struct console *co, unsigned long *flags) +{ + struct uart_port *up = &imx_uart_ports[co->index]->port; + + return __uart_port_lock_irqsave(up, flags); +} + +static void imx_uart_console_device_unlock(struct console *co, unsigned long flags) +{ + struct uart_port *up = &imx_uart_ports[co->index]->port; + + return __uart_port_unlock_irqrestore(up, flags); +} + +static void imx_uart_console_write_atomic(struct console *co, + struct nbcon_write_context *wctxt) { struct imx_port *sport = imx_uart_ports[co->index]; + struct uart_port *port = &sport->port; struct imx_port_ucrs old_ucr; - unsigned long flags; - unsigned int ucr1; - int locked = 1; + unsigned int ucr1, usr2; - if (sport->port.sysrq) - locked = 0; - else if (oops_in_progress) - locked = uart_port_trylock_irqsave(&sport->port, &flags); - else - uart_port_lock_irqsave(&sport->port, &flags); + if (!nbcon_enter_unsafe(wctxt)) + return; /* * First, save UCR1/2/3 and then disable interrupts @@ -2032,21 +2129,88 @@ imx_uart_console_write(struct console *co, const char *s, unsigned int count) ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN); imx_uart_writel(sport, ucr1, UCR1); - imx_uart_writel(sport, old_ucr.ucr2 | UCR2_TXEN, UCR2); - uart_console_write(&sport->port, s, count, imx_uart_console_putchar); + if (!sport->last_putchar_was_newline) + uart_console_write(port, "\n", 1, imx_uart_console_putchar); + uart_console_write(port, wctxt->outbuf, wctxt->len, + imx_uart_console_putchar); /* * Finally, wait for transmitter to become empty * and restore UCR1/2/3 */ - while (!(imx_uart_readl(sport, USR2) & USR2_TXDC)); + read_poll_timeout_atomic(imx_uart_readl, usr2, usr2 & USR2_TXDC, + 0, USEC_PER_SEC, false, sport, USR2); + imx_uart_ucrs_restore(sport, &old_ucr); + + nbcon_exit_unsafe(wctxt); +} + +static void imx_uart_console_write_thread(struct console *co, + struct nbcon_write_context *wctxt) +{ + struct imx_port *sport = imx_uart_ports[co->index]; + struct uart_port *port = &sport->port; + struct imx_port_ucrs old_ucr; + unsigned int ucr1, usr2; + + if (!nbcon_enter_unsafe(wctxt)) + return; + + /* + * First, save UCR1/2/3 and then disable interrupts + */ + imx_uart_ucrs_save(sport, &old_ucr); + ucr1 = old_ucr.ucr1; + + if (imx_uart_is_imx1(sport)) + ucr1 |= IMX1_UCR1_UARTCLKEN; + ucr1 |= UCR1_UARTEN; + ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN); + + imx_uart_writel(sport, ucr1, UCR1); + imx_uart_writel(sport, old_ucr.ucr2 | UCR2_TXEN, UCR2); + + if (nbcon_exit_unsafe(wctxt)) { + int len = READ_ONCE(wctxt->len); + int i; + + /* + * Write out the message. Toggle unsafe for each byte in order + * to give another (higher priority) context the opportunity + * for a friendly takeover. If such a takeover occurs, this + * context must reacquire ownership in order to perform final + * actions (such as re-enabling the interrupts). + * + * IMPORTANT: wctxt->outbuf and wctxt->len are no longer valid + * after a reacquire so writing the message must be + * aborted. + */ + for (i = 0; i < len; i++) { + if (!nbcon_enter_unsafe(wctxt)) + break; + uart_console_write(port, wctxt->outbuf + i, 1, + imx_uart_console_putchar); + + if (!nbcon_exit_unsafe(wctxt)) + break; + } + } + + while (!nbcon_enter_unsafe(wctxt)) + nbcon_reacquire_nobuf(wctxt); + + /* + * Finally, wait for transmitter to become empty + * and restore UCR1/2/3 + */ + read_poll_timeout(imx_uart_readl, usr2, usr2 & USR2_TXDC, + 0, USEC_PER_SEC, false, sport, USR2); imx_uart_ucrs_restore(sport, &old_ucr); - if (locked) - uart_port_unlock_irqrestore(&sport->port, flags); + nbcon_exit_unsafe(wctxt); } /* @@ -2138,12 +2302,14 @@ imx_uart_console_setup(struct console *co, char *options) if (retval) goto error_console; + sport->last_putchar_was_newline = true; + if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else imx_uart_console_get_options(sport, &baud, &parity, &bits); - imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); + imx_uart_setup_ufcr(sport, TXTL_DEFAULT, sport->rxtl); retval = uart_set_options(&sport->port, co, baud, parity, bits, flow); @@ -2174,11 +2340,14 @@ imx_uart_console_exit(struct console *co) static struct uart_driver imx_uart_uart_driver; static struct console imx_uart_console = { .name = DEV_NAME, - .write = imx_uart_console_write, + .write_atomic = imx_uart_console_write_atomic, + .write_thread = imx_uart_console_write_thread, + .device_lock = imx_uart_console_device_lock, + .device_unlock = imx_uart_console_device_unlock, + .flags = CON_PRINTBUFFER | CON_NBCON, .device = uart_console_device, .setup = imx_uart_console_setup, .exit = imx_uart_console_exit, - .flags = CON_PRINTBUFFER, .index = -1, .data = &imx_uart_uart_driver, }; @@ -2420,10 +2589,10 @@ static int imx_uart_probe(struct platform_device *pdev) imx_uart_writel(sport, ucr3, UCR3); } - hrtimer_init(&sport->trigger_start_tx, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - hrtimer_init(&sport->trigger_stop_tx, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - sport->trigger_start_tx.function = imx_trigger_start_tx; - sport->trigger_stop_tx.function = imx_trigger_stop_tx; + hrtimer_setup(&sport->trigger_start_tx, imx_trigger_start_tx, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + hrtimer_setup(&sport->trigger_stop_tx, imx_trigger_stop_tx, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); /* * Allocate the IRQ(s) i.MX1 has three interrupts whereas later @@ -2525,10 +2694,13 @@ static void imx_uart_save_context(struct imx_port *sport) uart_port_unlock_irqrestore(&sport->port, flags); } +/* called with irq off */ static void imx_uart_enable_wakeup(struct imx_port *sport, bool on) { u32 ucr3; + uart_port_lock_irq(&sport->port); + ucr3 = imx_uart_readl(sport, UCR3); if (on) { imx_uart_writel(sport, USR1_AWAKE, USR1); @@ -2548,6 +2720,8 @@ static void imx_uart_enable_wakeup(struct imx_port *sport, bool on) } imx_uart_writel(sport, ucr1, UCR1); } + + uart_port_unlock_irq(&sport->port); } static int imx_uart_suspend_noirq(struct device *dev) @@ -2647,7 +2821,7 @@ static const struct dev_pm_ops imx_uart_pm_ops = { static struct platform_driver imx_uart_platform_driver = { .probe = imx_uart_probe, - .remove_new = imx_uart_remove, + .remove = imx_uart_remove, .driver = { .name = "imx-uart", diff --git a/drivers/tty/serial/ip22zilog.c b/drivers/tty/serial/ip22zilog.c index 320b29cd4683..c2cae50f06f3 100644 --- a/drivers/tty/serial/ip22zilog.c +++ b/drivers/tty/serial/ip22zilog.c @@ -355,7 +355,8 @@ static void ip22zilog_status_handle(struct uart_ip22zilog_port *up, static void ip22zilog_transmit_chars(struct uart_ip22zilog_port *up, struct zilog_channel *channel) { - struct circ_buf *xmit; + struct tty_port *tport; + unsigned char c; if (ZS_IS_CONS(up)) { unsigned char status = readb(&channel->control); @@ -398,20 +399,18 @@ static void ip22zilog_transmit_chars(struct uart_ip22zilog_port *up, if (up->port.state == NULL) goto ack_tx_int; - xmit = &up->port.state->xmit; - if (uart_circ_empty(xmit)) - goto ack_tx_int; + tport = &up->port.state->port; if (uart_tx_stopped(&up->port)) goto ack_tx_int; + if (!uart_fifo_get(&up->port, &c)) + goto ack_tx_int; up->flags |= IP22ZILOG_FLAG_TX_ACTIVE; - writeb(xmit->buf[xmit->tail], &channel->data); + writeb(c, &channel->data); ZSDELAY(); ZS_WSYNC(channel); - uart_xmit_advance(&up->port, 1); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&up->port); return; @@ -600,17 +599,16 @@ static void ip22zilog_start_tx(struct uart_port *port) port->icount.tx++; port->x_char = 0; } else { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; + unsigned char c; - if (uart_circ_empty(xmit)) + if (!uart_fifo_get(port, &c)) return; - writeb(xmit->buf[xmit->tail], &channel->data); + writeb(c, &channel->data); ZSDELAY(); ZS_WSYNC(channel); - uart_xmit_advance(port, 1); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&up->port); } } diff --git a/drivers/tty/serial/jsm/jsm_cls.c b/drivers/tty/serial/jsm/jsm_cls.c index ddbd42c09637..6e40792f92cf 100644 --- a/drivers/tty/serial/jsm/jsm_cls.c +++ b/drivers/tty/serial/jsm/jsm_cls.c @@ -443,20 +443,14 @@ static void cls_copy_data_from_uart_to_queue(struct jsm_channel *ch) static void cls_copy_data_from_queue_to_uart(struct jsm_channel *ch) { - u16 tail; + struct tty_port *tport; int n; - int qlen; u32 len_written = 0; - struct circ_buf *circ; if (!ch) return; - circ = &ch->uart_port.state->xmit; - - /* No data to write to the UART */ - if (uart_circ_empty(circ)) - return; + tport = &ch->uart_port.state->port; /* If port is "stopped", don't send any data to the UART */ if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_BREAK_SENDING)) @@ -467,29 +461,22 @@ static void cls_copy_data_from_queue_to_uart(struct jsm_channel *ch) return; n = 32; + while (n > 0) { + unsigned char c; - /* cache tail of queue */ - tail = circ->tail & (UART_XMIT_SIZE - 1); - qlen = uart_circ_chars_pending(circ); - - /* Find minimum of the FIFO space, versus queue length */ - n = min(n, qlen); + if (!kfifo_get(&tport->xmit_fifo, &c)) + break; - while (n > 0) { - writeb(circ->buf[tail], &ch->ch_cls_uart->txrx); - tail = (tail + 1) & (UART_XMIT_SIZE - 1); + writeb(c, &ch->ch_cls_uart->txrx); n--; ch->ch_txcount++; len_written++; } - /* Update the final tail */ - circ->tail = tail & (UART_XMIT_SIZE - 1); - if (len_written > ch->ch_t_tlevel) ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); - if (uart_circ_empty(circ)) + if (kfifo_is_empty(&tport->xmit_fifo)) uart_write_wakeup(&ch->uart_port); } diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c index 1fa10f19368f..e8e13bf056e2 100644 --- a/drivers/tty/serial/jsm/jsm_neo.c +++ b/drivers/tty/serial/jsm/jsm_neo.c @@ -474,21 +474,21 @@ static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch) static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch) { - u16 head; - u16 tail; + struct tty_port *tport; + unsigned char *tail; + unsigned char c; int n; int s; int qlen; u32 len_written = 0; - struct circ_buf *circ; if (!ch) return; - circ = &ch->uart_port.state->xmit; + tport = &ch->uart_port.state->port; /* No data to write to the UART */ - if (uart_circ_empty(circ)) + if (kfifo_is_empty(&tport->xmit_fifo)) return; /* If port is "stopped", don't send any data to the UART */ @@ -504,10 +504,9 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch) if (ch->ch_cached_lsr & UART_LSR_THRE) { ch->ch_cached_lsr &= ~(UART_LSR_THRE); - writeb(circ->buf[circ->tail], &ch->ch_neo_uart->txrx); - jsm_dbg(WRITE, &ch->ch_bd->pci_dev, - "Tx data: %x\n", circ->buf[circ->tail]); - circ->tail = (circ->tail + 1) & (UART_XMIT_SIZE - 1); + WARN_ON_ONCE(!kfifo_get(&tport->xmit_fifo, &c)); + writeb(c, &ch->ch_neo_uart->txrx); + jsm_dbg(WRITE, &ch->ch_bd->pci_dev, "Tx data: %x\n", c); ch->ch_txcount++; } return; @@ -520,38 +519,27 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch) return; n = UART_17158_TX_FIFOSIZE - ch->ch_t_tlevel; - - /* cache head and tail of queue */ - head = circ->head & (UART_XMIT_SIZE - 1); - tail = circ->tail & (UART_XMIT_SIZE - 1); - qlen = uart_circ_chars_pending(circ); + qlen = kfifo_len(&tport->xmit_fifo); /* Find minimum of the FIFO space, versus queue length */ n = min(n, qlen); while (n > 0) { - - s = ((head >= tail) ? head : UART_XMIT_SIZE) - tail; - s = min(s, n); - + s = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, n); if (s <= 0) break; - memcpy_toio(&ch->ch_neo_uart->txrxburst, circ->buf + tail, s); - /* Add and flip queue if needed */ - tail = (tail + s) & (UART_XMIT_SIZE - 1); + memcpy_toio(&ch->ch_neo_uart->txrxburst, tail, s); + kfifo_skip_count(&tport->xmit_fifo, s); n -= s; ch->ch_txcount += s; len_written += s; } - /* Update the final tail */ - circ->tail = tail & (UART_XMIT_SIZE - 1); - if (len_written >= ch->ch_t_tlevel) ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); - if (uart_circ_empty(circ)) + if (kfifo_is_empty(&tport->xmit_fifo)) uart_write_wakeup(&ch->uart_port); } diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index ce0fef7e2c66..be2f130696b3 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -451,6 +451,7 @@ int jsm_uart_port_init(struct jsm_board *brd) if (!brd->channels[i]) continue; + brd->channels[i]->uart_port.dev = &brd->pci_dev->dev; brd->channels[i]->uart_port.irq = brd->irq; brd->channels[i]->uart_port.uartclk = 14745600; brd->channels[i]->uart_port.type = PORT_JSM; diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c deleted file mode 100644 index e93850f6447a..000000000000 --- a/drivers/tty/serial/kgdb_nmi.c +++ /dev/null @@ -1,381 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * KGDB NMI serial console - * - * Copyright 2010 Google, Inc. - * Arve Hjønnevåg <arve@android.com> - * Colin Cross <ccross@android.com> - * Copyright 2012 Linaro Ltd. - * Anton Vorontsov <anton.vorontsov@linaro.org> - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/compiler.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/atomic.h> -#include <linux/console.h> -#include <linux/tty.h> -#include <linux/tty_driver.h> -#include <linux/tty_flip.h> -#include <linux/serial_core.h> -#include <linux/interrupt.h> -#include <linux/hrtimer.h> -#include <linux/tick.h> -#include <linux/kfifo.h> -#include <linux/kgdb.h> -#include <linux/kdb.h> - -static int kgdb_nmi_knock = 1; -module_param_named(knock, kgdb_nmi_knock, int, 0600); -MODULE_PARM_DESC(knock, "if set to 1 (default), the special '$3#33' command " \ - "must be used to enter the debugger; when set to 0, " \ - "hitting return key is enough to enter the debugger; " \ - "when set to -1, the debugger is entered immediately " \ - "upon NMI"); - -static char *kgdb_nmi_magic = "$3#33"; -module_param_named(magic, kgdb_nmi_magic, charp, 0600); -MODULE_PARM_DESC(magic, "magic sequence to enter NMI debugger (default $3#33)"); - -static atomic_t kgdb_nmi_num_readers = ATOMIC_INIT(0); - -static int kgdb_nmi_console_setup(struct console *co, char *options) -{ - arch_kgdb_ops.enable_nmi(1); - - /* The NMI console uses the dbg_io_ops to issue console messages. To - * avoid duplicate messages during kdb sessions we must inform kdb's - * I/O utilities that messages sent to the console will automatically - * be displayed on the dbg_io. - */ - dbg_io_ops->cons = co; - - return 0; -} - -static void kgdb_nmi_console_write(struct console *co, const char *s, uint c) -{ - int i; - - for (i = 0; i < c; i++) - dbg_io_ops->write_char(s[i]); -} - -static struct tty_driver *kgdb_nmi_tty_driver; - -static struct tty_driver *kgdb_nmi_console_device(struct console *co, int *idx) -{ - *idx = co->index; - return kgdb_nmi_tty_driver; -} - -static struct console kgdb_nmi_console = { - .name = "ttyNMI", - .setup = kgdb_nmi_console_setup, - .write = kgdb_nmi_console_write, - .device = kgdb_nmi_console_device, - .flags = CON_PRINTBUFFER | CON_ANYTIME, - .index = -1, -}; - -/* - * This is usually the maximum rate on debug ports. We make fifo large enough - * to make copy-pasting to the terminal usable. - */ -#define KGDB_NMI_BAUD 115200 -#define KGDB_NMI_FIFO_SIZE roundup_pow_of_two(KGDB_NMI_BAUD / 8 / HZ) - -struct kgdb_nmi_tty_priv { - struct tty_port port; - struct timer_list timer; - STRUCT_KFIFO(char, KGDB_NMI_FIFO_SIZE) fifo; -}; - -static struct tty_port *kgdb_nmi_port; - -static void kgdb_tty_recv(int ch) -{ - struct kgdb_nmi_tty_priv *priv; - char c = ch; - - if (!kgdb_nmi_port || ch < 0) - return; - /* - * Can't use port->tty->driver_data as tty might be not there. Timer - * will check for tty and will get the ref, but here we don't have to - * do that, and actually, we can't: we're in NMI context, no locks are - * possible. - */ - priv = container_of(kgdb_nmi_port, struct kgdb_nmi_tty_priv, port); - kfifo_in(&priv->fifo, &c, 1); -} - -static int kgdb_nmi_poll_one_knock(void) -{ - static int n; - int c; - const char *magic = kgdb_nmi_magic; - size_t m = strlen(magic); - bool printch = false; - - c = dbg_io_ops->read_char(); - if (c == NO_POLL_CHAR) - return c; - - if (!kgdb_nmi_knock && (c == '\r' || c == '\n')) { - return 1; - } else if (c == magic[n]) { - n = (n + 1) % m; - if (!n) - return 1; - printch = true; - } else { - n = 0; - } - - if (atomic_read(&kgdb_nmi_num_readers)) { - kgdb_tty_recv(c); - return 0; - } - - if (printch) { - kdb_printf("%c", c); - return 0; - } - - kdb_printf("\r%s %s to enter the debugger> %*s", - kgdb_nmi_knock ? "Type" : "Hit", - kgdb_nmi_knock ? magic : "<return>", (int)m, ""); - while (m--) - kdb_printf("\b"); - return 0; -} - -/** - * kgdb_nmi_poll_knock - Check if it is time to enter the debugger - * - * "Serial ports are often noisy, especially when muxed over another port (we - * often use serial over the headset connector). Noise on the async command - * line just causes characters that are ignored, on a command line that blocked - * execution noise would be catastrophic." -- Colin Cross - * - * So, this function implements KGDB/KDB knocking on the serial line: we won't - * enter the debugger until we receive a known magic phrase (which is actually - * "$3#33", known as "escape to KDB" command. There is also a relaxed variant - * of knocking, i.e. just pressing the return key is enough to enter the - * debugger. And if knocking is disabled, the function always returns 1. - */ -bool kgdb_nmi_poll_knock(void) -{ - if (kgdb_nmi_knock < 0) - return true; - - while (1) { - int ret; - - ret = kgdb_nmi_poll_one_knock(); - if (ret == NO_POLL_CHAR) - return false; - else if (ret == 1) - break; - } - return true; -} - -/* - * The tasklet is cheap, it does not cause wakeups when reschedules itself, - * instead it waits for the next tick. - */ -static void kgdb_nmi_tty_receiver(struct timer_list *t) -{ - struct kgdb_nmi_tty_priv *priv = from_timer(priv, t, timer); - char ch; - - priv->timer.expires = jiffies + (HZ/100); - add_timer(&priv->timer); - - if (likely(!atomic_read(&kgdb_nmi_num_readers) || - !kfifo_len(&priv->fifo))) - return; - - while (kfifo_out(&priv->fifo, &ch, 1)) - tty_insert_flip_char(&priv->port, ch, TTY_NORMAL); - tty_flip_buffer_push(&priv->port); -} - -static int kgdb_nmi_tty_activate(struct tty_port *port, struct tty_struct *tty) -{ - struct kgdb_nmi_tty_priv *priv = - container_of(port, struct kgdb_nmi_tty_priv, port); - - kgdb_nmi_port = port; - priv->timer.expires = jiffies + (HZ/100); - add_timer(&priv->timer); - - return 0; -} - -static void kgdb_nmi_tty_shutdown(struct tty_port *port) -{ - struct kgdb_nmi_tty_priv *priv = - container_of(port, struct kgdb_nmi_tty_priv, port); - - del_timer(&priv->timer); - kgdb_nmi_port = NULL; -} - -static const struct tty_port_operations kgdb_nmi_tty_port_ops = { - .activate = kgdb_nmi_tty_activate, - .shutdown = kgdb_nmi_tty_shutdown, -}; - -static int kgdb_nmi_tty_install(struct tty_driver *drv, struct tty_struct *tty) -{ - struct kgdb_nmi_tty_priv *priv; - int ret; - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - INIT_KFIFO(priv->fifo); - timer_setup(&priv->timer, kgdb_nmi_tty_receiver, 0); - tty_port_init(&priv->port); - priv->port.ops = &kgdb_nmi_tty_port_ops; - tty->driver_data = priv; - - ret = tty_port_install(&priv->port, drv, tty); - if (ret) { - pr_err("%s: can't install tty port: %d\n", __func__, ret); - goto err; - } - return 0; -err: - tty_port_destroy(&priv->port); - kfree(priv); - return ret; -} - -static void kgdb_nmi_tty_cleanup(struct tty_struct *tty) -{ - struct kgdb_nmi_tty_priv *priv = tty->driver_data; - - tty->driver_data = NULL; - tty_port_destroy(&priv->port); - kfree(priv); -} - -static int kgdb_nmi_tty_open(struct tty_struct *tty, struct file *file) -{ - struct kgdb_nmi_tty_priv *priv = tty->driver_data; - unsigned int mode = file->f_flags & O_ACCMODE; - int ret; - - ret = tty_port_open(&priv->port, tty, file); - if (!ret && (mode == O_RDONLY || mode == O_RDWR)) - atomic_inc(&kgdb_nmi_num_readers); - - return ret; -} - -static void kgdb_nmi_tty_close(struct tty_struct *tty, struct file *file) -{ - struct kgdb_nmi_tty_priv *priv = tty->driver_data; - unsigned int mode = file->f_flags & O_ACCMODE; - - if (mode == O_RDONLY || mode == O_RDWR) - atomic_dec(&kgdb_nmi_num_readers); - - tty_port_close(&priv->port, tty, file); -} - -static void kgdb_nmi_tty_hangup(struct tty_struct *tty) -{ - struct kgdb_nmi_tty_priv *priv = tty->driver_data; - - tty_port_hangup(&priv->port); -} - -static unsigned int kgdb_nmi_tty_write_room(struct tty_struct *tty) -{ - /* Actually, we can handle any amount as we use polled writes. */ - return 2048; -} - -static ssize_t kgdb_nmi_tty_write(struct tty_struct *tty, const u8 *buf, - size_t c) -{ - int i; - - for (i = 0; i < c; i++) - dbg_io_ops->write_char(buf[i]); - return c; -} - -static const struct tty_operations kgdb_nmi_tty_ops = { - .open = kgdb_nmi_tty_open, - .close = kgdb_nmi_tty_close, - .install = kgdb_nmi_tty_install, - .cleanup = kgdb_nmi_tty_cleanup, - .hangup = kgdb_nmi_tty_hangup, - .write_room = kgdb_nmi_tty_write_room, - .write = kgdb_nmi_tty_write, -}; - -int kgdb_register_nmi_console(void) -{ - int ret; - - if (!arch_kgdb_ops.enable_nmi) - return 0; - - kgdb_nmi_tty_driver = tty_alloc_driver(1, TTY_DRIVER_REAL_RAW); - if (IS_ERR(kgdb_nmi_tty_driver)) { - pr_err("%s: cannot allocate tty\n", __func__); - return PTR_ERR(kgdb_nmi_tty_driver); - } - kgdb_nmi_tty_driver->driver_name = "ttyNMI"; - kgdb_nmi_tty_driver->name = "ttyNMI"; - kgdb_nmi_tty_driver->num = 1; - kgdb_nmi_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; - kgdb_nmi_tty_driver->subtype = SERIAL_TYPE_NORMAL; - kgdb_nmi_tty_driver->init_termios = tty_std_termios; - tty_termios_encode_baud_rate(&kgdb_nmi_tty_driver->init_termios, - KGDB_NMI_BAUD, KGDB_NMI_BAUD); - tty_set_operations(kgdb_nmi_tty_driver, &kgdb_nmi_tty_ops); - - ret = tty_register_driver(kgdb_nmi_tty_driver); - if (ret) { - pr_err("%s: can't register tty driver: %d\n", __func__, ret); - goto err_drv_reg; - } - - register_console(&kgdb_nmi_console); - - return 0; -err_drv_reg: - tty_driver_kref_put(kgdb_nmi_tty_driver); - return ret; -} -EXPORT_SYMBOL_GPL(kgdb_register_nmi_console); - -int kgdb_unregister_nmi_console(void) -{ - int ret; - - if (!arch_kgdb_ops.enable_nmi) - return 0; - arch_kgdb_ops.enable_nmi(0); - - ret = unregister_console(&kgdb_nmi_console); - if (ret) - return ret; - - tty_unregister_driver(kgdb_nmi_tty_driver); - tty_driver_kref_put(kgdb_nmi_tty_driver); - - return 0; -} -EXPORT_SYMBOL_GPL(kgdb_unregister_nmi_console); diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index 7ce7bb164005..85f6c5a76e0f 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -19,6 +19,7 @@ #include <linux/console.h> #include <linux/vt_kern.h> #include <linux/input.h> +#include <linux/irq_work.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/serial_core.h> @@ -48,6 +49,25 @@ static struct kgdb_io kgdboc_earlycon_io_ops; static int (*earlycon_orig_exit)(struct console *con); #endif /* IS_BUILTIN(CONFIG_KGDB_SERIAL_CONSOLE) */ +/* + * When we leave the debug trap handler we need to reset the keyboard status + * (since the original keyboard state gets partially clobbered by kdb use of + * the keyboard). + * + * The path to deliver the reset is somewhat circuitous. + * + * To deliver the reset we register an input handler, reset the keyboard and + * then deregister the input handler. However, to get this done right, we do + * have to carefully manage the calling context because we can only register + * input handlers from task context. + * + * In particular we need to trigger the action from the debug trap handler with + * all its NMI and/or NMI-like oddities. To solve this the kgdboc trap exit code + * (the "post_exception" callback) uses irq_work_queue(), which is NMI-safe, to + * schedule a callback from a hardirq context. From there we have to defer the + * work again, this time using schedule_work(), to get a callback using the + * system workqueue, which runs in task context. + */ #ifdef CONFIG_KDB_KEYBOARD static int kgdboc_reset_connect(struct input_handler *handler, struct input_dev *dev, @@ -99,10 +119,17 @@ static void kgdboc_restore_input_helper(struct work_struct *dummy) static DECLARE_WORK(kgdboc_restore_input_work, kgdboc_restore_input_helper); +static void kgdboc_queue_restore_input_helper(struct irq_work *unused) +{ + schedule_work(&kgdboc_restore_input_work); +} + +static DEFINE_IRQ_WORK(kgdboc_restore_input_irq_work, kgdboc_queue_restore_input_helper); + static void kgdboc_restore_input(void) { if (likely(system_state == SYSTEM_RUNNING)) - schedule_work(&kgdboc_restore_input_work); + irq_work_queue(&kgdboc_restore_input_irq_work); } static int kgdboc_register_kbd(char **cptr) @@ -133,6 +160,7 @@ static void kgdboc_unregister_kbd(void) i--; } } + irq_work_sync(&kgdboc_restore_input_irq_work); flush_work(&kgdboc_restore_input_work); } #else /* ! CONFIG_KDB_KEYBOARD */ @@ -158,8 +186,6 @@ static void cleanup_kgdboc(void) if (configured != 1) return; - if (kgdb_unregister_nmi_console()) - return; kgdboc_unregister_kbd(); kgdb_unregister_io_module(&kgdboc_io_ops); } @@ -222,16 +248,10 @@ do_register: if (err) goto noconfig; - err = kgdb_register_nmi_console(); - if (err) - goto nmi_con_failed; - configured = 1; return 0; -nmi_con_failed: - kgdb_unregister_io_module(&kgdboc_io_ops); noconfig: kgdboc_unregister_kbd(); configured = 0; diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c index a0731773ce75..62cd9e0bb377 100644 --- a/drivers/tty/serial/lantiq.c +++ b/drivers/tty/serial/lantiq.c @@ -773,10 +773,8 @@ static int fetch_irq_intel(struct device *dev, struct ltq_uart_port *ltq_port) int ret; ret = platform_get_irq(to_platform_device(dev), 0); - if (ret < 0) { - dev_err(dev, "failed to fetch IRQ for serial port\n"); + if (ret < 0) return ret; - } ltq_port->common_irq = ret; port->irq = ret; @@ -915,7 +913,7 @@ MODULE_DEVICE_TABLE(of, ltq_asc_match); static struct platform_driver lqasc_driver = { .probe = lqasc_probe, - .remove_new = lqasc_remove, + .remove = lqasc_remove, .driver = { .name = DRVNAME, .of_match_table = ltq_asc_match, diff --git a/drivers/tty/serial/liteuart.c b/drivers/tty/serial/liteuart.c index 3ce369f76349..6429e8f11f36 100644 --- a/drivers/tty/serial/liteuart.c +++ b/drivers/tty/serial/liteuart.c @@ -96,7 +96,7 @@ static void liteuart_stop_rx(struct uart_port *port) struct liteuart_port *uart = to_liteuart_port(port); /* just delete timer */ - del_timer(&uart->timer); + timer_delete(&uart->timer); } static void liteuart_rx_chars(struct uart_port *port) @@ -152,7 +152,7 @@ static irqreturn_t liteuart_interrupt(int irq, void *data) static void liteuart_timer(struct timer_list *t) { - struct liteuart_port *uart = from_timer(uart, t, timer); + struct liteuart_port *uart = timer_container_of(uart, t, timer); struct uart_port *port = &uart->port; liteuart_interrupt(0, port); @@ -220,7 +220,7 @@ static void liteuart_shutdown(struct uart_port *port) if (port->irq) free_irq(port->irq, port); else - del_timer_sync(&uart->timer); + timer_delete_sync(&uart->timer); } static void liteuart_set_termios(struct uart_port *port, struct ktermios *new, @@ -353,7 +353,7 @@ MODULE_DEVICE_TABLE(of, liteuart_of_match); static struct platform_driver liteuart_platform_driver = { .probe = liteuart_probe, - .remove_new = liteuart_remove, + .remove = liteuart_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = liteuart_of_match, diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c index 3e4ac46de1bc..42c5f9bc18b7 100644 --- a/drivers/tty/serial/lpc32xx_hs.c +++ b/drivers/tty/serial/lpc32xx_hs.c @@ -695,7 +695,7 @@ MODULE_DEVICE_TABLE(of, serial_hs_lpc32xx_dt_ids); static struct platform_driver serial_hs_lpc32xx_driver = { .probe = serial_hs_lpc32xx_probe, - .remove_new = serial_hs_lpc32xx_remove, + .remove = serial_hs_lpc32xx_remove, .suspend = serial_hs_lpc32xx_suspend, .resume = serial_hs_lpc32xx_resume, .driver = { diff --git a/drivers/tty/serial/ma35d1_serial.c b/drivers/tty/serial/ma35d1_serial.c index 19f0a305cc43..285b0fe41a86 100644 --- a/drivers/tty/serial/ma35d1_serial.c +++ b/drivers/tty/serial/ma35d1_serial.c @@ -688,12 +688,13 @@ static int ma35d1serial_probe(struct platform_device *pdev) struct uart_ma35d1_port *up; int ret = 0; - if (pdev->dev.of_node) { - ret = of_alias_get_id(pdev->dev.of_node, "serial"); - if (ret < 0) { - dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n", ret); - return ret; - } + if (!pdev->dev.of_node) + return -ENODEV; + + ret = of_alias_get_id(pdev->dev.of_node, "serial"); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n", ret); + return ret; } up = &ma35d1serial_ports[ret]; up->port.line = ret; @@ -793,12 +794,12 @@ static int ma35d1serial_resume(struct platform_device *dev) static struct platform_driver ma35d1serial_driver = { .probe = ma35d1serial_probe, - .remove_new = ma35d1serial_remove, + .remove = ma35d1serial_remove, .suspend = ma35d1serial_suspend, .resume = ma35d1serial_resume, .driver = { .name = "ma35d1-uart", - .of_match_table = of_match_ptr(ma35d1_serial_of_match), + .of_match_table = ma35d1_serial_of_match, }, }; diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 5efb2b593be3..67d80f8f801e 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * * Copyright (C) 2008 Christian Pellegrin <chripell@evolware.org> * * Notes: the MAX3100 doesn't provide an interrupt on CTS so we have @@ -8,24 +7,6 @@ * writing conf clears FIFO buffer and we cannot have this interrupt * always asking us for attention. * - * Example platform data: - - static struct plat_max3100 max3100_plat_data = { - .loopback = 0, - .crystal = 0, - .poll_time = 100, - }; - - static struct spi_board_info spi_board_info[] = { - { - .modalias = "max3100", - .platform_data = &max3100_plat_data, - .irq = IRQ_EINT12, - .max_speed_hz = 5*1000*1000, - .chip_select = 0, - }, - }; - * The initial minor number is 209 in the low-density serial port: * mknod /dev/ttyMAX0 c 204 209 */ @@ -35,18 +16,24 @@ /* 4 MAX3100s should be enough for everyone */ #define MAX_MAX3100 4 +#include <linux/bitops.h> +#include <linux/container_of.h> #include <linux/delay.h> -#include <linux/slab.h> #include <linux/device.h> +#include <linux/freezer.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/pm.h> +#include <linux/property.h> #include <linux/serial_core.h> #include <linux/serial.h> +#include <linux/slab.h> #include <linux/spi/spi.h> -#include <linux/freezer.h> -#include <linux/tty.h> #include <linux/tty_flip.h> +#include <linux/tty.h> +#include <linux/types.h> -#include <linux/serial_max3100.h> +#include <linux/unaligned.h> #define MAX3100_C (1<<14) #define MAX3100_D (0<<14) @@ -110,10 +97,8 @@ struct max3100_port { #define MAX3100_7BIT 4 int rx_enabled; /* if we should rx chars */ - int irq; /* irq assigned to the max3100 */ - int minor; /* minor number */ - int crystal; /* 1 if 3.6864Mhz crystal 0 for 1.8432 */ + int loopback_commit; /* need to change loopback */ int loopback; /* 1 if we are in loopback mode */ /* for handling irqs: need workqueue since we do spi_sync */ @@ -124,15 +109,14 @@ struct max3100_port { /* need to know we are suspending to avoid deadlock on workqueue */ int suspending; - /* hook for suspending MAX3100 via dedicated pin */ - void (*max3100_hw_suspend) (int suspend); - - /* poll time (in ms) for ctrl lines */ - int poll_time; - /* and its timer */ struct timer_list timer; }; +static inline struct max3100_port *to_max3100_port(struct uart_port *port) +{ + return container_of(port, struct max3100_port, port); +} + static struct max3100_port *max3100s[MAX_MAX3100]; /* the chips */ static DEFINE_MUTEX(max3100s_lock); /* race on probe */ @@ -150,7 +134,7 @@ static int max3100_do_parity(struct max3100_port *s, u16 c) else c &= 0xff; - parity = parity ^ (hweight8(c) & 1); + parity = parity ^ parity8(c); return parity; } @@ -170,28 +154,10 @@ static void max3100_calc_parity(struct max3100_port *s, u16 *c) *c |= max3100_do_parity(s, *c) << 8; } -static void max3100_work(struct work_struct *w); - -static void max3100_dowork(struct max3100_port *s) -{ - if (!s->force_end_work && !freezing(current) && !s->suspending) - queue_work(s->workqueue, &s->work); -} - -static void max3100_timeout(struct timer_list *t) -{ - struct max3100_port *s = from_timer(s, t, timer); - - if (s->port.state) { - max3100_dowork(s); - mod_timer(&s->timer, jiffies + s->poll_time); - } -} - static int max3100_sr(struct max3100_port *s, u16 tx, u16 *rx) { struct spi_message message; - u16 etx, erx; + __be16 etx, erx; int status; struct spi_transfer tran = { .tx_buf = &etx, @@ -213,7 +179,7 @@ static int max3100_sr(struct max3100_port *s, u16 tx, u16 *rx) return 0; } -static int max3100_handlerx(struct max3100_port *s, u16 rx) +static int max3100_handlerx_unlocked(struct max3100_port *s, u16 rx) { unsigned int status = 0; int ret = 0, cts; @@ -254,13 +220,25 @@ static int max3100_handlerx(struct max3100_port *s, u16 rx) return ret; } +static int max3100_handlerx(struct max3100_port *s, u16 rx) +{ + unsigned long flags; + int ret; + + uart_port_lock_irqsave(&s->port, &flags); + ret = max3100_handlerx_unlocked(s, rx); + uart_port_unlock_irqrestore(&s->port, flags); + return ret; +} + static void max3100_work(struct work_struct *w) { struct max3100_port *s = container_of(w, struct max3100_port, work); + struct tty_port *tport = &s->port.state->port; + unsigned char ch; + int conf, cconf, cloopback, crts; int rxchars; u16 tx, rx; - int conf, cconf, crts; - struct circ_buf *xmit = &s->port.state->xmit; dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -270,11 +248,15 @@ static void max3100_work(struct work_struct *w) conf = s->conf; cconf = s->conf_commit; s->conf_commit = 0; + cloopback = s->loopback_commit; + s->loopback_commit = 0; crts = s->rts_commit; s->rts_commit = 0; spin_unlock(&s->conf_lock); if (cconf) max3100_sr(s, MAX3100_WC | conf, &rx); + if (cloopback) + max3100_sr(s, 0x4001, &rx); if (crts) { max3100_sr(s, MAX3100_WD | MAX3100_TE | (s->rts ? MAX3100_RTS : 0), &rx); @@ -290,10 +272,9 @@ static void max3100_work(struct work_struct *w) tx = s->port.x_char; s->port.icount.tx++; s->port.x_char = 0; - } else if (!uart_circ_empty(xmit) && - !uart_tx_stopped(&s->port)) { - tx = xmit->buf[xmit->tail]; - uart_xmit_advance(&s->port, 1); + } else if (!uart_tx_stopped(&s->port) && + uart_fifo_get(&s->port, &ch)) { + tx = ch; } if (tx != 0xffff) { max3100_calc_parity(s, &tx); @@ -307,19 +288,33 @@ static void max3100_work(struct work_struct *w) tty_flip_buffer_push(&s->port.state->port); rxchars = 0; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&s->port); } while (!s->force_end_work && !freezing(current) && ((rx & MAX3100_R) || - (!uart_circ_empty(xmit) && + (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(&s->port)))); if (rxchars > 0) tty_flip_buffer_push(&s->port.state->port); } +static void max3100_dowork(struct max3100_port *s) +{ + if (!s->force_end_work && !freezing(current) && !s->suspending) + queue_work(s->workqueue, &s->work); +} + +static void max3100_timeout(struct timer_list *t) +{ + struct max3100_port *s = timer_container_of(s, t, timer); + + max3100_dowork(s); + mod_timer(&s->timer, jiffies + uart_poll_timeout(&s->port)); +} + static irqreturn_t max3100_irq(int irqno, void *dev_id) { struct max3100_port *s = dev_id; @@ -332,20 +327,15 @@ static irqreturn_t max3100_irq(int irqno, void *dev_id) static void max3100_enable_ms(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); - if (s->poll_time > 0) - mod_timer(&s->timer, jiffies); + mod_timer(&s->timer, jiffies); dev_dbg(&s->spi->dev, "%s\n", __func__); } static void max3100_start_tx(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -354,9 +344,7 @@ static void max3100_start_tx(struct uart_port *port) static void max3100_stop_rx(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -370,9 +358,7 @@ static void max3100_stop_rx(struct uart_port *port) static unsigned int max3100_tx_empty(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -383,9 +369,7 @@ static unsigned int max3100_tx_empty(struct uart_port *port) static unsigned int max3100_get_mctrl(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -397,21 +381,25 @@ static unsigned int max3100_get_mctrl(struct uart_port *port) static void max3100_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); - int rts; + struct max3100_port *s = to_max3100_port(port); + int loopback, rts; dev_dbg(&s->spi->dev, "%s\n", __func__); + loopback = (mctrl & TIOCM_LOOP) > 0; rts = (mctrl & TIOCM_RTS) > 0; spin_lock(&s->conf_lock); + if (s->loopback != loopback) { + s->loopback = loopback; + s->loopback_commit = 1; + } if (s->rts != rts) { s->rts = rts; s->rts_commit = 1; - max3100_dowork(s); } + if (s->loopback_commit || s->rts_commit) + max3100_dowork(s); spin_unlock(&s->conf_lock); } @@ -419,10 +407,9 @@ static void max3100_set_termios(struct uart_port *port, struct ktermios *termios, const struct ktermios *old) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); - int baud = 0; + struct max3100_port *s = to_max3100_port(port); + unsigned int baud = port->uartclk / 16; + unsigned int baud230400 = (baud == 230400) ? 1 : 0; unsigned cflag; u32 param_new, param_mask, parity = 0; @@ -435,40 +422,40 @@ max3100_set_termios(struct uart_port *port, struct ktermios *termios, param_new = s->conf & MAX3100_BAUD; switch (baud) { case 300: - if (s->crystal) + if (baud230400) baud = s->baud; else param_new = 15; break; case 600: - param_new = 14 + s->crystal; + param_new = 14 + baud230400; break; case 1200: - param_new = 13 + s->crystal; + param_new = 13 + baud230400; break; case 2400: - param_new = 12 + s->crystal; + param_new = 12 + baud230400; break; case 4800: - param_new = 11 + s->crystal; + param_new = 11 + baud230400; break; case 9600: - param_new = 10 + s->crystal; + param_new = 10 + baud230400; break; case 19200: - param_new = 9 + s->crystal; + param_new = 9 + baud230400; break; case 38400: - param_new = 8 + s->crystal; + param_new = 8 + baud230400; break; case 57600: - param_new = 1 + s->crystal; + param_new = 1 + baud230400; break; case 115200: - param_new = 0 + s->crystal; + param_new = 0 + baud230400; break; case 230400: - if (s->crystal) + if (baud230400) param_new = 0; else baud = s->baud; @@ -520,9 +507,7 @@ max3100_set_termios(struct uart_port *port, struct ktermios *termios, MAX3100_STATUS_PE | MAX3100_STATUS_FE | MAX3100_STATUS_OE; - if (s->poll_time > 0) - del_timer_sync(&s->timer); - + timer_delete_sync(&s->timer); uart_update_timeout(port, termios->c_cflag, baud); spin_lock(&s->conf_lock); @@ -538,9 +523,8 @@ max3100_set_termios(struct uart_port *port, struct ktermios *termios, static void max3100_shutdown(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); + u16 rx; dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -549,38 +533,29 @@ static void max3100_shutdown(struct uart_port *port) s->force_end_work = 1; - if (s->poll_time > 0) - del_timer_sync(&s->timer); + timer_delete_sync(&s->timer); if (s->workqueue) { destroy_workqueue(s->workqueue); s->workqueue = NULL; } - if (s->irq) - free_irq(s->irq, s); + if (port->irq) + free_irq(port->irq, s); /* set shutdown mode to save power */ - if (s->max3100_hw_suspend) - s->max3100_hw_suspend(1); - else { - u16 tx, rx; - - tx = MAX3100_WC | MAX3100_SHDN; - max3100_sr(s, tx, &rx); - } + max3100_sr(s, MAX3100_WC | MAX3100_SHDN, &rx); } static int max3100_startup(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); char b[12]; + int ret; dev_dbg(&s->spi->dev, "%s\n", __func__); s->conf = MAX3100_RM; - s->baud = s->crystal ? 230400 : 115200; + s->baud = port->uartclk / 16; s->rx_enabled = 1; if (s->suspending) @@ -598,23 +573,15 @@ static int max3100_startup(struct uart_port *port) } INIT_WORK(&s->work, max3100_work); - if (request_irq(s->irq, max3100_irq, - IRQF_TRIGGER_FALLING, "max3100", s) < 0) { - dev_warn(&s->spi->dev, "cannot allocate irq %d\n", s->irq); - s->irq = 0; + ret = request_irq(port->irq, max3100_irq, IRQF_TRIGGER_FALLING, "max3100", s); + if (ret < 0) { + dev_warn(&s->spi->dev, "cannot allocate irq %d\n", port->irq); + port->irq = 0; destroy_workqueue(s->workqueue); s->workqueue = NULL; return -EBUSY; } - if (s->loopback) { - u16 tx, rx; - tx = 0x4001; - max3100_sr(s, tx, &rx); - } - - if (s->max3100_hw_suspend) - s->max3100_hw_suspend(0); s->conf_commit = 1; max3100_dowork(s); /* wait for clock to settle */ @@ -627,9 +594,7 @@ static int max3100_startup(struct uart_port *port) static const char *max3100_type(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -638,18 +603,14 @@ static const char *max3100_type(struct uart_port *port) static void max3100_release_port(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); } static void max3100_config_port(struct uart_port *port, int flags) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -660,9 +621,7 @@ static void max3100_config_port(struct uart_port *port, int flags) static int max3100_verify_port(struct uart_port *port, struct serial_struct *ser) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); int ret = -EINVAL; dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -674,18 +633,14 @@ static int max3100_verify_port(struct uart_port *port, static void max3100_stop_tx(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); } static int max3100_request_port(struct uart_port *port) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); return 0; @@ -693,9 +648,7 @@ static int max3100_request_port(struct uart_port *port) static void max3100_break_ctl(struct uart_port *port, int break_state) { - struct max3100_port *s = container_of(port, - struct max3100_port, - port); + struct max3100_port *s = to_max3100_port(port); dev_dbg(&s->spi->dev, "%s\n", __func__); } @@ -731,74 +684,59 @@ static int uart_driver_registered; static int max3100_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; int i, retval; - struct plat_max3100 *pdata; - u16 tx, rx; + u16 rx; mutex_lock(&max3100s_lock); if (!uart_driver_registered) { - uart_driver_registered = 1; retval = uart_register_driver(&max3100_uart_driver); if (retval) { - printk(KERN_ERR "Couldn't register max3100 uart driver\n"); mutex_unlock(&max3100s_lock); - return retval; + return dev_err_probe(dev, retval, "Couldn't register max3100 uart driver\n"); } + + uart_driver_registered = 1; } for (i = 0; i < MAX_MAX3100; i++) if (!max3100s[i]) break; if (i == MAX_MAX3100) { - dev_warn(&spi->dev, "too many MAX3100 chips\n"); mutex_unlock(&max3100s_lock); - return -ENOMEM; + return dev_err_probe(dev, -ENOMEM, "too many MAX3100 chips\n"); } max3100s[i] = kzalloc(sizeof(struct max3100_port), GFP_KERNEL); if (!max3100s[i]) { - dev_warn(&spi->dev, - "kmalloc for max3100 structure %d failed!\n", i); mutex_unlock(&max3100s_lock); return -ENOMEM; } max3100s[i]->spi = spi; - max3100s[i]->irq = spi->irq; spin_lock_init(&max3100s[i]->conf_lock); spi_set_drvdata(spi, max3100s[i]); - pdata = dev_get_platdata(&spi->dev); - max3100s[i]->crystal = pdata->crystal; - max3100s[i]->loopback = pdata->loopback; - max3100s[i]->poll_time = msecs_to_jiffies(pdata->poll_time); - if (pdata->poll_time > 0 && max3100s[i]->poll_time == 0) - max3100s[i]->poll_time = 1; - max3100s[i]->max3100_hw_suspend = pdata->max3100_hw_suspend; max3100s[i]->minor = i; timer_setup(&max3100s[i]->timer, max3100_timeout, 0); dev_dbg(&spi->dev, "%s: adding port %d\n", __func__, i); - max3100s[i]->port.irq = max3100s[i]->irq; - max3100s[i]->port.uartclk = max3100s[i]->crystal ? 3686400 : 1843200; + max3100s[i]->port.irq = spi->irq; max3100s[i]->port.fifosize = 16; max3100s[i]->port.ops = &max3100_ops; max3100s[i]->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; max3100s[i]->port.line = i; max3100s[i]->port.type = PORT_MAX3100; max3100s[i]->port.dev = &spi->dev; + + /* Read clock frequency from a property, uart_add_one_port() will fail if it's not set */ + device_property_read_u32(dev, "clock-frequency", &max3100s[i]->port.uartclk); + retval = uart_add_one_port(&max3100_uart_driver, &max3100s[i]->port); if (retval < 0) - dev_warn(&spi->dev, - "uart_add_one_port failed for line %d with error %d\n", - i, retval); + dev_err_probe(dev, retval, "uart_add_one_port failed for line %d\n", i); /* set shutdown mode to save power. Will be woken-up on open */ - if (max3100s[i]->max3100_hw_suspend) - max3100s[i]->max3100_hw_suspend(1); - else { - tx = MAX3100_WC | MAX3100_SHDN; - max3100_sr(max3100s[i], tx, &rx); - } + max3100_sr(max3100s[i], MAX3100_WC | MAX3100_SHDN, &rx); mutex_unlock(&max3100s_lock); return 0; } @@ -830,32 +768,25 @@ static void max3100_remove(struct spi_device *spi) } pr_debug("removing max3100 driver\n"); uart_unregister_driver(&max3100_uart_driver); + uart_driver_registered = 0; mutex_unlock(&max3100s_lock); } -#ifdef CONFIG_PM_SLEEP - static int max3100_suspend(struct device *dev) { struct max3100_port *s = dev_get_drvdata(dev); + u16 rx; dev_dbg(&s->spi->dev, "%s\n", __func__); - disable_irq(s->irq); + disable_irq(s->port.irq); s->suspending = 1; uart_suspend_port(&max3100_uart_driver, &s->port); - if (s->max3100_hw_suspend) - s->max3100_hw_suspend(1); - else { - /* no HW suspend, so do SW one */ - u16 tx, rx; - - tx = MAX3100_WC | MAX3100_SHDN; - max3100_sr(s, tx, &rx); - } + /* no HW suspend, so do SW one */ + max3100_sr(s, MAX3100_WC | MAX3100_SHDN, &rx); return 0; } @@ -865,12 +796,10 @@ static int max3100_resume(struct device *dev) dev_dbg(&s->spi->dev, "%s\n", __func__); - if (s->max3100_hw_suspend) - s->max3100_hw_suspend(0); uart_resume_port(&max3100_uart_driver, &s->port); s->suspending = 0; - enable_irq(s->irq); + enable_irq(s->port.irq); s->conf_commit = 1; if (s->workqueue) @@ -879,20 +808,29 @@ static int max3100_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(max3100_pm_ops, max3100_suspend, max3100_resume); -#define MAX3100_PM_OPS (&max3100_pm_ops) +static DEFINE_SIMPLE_DEV_PM_OPS(max3100_pm_ops, max3100_suspend, max3100_resume); + +static const struct spi_device_id max3100_spi_id[] = { + { "max3100" }, + { } +}; +MODULE_DEVICE_TABLE(spi, max3100_spi_id); -#else -#define MAX3100_PM_OPS NULL -#endif +static const struct of_device_id max3100_of_match[] = { + { .compatible = "maxim,max3100" }, + { } +}; +MODULE_DEVICE_TABLE(of, max3100_of_match); static struct spi_driver max3100_driver = { .driver = { .name = "max3100", - .pm = MAX3100_PM_OPS, + .of_match_table = max3100_of_match, + .pm = pm_sleep_ptr(&max3100_pm_ops), }, .probe = max3100_probe, .remove = max3100_remove, + .id_table = max3100_spi_id, }; module_spi_driver(max3100_driver); @@ -900,4 +838,3 @@ module_spi_driver(max3100_driver); MODULE_DESCRIPTION("MAX3100 driver"); MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("spi:max3100"); diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 14dd9cfaa9f7..541c790c0109 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -747,8 +747,9 @@ static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen) static void max310x_handle_tx(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; - unsigned int txlen, to_send, until_end; + struct tty_port *tport = &port->state->port; + unsigned int txlen, to_send; + unsigned char *tail; if (unlikely(port->x_char)) { max310x_port_write(port, MAX310X_THR_REG, port->x_char); @@ -757,32 +758,26 @@ static void max310x_handle_tx(struct uart_port *port) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) return; - /* Get length of data pending in circular buffer */ - to_send = uart_circ_chars_pending(xmit); - until_end = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); - if (likely(to_send)) { + /* + * It's a circ buffer -- wrap around. + * We could do that in one SPI transaction, but meh. + */ + while (!kfifo_is_empty(&tport->xmit_fifo)) { /* Limit to space available in TX FIFO */ txlen = max310x_port_read(port, MAX310X_TXFIFOLVL_REG); txlen = port->fifosize - txlen; - to_send = (to_send > txlen) ? txlen : to_send; - - if (until_end < to_send) { - /* - * It's a circ buffer -- wrap around. - * We could do that in one SPI transaction, but meh. - */ - max310x_batch_write(port, xmit->buf + xmit->tail, until_end); - max310x_batch_write(port, xmit->buf, to_send - until_end); - } else { - max310x_batch_write(port, xmit->buf + xmit->tail, to_send); - } + if (!txlen) + break; + + to_send = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, txlen); + max310x_batch_write(port, tail, to_send); uart_xmit_advance(port, to_send); } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); } @@ -1194,13 +1189,16 @@ static int max310x_gpio_get(struct gpio_chip *chip, unsigned int offset) return !!((val >> 4) & (1 << (offset % 4))); } -static void max310x_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +static int max310x_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct max310x_port *s = gpiochip_get_data(chip); struct uart_port *port = &s->p[offset / 4].port; max310x_port_update(port, MAX310X_GPIODATA_REG, 1 << (offset % 4), value ? 1 << (offset % 4) : 0); + + return 0; } static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) @@ -1416,7 +1414,7 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty s->gpio.direction_input = max310x_gpio_direction_input; s->gpio.get = max310x_gpio_get; s->gpio.direction_output= max310x_gpio_direction_output; - s->gpio.set = max310x_gpio_set; + s->gpio.set_rv = max310x_gpio_set; s->gpio.set_config = max310x_gpio_set_config; s->gpio.base = -1; s->gpio.ngpio = devtype->nr * 4; @@ -1478,7 +1476,7 @@ static struct regmap_config regcfg = { .reg_bits = 8, .val_bits = 8, .write_flag_mask = MAX310X_WRITE_BIT, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .max_register = MAX310X_REG_1F, .writeable_reg = max310x_reg_writeable, .volatile_reg = max310x_reg_volatile, @@ -1582,7 +1580,7 @@ static int max310x_i2c_extended_reg_enable(struct device *dev, bool enable) static struct regmap_config regcfg_i2c = { .reg_bits = 8, .val_bits = 8, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .writeable_reg = max310x_reg_writeable, .volatile_reg = max310x_reg_volatile, .precious_reg = max310x_reg_precious, diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index b0604d6da025..93e7dda4d39a 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -462,7 +462,7 @@ static const struct uart_ops mcf_uart_ops = { .verify_port = mcf_verify_port, }; -static struct mcf_uart mcf_ports[4]; +static struct mcf_uart mcf_ports[10]; #define MCF_MAXPORTS ARRAY_SIZE(mcf_ports) @@ -616,7 +616,7 @@ static void mcf_remove(struct platform_device *pdev) static struct platform_driver mcf_platform_driver = { .probe = mcf_probe, - .remove_new = mcf_remove, + .remove = mcf_remove, .driver = { .name = "mcfuart", }, diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c index 8048fa542fc4..9cc15449b673 100644 --- a/drivers/tty/serial/men_z135_uart.c +++ b/drivers/tty/serial/men_z135_uart.c @@ -293,17 +293,14 @@ static void men_z135_handle_rx(struct men_z135_port *uart) static void men_z135_handle_tx(struct men_z135_port *uart) { struct uart_port *port = &uart->port; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; + unsigned char *tail; + unsigned int n, txfree; u32 txc; u32 wptr; int qlen; - int n; - int txfree; - int head; - int tail; - int s; - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) goto out; if (uart_tx_stopped(port)) @@ -313,7 +310,7 @@ static void men_z135_handle_tx(struct men_z135_port *uart) goto out; /* calculate bytes to copy */ - qlen = uart_circ_chars_pending(xmit); + qlen = kfifo_len(&tport->xmit_fifo); if (qlen <= 0) goto out; @@ -345,21 +342,18 @@ static void men_z135_handle_tx(struct men_z135_port *uart) if (n <= 0) goto irq_en; - head = xmit->head & (UART_XMIT_SIZE - 1); - tail = xmit->tail & (UART_XMIT_SIZE - 1); - - s = ((head >= tail) ? head : UART_XMIT_SIZE) - tail; - n = min(n, s); + n = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, + min_t(unsigned int, UART_XMIT_SIZE, n)); + memcpy_toio(port->membase + MEN_Z135_TX_RAM, tail, n); - memcpy_toio(port->membase + MEN_Z135_TX_RAM, &xmit->buf[xmit->tail], n); iowrite32(n & 0x3ff, port->membase + MEN_Z135_TX_CTRL); uart_xmit_advance(port, n); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); irq_en: - if (!uart_circ_empty(xmit)) + if (!kfifo_is_empty(&tport->xmit_fifo)) men_z135_reg_set(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN); else men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN); @@ -926,4 +920,4 @@ MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MEN 16z135 High Speed UART"); MODULE_ALIAS("mcb:16z135"); -MODULE_IMPORT_NS(MCB); +MODULE_IMPORT_NS("MCB"); diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c index 6feac459c0cf..a6cb2a535f9d 100644 --- a/drivers/tty/serial/meson_uart.c +++ b/drivers/tty/serial/meson_uart.c @@ -141,8 +141,8 @@ static void meson_uart_shutdown(struct uart_port *port) static void meson_uart_start_tx(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; - unsigned int ch; + struct tty_port *tport = &port->state->port; + unsigned char ch; u32 val; if (uart_tx_stopped(port)) { @@ -158,21 +158,19 @@ static void meson_uart_start_tx(struct uart_port *port) continue; } - if (uart_circ_empty(xmit)) + if (!uart_fifo_get(port, &ch)) break; - ch = xmit->buf[xmit->tail]; writel(ch, port->membase + AML_UART_WFIFO); - uart_xmit_advance(port, 1); } - if (!uart_circ_empty(xmit)) { + if (!kfifo_is_empty(&tport->xmit_fifo)) { val = readl(port->membase + AML_UART_CONTROL); val |= AML_UART_TX_INT_EN; writel(val, port->membase + AML_UART_CONTROL); } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); } @@ -844,7 +842,7 @@ MODULE_DEVICE_TABLE(of, meson_uart_dt_match); static struct platform_driver meson_uart_platform_driver = { .probe = meson_uart_probe, - .remove_new = meson_uart_remove, + .remove = meson_uart_remove, .driver = { .name = "meson_uart", .of_match_table = meson_uart_dt_match, diff --git a/drivers/tty/serial/milbeaut_usio.c b/drivers/tty/serial/milbeaut_usio.c index da4c6f7e2a30..4e47dca2c4ed 100644 --- a/drivers/tty/serial/milbeaut_usio.c +++ b/drivers/tty/serial/milbeaut_usio.c @@ -72,7 +72,7 @@ static void mlb_usio_stop_tx(struct uart_port *port) static void mlb_usio_tx_chars(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; int count; writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE, @@ -87,7 +87,7 @@ static void mlb_usio_tx_chars(struct uart_port *port) port->x_char = 0; return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { mlb_usio_stop_tx(port); return; } @@ -96,12 +96,13 @@ static void mlb_usio_tx_chars(struct uart_port *port) (readw(port->membase + MLB_USIO_REG_FBYTE) & 0xff); do { - writew(xmit->buf[xmit->tail], port->membase + MLB_USIO_REG_DR); + unsigned char ch; - uart_xmit_advance(port, 1); - if (uart_circ_empty(xmit)) + if (!uart_fifo_get(port, &ch)) break; + writew(ch, port->membase + MLB_USIO_REG_DR); + port->icount.tx++; } while (--count > 0); writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FDRQ, @@ -110,10 +111,10 @@ static void mlb_usio_tx_chars(struct uart_port *port) writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE, port->membase + MLB_USIO_REG_SCR); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) mlb_usio_stop_tx(port); } @@ -522,7 +523,10 @@ static int mlb_usio_probe(struct platform_device *pdev) } port->membase = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - + if (!port->membase) { + ret = -ENOMEM; + goto failed; + } ret = platform_get_irq_byname(pdev, "rx"); mlb_usio_irq[index][RX] = ret; @@ -569,7 +573,7 @@ MODULE_DEVICE_TABLE(of, mlb_usio_dt_ids); static struct platform_driver mlb_usio_driver = { .probe = mlb_usio_probe, - .remove_new = mlb_usio_remove, + .remove = mlb_usio_remove, .driver = { .name = USIO_NAME, .of_match_table = mlb_usio_dt_ids, diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index 95dae5e27b28..37eb701b0b46 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -1351,7 +1351,6 @@ static const struct uart_ops mpc52xx_uart_ops = { .startup = mpc52xx_uart_startup, .shutdown = mpc52xx_uart_shutdown, .set_termios = mpc52xx_uart_set_termios, -/* .pm = mpc52xx_uart_pm, Not supported yet */ .type = mpc52xx_uart_type, .release_port = mpc52xx_uart_release_port, .request_port = mpc52xx_uart_request_port, @@ -1621,7 +1620,7 @@ mpc52xx_console_setup(struct console *co, char *options) (void *)port->mapbase, port->membase, port->irq, port->uartclk); - /* Setup the port parameters accoding to options */ + /* Setup the port parameters according to options */ if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else @@ -1843,7 +1842,7 @@ MODULE_DEVICE_TABLE(of, mpc52xx_uart_of_match); static struct platform_driver mpc52xx_uart_of_driver = { .probe = mpc52xx_uart_of_probe, - .remove_new = mpc52xx_uart_of_remove, + .remove = mpc52xx_uart_of_remove, #ifdef CONFIG_PM .suspend = mpc52xx_uart_of_suspend, .resume = mpc52xx_uart_of_resume, diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index d27c4c8c84e1..3449945493ce 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -161,11 +161,16 @@ enum { struct msm_dma { struct dma_chan *chan; enum dma_data_direction dir; - dma_addr_t phys; - unsigned char *virt; + union { + struct { + dma_addr_t phys; + unsigned char *virt; + unsigned int count; + } rx; + struct scatterlist tx_sg; + }; dma_cookie_t cookie; u32 enable_bit; - unsigned int count; struct dma_async_tx_descriptor *desc; }; @@ -249,8 +254,12 @@ static void msm_stop_dma(struct uart_port *port, struct msm_dma *dma) unsigned int mapped; u32 val; - mapped = dma->count; - dma->count = 0; + if (dma->dir == DMA_TO_DEVICE) { + mapped = sg_dma_len(&dma->tx_sg); + } else { + mapped = dma->rx.count; + dma->rx.count = 0; + } dmaengine_terminate_all(dma->chan); @@ -265,8 +274,13 @@ static void msm_stop_dma(struct uart_port *port, struct msm_dma *dma) val &= ~dma->enable_bit; msm_write(port, val, UARTDM_DMEN); - if (mapped) - dma_unmap_single(dev, dma->phys, mapped, dma->dir); + if (mapped) { + if (dma->dir == DMA_TO_DEVICE) { + dma_unmap_sg(dev, &dma->tx_sg, 1, dma->dir); + sg_init_table(&dma->tx_sg, 1); + } else + dma_unmap_single(dev, dma->rx.phys, mapped, dma->dir); + } } static void msm_release_dma(struct msm_port *msm_port) @@ -285,7 +299,7 @@ static void msm_release_dma(struct msm_port *msm_port) if (dma->chan) { msm_stop_dma(&msm_port->uart, dma); dma_release_channel(dma->chan); - kfree(dma->virt); + kfree(dma->rx.virt); } memset(dma, 0, sizeof(*dma)); @@ -357,8 +371,8 @@ static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base) of_property_read_u32(dev->of_node, "qcom,rx-crci", &crci); - dma->virt = kzalloc(UARTDM_RX_SIZE, GFP_KERNEL); - if (!dma->virt) + dma->rx.virt = kzalloc(UARTDM_RX_SIZE, GFP_KERNEL); + if (!dma->rx.virt) goto rel_rx; memset(&conf, 0, sizeof(conf)); @@ -385,7 +399,7 @@ static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base) return; err: - kfree(dma->virt); + kfree(dma->rx.virt); rel_rx: dma_release_channel(dma->chan); no_rx: @@ -420,7 +434,7 @@ static void msm_start_tx(struct uart_port *port) struct msm_dma *dma = &msm_port->tx_dma; /* Already started in DMA mode */ - if (dma->count) + if (sg_dma_len(&dma->tx_sg)) return; msm_port->imr |= MSM_UART_IMR_TXLEV; @@ -438,7 +452,7 @@ static void msm_complete_tx_dma(void *args) { struct msm_port *msm_port = args; struct uart_port *port = &msm_port->uart; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; struct msm_dma *dma = &msm_port->tx_dma; struct dma_tx_state state; unsigned long flags; @@ -448,12 +462,12 @@ static void msm_complete_tx_dma(void *args) uart_port_lock_irqsave(port, &flags); /* Already stopped */ - if (!dma->count) + if (!sg_dma_len(&dma->tx_sg)) goto done; dmaengine_tx_status(dma->chan, dma->cookie, &state); - dma_unmap_single(port->dev, dma->phys, dma->count, dma->dir); + dma_unmap_sg(port->dev, &dma->tx_sg, 1, dma->dir); val = msm_read(port, UARTDM_DMEN); val &= ~dma->enable_bit; @@ -464,15 +478,15 @@ static void msm_complete_tx_dma(void *args) msm_write(port, MSM_UART_CR_TX_ENABLE, MSM_UART_CR); } - count = dma->count - state.residue; + count = sg_dma_len(&dma->tx_sg) - state.residue; uart_xmit_advance(port, count); - dma->count = 0; + sg_init_table(&dma->tx_sg, 1); /* Restore "Tx FIFO below watermark" interrupt */ msm_port->imr |= MSM_UART_IMR_TXLEV; msm_write(port, msm_port->imr, MSM_UART_IMR); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); msm_handle_tx(port); @@ -482,22 +496,24 @@ done: static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count) { - struct circ_buf *xmit = &msm_port->uart.state->xmit; struct uart_port *port = &msm_port->uart; + struct tty_port *tport = &port->state->port; struct msm_dma *dma = &msm_port->tx_dma; - void *cpu_addr; + unsigned int mapped; int ret; u32 val; - cpu_addr = &xmit->buf[xmit->tail]; + sg_init_table(&dma->tx_sg, 1); + kfifo_dma_out_prepare(&tport->xmit_fifo, &dma->tx_sg, 1, count); - dma->phys = dma_map_single(port->dev, cpu_addr, count, dma->dir); - ret = dma_mapping_error(port->dev, dma->phys); - if (ret) - return ret; + mapped = dma_map_sg(port->dev, &dma->tx_sg, 1, dma->dir); + if (!mapped) { + ret = -EIO; + goto zero_sg; + } - dma->desc = dmaengine_prep_slave_single(dma->chan, dma->phys, - count, DMA_MEM_TO_DEV, + dma->desc = dmaengine_prep_slave_sg(dma->chan, &dma->tx_sg, 1, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_PREP_FENCE); if (!dma->desc) { @@ -520,8 +536,6 @@ static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count) msm_port->imr &= ~MSM_UART_IMR_TXLEV; msm_write(port, msm_port->imr, MSM_UART_IMR); - dma->count = count; - val = msm_read(port, UARTDM_DMEN); val |= dma->enable_bit; @@ -536,7 +550,9 @@ static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count) dma_async_issue_pending(dma->chan); return 0; unmap: - dma_unmap_single(port->dev, dma->phys, count, dma->dir); + dma_unmap_sg(port->dev, &dma->tx_sg, 1, dma->dir); +zero_sg: + sg_init_table(&dma->tx_sg, 1); return ret; } @@ -553,7 +569,7 @@ static void msm_complete_rx_dma(void *args) uart_port_lock_irqsave(port, &flags); /* Already stopped */ - if (!dma->count) + if (!dma->rx.count) goto done; val = msm_read(port, UARTDM_DMEN); @@ -570,14 +586,14 @@ static void msm_complete_rx_dma(void *args) port->icount.rx += count; - dma->count = 0; + dma->rx.count = 0; - dma_unmap_single(port->dev, dma->phys, UARTDM_RX_SIZE, dma->dir); + dma_unmap_single(port->dev, dma->rx.phys, UARTDM_RX_SIZE, dma->dir); for (i = 0; i < count; i++) { char flag = TTY_NORMAL; - if (msm_port->break_detected && dma->virt[i] == 0) { + if (msm_port->break_detected && dma->rx.virt[i] == 0) { port->icount.brk++; flag = TTY_BREAK; msm_port->break_detected = false; @@ -588,9 +604,9 @@ static void msm_complete_rx_dma(void *args) if (!(port->read_status_mask & MSM_UART_SR_RX_BREAK)) flag = TTY_NORMAL; - sysrq = uart_prepare_sysrq_char(port, dma->virt[i]); + sysrq = uart_prepare_sysrq_char(port, dma->rx.virt[i]); if (!sysrq) - tty_insert_flip_char(tport, dma->virt[i], flag); + tty_insert_flip_char(tport, dma->rx.virt[i], flag); } msm_start_rx_dma(msm_port); @@ -614,13 +630,13 @@ static void msm_start_rx_dma(struct msm_port *msm_port) if (!dma->chan) return; - dma->phys = dma_map_single(uart->dev, dma->virt, + dma->rx.phys = dma_map_single(uart->dev, dma->rx.virt, UARTDM_RX_SIZE, dma->dir); - ret = dma_mapping_error(uart->dev, dma->phys); + ret = dma_mapping_error(uart->dev, dma->rx.phys); if (ret) goto sw_mode; - dma->desc = dmaengine_prep_slave_single(dma->chan, dma->phys, + dma->desc = dmaengine_prep_slave_single(dma->chan, dma->rx.phys, UARTDM_RX_SIZE, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); if (!dma->desc) @@ -648,7 +664,7 @@ static void msm_start_rx_dma(struct msm_port *msm_port) msm_write(uart, msm_port->imr, MSM_UART_IMR); - dma->count = UARTDM_RX_SIZE; + dma->rx.count = UARTDM_RX_SIZE; dma_async_issue_pending(dma->chan); @@ -668,7 +684,7 @@ static void msm_start_rx_dma(struct msm_port *msm_port) return; unmap: - dma_unmap_single(uart->dev, dma->phys, UARTDM_RX_SIZE, dma->dir); + dma_unmap_single(uart->dev, dma->rx.phys, UARTDM_RX_SIZE, dma->dir); sw_mode: /* @@ -831,8 +847,8 @@ static void msm_handle_rx(struct uart_port *port) static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count) { - struct circ_buf *xmit = &port->state->xmit; struct msm_port *msm_port = to_msm_port(port); + struct tty_port *tport = &port->state->port; unsigned int num_chars; unsigned int tf_pointer = 0; void __iomem *tf; @@ -846,8 +862,7 @@ static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count) msm_reset_dm_count(port, tx_count); while (tf_pointer < tx_count) { - int i; - char buf[4] = { 0 }; + unsigned char buf[4] = { 0 }; if (!(msm_read(port, MSM_UART_SR) & MSM_UART_SR_TX_READY)) break; @@ -858,26 +873,23 @@ static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count) else num_chars = 1; - for (i = 0; i < num_chars; i++) - buf[i] = xmit->buf[xmit->tail + i]; - + num_chars = uart_fifo_out(port, buf, num_chars); iowrite32_rep(tf, buf, 1); - uart_xmit_advance(port, num_chars); tf_pointer += num_chars; } /* disable tx interrupts if nothing more to send */ - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) msm_stop_tx(port); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); } static void msm_handle_tx(struct uart_port *port) { struct msm_port *msm_port = to_msm_port(port); - struct circ_buf *xmit = &msm_port->uart.state->xmit; + struct tty_port *tport = &port->state->port; struct msm_dma *dma = &msm_port->tx_dma; unsigned int pio_count, dma_count, dma_min; char buf[4] = { 0 }; @@ -901,13 +913,13 @@ static void msm_handle_tx(struct uart_port *port) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { msm_stop_tx(port); return; } - pio_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); - dma_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + dma_count = pio_count = kfifo_out_linear(&tport->xmit_fifo, NULL, + UART_XMIT_SIZE); dma_min = 1; /* Always DMA */ if (msm_port->is_uartdm > UARTDM_1P3) { @@ -955,7 +967,7 @@ static irqreturn_t msm_uart_irq(int irq, void *dev_id) } if (misr & (MSM_UART_IMR_RXLEV | MSM_UART_IMR_RXSTALE)) { - if (dma->count) { + if (dma->rx.count) { val = MSM_UART_CR_CMD_STALE_EVENT_DISABLE; msm_write(port, val, MSM_UART_CR); val = MSM_UART_CR_CMD_RESET_STALE_INT; @@ -1734,6 +1746,12 @@ msm_serial_early_console_setup_dm(struct earlycon_device *device, if (!device->port.membase) return -ENODEV; + /* Disable DM / single-character modes */ + msm_write(&device->port, 0, UARTDM_DMEN); + msm_write(&device->port, MSM_UART_CR_CMD_RESET_RX, MSM_UART_CR); + msm_write(&device->port, MSM_UART_CR_CMD_RESET_TX, MSM_UART_CR); + msm_write(&device->port, MSM_UART_CR_TX_ENABLE, MSM_UART_CR); + device->con->write = msm_serial_early_write_dm; return 0; } @@ -1882,7 +1900,7 @@ static const struct dev_pm_ops msm_serial_dev_pm_ops = { }; static struct platform_driver msm_platform_driver = { - .remove_new = msm_serial_remove, + .remove = msm_serial_remove, .probe = msm_serial_probe, .driver = { .name = "msm_serial", diff --git a/drivers/tty/serial/mux.c b/drivers/tty/serial/mux.c index 85ce1e9af44a..b417faead20f 100644 --- a/drivers/tty/serial/mux.c +++ b/drivers/tty/serial/mux.c @@ -563,7 +563,7 @@ static void __exit mux_exit(void) { /* Delete the Mux timer. */ if(port_cnt > 0) { - del_timer_sync(&mux_timer); + timer_delete_sync(&mux_timer); #ifdef CONFIG_SERIAL_MUX_CONSOLE unregister_console(&mux_console); #endif diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index 0255646bc175..5de57b77abdb 100644 --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -219,12 +219,10 @@ static void mvebu_uart_stop_tx(struct uart_port *port) static void mvebu_uart_start_tx(struct uart_port *port) { unsigned int ctl; - struct circ_buf *xmit = &port->state->xmit; + unsigned char c; - if (IS_EXTENDED(port) && !uart_circ_empty(xmit)) { - writel(xmit->buf[xmit->tail], port->membase + UART_TSH(port)); - uart_xmit_advance(port, 1); - } + if (IS_EXTENDED(port) && uart_fifo_get(port, &c)) + writel(c, port->membase + UART_TSH(port)); ctl = readl(port->membase + UART_INTR(port)); ctl |= CTRL_TX_RDY_INT(port); diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 4749331fe618..cc65c9fb6446 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -517,7 +517,7 @@ static void mxs_auart_tx_chars(struct mxs_auart_port *s); static void dma_tx_callback(void *param) { struct mxs_auart_port *s = param; - struct circ_buf *xmit = &s->port.state->xmit; + struct tty_port *tport = &s->port.state->port; dma_unmap_sg(s->dev, &s->tx_sgl, 1, DMA_TO_DEVICE); @@ -526,7 +526,7 @@ static void dma_tx_callback(void *param) smp_mb__after_atomic(); /* wake up the possible processes. */ - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&s->port); mxs_auart_tx_chars(s); @@ -568,33 +568,22 @@ static int mxs_auart_dma_tx(struct mxs_auart_port *s, int size) static void mxs_auart_tx_chars(struct mxs_auart_port *s) { - struct circ_buf *xmit = &s->port.state->xmit; + struct tty_port *tport = &s->port.state->port; bool pending; u8 ch; if (auart_dma_enabled(s)) { u32 i = 0; - int size; void *buffer = s->tx_dma_buf; if (test_and_set_bit(MXS_AUART_DMA_TX_SYNC, &s->flags)) return; - while (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) { - size = min_t(u32, UART_XMIT_SIZE - i, - CIRC_CNT_TO_END(xmit->head, - xmit->tail, - UART_XMIT_SIZE)); - memcpy(buffer + i, xmit->buf + xmit->tail, size); - xmit->tail = (xmit->tail + size) & (UART_XMIT_SIZE - 1); - - i += size; - if (i >= UART_XMIT_SIZE) - break; - } - if (uart_tx_stopped(&s->port)) mxs_auart_stop_tx(&s->port); + else + i = kfifo_out(&tport->xmit_fifo, buffer, + UART_XMIT_SIZE); if (i) { mxs_auart_dma_tx(s, i); @@ -1086,11 +1075,13 @@ static void mxs_auart_set_ldisc(struct uart_port *port, static irqreturn_t mxs_auart_irq_handle(int irq, void *context) { - u32 istat; + u32 istat, stat; struct mxs_auart_port *s = context; u32 mctrl_temp = s->mctrl_prev; - u32 stat = mxs_read(s, REG_STAT); + uart_port_lock(&s->port); + + stat = mxs_read(s, REG_STAT); istat = mxs_read(s, REG_INTR); /* ack irq */ @@ -1126,6 +1117,8 @@ static irqreturn_t mxs_auart_irq_handle(int irq, void *context) istat &= ~AUART_INTR_TXIS; } + uart_port_unlock(&s->port); + return IRQ_HANDLED; } @@ -1711,7 +1704,7 @@ static void mxs_auart_remove(struct platform_device *pdev) static struct platform_driver mxs_auart_driver = { .probe = mxs_auart_probe, - .remove_new = mxs_auart_remove, + .remove = mxs_auart_remove, .driver = { .name = "mxs-auart", .of_match_table = mxs_auart_dt_ids, diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 9be1c871cf11..0b85f47ff19e 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1093,7 +1093,6 @@ static void __maybe_unused wait_for_xmitr(struct uart_omap_port *up) /* Wait up to 1s for flow control if necessary */ if (up->port.flags & UPF_CONS_FLOW) { - tmout = 1000000; for (tmout = 1000000; tmout; tmout--) { unsigned int msr = serial_in(up, UART_MSR); @@ -1803,7 +1802,7 @@ MODULE_DEVICE_TABLE(of, omap_serial_of_match); static struct platform_driver serial_omap_driver = { .probe = serial_omap_probe, - .remove_new = serial_omap_remove, + .remove = serial_omap_remove, .driver = { .name = OMAP_SERIAL_DRIVER_NAME, .pm = &serial_omap_dev_pm_ops, diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c index 8b60ac0ad7cd..0542882cfbbe 100644 --- a/drivers/tty/serial/owl-uart.c +++ b/drivers/tty/serial/owl-uart.c @@ -730,7 +730,7 @@ static void owl_uart_remove(struct platform_device *pdev) static struct platform_driver owl_uart_platform_driver = { .probe = owl_uart_probe, - .remove_new = owl_uart_remove, + .remove = owl_uart_remove, .driver = { .name = "owl-uart", .of_match_table = owl_uart_dt_matches, @@ -761,4 +761,5 @@ static void __exit owl_uart_exit(void) module_init(owl_uart_init); module_exit(owl_uart_exit); +MODULE_DESCRIPTION("Actions Semi Owl family serial console"); MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 89257cddf540..884fefbfd5a1 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -808,7 +808,7 @@ static int dma_handle_rx(struct eg20t_port *priv) static unsigned int handle_tx(struct eg20t_port *priv) { struct uart_port *port = &priv->port; - struct circ_buf *xmit = &port->state->xmit; + unsigned char ch; int fifo_size; int tx_empty; @@ -830,9 +830,9 @@ static unsigned int handle_tx(struct eg20t_port *priv) fifo_size--; } - while (!uart_tx_stopped(port) && !uart_circ_empty(xmit) && fifo_size) { - iowrite8(xmit->buf[xmit->tail], priv->membase + PCH_UART_THR); - uart_xmit_advance(port, 1); + while (!uart_tx_stopped(port) && fifo_size && + uart_fifo_get(port, &ch)) { + iowrite8(ch, priv->membase + PCH_UART_THR); fifo_size--; tx_empty = 0; } @@ -850,14 +850,14 @@ static unsigned int handle_tx(struct eg20t_port *priv) static unsigned int dma_handle_tx(struct eg20t_port *priv) { struct uart_port *port = &priv->port; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; struct scatterlist *sg; int nent; int fifo_size; struct dma_async_tx_descriptor *desc; + unsigned int bytes, tail; int num; int i; - int bytes; int size; int rem; @@ -886,7 +886,7 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv) fifo_size--; } - bytes = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + bytes = kfifo_out_linear(&tport->xmit_fifo, &tail, UART_XMIT_SIZE); if (!bytes) { dev_dbg(priv->port.dev, "%s 0 bytes return\n", __func__); pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT); @@ -920,10 +920,10 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv) for (i = 0; i < num; i++, sg++) { if (i == (num - 1)) - sg_set_page(sg, virt_to_page(xmit->buf), + sg_set_page(sg, virt_to_page(tport->xmit_buf), rem, fifo_size * i); else - sg_set_page(sg, virt_to_page(xmit->buf), + sg_set_page(sg, virt_to_page(tport->xmit_buf), size, fifo_size * i); } @@ -937,8 +937,7 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv) priv->nent = nent; for (i = 0; i < nent; i++, sg++) { - sg->offset = (xmit->tail & (UART_XMIT_SIZE - 1)) + - fifo_size * i; + sg->offset = tail + fifo_size * i; sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + sg->offset; if (i == (nent - 1)) @@ -955,7 +954,7 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv) __func__); return 0; } - dma_sync_sg_for_device(port->dev, priv->sg_tx_p, nent, DMA_TO_DEVICE); + dma_sync_sg_for_device(port->dev, priv->sg_tx_p, num, DMA_TO_DEVICE); priv->desc_tx = desc; desc->callback = pch_dma_tx_complete; desc->callback_param = priv; @@ -1516,7 +1515,6 @@ static const struct uart_ops pch_uart_ops = { .startup = pch_uart_startup, .shutdown = pch_uart_shutdown, .set_termios = pch_uart_set_termios, -/* .pm = pch_uart_pm, Not supported yet */ .type = pch_uart_type, .release_port = pch_uart_release_port, .request_port = pch_uart_request_port, diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c index bbb46e6e98a2..14d50bd7f1bd 100644 --- a/drivers/tty/serial/pic32_uart.c +++ b/drivers/tty/serial/pic32_uart.c @@ -8,11 +8,11 @@ * Sorin-Andrei Pistirica <andrei.pistirica@microchip.com> */ +#include <linux/gpio/consumer.h> #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/of_irq.h> -#include <linux/of_gpio.h> #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> @@ -342,7 +342,7 @@ static void pic32_uart_do_rx(struct uart_port *port) static void pic32_uart_do_tx(struct uart_port *port) { struct pic32_sport *sport = to_pic32_sport(port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; unsigned int max_count = PIC32_UART_TX_FIFO_DEPTH; if (port->x_char) { @@ -357,7 +357,7 @@ static void pic32_uart_do_tx(struct uart_port *port) return; } - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) goto txq_empty; /* keep stuffing chars into uart tx buffer @@ -371,21 +371,20 @@ static void pic32_uart_do_tx(struct uart_port *port) */ while (!(PIC32_UART_STA_UTXBF & pic32_uart_readl(sport, PIC32_UART_STA))) { - unsigned int c = xmit->buf[xmit->tail]; + unsigned char c; + if (!uart_fifo_get(port, &c)) + break; pic32_uart_writel(sport, PIC32_UART_TX, c); - uart_xmit_advance(port, 1); - if (uart_circ_empty(xmit)) - break; if (--max_count == 0) break; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) goto txq_empty; return; @@ -957,7 +956,7 @@ MODULE_DEVICE_TABLE(of, pic32_serial_dt_ids); static struct platform_driver pic32_uart_platform_driver = { .probe = pic32_uart_probe, - .remove_new = pic32_uart_remove, + .remove = pic32_uart_remove, .driver = { .name = PIC32_DEV_NAME, .of_match_table = of_match_ptr(pic32_serial_dt_ids), diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index 05d97e89511e..e3a919328695 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -210,7 +210,6 @@ static bool pmz_receive_chars(struct uart_pmac_port *uap) { struct tty_port *port; unsigned char ch, r1, drop, flag; - int loops = 0; /* Sanity check, make sure the old bug is no longer happening */ if (uap->port.state == NULL) { @@ -291,25 +290,12 @@ static bool pmz_receive_chars(struct uart_pmac_port *uap) if (r1 & Rx_OVR) tty_insert_flip_char(port, 0, TTY_OVERRUN); next_char: - /* We can get stuck in an infinite loop getting char 0 when the - * line is in a wrong HW state, we break that here. - * When that happens, I disable the receive side of the driver. - * Note that what I've been experiencing is a real irq loop where - * I'm getting flooded regardless of the actual port speed. - * Something strange is going on with the HW - */ - if ((++loops) > 1000) - goto flood; ch = read_zsreg(uap, R0); if (!(ch & Rx_CH_AV)) break; } return true; - flood: - pmz_interrupt_control(uap, 0); - pmz_error("pmz: rx irq flood !\n"); - return true; } static void pmz_status_handle(struct uart_pmac_port *uap) @@ -347,7 +333,8 @@ static void pmz_status_handle(struct uart_pmac_port *uap) static void pmz_transmit_chars(struct uart_pmac_port *uap) { - struct circ_buf *xmit; + struct tty_port *tport; + unsigned char ch; if (ZS_IS_CONS(uap)) { unsigned char status = read_zsreg(uap, R0); @@ -398,8 +385,8 @@ static void pmz_transmit_chars(struct uart_pmac_port *uap) if (uap->port.state == NULL) goto ack_tx_int; - xmit = &uap->port.state->xmit; - if (uart_circ_empty(xmit)) { + tport = &uap->port.state->port; + if (kfifo_is_empty(&tport->xmit_fifo)) { uart_write_wakeup(&uap->port); goto ack_tx_int; } @@ -407,12 +394,11 @@ static void pmz_transmit_chars(struct uart_pmac_port *uap) goto ack_tx_int; uap->flags |= PMACZILOG_FLAG_TX_ACTIVE; - write_zsdata(uap, xmit->buf[xmit->tail]); + WARN_ON(!uart_fifo_get(&uap->port, &ch)); + write_zsdata(uap, ch); zssync(uap); - uart_xmit_advance(&uap->port, 1); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&uap->port); return; @@ -620,15 +606,15 @@ static void pmz_start_tx(struct uart_port *port) port->icount.tx++; port->x_char = 0; } else { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; + unsigned char ch; - if (uart_circ_empty(xmit)) + if (!uart_fifo_get(&uap->port, &ch)) return; - write_zsdata(uap, xmit->buf[xmit->tail]); + write_zsdata(uap, ch); zssync(uap); - uart_xmit_advance(port, 1); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&uap->port); } } @@ -1695,7 +1681,7 @@ static void pmz_dispose_port(struct uart_pmac_port *uap) memset(uap, 0, sizeof(struct uart_pmac_port)); } -static int __init pmz_attach(struct platform_device *pdev) +static int pmz_attach(struct platform_device *pdev) { struct uart_pmac_port *uap; int i; @@ -1714,7 +1700,7 @@ static int __init pmz_attach(struct platform_device *pdev) return uart_add_one_port(&pmz_uart_reg, &uap->port); } -static void __exit pmz_detach(struct platform_device *pdev) +static void pmz_detach(struct platform_device *pdev) { struct uart_pmac_port *uap = platform_get_drvdata(pdev); @@ -1789,7 +1775,8 @@ static struct macio_driver pmz_driver = { #else static struct platform_driver pmz_driver = { - .remove_new = __exit_p(pmz_detach), + .probe = pmz_attach, + .remove = pmz_detach, .driver = { .name = "scc", }, @@ -1837,7 +1824,7 @@ static int __init init_pmz(void) #ifdef CONFIG_PPC_PMAC return macio_register_driver(&pmz_driver); #else - return platform_driver_probe(&pmz_driver, pmz_attach); + return platform_driver_register(&pmz_driver); #endif } diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index f9f7ac1a10df..0293b6210aa6 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -98,6 +98,8 @@ #define DMA_RX_BUF_SIZE 2048 +static DEFINE_IDA(port_ida); + struct qcom_geni_device_data { bool console; enum geni_se_xfer_mode mode; @@ -124,13 +126,14 @@ struct qcom_geni_serial_port { dma_addr_t tx_dma_addr; dma_addr_t rx_dma_addr; bool setup; - unsigned int baud; + unsigned long poll_timeout_us; unsigned long clk_rate; void *rx_buf; u32 loopback; bool brk; unsigned int tx_remaining; + unsigned int tx_queued; int wakeup_irq; bool rx_tx_swap; bool cts_rts_swap; @@ -144,6 +147,10 @@ static const struct uart_ops qcom_geni_uart_pops; static struct uart_driver qcom_geni_console_driver; static struct uart_driver qcom_geni_uart_driver; +static void __qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport); +static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport); +static int qcom_geni_serial_port_setup(struct uart_port *uport); + static inline struct qcom_geni_serial_port *to_dev_port(struct uart_port *uport) { return container_of(uport, struct qcom_geni_serial_port, uport); @@ -248,10 +255,24 @@ static struct qcom_geni_serial_port *get_port_from_line(int line, bool console) struct qcom_geni_serial_port *port; int nr_ports = console ? GENI_UART_CONS_PORTS : GENI_UART_PORTS; - if (line < 0 || line >= nr_ports) - return ERR_PTR(-ENXIO); + if (console) { + if (line < 0 || line >= nr_ports) + return ERR_PTR(-ENXIO); + + port = &qcom_geni_console_port; + } else { + int max_alias_num = of_alias_get_highest_id("serial"); - port = console ? &qcom_geni_console_port : &qcom_geni_uart_ports[line]; + if (line < 0 || line >= nr_ports) + line = ida_alloc_range(&port_ida, max_alias_num + 1, nr_ports, GFP_KERNEL); + else + line = ida_alloc_range(&port_ida, line, nr_ports, GFP_KERNEL); + + if (line < 0) + return ERR_PTR(-ENXIO); + + port = &qcom_geni_uart_ports[line]; + } return port; } @@ -265,27 +286,18 @@ static bool qcom_geni_serial_secondary_active(struct uart_port *uport) return readl(uport->membase + SE_GENI_STATUS) & S_GENI_CMD_ACTIVE; } -static bool qcom_geni_serial_poll_bit(struct uart_port *uport, - int offset, int field, bool set) +static bool qcom_geni_serial_poll_bitfield(struct uart_port *uport, + unsigned int offset, u32 field, u32 val) { u32 reg; struct qcom_geni_serial_port *port; - unsigned int baud; - unsigned int fifo_bits; unsigned long timeout_us = 20000; struct qcom_geni_private_data *private_data = uport->private_data; if (private_data->drv) { port = to_dev_port(uport); - baud = port->baud; - if (!baud) - baud = 115200; - fifo_bits = port->tx_fifo_depth * port->tx_fifo_width; - /* - * Total polling iterations based on FIFO worth of bytes to be - * sent at current baud. Add a little fluff to the wait. - */ - timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500; + if (port->poll_timeout_us) + timeout_us = port->poll_timeout_us; } /* @@ -295,7 +307,7 @@ static bool qcom_geni_serial_poll_bit(struct uart_port *uport, timeout_us = DIV_ROUND_UP(timeout_us, 10) * 10; while (timeout_us) { reg = readl(uport->membase + offset); - if ((bool)(reg & field) == set) + if ((reg & field) == val) return true; udelay(10); timeout_us -= 10; @@ -303,6 +315,12 @@ static bool qcom_geni_serial_poll_bit(struct uart_port *uport, return false; } +static bool qcom_geni_serial_poll_bit(struct uart_port *uport, + unsigned int offset, u32 field, bool set) +{ + return qcom_geni_serial_poll_bitfield(uport, offset, field, set ? field : 0); +} + static void qcom_geni_serial_setup_tx(struct uart_port *uport, u32 xmit_size) { u32 m_cmd; @@ -315,18 +333,16 @@ static void qcom_geni_serial_setup_tx(struct uart_port *uport, u32 xmit_size) static void qcom_geni_serial_poll_tx_done(struct uart_port *uport) { int done; - u32 irq_clear = M_CMD_DONE_EN; done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, M_CMD_DONE_EN, true); if (!done) { writel(M_GENI_CMD_ABORT, uport->membase + SE_GENI_M_CMD_CTRL_REG); - irq_clear |= M_CMD_ABORT_EN; qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, M_CMD_ABORT_EN, true); + writel(M_CMD_ABORT_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); } - writel(irq_clear, uport->membase + SE_GENI_M_IRQ_CLEAR); } static void qcom_geni_serial_abort_rx(struct uart_port *uport) @@ -386,17 +402,44 @@ static int qcom_geni_serial_get_char(struct uart_port *uport) static void qcom_geni_serial_poll_put_char(struct uart_port *uport, unsigned char c) { - writel(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG); + if (qcom_geni_serial_main_active(uport)) { + qcom_geni_serial_poll_tx_done(uport); + __qcom_geni_serial_cancel_tx_cmd(uport); + } + + writel(M_CMD_DONE_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); qcom_geni_serial_setup_tx(uport, 1); - WARN_ON(!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_TX_FIFO_WATERMARK_EN, true)); writel(c, uport->membase + SE_GENI_TX_FIFOn); - writel(M_TX_FIFO_WATERMARK_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); qcom_geni_serial_poll_tx_done(uport); } + +static int qcom_geni_serial_poll_init(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + int ret; + + if (!port->setup) { + ret = qcom_geni_serial_port_setup(uport); + if (ret) + return ret; + } + + if (!qcom_geni_serial_secondary_active(uport)) + geni_se_setup_s_cmd(&port->se, UART_START_READ, 0); + + return 0; +} #endif #ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE +static void qcom_geni_serial_drain_fifo(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + + qcom_geni_serial_poll_bitfield(uport, SE_GENI_M_GP_LENGTH, GP_LENGTH, + port->tx_queued); +} + static void qcom_geni_serial_wr_char(struct uart_port *uport, unsigned char ch) { struct qcom_geni_private_data *private_data = uport->private_data; @@ -431,6 +474,7 @@ __qcom_geni_serial_console_write(struct uart_port *uport, const char *s, } writel(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG); + writel(M_CMD_DONE_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); qcom_geni_serial_setup_tx(uport, bytes_to_send); for (i = 0; i < count; ) { size_t chars_to_write = 0; @@ -469,10 +513,9 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, { struct uart_port *uport; struct qcom_geni_serial_port *port; + u32 m_irq_en, s_irq_en; bool locked = true; unsigned long flags; - u32 geni_status; - u32 irq_en; WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS); @@ -486,40 +529,28 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, else uart_port_lock_irqsave(uport, &flags); - geni_status = readl(uport->membase + SE_GENI_STATUS); - - if (!locked) { - /* - * We can only get here if an oops is in progress then we were - * unable to get the lock. This means we can't safely access - * our state variables like tx_remaining. About the best we - * can do is wait for the FIFO to be empty before we start our - * transfer, so we'll do that. - */ - qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_TX_FIFO_NOT_EMPTY_EN, false); - } else if ((geni_status & M_GENI_CMD_ACTIVE) && !port->tx_remaining) { - /* - * It seems we can't interrupt existing transfers if all data - * has been sent, in which case we need to look for done first. - */ - qcom_geni_serial_poll_tx_done(uport); - - if (!uart_circ_empty(&uport->state->xmit)) { - irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); - writel(irq_en | M_TX_FIFO_WATERMARK_EN, - uport->membase + SE_GENI_M_IRQ_EN); - } + m_irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); + s_irq_en = readl(uport->membase + SE_GENI_S_IRQ_EN); + writel(0, uport->membase + SE_GENI_M_IRQ_EN); + writel(0, uport->membase + SE_GENI_S_IRQ_EN); + + if (qcom_geni_serial_main_active(uport)) { + /* Wait for completion or drain FIFO */ + if (!locked || port->tx_remaining == 0) + qcom_geni_serial_poll_tx_done(uport); + else + qcom_geni_serial_drain_fifo(uport); + + qcom_geni_serial_cancel_tx_cmd(uport); } __qcom_geni_serial_console_write(uport, s, count); + writel(m_irq_en, uport->membase + SE_GENI_M_IRQ_EN); + writel(s_irq_en, uport->membase + SE_GENI_S_IRQ_EN); - if (locked) { - if (port->tx_remaining) - qcom_geni_serial_setup_tx(uport, port->tx_remaining); + if (locked) uart_port_unlock_irqrestore(uport, flags); - } } static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) @@ -565,7 +596,7 @@ static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) } #endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */ -static void handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop) +static void handle_rx_uart(struct uart_port *uport, u32 bytes) { struct qcom_geni_serial_port *port = to_dev_port(uport); struct tty_port *tport = &uport->state->port; @@ -573,9 +604,8 @@ static void handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop) ret = tty_insert_flip_string(tport, port->rx_buf, bytes); if (ret != bytes) { - dev_err(uport->dev, "%s:Unable to push data ret %d_bytes %d\n", - __func__, ret, bytes); - WARN_ON_ONCE(1); + dev_err_ratelimited(uport->dev, "failed to push data (%d < %u)\n", + ret, bytes); } uport->icount.rx += ret; tty_flip_buffer_push(tport); @@ -620,22 +650,24 @@ static void qcom_geni_serial_stop_tx_dma(struct uart_port *uport) static void qcom_geni_serial_start_tx_dma(struct uart_port *uport) { struct qcom_geni_serial_port *port = to_dev_port(uport); - struct circ_buf *xmit = &uport->state->xmit; + struct tty_port *tport = &uport->state->port; unsigned int xmit_size; + u8 *tail; int ret; if (port->tx_dma_addr) return; - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) return; - xmit_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + xmit_size = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, + UART_XMIT_SIZE); qcom_geni_serial_setup_tx(uport, xmit_size); - ret = geni_se_tx_dma_prep(&port->se, &xmit->buf[xmit->tail], - xmit_size, &port->tx_dma_addr); + ret = geni_se_tx_dma_prep(&port->se, tail, xmit_size, + &port->tx_dma_addr); if (ret) { dev_err(uport->dev, "unable to start TX SE DMA: %d\n", ret); qcom_geni_serial_stop_tx_dma(uport); @@ -647,15 +679,25 @@ static void qcom_geni_serial_start_tx_dma(struct uart_port *uport) static void qcom_geni_serial_start_tx_fifo(struct uart_port *uport) { + unsigned char c; u32 irq_en; - if (qcom_geni_serial_main_active(uport) || - !qcom_geni_serial_tx_empty(uport)) - return; + /* + * Start a new transfer in case the previous command was cancelled and + * left data in the FIFO which may prevent the watermark interrupt + * from triggering. Note that the stale data is discarded. + */ + if (!qcom_geni_serial_main_active(uport) && + !qcom_geni_serial_tx_empty(uport)) { + if (uart_fifo_out(uport, &c, 1) == 1) { + writel(M_CMD_DONE_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); + qcom_geni_serial_setup_tx(uport, 1); + writel(c, uport->membase + SE_GENI_TX_FIFOn); + } + } irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); irq_en |= M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN; - writel(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG); writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); } @@ -663,15 +705,16 @@ static void qcom_geni_serial_start_tx_fifo(struct uart_port *uport) static void qcom_geni_serial_stop_tx_fifo(struct uart_port *uport) { u32 irq_en; - struct qcom_geni_serial_port *port = to_dev_port(uport); irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); irq_en &= ~(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN); writel(0, uport->membase + SE_GENI_TX_WATERMARK_REG); writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN); - /* Possible stop tx is called multiple times. */ - if (!qcom_geni_serial_main_active(uport)) - return; +} + +static void __qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); geni_se_cancel_m_cmd(&port->se); if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, @@ -684,6 +727,19 @@ static void qcom_geni_serial_stop_tx_fifo(struct uart_port *uport) writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); } +static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport) +{ + struct qcom_geni_serial_port *port = to_dev_port(uport); + + if (!qcom_geni_serial_main_active(uport)) + return; + + __qcom_geni_serial_cancel_tx_cmd(uport); + + port->tx_remaining = 0; + port->tx_queued = 0; +} + static void qcom_geni_serial_handle_rx_fifo(struct uart_port *uport, bool drop) { u32 status; @@ -764,17 +820,27 @@ static void qcom_geni_serial_start_rx_fifo(struct uart_port *uport) static void qcom_geni_serial_stop_rx_dma(struct uart_port *uport) { struct qcom_geni_serial_port *port = to_dev_port(uport); + bool done; if (!qcom_geni_serial_secondary_active(uport)) return; geni_se_cancel_s_cmd(&port->se); - qcom_geni_serial_poll_bit(uport, SE_GENI_S_IRQ_STATUS, - S_CMD_CANCEL_EN, true); - - if (qcom_geni_serial_secondary_active(uport)) + done = qcom_geni_serial_poll_bit(uport, SE_DMA_RX_IRQ_STAT, + RX_EOT, true); + if (done) { + writel(RX_EOT | RX_DMA_DONE, + uport->membase + SE_DMA_RX_IRQ_CLR); + } else { qcom_geni_serial_abort_rx(uport); + writel(1, uport->membase + SE_DMA_RX_FSM_RST); + qcom_geni_serial_poll_bit(uport, SE_DMA_RX_IRQ_STAT, + RX_RESET_DONE, true); + writel(RX_RESET_DONE | RX_DMA_DONE, + uport->membase + SE_DMA_RX_IRQ_CLR); + } + if (port->rx_dma_addr) { geni_se_rx_dma_unprep(&port->se, port->rx_dma_addr, DMA_RX_BUF_SIZE); @@ -823,7 +889,7 @@ static void qcom_geni_serial_handle_rx_dma(struct uart_port *uport, bool drop) } if (!drop) - handle_rx_uart(uport, rx_in, drop); + handle_rx_uart(uport, rx_in); ret = geni_se_rx_dma_prep(&port->se, port->rx_buf, DMA_RX_BUF_SIZE, @@ -853,18 +919,14 @@ static void qcom_geni_serial_send_chunk_fifo(struct uart_port *uport, unsigned int chunk) { struct qcom_geni_serial_port *port = to_dev_port(uport); - struct circ_buf *xmit = &uport->state->xmit; - unsigned int tx_bytes, c, remaining = chunk; + unsigned int tx_bytes, remaining = chunk; u8 buf[BYTES_PER_FIFO_WORD]; while (remaining) { memset(buf, 0, sizeof(buf)); tx_bytes = min(remaining, BYTES_PER_FIFO_WORD); - for (c = 0; c < tx_bytes ; c++) { - buf[c] = xmit->buf[xmit->tail]; - uart_xmit_advance(uport, 1); - } + uart_fifo_out(uport, buf, tx_bytes); iowrite32_rep(uport->membase + SE_GENI_TX_FIFOn, buf, 1); @@ -877,7 +939,7 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport, bool done, bool active) { struct qcom_geni_serial_port *port = to_dev_port(uport); - struct circ_buf *xmit = &uport->state->xmit; + struct tty_port *tport = &uport->state->port; size_t avail; size_t pending; u32 status; @@ -890,24 +952,29 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport, if (active) pending = port->tx_remaining; else - pending = uart_circ_chars_pending(xmit); + pending = kfifo_len(&tport->xmit_fifo); - /* All data has been transmitted and acknowledged as received */ - if (!pending && !status && done) { + /* All data has been transmitted or command has been cancelled */ + if (!pending && done) { qcom_geni_serial_stop_tx_fifo(uport); goto out_write_wakeup; } - avail = port->tx_fifo_depth - (status & TX_FIFO_WC); + if (active) + avail = port->tx_fifo_depth - (status & TX_FIFO_WC); + else + avail = port->tx_fifo_depth; + avail *= BYTES_PER_FIFO_WORD; chunk = min(avail, pending); if (!chunk) goto out_write_wakeup; - if (!port->tx_remaining) { + if (!active) { qcom_geni_serial_setup_tx(uport, pending); port->tx_remaining = pending; + port->tx_queued = 0; irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); if (!(irq_en & M_TX_FIFO_WATERMARK_EN)) @@ -916,6 +983,7 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport, } qcom_geni_serial_send_chunk_fifo(uport, chunk); + port->tx_queued += chunk; /* * The tx fifo watermark is level triggered and latched. Though we had @@ -933,24 +1001,24 @@ out_write_wakeup: uport->membase + SE_GENI_M_IRQ_EN); } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(uport); } static void qcom_geni_serial_handle_tx_dma(struct uart_port *uport) { struct qcom_geni_serial_port *port = to_dev_port(uport); - struct circ_buf *xmit = &uport->state->xmit; + struct tty_port *tport = &uport->state->port; uart_xmit_advance(uport, port->tx_remaining); geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr, port->tx_remaining); port->tx_dma_addr = 0; port->tx_remaining = 0; - if (!uart_circ_empty(xmit)) + if (!kfifo_is_empty(&tport->xmit_fifo)) qcom_geni_serial_start_tx_dma(uport); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(uport); } @@ -1071,11 +1139,17 @@ static void qcom_geni_serial_shutdown(struct uart_port *uport) { disable_irq(uport->irq); - if (uart_console(uport)) - return; - + uart_port_lock_irq(uport); qcom_geni_serial_stop_tx(uport); qcom_geni_serial_stop_rx(uport); + + qcom_geni_serial_cancel_tx_cmd(uport); + uart_port_unlock_irq(uport); +} + +static void qcom_geni_serial_flush_buffer(struct uart_port *uport) +{ + qcom_geni_serial_cancel_tx_cmd(uport); } static int qcom_geni_serial_port_setup(struct uart_port *uport) @@ -1123,7 +1197,6 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport) false, true, true); geni_se_init(&port->se, UART_RX_WM, port->rx_fifo_depth - 2); geni_se_select_mode(&port->se, port->dev_data->mode); - qcom_geni_serial_start_rx(uport); port->setup = true; return 0; @@ -1139,6 +1212,11 @@ static int qcom_geni_serial_startup(struct uart_port *uport) if (ret) return ret; } + + uart_port_lock_irq(uport); + qcom_geni_serial_start_rx(uport); + uart_port_unlock_irq(uport); + enable_irq(uport->irq); return 0; @@ -1222,11 +1300,10 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, unsigned long clk_rate; u32 ver, sampling_rate; unsigned int avg_bw_core; + unsigned long timeout; - qcom_geni_serial_stop_rx(uport); /* baud rate */ baud = uart_get_baud_rate(uport, termios, old, 300, 4000000); - port->baud = baud; sampling_rate = UART_OVERSAMPLING; /* Sampling rate is halved for IP versions >= 2.5 */ @@ -1240,7 +1317,7 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, dev_err(port->se.dev, "Couldn't find suitable clock rate for %u\n", baud * sampling_rate); - goto out_restart_rx; + return; } dev_dbg(port->se.dev, "desired_rate = %u, clk_rate = %lu, clk_div = %u\n", @@ -1304,9 +1381,21 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, else tx_trans_cfg |= UART_CTS_MASK; - if (baud) + if (baud) { uart_update_timeout(uport, termios->c_cflag, baud); + /* + * Make sure that qcom_geni_serial_poll_bitfield() waits for + * the FIFO, two-word intermediate transfer register and shift + * register to clear. + * + * Note that uart_fifo_timeout() also adds a 20 ms margin. + */ + timeout = jiffies_to_usecs(uart_fifo_timeout(uport)); + timeout += 3 * timeout / port->tx_fifo_depth; + WRITE_ONCE(port->poll_timeout_us, timeout); + } + if (!uart_console(uport)) writel(port->loopback, uport->membase + SE_UART_LOOPBACK_CFG); @@ -1319,8 +1408,6 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, writel(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN); writel(ser_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG); writel(ser_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG); -out_restart_rx: - qcom_geni_serial_start_rx(uport); } #ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE @@ -1534,13 +1621,14 @@ static const struct uart_ops qcom_geni_console_pops = { .request_port = qcom_geni_serial_request_port, .config_port = qcom_geni_serial_config_port, .shutdown = qcom_geni_serial_shutdown, + .flush_buffer = qcom_geni_serial_flush_buffer, .type = qcom_geni_serial_get_type, .set_mctrl = qcom_geni_serial_set_mctrl, .get_mctrl = qcom_geni_serial_get_mctrl, #ifdef CONFIG_CONSOLE_POLL .poll_get_char = qcom_geni_serial_get_char, .poll_put_char = qcom_geni_serial_poll_put_char, - .poll_init = qcom_geni_serial_port_setup, + .poll_init = qcom_geni_serial_poll_init, #endif .pm = qcom_geni_serial_pm, }; @@ -1689,6 +1777,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) port->wakeup_irq); if (ret) { device_init_wakeup(&pdev->dev, false); + ida_free(&port_ida, uport->line); uart_remove_one_port(drv, uport); return ret; } @@ -1700,14 +1789,16 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) static void qcom_geni_serial_remove(struct platform_device *pdev) { struct qcom_geni_serial_port *port = platform_get_drvdata(pdev); + struct uart_port *uport = &port->uport; struct uart_driver *drv = port->private_data.drv; dev_pm_clear_wake_irq(&pdev->dev); device_init_wakeup(&pdev->dev, false); + ida_free(&port_ida, uport->line); uart_remove_one_port(drv, &port->uport); } -static int qcom_geni_serial_sys_suspend(struct device *dev) +static int qcom_geni_serial_suspend(struct device *dev) { struct qcom_geni_serial_port *port = dev_get_drvdata(dev); struct uart_port *uport = &port->uport; @@ -1724,7 +1815,7 @@ static int qcom_geni_serial_sys_suspend(struct device *dev) return uart_suspend_port(private_data->drv, uport); } -static int qcom_geni_serial_sys_resume(struct device *dev) +static int qcom_geni_serial_resume(struct device *dev) { int ret; struct qcom_geni_serial_port *port = dev_get_drvdata(dev); @@ -1739,38 +1830,6 @@ static int qcom_geni_serial_sys_resume(struct device *dev) return ret; } -static int qcom_geni_serial_sys_hib_resume(struct device *dev) -{ - int ret = 0; - struct uart_port *uport; - struct qcom_geni_private_data *private_data; - struct qcom_geni_serial_port *port = dev_get_drvdata(dev); - - uport = &port->uport; - private_data = uport->private_data; - - if (uart_console(uport)) { - geni_icc_set_tag(&port->se, QCOM_ICC_TAG_ALWAYS); - geni_icc_set_bw(&port->se); - ret = uart_resume_port(private_data->drv, uport); - /* - * For hibernation usecase clients for - * console UART won't call port setup during restore, - * hence call port setup for console uart. - */ - qcom_geni_serial_port_setup(uport); - } else { - /* - * Peripheral register settings are lost during hibernation. - * Update setup flag such that port setup happens again - * during next session. Clients of HS-UART will close and - * open the port during hibernation. - */ - port->setup = false; - } - return ret; -} - static const struct qcom_geni_device_data qcom_geni_console_data = { .console = true, .mode = GENI_SE_FIFO, @@ -1782,12 +1841,7 @@ static const struct qcom_geni_device_data qcom_geni_uart_data = { }; static const struct dev_pm_ops qcom_geni_serial_pm_ops = { - .suspend = pm_sleep_ptr(qcom_geni_serial_sys_suspend), - .resume = pm_sleep_ptr(qcom_geni_serial_sys_resume), - .freeze = pm_sleep_ptr(qcom_geni_serial_sys_suspend), - .poweroff = pm_sleep_ptr(qcom_geni_serial_sys_suspend), - .restore = pm_sleep_ptr(qcom_geni_serial_sys_hib_resume), - .thaw = pm_sleep_ptr(qcom_geni_serial_sys_hib_resume), + SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_suspend, qcom_geni_serial_resume) }; static const struct of_device_id qcom_geni_serial_match_table[] = { @@ -1804,7 +1858,7 @@ static const struct of_device_id qcom_geni_serial_match_table[] = { MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table); static struct platform_driver qcom_geni_serial_platform_driver = { - .remove_new = qcom_geni_serial_remove, + .remove = qcom_geni_serial_remove, .probe = qcom_geni_serial_probe, .driver = { .name = "qcom_geni_serial", diff --git a/drivers/tty/serial/rda-uart.c b/drivers/tty/serial/rda-uart.c index 82def9b8632a..87fa30d68687 100644 --- a/drivers/tty/serial/rda-uart.c +++ b/drivers/tty/serial/rda-uart.c @@ -330,8 +330,8 @@ static void rda_uart_set_termios(struct uart_port *port, static void rda_uart_send_chars(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; - unsigned int ch; + struct tty_port *tport = &port->state->port; + unsigned char ch; u32 val; if (uart_tx_stopped(port)) @@ -347,19 +347,14 @@ static void rda_uart_send_chars(struct uart_port *port) port->x_char = 0; } - while (rda_uart_read(port, RDA_UART_STATUS) & RDA_UART_TX_FIFO_MASK) { - if (uart_circ_empty(xmit)) - break; - - ch = xmit->buf[xmit->tail]; + while ((rda_uart_read(port, RDA_UART_STATUS) & RDA_UART_TX_FIFO_MASK) && + uart_fifo_get(port, &ch)) rda_uart_write(port, ch, RDA_UART_RXTX_BUFFER); - uart_xmit_advance(port, 1); - } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (!uart_circ_empty(xmit)) { + if (!kfifo_is_empty(&tport->xmit_fifo)) { /* Re-enable Tx FIFO interrupt */ val = rda_uart_read(port, RDA_UART_IRQ_MASK); val |= RDA_UART_TX_DATA_NEEDED; @@ -782,7 +777,7 @@ static void rda_uart_remove(struct platform_device *pdev) static struct platform_driver rda_uart_platform_driver = { .probe = rda_uart_probe, - .remove_new = rda_uart_remove, + .remove = rda_uart_remove, .driver = { .name = "rda-uart", .of_match_table = rda_uart_dt_matches, diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c index 4132fcff7d4e..6d99a02dd439 100644 --- a/drivers/tty/serial/rp2.c +++ b/drivers/tty/serial/rp2.c @@ -577,8 +577,8 @@ static void rp2_reset_asic(struct rp2_card *card, unsigned int asic_id) u32 clk_cfg; writew(1, base + RP2_GLOBAL_CMD); - readw(base + RP2_GLOBAL_CMD); msleep(100); + readw(base + RP2_GLOBAL_CMD); writel(0, base + RP2_CLK_PRESCALER); /* TDM clock configuration */ @@ -698,7 +698,6 @@ static int rp2_probe(struct pci_dev *pdev, const struct firmware *fw; struct rp2_card *card; struct rp2_uart_port *ports; - void __iomem * const *bars; int rc; card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL); @@ -711,13 +710,16 @@ static int rp2_probe(struct pci_dev *pdev, if (rc) return rc; - rc = pcim_iomap_regions_request_all(pdev, 0x03, DRV_NAME); + rc = pcim_request_all_regions(pdev, DRV_NAME); if (rc) return rc; - bars = pcim_iomap_table(pdev); - card->bar0 = bars[0]; - card->bar1 = bars[1]; + card->bar0 = pcim_iomap(pdev, 0, 0); + if (!card->bar0) + return -ENOMEM; + card->bar1 = pcim_iomap(pdev, 1, 0); + if (!card->bar1) + return -ENOMEM; card->pdev = pdev; rp2_decode_cap(id, &card->n_ports, &card->smpte); diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c index 79c794fa6545..72b1bb76415c 100644 --- a/drivers/tty/serial/sa1100.c +++ b/drivers/tty/serial/sa1100.c @@ -111,7 +111,7 @@ static void sa1100_mctrl_check(struct sa1100_port *sport) */ static void sa1100_timeout(struct timer_list *t) { - struct sa1100_port *sport = from_timer(sport, t, timer); + struct sa1100_port *sport = timer_container_of(sport, t, timer); unsigned long flags; if (sport->port.state) { @@ -369,7 +369,7 @@ static void sa1100_shutdown(struct uart_port *port) /* * Stop our timer. */ - del_timer_sync(&sport->timer); + timer_delete_sync(&sport->timer); /* * Free the interrupt @@ -421,7 +421,7 @@ sa1100_set_termios(struct uart_port *port, struct ktermios *termios, baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); quot = uart_get_divisor(port, baud); - del_timer_sync(&sport->timer); + timer_delete_sync(&sport->timer); uart_port_lock_irqsave(&sport->port, &flags); @@ -880,7 +880,7 @@ static void sa1100_serial_remove(struct platform_device *pdev) static struct platform_driver sa11x0_serial_driver = { .probe = sa1100_serial_probe, - .remove_new = sa1100_serial_remove, + .remove = sa1100_serial_remove, .suspend = sa1100_serial_suspend, .resume = sa1100_serial_resume, .driver = { diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c index a2d07e05c502..2fb58c626daf 100644 --- a/drivers/tty/serial/samsung_tty.c +++ b/drivers/tty/serial/samsung_tty.c @@ -52,7 +52,7 @@ #define S3C24XX_SERIAL_MINOR 64 #ifdef CONFIG_ARM64 -#define UART_NR 12 +#define UART_NR 18 #else #define UART_NR CONFIG_SERIAL_SAMSUNG_UARTS #endif @@ -190,6 +190,8 @@ static void wr_reg(const struct uart_port *port, u32 reg, u32 val) case UPIO_MEM32: writel_relaxed(val, portaddr(port, reg)); break; + default: + break; } } @@ -329,7 +331,7 @@ static void s3c24xx_serial_tx_dma_complete(void *args) { struct s3c24xx_uart_port *ourport = args; struct uart_port *port = &ourport->port; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; struct s3c24xx_uart_dma *dma = ourport->dma; struct dma_tx_state state; unsigned long flags; @@ -348,7 +350,7 @@ static void s3c24xx_serial_tx_dma_complete(void *args) uart_xmit_advance(port, count); ourport->tx_in_progress = 0; - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); s3c24xx_serial_start_next_tx(ourport); @@ -431,17 +433,15 @@ static void s3c24xx_serial_start_tx_pio(struct s3c24xx_uart_port *ourport) } static int s3c24xx_serial_start_tx_dma(struct s3c24xx_uart_port *ourport, - unsigned int count) + unsigned int count, unsigned int tail) { - struct uart_port *port = &ourport->port; - struct circ_buf *xmit = &port->state->xmit; struct s3c24xx_uart_dma *dma = ourport->dma; if (ourport->tx_mode != S3C24XX_TX_DMA) enable_tx_dma(ourport); dma->tx_size = count & ~(dma_get_cache_alignment() - 1); - dma->tx_transfer_addr = dma->tx_addr + xmit->tail; + dma->tx_transfer_addr = dma->tx_addr + tail; dma_sync_single_for_device(dma->tx_chan->device->dev, dma->tx_transfer_addr, dma->tx_size, @@ -468,11 +468,11 @@ static int s3c24xx_serial_start_tx_dma(struct s3c24xx_uart_port *ourport, static void s3c24xx_serial_start_next_tx(struct s3c24xx_uart_port *ourport) { struct uart_port *port = &ourport->port; - struct circ_buf *xmit = &port->state->xmit; - unsigned long count; + struct tty_port *tport = &port->state->port; + unsigned int count, tail; /* Get data size up to the end of buffer */ - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + count = kfifo_out_linear(&tport->xmit_fifo, &tail, UART_XMIT_SIZE); if (!count) { s3c24xx_serial_stop_tx(port); @@ -481,16 +481,16 @@ static void s3c24xx_serial_start_next_tx(struct s3c24xx_uart_port *ourport) if (!ourport->dma || !ourport->dma->tx_chan || count < ourport->min_dma_size || - xmit->tail & (dma_get_cache_alignment() - 1)) + tail & (dma_get_cache_alignment() - 1)) s3c24xx_serial_start_tx_pio(ourport); else - s3c24xx_serial_start_tx_dma(ourport, count); + s3c24xx_serial_start_tx_dma(ourport, count, tail); } static void s3c24xx_serial_start_tx(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; if (!ourport->tx_enabled) { if (port->flags & UPF_CONS_FLOW) @@ -502,7 +502,8 @@ static void s3c24xx_serial_start_tx(struct uart_port *port) } if (ourport->dma && ourport->dma->tx_chan) { - if (!uart_circ_empty(xmit) && !ourport->tx_in_progress) + if (!kfifo_is_empty(&tport->xmit_fifo) && + !ourport->tx_in_progress) s3c24xx_serial_start_next_tx(ourport); } } @@ -551,6 +552,7 @@ static void s3c24xx_serial_stop_rx(struct uart_port *port) case TYPE_APPLE_S5L: s3c24xx_clear_bit(port, APPLE_S5L_UCON_RXTHRESH_ENA, S3C2410_UCON); s3c24xx_clear_bit(port, APPLE_S5L_UCON_RXTO_ENA, S3C2410_UCON); + s3c24xx_clear_bit(port, APPLE_S5L_UCON_RXTO_LEGACY_ENA, S3C2410_UCON); break; default: disable_irq_nosync(ourport->rx_irq); @@ -708,9 +710,8 @@ static void enable_rx_pio(struct s3c24xx_uart_port *ourport) static void s3c24xx_serial_rx_drain_fifo(struct s3c24xx_uart_port *ourport); -static irqreturn_t s3c24xx_serial_rx_chars_dma(void *dev_id) +static irqreturn_t s3c24xx_serial_rx_chars_dma(struct s3c24xx_uart_port *ourport) { - struct s3c24xx_uart_port *ourport = dev_id; struct uart_port *port = &ourport->port; struct s3c24xx_uart_dma *dma = ourport->dma; struct tty_struct *tty = tty_port_tty_get(&ourport->port.state->port); @@ -844,9 +845,8 @@ static void s3c24xx_serial_rx_drain_fifo(struct s3c24xx_uart_port *ourport) tty_flip_buffer_push(&port->state->port); } -static irqreturn_t s3c24xx_serial_rx_chars_pio(void *dev_id) +static irqreturn_t s3c24xx_serial_rx_chars_pio(struct s3c24xx_uart_port *ourport) { - struct s3c24xx_uart_port *ourport = dev_id; struct uart_port *port = &ourport->port; uart_port_lock(port); @@ -856,30 +856,29 @@ static irqreturn_t s3c24xx_serial_rx_chars_pio(void *dev_id) return IRQ_HANDLED; } -static irqreturn_t s3c24xx_serial_rx_irq(int irq, void *dev_id) +static irqreturn_t s3c24xx_serial_rx_irq(struct s3c24xx_uart_port *ourport) { - struct s3c24xx_uart_port *ourport = dev_id; - if (ourport->dma && ourport->dma->rx_chan) - return s3c24xx_serial_rx_chars_dma(dev_id); - return s3c24xx_serial_rx_chars_pio(dev_id); + return s3c24xx_serial_rx_chars_dma(ourport); + return s3c24xx_serial_rx_chars_pio(ourport); } static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport) { struct uart_port *port = &ourport->port; - struct circ_buf *xmit = &port->state->xmit; - int count, dma_count = 0; + struct tty_port *tport = &port->state->port; + unsigned int count, dma_count = 0, tail; - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + count = kfifo_out_linear(&tport->xmit_fifo, &tail, UART_XMIT_SIZE); if (ourport->dma && ourport->dma->tx_chan && count >= ourport->min_dma_size) { int align = dma_get_cache_alignment() - - (xmit->tail & (dma_get_cache_alignment() - 1)); + (tail & (dma_get_cache_alignment() - 1)); if (count - align >= ourport->min_dma_size) { dma_count = count - align; count = align; + tail += align; } } @@ -894,7 +893,7 @@ static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport) * stopped, disable the uart and exit */ - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { s3c24xx_serial_stop_tx(port); return; } @@ -906,30 +905,30 @@ static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport) dma_count = 0; } - while (!uart_circ_empty(xmit) && count > 0) { - if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull) + while (!(rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)) { + unsigned char ch; + + if (!uart_fifo_get(port, &ch)) break; - wr_reg(port, S3C2410_UTXH, xmit->buf[xmit->tail]); - uart_xmit_advance(port, 1); + wr_reg(port, S3C2410_UTXH, ch); count--; } if (!count && dma_count) { - s3c24xx_serial_start_tx_dma(ourport, dma_count); + s3c24xx_serial_start_tx_dma(ourport, dma_count, tail); return; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) s3c24xx_serial_stop_tx(port); } -static irqreturn_t s3c24xx_serial_tx_irq(int irq, void *id) +static irqreturn_t s3c24xx_serial_tx_irq(struct s3c24xx_uart_port *ourport) { - struct s3c24xx_uart_port *ourport = id; struct uart_port *port = &ourport->port; uart_port_lock(port); @@ -943,17 +942,17 @@ static irqreturn_t s3c24xx_serial_tx_irq(int irq, void *id) /* interrupt handler for s3c64xx and later SoC's.*/ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id) { - const struct s3c24xx_uart_port *ourport = id; + struct s3c24xx_uart_port *ourport = id; const struct uart_port *port = &ourport->port; u32 pend = rd_regl(port, S3C64XX_UINTP); irqreturn_t ret = IRQ_HANDLED; if (pend & S3C64XX_UINTM_RXD_MSK) { - ret = s3c24xx_serial_rx_irq(irq, id); + ret = s3c24xx_serial_rx_irq(ourport); wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_RXD_MSK); } if (pend & S3C64XX_UINTM_TXD_MSK) { - ret = s3c24xx_serial_tx_irq(irq, id); + ret = s3c24xx_serial_tx_irq(ourport); wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_TXD_MSK); } return ret; @@ -962,19 +961,21 @@ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id) /* interrupt handler for Apple SoC's.*/ static irqreturn_t apple_serial_handle_irq(int irq, void *id) { - const struct s3c24xx_uart_port *ourport = id; + struct s3c24xx_uart_port *ourport = id; const struct uart_port *port = &ourport->port; u32 pend = rd_regl(port, S3C2410_UTRSTAT); irqreturn_t ret = IRQ_NONE; - if (pend & (APPLE_S5L_UTRSTAT_RXTHRESH | APPLE_S5L_UTRSTAT_RXTO)) { + if (pend & (APPLE_S5L_UTRSTAT_RXTHRESH | APPLE_S5L_UTRSTAT_RXTO | + APPLE_S5L_UTRSTAT_RXTO_LEGACY)) { wr_regl(port, S3C2410_UTRSTAT, - APPLE_S5L_UTRSTAT_RXTHRESH | APPLE_S5L_UTRSTAT_RXTO); - ret = s3c24xx_serial_rx_irq(irq, id); + APPLE_S5L_UTRSTAT_RXTHRESH | APPLE_S5L_UTRSTAT_RXTO | + APPLE_S5L_UTRSTAT_RXTO_LEGACY); + ret = s3c24xx_serial_rx_irq(ourport); } if (pend & APPLE_S5L_UTRSTAT_TXTHRESH) { wr_regl(port, S3C2410_UTRSTAT, APPLE_S5L_UTRSTAT_TXTHRESH); - ret = s3c24xx_serial_tx_irq(irq, id); + ret = s3c24xx_serial_tx_irq(ourport); } return ret; @@ -1118,7 +1119,8 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p) /* TX buffer */ dma->tx_addr = dma_map_single(dma->tx_chan->device->dev, - p->port.state->xmit.buf, UART_XMIT_SIZE, + p->port.state->port.xmit_buf, + UART_XMIT_SIZE, DMA_TO_DEVICE); if (dma_mapping_error(dma->tx_chan->device->dev, dma->tx_addr)) { reason = "DMA mapping error for TX buffer"; @@ -1193,7 +1195,8 @@ static void apple_s5l_serial_shutdown(struct uart_port *port) ucon = rd_regl(port, S3C2410_UCON); ucon &= ~(APPLE_S5L_UCON_TXTHRESH_ENA_MSK | APPLE_S5L_UCON_RXTHRESH_ENA_MSK | - APPLE_S5L_UCON_RXTO_ENA_MSK); + APPLE_S5L_UCON_RXTO_ENA_MSK | + APPLE_S5L_UCON_RXTO_LEGACY_ENA_MSK); wr_regl(port, S3C2410_UCON, ucon); wr_regl(port, S3C2410_UTRSTAT, APPLE_S5L_UTRSTAT_ALL_FLAGS); @@ -1290,6 +1293,7 @@ static int apple_s5l_serial_startup(struct uart_port *port) /* Enable Rx Interrupt */ s3c24xx_set_bit(port, APPLE_S5L_UCON_RXTHRESH_ENA, S3C2410_UCON); s3c24xx_set_bit(port, APPLE_S5L_UCON_RXTO_ENA, S3C2410_UCON); + s3c24xx_set_bit(port, APPLE_S5L_UCON_RXTO_LEGACY_ENA, S3C2410_UCON); return ret; } @@ -2146,13 +2150,15 @@ static int s3c24xx_serial_resume_noirq(struct device *dev) ucon &= ~(APPLE_S5L_UCON_TXTHRESH_ENA_MSK | APPLE_S5L_UCON_RXTHRESH_ENA_MSK | - APPLE_S5L_UCON_RXTO_ENA_MSK); + APPLE_S5L_UCON_RXTO_ENA_MSK | + APPLE_S5L_UCON_RXTO_LEGACY_ENA_MSK); if (ourport->tx_enabled) ucon |= APPLE_S5L_UCON_TXTHRESH_ENA_MSK; if (ourport->rx_enabled) ucon |= APPLE_S5L_UCON_RXTHRESH_ENA_MSK | - APPLE_S5L_UCON_RXTO_ENA_MSK; + APPLE_S5L_UCON_RXTO_ENA_MSK | + APPLE_S5L_UCON_RXTO_LEGACY_ENA_MSK; wr_regl(port, S3C2410_UCON, ucon); @@ -2494,6 +2500,12 @@ static const struct s3c24xx_serial_drv_data exynos850_serial_drv_data = { .fifosize = { 256, 64, 64, 64 }, }; +static const struct s3c24xx_serial_drv_data exynos8895_serial_drv_data = { + EXYNOS_COMMON_SERIAL_DRV_DATA, + /* samsung,uart-fifosize must be specified in the device tree. */ + .fifosize = { 0 }, +}; + static const struct s3c24xx_serial_drv_data gs101_serial_drv_data = { .info = { .name = "Google GS101 UART", @@ -2524,12 +2536,14 @@ static const struct s3c24xx_serial_drv_data gs101_serial_drv_data = { #define EXYNOS4210_SERIAL_DRV_DATA (&exynos4210_serial_drv_data) #define EXYNOS5433_SERIAL_DRV_DATA (&exynos5433_serial_drv_data) #define EXYNOS850_SERIAL_DRV_DATA (&exynos850_serial_drv_data) +#define EXYNOS8895_SERIAL_DRV_DATA (&exynos8895_serial_drv_data) #define GS101_SERIAL_DRV_DATA (&gs101_serial_drv_data) #else #define EXYNOS4210_SERIAL_DRV_DATA NULL #define EXYNOS5433_SERIAL_DRV_DATA NULL #define EXYNOS850_SERIAL_DRV_DATA NULL +#define EXYNOS8895_SERIAL_DRV_DATA NULL #define GS101_SERIAL_DRV_DATA NULL #endif @@ -2539,7 +2553,7 @@ static const struct s3c24xx_serial_drv_data s5l_serial_drv_data = { .name = "Apple S5L UART", .type = TYPE_APPLE_S5L, .port_type = PORT_8250, - .iotype = UPIO_MEM, + .iotype = UPIO_MEM32, .fifosize = 16, .rx_fifomask = S3C2410_UFSTAT_RXMASK, .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT, @@ -2619,6 +2633,9 @@ static const struct platform_device_id s3c24xx_serial_driver_ids[] = { }, { .name = "gs101-uart", .driver_data = (kernel_ulong_t)GS101_SERIAL_DRV_DATA, + }, { + .name = "exynos8895-uart", + .driver_data = (kernel_ulong_t)EXYNOS8895_SERIAL_DRV_DATA, }, { }, }; @@ -2642,6 +2659,8 @@ static const struct of_device_id s3c24xx_uart_dt_match[] = { .data = ARTPEC8_SERIAL_DRV_DATA }, { .compatible = "google,gs101-uart", .data = GS101_SERIAL_DRV_DATA }, + { .compatible = "samsung,exynos8895-uart", + .data = EXYNOS8895_SERIAL_DRV_DATA }, {}, }; MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match); @@ -2649,7 +2668,7 @@ MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match); static struct platform_driver samsung_serial_driver = { .probe = s3c24xx_serial_probe, - .remove_new = s3c24xx_serial_remove, + .remove = s3c24xx_serial_remove, .id_table = s3c24xx_serial_driver_ids, .driver = { .name = "samsung-uart", @@ -2696,6 +2715,8 @@ static void wr_reg_barrier(const struct uart_port *port, u32 reg, u32 val) case UPIO_MEM32: writel(val, portaddr(port, reg)); break; + default: + break; } } @@ -2825,6 +2846,9 @@ OF_EARLYCON_DECLARE(gs101, "google,gs101-uart", gs101_early_console_setup); static int __init apple_s5l_early_console_setup(struct earlycon_device *device, const char *opt) { + /* Apple A7-A11 requires MMIO32 register accesses. */ + device->port.iotype = UPIO_MEM32; + /* Close enough to S3C2410 for earlycon... */ device->port.private_data = &s3c2410_early_console_data; diff --git a/drivers/tty/serial/sb1250-duart.c b/drivers/tty/serial/sb1250-duart.c index dbec29d9a6c3..b4e1b90e5960 100644 --- a/drivers/tty/serial/sb1250-duart.c +++ b/drivers/tty/serial/sb1250-duart.c @@ -382,7 +382,8 @@ static void sbd_receive_chars(struct sbd_port *sport) static void sbd_transmit_chars(struct sbd_port *sport) { struct uart_port *uport = &sport->port; - struct circ_buf *xmit = &sport->port.state->xmit; + struct tty_port *tport = &sport->port.state->port; + unsigned char ch; unsigned int mask; int stop_tx; @@ -395,19 +396,19 @@ static void sbd_transmit_chars(struct sbd_port *sport) } /* If nothing to do or stopped or hardware stopped. */ - stop_tx = (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)); + stop_tx = uart_tx_stopped(&sport->port) || + !uart_fifo_get(&sport->port, &ch); /* Send char. */ if (!stop_tx) { - write_sbdchn(sport, R_DUART_TX_HOLD, xmit->buf[xmit->tail]); - uart_xmit_advance(&sport->port, 1); + write_sbdchn(sport, R_DUART_TX_HOLD, ch); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&sport->port); } /* Are we are done? */ - if (stop_tx || uart_circ_empty(xmit)) { + if (stop_tx || kfifo_is_empty(&tport->xmit_fifo)) { /* Disable tx interrupts. */ mask = read_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2)); mask &= ~M_DUART_IMR_TX; diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 929206a9a6e1..5ea8aadb6e69 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1,35 +1,40 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * SC16IS7xx tty serial driver - Copyright (C) 2014 GridPoint - * Author: Jon Ringle <jringle@gridpoint.com> + * SC16IS7xx tty serial driver - common code * - * Based on max310x.c, by Alexander Shiyan <shc_work@mail.ru> + * Copyright (C) 2014 GridPoint + * Author: Jon Ringle <jringle@gridpoint.com> + * Based on max310x.c, by Alexander Shiyan <shc_work@mail.ru> */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#undef DEFAULT_SYMBOL_NAMESPACE +#define DEFAULT_SYMBOL_NAMESPACE "SERIAL_NXP_SC16IS7XX" -#include <linux/bitops.h> +#include <linux/bits.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/export.h> +#include <linux/gpio/consumer.h> #include <linux/gpio/driver.h> -#include <linux/i2c.h> +#include <linux/idr.h> +#include <linux/kthread.h> #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/property.h> #include <linux/regmap.h> +#include <linux/sched.h> #include <linux/serial_core.h> #include <linux/serial.h> +#include <linux/string.h> #include <linux/tty.h> #include <linux/tty_flip.h> -#include <linux/spi/spi.h> #include <linux/uaccess.h> #include <linux/units.h> -#include <uapi/linux/sched/types.h> -#define SC16IS7XX_NAME "sc16is7xx" +#include "sc16is7xx.h" + #define SC16IS7XX_MAX_DEVS 8 -#define SC16IS7XX_MAX_PORTS 2 /* Maximum number of UART ports per IC. */ /* SC16IS7XX register definitions */ #define SC16IS7XX_RHR_REG (0x00) /* RX FIFO */ @@ -74,52 +79,52 @@ #define SC16IS7XX_XOFF2_REG (0x07) /* Xoff2 word */ /* IER register bits */ -#define SC16IS7XX_IER_RDI_BIT (1 << 0) /* Enable RX data interrupt */ -#define SC16IS7XX_IER_THRI_BIT (1 << 1) /* Enable TX holding register +#define SC16IS7XX_IER_RDI_BIT BIT(0) /* Enable RX data interrupt */ +#define SC16IS7XX_IER_THRI_BIT BIT(1) /* Enable TX holding register * interrupt */ -#define SC16IS7XX_IER_RLSI_BIT (1 << 2) /* Enable RX line status +#define SC16IS7XX_IER_RLSI_BIT BIT(2) /* Enable RX line status * interrupt */ -#define SC16IS7XX_IER_MSI_BIT (1 << 3) /* Enable Modem status +#define SC16IS7XX_IER_MSI_BIT BIT(3) /* Enable Modem status * interrupt */ /* IER register bits - write only if (EFR[4] == 1) */ -#define SC16IS7XX_IER_SLEEP_BIT (1 << 4) /* Enable Sleep mode */ -#define SC16IS7XX_IER_XOFFI_BIT (1 << 5) /* Enable Xoff interrupt */ -#define SC16IS7XX_IER_RTSI_BIT (1 << 6) /* Enable nRTS interrupt */ -#define SC16IS7XX_IER_CTSI_BIT (1 << 7) /* Enable nCTS interrupt */ +#define SC16IS7XX_IER_SLEEP_BIT BIT(4) /* Enable Sleep mode */ +#define SC16IS7XX_IER_XOFFI_BIT BIT(5) /* Enable Xoff interrupt */ +#define SC16IS7XX_IER_RTSI_BIT BIT(6) /* Enable nRTS interrupt */ +#define SC16IS7XX_IER_CTSI_BIT BIT(7) /* Enable nCTS interrupt */ /* FCR register bits */ -#define SC16IS7XX_FCR_FIFO_BIT (1 << 0) /* Enable FIFO */ -#define SC16IS7XX_FCR_RXRESET_BIT (1 << 1) /* Reset RX FIFO */ -#define SC16IS7XX_FCR_TXRESET_BIT (1 << 2) /* Reset TX FIFO */ -#define SC16IS7XX_FCR_RXLVLL_BIT (1 << 6) /* RX Trigger level LSB */ -#define SC16IS7XX_FCR_RXLVLH_BIT (1 << 7) /* RX Trigger level MSB */ +#define SC16IS7XX_FCR_FIFO_BIT BIT(0) /* Enable FIFO */ +#define SC16IS7XX_FCR_RXRESET_BIT BIT(1) /* Reset RX FIFO */ +#define SC16IS7XX_FCR_TXRESET_BIT BIT(2) /* Reset TX FIFO */ +#define SC16IS7XX_FCR_RXLVLL_BIT BIT(6) /* RX Trigger level LSB */ +#define SC16IS7XX_FCR_RXLVLH_BIT BIT(7) /* RX Trigger level MSB */ /* FCR register bits - write only if (EFR[4] == 1) */ -#define SC16IS7XX_FCR_TXLVLL_BIT (1 << 4) /* TX Trigger level LSB */ -#define SC16IS7XX_FCR_TXLVLH_BIT (1 << 5) /* TX Trigger level MSB */ +#define SC16IS7XX_FCR_TXLVLL_BIT BIT(4) /* TX Trigger level LSB */ +#define SC16IS7XX_FCR_TXLVLH_BIT BIT(5) /* TX Trigger level MSB */ /* IIR register bits */ -#define SC16IS7XX_IIR_NO_INT_BIT (1 << 0) /* No interrupts pending */ -#define SC16IS7XX_IIR_ID_MASK 0x3e /* Mask for the interrupt ID */ -#define SC16IS7XX_IIR_THRI_SRC 0x02 /* TX holding register empty */ -#define SC16IS7XX_IIR_RDI_SRC 0x04 /* RX data interrupt */ -#define SC16IS7XX_IIR_RLSE_SRC 0x06 /* RX line status error */ -#define SC16IS7XX_IIR_RTOI_SRC 0x0c /* RX time-out interrupt */ -#define SC16IS7XX_IIR_MSI_SRC 0x00 /* Modem status interrupt - * - only on 75x/76x - */ -#define SC16IS7XX_IIR_INPIN_SRC 0x30 /* Input pin change of state - * - only on 75x/76x - */ -#define SC16IS7XX_IIR_XOFFI_SRC 0x10 /* Received Xoff */ -#define SC16IS7XX_IIR_CTSRTS_SRC 0x20 /* nCTS,nRTS change of state - * from active (LOW) - * to inactive (HIGH) - */ +#define SC16IS7XX_IIR_NO_INT_BIT 0x01 /* No interrupts pending */ +#define SC16IS7XX_IIR_ID_MASK GENMASK(5, 1) /* Mask for the interrupt ID */ +#define SC16IS7XX_IIR_THRI_SRC 0x02 /* TX holding register empty */ +#define SC16IS7XX_IIR_RDI_SRC 0x04 /* RX data interrupt */ +#define SC16IS7XX_IIR_RLSE_SRC 0x06 /* RX line status error */ +#define SC16IS7XX_IIR_RTOI_SRC 0x0c /* RX time-out interrupt */ +#define SC16IS7XX_IIR_MSI_SRC 0x00 /* Modem status interrupt + * - only on 75x/76x + */ +#define SC16IS7XX_IIR_INPIN_SRC 0x30 /* Input pin change of state + * - only on 75x/76x + */ +#define SC16IS7XX_IIR_XOFFI_SRC 0x10 /* Received Xoff */ +#define SC16IS7XX_IIR_CTSRTS_SRC 0x20 /* nCTS,nRTS change of state + * from active (LOW) + * to inactive (HIGH) + */ /* LCR register bits */ -#define SC16IS7XX_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */ -#define SC16IS7XX_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1 +#define SC16IS7XX_LCR_LENGTH0_BIT BIT(0) /* Word length bit 0 */ +#define SC16IS7XX_LCR_LENGTH1_BIT BIT(1) /* Word length bit 1 * * Word length bits table: * 00 -> 5 bit words @@ -127,7 +132,7 @@ * 10 -> 7 bit words * 11 -> 8 bit words */ -#define SC16IS7XX_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit +#define SC16IS7XX_LCR_STOPLEN_BIT BIT(2) /* STOP length bit * * STOP length bit table: * 0 -> 1 stop bit @@ -135,11 +140,11 @@ * word length is 5, * 2 stop bits otherwise */ -#define SC16IS7XX_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */ -#define SC16IS7XX_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */ -#define SC16IS7XX_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */ -#define SC16IS7XX_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */ -#define SC16IS7XX_LCR_DLAB_BIT (1 << 7) /* Divisor Latch enable */ +#define SC16IS7XX_LCR_PARITY_BIT BIT(3) /* Parity bit enable */ +#define SC16IS7XX_LCR_EVENPARITY_BIT BIT(4) /* Even parity bit enable */ +#define SC16IS7XX_LCR_FORCEPARITY_BIT BIT(5) /* 9-bit multidrop parity */ +#define SC16IS7XX_LCR_TXBREAK_BIT BIT(6) /* TX break enable */ +#define SC16IS7XX_LCR_DLAB_BIT BIT(7) /* Divisor Latch enable */ #define SC16IS7XX_LCR_WORD_LEN_5 (0x00) #define SC16IS7XX_LCR_WORD_LEN_6 (0x01) #define SC16IS7XX_LCR_WORD_LEN_7 (0x02) @@ -150,61 +155,65 @@ * reg set */ /* MCR register bits */ -#define SC16IS7XX_MCR_DTR_BIT (1 << 0) /* DTR complement +#define SC16IS7XX_MCR_DTR_BIT BIT(0) /* DTR complement * - only on 75x/76x */ -#define SC16IS7XX_MCR_RTS_BIT (1 << 1) /* RTS complement */ -#define SC16IS7XX_MCR_TCRTLR_BIT (1 << 2) /* TCR/TLR register enable */ -#define SC16IS7XX_MCR_LOOP_BIT (1 << 4) /* Enable loopback test mode */ -#define SC16IS7XX_MCR_XONANY_BIT (1 << 5) /* Enable Xon Any +#define SC16IS7XX_MCR_RTS_BIT BIT(1) /* RTS complement */ +#define SC16IS7XX_MCR_TCRTLR_BIT BIT(2) /* TCR/TLR register enable */ +#define SC16IS7XX_MCR_LOOP_BIT BIT(4) /* Enable loopback test mode */ +#define SC16IS7XX_MCR_XONANY_BIT BIT(5) /* Enable Xon Any * - write enabled * if (EFR[4] == 1) */ -#define SC16IS7XX_MCR_IRDA_BIT (1 << 6) /* Enable IrDA mode +#define SC16IS7XX_MCR_IRDA_BIT BIT(6) /* Enable IrDA mode * - write enabled * if (EFR[4] == 1) */ -#define SC16IS7XX_MCR_CLKSEL_BIT (1 << 7) /* Divide clock by 4 +#define SC16IS7XX_MCR_CLKSEL_BIT BIT(7) /* Divide clock by 4 * - write enabled * if (EFR[4] == 1) */ /* LSR register bits */ -#define SC16IS7XX_LSR_DR_BIT (1 << 0) /* Receiver data ready */ -#define SC16IS7XX_LSR_OE_BIT (1 << 1) /* Overrun Error */ -#define SC16IS7XX_LSR_PE_BIT (1 << 2) /* Parity Error */ -#define SC16IS7XX_LSR_FE_BIT (1 << 3) /* Frame Error */ -#define SC16IS7XX_LSR_BI_BIT (1 << 4) /* Break Interrupt */ -#define SC16IS7XX_LSR_BRK_ERROR_MASK 0x1E /* BI, FE, PE, OE bits */ -#define SC16IS7XX_LSR_THRE_BIT (1 << 5) /* TX holding register empty */ -#define SC16IS7XX_LSR_TEMT_BIT (1 << 6) /* Transmitter empty */ -#define SC16IS7XX_LSR_FIFOE_BIT (1 << 7) /* Fifo Error */ +#define SC16IS7XX_LSR_DR_BIT BIT(0) /* Receiver data ready */ +#define SC16IS7XX_LSR_OE_BIT BIT(1) /* Overrun Error */ +#define SC16IS7XX_LSR_PE_BIT BIT(2) /* Parity Error */ +#define SC16IS7XX_LSR_FE_BIT BIT(3) /* Frame Error */ +#define SC16IS7XX_LSR_BI_BIT BIT(4) /* Break Interrupt */ +#define SC16IS7XX_LSR_BRK_ERROR_MASK \ + (SC16IS7XX_LSR_OE_BIT | \ + SC16IS7XX_LSR_PE_BIT | \ + SC16IS7XX_LSR_FE_BIT | \ + SC16IS7XX_LSR_BI_BIT) + +#define SC16IS7XX_LSR_THRE_BIT BIT(5) /* TX holding register empty */ +#define SC16IS7XX_LSR_TEMT_BIT BIT(6) /* Transmitter empty */ +#define SC16IS7XX_LSR_FIFOE_BIT BIT(7) /* Fifo Error */ /* MSR register bits */ -#define SC16IS7XX_MSR_DCTS_BIT (1 << 0) /* Delta CTS Clear To Send */ -#define SC16IS7XX_MSR_DDSR_BIT (1 << 1) /* Delta DSR Data Set Ready +#define SC16IS7XX_MSR_DCTS_BIT BIT(0) /* Delta CTS Clear To Send */ +#define SC16IS7XX_MSR_DDSR_BIT BIT(1) /* Delta DSR Data Set Ready * or (IO4) * - only on 75x/76x */ -#define SC16IS7XX_MSR_DRI_BIT (1 << 2) /* Delta RI Ring Indicator +#define SC16IS7XX_MSR_DRI_BIT BIT(2) /* Delta RI Ring Indicator * or (IO7) * - only on 75x/76x */ -#define SC16IS7XX_MSR_DCD_BIT (1 << 3) /* Delta CD Carrier Detect +#define SC16IS7XX_MSR_DCD_BIT BIT(3) /* Delta CD Carrier Detect * or (IO6) * - only on 75x/76x */ -#define SC16IS7XX_MSR_CTS_BIT (1 << 4) /* CTS */ -#define SC16IS7XX_MSR_DSR_BIT (1 << 5) /* DSR (IO4) +#define SC16IS7XX_MSR_CTS_BIT BIT(4) /* CTS */ +#define SC16IS7XX_MSR_DSR_BIT BIT(5) /* DSR (IO4) * - only on 75x/76x */ -#define SC16IS7XX_MSR_RI_BIT (1 << 6) /* RI (IO7) +#define SC16IS7XX_MSR_RI_BIT BIT(6) /* RI (IO7) * - only on 75x/76x */ -#define SC16IS7XX_MSR_CD_BIT (1 << 7) /* CD (IO6) +#define SC16IS7XX_MSR_CD_BIT BIT(7) /* CD (IO6) * - only on 75x/76x */ -#define SC16IS7XX_MSR_DELTA_MASK 0x0F /* Any of the delta bits! */ /* * TCR register bits @@ -237,19 +246,19 @@ #define SC16IS7XX_TLR_RX_TRIGGER(words) ((((words) / 4) & 0x0f) << 4) /* IOControl register bits (Only 75x/76x) */ -#define SC16IS7XX_IOCONTROL_LATCH_BIT (1 << 0) /* Enable input latching */ -#define SC16IS7XX_IOCONTROL_MODEM_A_BIT (1 << 1) /* Enable GPIO[7:4] as modem A pins */ -#define SC16IS7XX_IOCONTROL_MODEM_B_BIT (1 << 2) /* Enable GPIO[3:0] as modem B pins */ -#define SC16IS7XX_IOCONTROL_SRESET_BIT (1 << 3) /* Software Reset */ +#define SC16IS7XX_IOCONTROL_LATCH_BIT BIT(0) /* Enable input latching */ +#define SC16IS7XX_IOCONTROL_MODEM_A_BIT BIT(1) /* Enable GPIO[7:4] as modem A pins */ +#define SC16IS7XX_IOCONTROL_MODEM_B_BIT BIT(2) /* Enable GPIO[3:0] as modem B pins */ +#define SC16IS7XX_IOCONTROL_SRESET_BIT BIT(3) /* Software Reset */ /* EFCR register bits */ -#define SC16IS7XX_EFCR_9BIT_MODE_BIT (1 << 0) /* Enable 9-bit or Multidrop +#define SC16IS7XX_EFCR_9BIT_MODE_BIT BIT(0) /* Enable 9-bit or Multidrop * mode (RS485) */ -#define SC16IS7XX_EFCR_RXDISABLE_BIT (1 << 1) /* Disable receiver */ -#define SC16IS7XX_EFCR_TXDISABLE_BIT (1 << 2) /* Disable transmitter */ -#define SC16IS7XX_EFCR_AUTO_RS485_BIT (1 << 4) /* Auto RS485 RTS direction */ -#define SC16IS7XX_EFCR_RTS_INVERT_BIT (1 << 5) /* RTS output inversion */ -#define SC16IS7XX_EFCR_IRDA_MODE_BIT (1 << 7) /* IrDA mode +#define SC16IS7XX_EFCR_RXDISABLE_BIT BIT(1) /* Disable receiver */ +#define SC16IS7XX_EFCR_TXDISABLE_BIT BIT(2) /* Disable transmitter */ +#define SC16IS7XX_EFCR_AUTO_RS485_BIT BIT(4) /* Auto RS485 RTS direction */ +#define SC16IS7XX_EFCR_RTS_INVERT_BIT BIT(5) /* RTS output inversion */ +#define SC16IS7XX_EFCR_IRDA_MODE_BIT BIT(7) /* IrDA mode * 0 = rate upto 115.2 kbit/s * - Only 75x/76x * 1 = rate upto 1.152 Mbit/s @@ -257,16 +266,16 @@ */ /* EFR register bits */ -#define SC16IS7XX_EFR_AUTORTS_BIT (1 << 6) /* Auto RTS flow ctrl enable */ -#define SC16IS7XX_EFR_AUTOCTS_BIT (1 << 7) /* Auto CTS flow ctrl enable */ -#define SC16IS7XX_EFR_XOFF2_DETECT_BIT (1 << 5) /* Enable Xoff2 detection */ -#define SC16IS7XX_EFR_ENABLE_BIT (1 << 4) /* Enable enhanced functions +#define SC16IS7XX_EFR_AUTORTS_BIT BIT(6) /* Auto RTS flow ctrl enable */ +#define SC16IS7XX_EFR_AUTOCTS_BIT BIT(7) /* Auto CTS flow ctrl enable */ +#define SC16IS7XX_EFR_XOFF2_DETECT_BIT BIT(5) /* Enable Xoff2 detection */ +#define SC16IS7XX_EFR_ENABLE_BIT BIT(4) /* Enable enhanced functions * and writing to IER[7:4], * FCR[5:4], MCR[7:5] */ -#define SC16IS7XX_EFR_SWFLOW3_BIT (1 << 3) /* SWFLOW bit 3 */ -#define SC16IS7XX_EFR_SWFLOW2_BIT (1 << 2) /* SWFLOW bit 2 - * +#define SC16IS7XX_EFR_SWFLOW3_BIT BIT(3) +#define SC16IS7XX_EFR_SWFLOW2_BIT BIT(2) + /* * SWFLOW bits 3 & 2 table: * 00 -> no transmitter flow * control @@ -278,10 +287,10 @@ * XON1, XON2, XOFF1 and * XOFF2 */ -#define SC16IS7XX_EFR_SWFLOW1_BIT (1 << 1) /* SWFLOW bit 2 */ -#define SC16IS7XX_EFR_SWFLOW0_BIT (1 << 0) /* SWFLOW bit 3 - * - * SWFLOW bits 3 & 2 table: +#define SC16IS7XX_EFR_SWFLOW1_BIT BIT(1) +#define SC16IS7XX_EFR_SWFLOW0_BIT BIT(0) + /* + * SWFLOW bits 1 & 0 table: * 00 -> no received flow * control * 01 -> receiver compares @@ -302,19 +311,13 @@ /* Misc definitions */ -#define SC16IS7XX_SPI_READ_BIT BIT(7) #define SC16IS7XX_FIFO_SIZE (64) #define SC16IS7XX_GPIOS_PER_BANK 4 -struct sc16is7xx_devtype { - char name[10]; - int nr_gpio; - int nr_uart; -}; - -#define SC16IS7XX_RECONF_MD (1 << 0) -#define SC16IS7XX_RECONF_IER (1 << 1) -#define SC16IS7XX_RECONF_RS485 (1 << 2) +#define SC16IS7XX_POLL_PERIOD_MS 10 +#define SC16IS7XX_RECONF_MD BIT(0) +#define SC16IS7XX_RECONF_IER BIT(1) +#define SC16IS7XX_RECONF_RS485 BIT(2) struct sc16is7xx_one_config { unsigned int flags; @@ -330,6 +333,7 @@ struct sc16is7xx_one { struct kthread_work reg_work; struct kthread_delayed_work ms_work; struct sc16is7xx_one_config config; + unsigned char buf[SC16IS7XX_FIFO_SIZE]; /* Rx buffer. */ unsigned int old_mctrl; u8 old_lcr; /* Value before EFR access. */ bool irda_mode; @@ -343,13 +347,14 @@ struct sc16is7xx_port { unsigned long gpio_valid_mask; #endif u8 mctrl_mask; - unsigned char buf[SC16IS7XX_FIFO_SIZE]; struct kthread_worker kworker; struct task_struct *kworker_task; + struct kthread_delayed_work poll_work; + bool polling; struct sc16is7xx_one p[]; }; -static DECLARE_BITMAP(sc16is7xx_lines, SC16IS7XX_MAX_DEVS); +static DEFINE_IDA(sc16is7xx_lines); static struct uart_driver sc16is7xx_uart = { .owner = THIS_MODULE, @@ -492,35 +497,40 @@ static void sc16is7xx_stop_rx(struct uart_port *port) sc16is7xx_ier_clear(port, SC16IS7XX_IER_RDI_BIT); } -static const struct sc16is7xx_devtype sc16is74x_devtype = { +const struct sc16is7xx_devtype sc16is74x_devtype = { .name = "SC16IS74X", .nr_gpio = 0, .nr_uart = 1, }; +EXPORT_SYMBOL_GPL(sc16is74x_devtype); -static const struct sc16is7xx_devtype sc16is750_devtype = { +const struct sc16is7xx_devtype sc16is750_devtype = { .name = "SC16IS750", .nr_gpio = 8, .nr_uart = 1, }; +EXPORT_SYMBOL_GPL(sc16is750_devtype); -static const struct sc16is7xx_devtype sc16is752_devtype = { +const struct sc16is7xx_devtype sc16is752_devtype = { .name = "SC16IS752", .nr_gpio = 8, .nr_uart = 2, }; +EXPORT_SYMBOL_GPL(sc16is752_devtype); -static const struct sc16is7xx_devtype sc16is760_devtype = { +const struct sc16is7xx_devtype sc16is760_devtype = { .name = "SC16IS760", .nr_gpio = 8, .nr_uart = 1, }; +EXPORT_SYMBOL_GPL(sc16is760_devtype); -static const struct sc16is7xx_devtype sc16is762_devtype = { +const struct sc16is7xx_devtype sc16is762_devtype = { .name = "SC16IS762", .nr_gpio = 8, .nr_uart = 2, }; +EXPORT_SYMBOL_GPL(sc16is762_devtype); static bool sc16is7xx_regmap_volatile(struct device *dev, unsigned int reg) { @@ -554,16 +564,28 @@ static bool sc16is7xx_regmap_noinc(struct device *dev, unsigned int reg) return reg == SC16IS7XX_RHR_REG; } +/* + * Configure programmable baud rate generator (divisor) according to the + * desired baud rate. + * + * From the datasheet, the divisor is computed according to: + * + * XTAL1 input frequency + * ----------------------- + * prescaler + * divisor = --------------------------- + * baud-rate x sampling-rate + */ static int sc16is7xx_set_baud(struct uart_port *port, int baud) { struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); u8 lcr; - u8 prescaler = 0; + unsigned int prescaler = 1; unsigned long clk = port->uartclk, div = clk / 16 / baud; if (div >= BIT(16)) { - prescaler = SC16IS7XX_MCR_CLKSEL_BIT; - div /= 4; + prescaler = 4; + div /= prescaler; } /* Enable enhanced features */ @@ -573,9 +595,12 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud) SC16IS7XX_EFR_ENABLE_BIT); sc16is7xx_efr_unlock(port); + /* If bit MCR_CLKSEL is set, the divide by 4 prescaler is activated. */ sc16is7xx_port_update(port, SC16IS7XX_MCR_REG, SC16IS7XX_MCR_CLKSEL_BIT, - prescaler); + prescaler == 1 ? 0 : SC16IS7XX_MCR_CLKSEL_BIT); + + mutex_lock(&one->efr_lock); /* Backup LCR and access special register set (DLL/DLH) */ lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG); @@ -591,24 +616,26 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud) /* Restore LCR and access to general register set */ sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr); - return DIV_ROUND_CLOSEST(clk / 16, div); + mutex_unlock(&one->efr_lock); + + return DIV_ROUND_CLOSEST((clk / prescaler) / 16, div); } static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen, unsigned int iir) { - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); unsigned int lsr = 0, bytes_read, i; bool read_lsr = (iir == SC16IS7XX_IIR_RLSE_SRC) ? true : false; u8 ch, flag; - if (unlikely(rxlen >= sizeof(s->buf))) { + if (unlikely(rxlen >= sizeof(one->buf))) { dev_warn_ratelimited(port->dev, "ttySC%i: Possible RX FIFO overrun: %d\n", port->line, rxlen); port->icount.buf_overrun++; /* Ensure sanity of RX level */ - rxlen = sizeof(s->buf); + rxlen = sizeof(one->buf); } while (rxlen) { @@ -621,10 +648,10 @@ static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen, lsr = 0; if (read_lsr) { - s->buf[0] = sc16is7xx_port_read(port, SC16IS7XX_RHR_REG); + one->buf[0] = sc16is7xx_port_read(port, SC16IS7XX_RHR_REG); bytes_read = 1; } else { - sc16is7xx_fifo_read(port, s->buf, rxlen); + sc16is7xx_fifo_read(port, one->buf, rxlen); bytes_read = rxlen; } @@ -657,7 +684,7 @@ static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen, } for (i = 0; i < bytes_read; ++i) { - ch = s->buf[i]; + ch = one->buf[i]; if (uart_handle_sysrq_char(port, ch)) continue; @@ -675,10 +702,10 @@ static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen, static void sc16is7xx_handle_tx(struct uart_port *port) { - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); - struct circ_buf *xmit = &port->state->xmit; - unsigned int txlen, to_send, i; + struct tty_port *tport = &port->state->port; unsigned long flags; + unsigned int txlen; + unsigned char *tail; if (unlikely(port->x_char)) { sc16is7xx_port_write(port, SC16IS7XX_THR_REG, port->x_char); @@ -687,40 +714,31 @@ static void sc16is7xx_handle_tx(struct uart_port *port) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { uart_port_lock_irqsave(port, &flags); sc16is7xx_stop_tx(port); uart_port_unlock_irqrestore(port, flags); return; } - /* Get length of data pending in circular buffer */ - to_send = uart_circ_chars_pending(xmit); - if (likely(to_send)) { - /* Limit to space available in TX FIFO */ - txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); - if (txlen > SC16IS7XX_FIFO_SIZE) { - dev_err_ratelimited(port->dev, - "chip reports %d free bytes in TX fifo, but it only has %d", - txlen, SC16IS7XX_FIFO_SIZE); - txlen = 0; - } - to_send = (to_send > txlen) ? txlen : to_send; - - /* Convert to linear buffer */ - for (i = 0; i < to_send; ++i) { - s->buf[i] = xmit->buf[xmit->tail]; - uart_xmit_advance(port, 1); - } - - sc16is7xx_fifo_write(port, s->buf, to_send); + /* Limit to space available in TX FIFO */ + txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); + if (txlen > SC16IS7XX_FIFO_SIZE) { + dev_err_ratelimited(port->dev, + "chip reports %d free bytes in TX fifo, but it only has %d", + txlen, SC16IS7XX_FIFO_SIZE); + txlen = 0; } + txlen = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, txlen); + sc16is7xx_fifo_write(port, tail, txlen); + uart_xmit_advance(port, txlen); + uart_port_lock_irqsave(port, &flags); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) sc16is7xx_stop_tx(port); else sc16is7xx_ier_set(port, SC16IS7XX_IER_THRI_BIT); @@ -846,6 +864,18 @@ static irqreturn_t sc16is7xx_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static void sc16is7xx_poll_proc(struct kthread_work *ws) +{ + struct sc16is7xx_port *s = container_of(ws, struct sc16is7xx_port, poll_work.work); + + /* Reuse standard IRQ handler. Interrupt ID is unused in this context. */ + sc16is7xx_irq(0, s); + + /* Setup delay based on SC16IS7XX_POLL_PERIOD_MS */ + kthread_queue_delayed_work(&s->kworker, &s->poll_work, + msecs_to_jiffies(SC16IS7XX_POLL_PERIOD_MS)); +} + static void sc16is7xx_tx_proc(struct kthread_work *ws) { struct uart_port *port = &(to_sc16is7xx_one(ws, tx_work)->port); @@ -1134,6 +1164,7 @@ static int sc16is7xx_config_rs485(struct uart_port *port, struct ktermios *termi static int sc16is7xx_startup(struct uart_port *port) { struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); unsigned int val; unsigned long flags; @@ -1196,6 +1227,10 @@ static int sc16is7xx_startup(struct uart_port *port) sc16is7xx_enable_ms(port); uart_port_unlock_irqrestore(port, flags); + if (s->polling) + kthread_queue_delayed_work(&s->kworker, &s->poll_work, + msecs_to_jiffies(SC16IS7XX_POLL_PERIOD_MS)); + return 0; } @@ -1217,6 +1252,9 @@ static void sc16is7xx_shutdown(struct uart_port *port) sc16is7xx_power(port, 0); + if (s->polling) + kthread_cancel_delayed_work_sync(&s->poll_work); + kthread_flush_worker(&s->kworker); } @@ -1295,13 +1333,16 @@ static int sc16is7xx_gpio_get(struct gpio_chip *chip, unsigned offset) return !!(val & BIT(offset)); } -static void sc16is7xx_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +static int sc16is7xx_gpio_set(struct gpio_chip *chip, unsigned int offset, + int val) { struct sc16is7xx_port *s = gpiochip_get_data(chip); struct uart_port *port = &s->p[0].port; sc16is7xx_port_update(port, SC16IS7XX_IOSTATE_REG, BIT(offset), val ? BIT(offset) : 0); + + return 0; } static int sc16is7xx_gpio_direction_input(struct gpio_chip *chip, @@ -1384,7 +1425,7 @@ static int sc16is7xx_setup_gpio_chip(struct sc16is7xx_port *s) s->gpio.direction_input = sc16is7xx_gpio_direction_input; s->gpio.get = sc16is7xx_gpio_get; s->gpio.direction_output = sc16is7xx_gpio_direction_output; - s->gpio.set = sc16is7xx_gpio_set; + s->gpio.set_rv = sc16is7xx_gpio_set; s->gpio.base = -1; s->gpio.ngpio = s->devtype->nr_gpio; s->gpio.can_sleep = 1; @@ -1458,20 +1499,43 @@ static int sc16is7xx_setup_mctrl_ports(struct sc16is7xx_port *s, } static const struct serial_rs485 sc16is7xx_rs485_supported = { - .flags = SER_RS485_ENABLED | SER_RS485_RTS_AFTER_SEND, + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, .delay_rts_before_send = 1, .delay_rts_after_send = 1, /* Not supported but keep returning -EINVAL */ }; -static int sc16is7xx_probe(struct device *dev, - const struct sc16is7xx_devtype *devtype, - struct regmap *regmaps[], int irq) +/* Reset device, purging any pending irq / data */ +static int sc16is7xx_reset(struct device *dev, struct regmap *regmap) +{ + struct gpio_desc *reset_gpio; + + /* Assert reset GPIO if defined and valid. */ + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpio)) + return dev_err_probe(dev, PTR_ERR(reset_gpio), "Failed to get reset GPIO\n"); + + if (reset_gpio) { + /* The minimum reset pulse width is 3 us. */ + fsleep(5); + gpiod_set_value_cansleep(reset_gpio, 0); /* Deassert GPIO */ + } else { + /* Software reset */ + regmap_write(regmap, SC16IS7XX_IOCONTROL_REG, + SC16IS7XX_IOCONTROL_SRESET_BIT); + } + + return 0; +} + +int sc16is7xx_probe(struct device *dev, const struct sc16is7xx_devtype *devtype, + struct regmap *regmaps[], int irq) { unsigned long freq = 0, *pfreq = dev_get_platdata(dev); unsigned int val; u32 uartclk = 0; int i, ret; struct sc16is7xx_port *s; + bool port_registered[SC16IS7XX_MAX_PORTS]; for (i = 0; i < devtype->nr_uart; i++) if (IS_ERR(regmaps[i])) @@ -1500,6 +1564,11 @@ static int sc16is7xx_probe(struct device *dev, /* Always ask for fixed clock rate from a property. */ device_property_read_u32(dev, "clock-frequency", &uartclk); + s->polling = (irq <= 0); + if (s->polling) + dev_dbg(dev, + "No interrupt pin definition, falling back to polling mode\n"); + s->clk = devm_clk_get_optional(dev, NULL); if (IS_ERR(s->clk)) return PTR_ERR(s->clk); @@ -1532,17 +1601,23 @@ static int sc16is7xx_probe(struct device *dev, } sched_set_fifo(s->kworker_task); - /* reset device, purging any pending irq / data */ - regmap_write(regmaps[0], SC16IS7XX_IOCONTROL_REG, - SC16IS7XX_IOCONTROL_SRESET_BIT); + ret = sc16is7xx_reset(dev, regmaps[0]); + if (ret) + goto out_kthread; + + /* Mark each port line and status as uninitialised. */ + for (i = 0; i < devtype->nr_uart; ++i) { + s->p[i].port.line = SC16IS7XX_MAX_DEVS; + port_registered[i] = false; + } for (i = 0; i < devtype->nr_uart; ++i) { - s->p[i].port.line = find_first_zero_bit(sc16is7xx_lines, - SC16IS7XX_MAX_DEVS); - if (s->p[i].port.line >= SC16IS7XX_MAX_DEVS) { - ret = -ERANGE; + ret = ida_alloc_max(&sc16is7xx_lines, + SC16IS7XX_MAX_DEVS - 1, GFP_KERNEL); + if (ret < 0) goto out_ports; - } + + s->p[i].port.line = ret; /* Initialize port data */ s->p[i].port.dev = dev; @@ -1588,7 +1663,7 @@ static int sc16is7xx_probe(struct device *dev, if (ret) goto out_ports; - set_bit(s->p[i].port.line, sc16is7xx_lines); + port_registered[i] = true; /* Enable EFR */ sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG, @@ -1621,6 +1696,12 @@ static int sc16is7xx_probe(struct device *dev, goto out_ports; #endif + if (s->polling) { + /* Initialize kernel thread for polling */ + kthread_init_delayed_work(&s->poll_work, sc16is7xx_poll_proc); + return 0; + } + /* * Setup interrupt. We first try to acquire the IRQ line as level IRQ. * If that succeeds, we can allow sharing the interrupt as well. @@ -1646,10 +1727,14 @@ static int sc16is7xx_probe(struct device *dev, #endif out_ports: - for (i = 0; i < devtype->nr_uart; i++) - if (test_and_clear_bit(s->p[i].port.line, sc16is7xx_lines)) + for (i = 0; i < devtype->nr_uart; i++) { + if (s->p[i].port.line < SC16IS7XX_MAX_DEVS) + ida_free(&sc16is7xx_lines, s->p[i].port.line); + if (port_registered[i]) uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); + } +out_kthread: kthread_stop(s->kworker_task); out_clk: @@ -1657,8 +1742,9 @@ out_clk: return ret; } +EXPORT_SYMBOL_GPL(sc16is7xx_probe); -static void sc16is7xx_remove(struct device *dev) +void sc16is7xx_remove(struct device *dev) { struct sc16is7xx_port *s = dev_get_drvdata(dev); int i; @@ -1670,18 +1756,22 @@ static void sc16is7xx_remove(struct device *dev) for (i = 0; i < s->devtype->nr_uart; i++) { kthread_cancel_delayed_work_sync(&s->p[i].ms_work); - if (test_and_clear_bit(s->p[i].port.line, sc16is7xx_lines)) - uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); + ida_free(&sc16is7xx_lines, s->p[i].port.line); + uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port); sc16is7xx_power(&s->p[i].port, 0); } + if (s->polling) + kthread_cancel_delayed_work_sync(&s->poll_work); + kthread_flush_worker(&s->kworker); kthread_stop(s->kworker_task); clk_disable_unprepare(s->clk); } +EXPORT_SYMBOL_GPL(sc16is7xx_remove); -static const struct of_device_id __maybe_unused sc16is7xx_dt_ids[] = { +const struct of_device_id __maybe_unused sc16is7xx_dt_ids[] = { { .compatible = "nxp,sc16is740", .data = &sc16is74x_devtype, }, { .compatible = "nxp,sc16is741", .data = &sc16is74x_devtype, }, { .compatible = "nxp,sc16is750", .data = &sc16is750_devtype, }, @@ -1690,13 +1780,14 @@ static const struct of_device_id __maybe_unused sc16is7xx_dt_ids[] = { { .compatible = "nxp,sc16is762", .data = &sc16is762_devtype, }, { } }; +EXPORT_SYMBOL_GPL(sc16is7xx_dt_ids); MODULE_DEVICE_TABLE(of, sc16is7xx_dt_ids); -static struct regmap_config regcfg = { +const struct regmap_config sc16is7xx_regcfg = { .reg_bits = 5, .pad_bits = 3, .val_bits = 8, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = sc16is7xx_regmap_volatile, .precious_reg = sc16is7xx_regmap_precious, .writeable_noinc_reg = sc16is7xx_regmap_noinc, @@ -1705,8 +1796,9 @@ static struct regmap_config regcfg = { .max_raw_write = SC16IS7XX_FIFO_SIZE, .max_register = SC16IS7XX_EFCR_REG, }; +EXPORT_SYMBOL_GPL(sc16is7xx_regcfg); -static const char *sc16is7xx_regmap_name(u8 port_id) +const char *sc16is7xx_regmap_name(u8 port_id) { switch (port_id) { case 0: return "port0"; @@ -1716,184 +1808,27 @@ static const char *sc16is7xx_regmap_name(u8 port_id) return NULL; } } +EXPORT_SYMBOL_GPL(sc16is7xx_regmap_name); -static unsigned int sc16is7xx_regmap_port_mask(unsigned int port_id) +unsigned int sc16is7xx_regmap_port_mask(unsigned int port_id) { /* CH1,CH0 are at bits 2:1. */ return port_id << 1; } - -#ifdef CONFIG_SERIAL_SC16IS7XX_SPI -static int sc16is7xx_spi_probe(struct spi_device *spi) -{ - const struct sc16is7xx_devtype *devtype; - struct regmap *regmaps[SC16IS7XX_MAX_PORTS]; - unsigned int i; - int ret; - - /* Setup SPI bus */ - spi->bits_per_word = 8; - /* For all variants, only mode 0 is supported */ - if ((spi->mode & SPI_MODE_X_MASK) != SPI_MODE_0) - return dev_err_probe(&spi->dev, -EINVAL, "Unsupported SPI mode\n"); - - spi->mode = spi->mode ? : SPI_MODE_0; - spi->max_speed_hz = spi->max_speed_hz ? : 4 * HZ_PER_MHZ; - ret = spi_setup(spi); - if (ret) - return ret; - - devtype = spi_get_device_match_data(spi); - if (!devtype) - return dev_err_probe(&spi->dev, -ENODEV, "Failed to match device\n"); - - for (i = 0; i < devtype->nr_uart; i++) { - regcfg.name = sc16is7xx_regmap_name(i); - /* - * If read_flag_mask is 0, the regmap code sets it to a default - * of 0x80. Since we specify our own mask, we must add the READ - * bit ourselves: - */ - regcfg.read_flag_mask = sc16is7xx_regmap_port_mask(i) | - SC16IS7XX_SPI_READ_BIT; - regcfg.write_flag_mask = sc16is7xx_regmap_port_mask(i); - regmaps[i] = devm_regmap_init_spi(spi, ®cfg); - } - - return sc16is7xx_probe(&spi->dev, devtype, regmaps, spi->irq); -} - -static void sc16is7xx_spi_remove(struct spi_device *spi) -{ - sc16is7xx_remove(&spi->dev); -} - -static const struct spi_device_id sc16is7xx_spi_id_table[] = { - { "sc16is74x", (kernel_ulong_t)&sc16is74x_devtype, }, - { "sc16is740", (kernel_ulong_t)&sc16is74x_devtype, }, - { "sc16is741", (kernel_ulong_t)&sc16is74x_devtype, }, - { "sc16is750", (kernel_ulong_t)&sc16is750_devtype, }, - { "sc16is752", (kernel_ulong_t)&sc16is752_devtype, }, - { "sc16is760", (kernel_ulong_t)&sc16is760_devtype, }, - { "sc16is762", (kernel_ulong_t)&sc16is762_devtype, }, - { } -}; - -MODULE_DEVICE_TABLE(spi, sc16is7xx_spi_id_table); - -static struct spi_driver sc16is7xx_spi_uart_driver = { - .driver = { - .name = SC16IS7XX_NAME, - .of_match_table = sc16is7xx_dt_ids, - }, - .probe = sc16is7xx_spi_probe, - .remove = sc16is7xx_spi_remove, - .id_table = sc16is7xx_spi_id_table, -}; -#endif - -#ifdef CONFIG_SERIAL_SC16IS7XX_I2C -static int sc16is7xx_i2c_probe(struct i2c_client *i2c) -{ - const struct sc16is7xx_devtype *devtype; - struct regmap *regmaps[SC16IS7XX_MAX_PORTS]; - unsigned int i; - - devtype = i2c_get_match_data(i2c); - if (!devtype) - return dev_err_probe(&i2c->dev, -ENODEV, "Failed to match device\n"); - - for (i = 0; i < devtype->nr_uart; i++) { - regcfg.name = sc16is7xx_regmap_name(i); - regcfg.read_flag_mask = sc16is7xx_regmap_port_mask(i); - regcfg.write_flag_mask = sc16is7xx_regmap_port_mask(i); - regmaps[i] = devm_regmap_init_i2c(i2c, ®cfg); - } - - return sc16is7xx_probe(&i2c->dev, devtype, regmaps, i2c->irq); -} - -static void sc16is7xx_i2c_remove(struct i2c_client *client) -{ - sc16is7xx_remove(&client->dev); -} - -static const struct i2c_device_id sc16is7xx_i2c_id_table[] = { - { "sc16is74x", (kernel_ulong_t)&sc16is74x_devtype, }, - { "sc16is740", (kernel_ulong_t)&sc16is74x_devtype, }, - { "sc16is741", (kernel_ulong_t)&sc16is74x_devtype, }, - { "sc16is750", (kernel_ulong_t)&sc16is750_devtype, }, - { "sc16is752", (kernel_ulong_t)&sc16is752_devtype, }, - { "sc16is760", (kernel_ulong_t)&sc16is760_devtype, }, - { "sc16is762", (kernel_ulong_t)&sc16is762_devtype, }, - { } -}; -MODULE_DEVICE_TABLE(i2c, sc16is7xx_i2c_id_table); - -static struct i2c_driver sc16is7xx_i2c_uart_driver = { - .driver = { - .name = SC16IS7XX_NAME, - .of_match_table = sc16is7xx_dt_ids, - }, - .probe = sc16is7xx_i2c_probe, - .remove = sc16is7xx_i2c_remove, - .id_table = sc16is7xx_i2c_id_table, -}; - -#endif +EXPORT_SYMBOL_GPL(sc16is7xx_regmap_port_mask); static int __init sc16is7xx_init(void) { - int ret; - - ret = uart_register_driver(&sc16is7xx_uart); - if (ret) { - pr_err("Registering UART driver failed\n"); - return ret; - } - -#ifdef CONFIG_SERIAL_SC16IS7XX_I2C - ret = i2c_add_driver(&sc16is7xx_i2c_uart_driver); - if (ret < 0) { - pr_err("failed to init sc16is7xx i2c --> %d\n", ret); - goto err_i2c; - } -#endif - -#ifdef CONFIG_SERIAL_SC16IS7XX_SPI - ret = spi_register_driver(&sc16is7xx_spi_uart_driver); - if (ret < 0) { - pr_err("failed to init sc16is7xx spi --> %d\n", ret); - goto err_spi; - } -#endif - return ret; - -#ifdef CONFIG_SERIAL_SC16IS7XX_SPI -err_spi: -#endif -#ifdef CONFIG_SERIAL_SC16IS7XX_I2C - i2c_del_driver(&sc16is7xx_i2c_uart_driver); -err_i2c: -#endif - uart_unregister_driver(&sc16is7xx_uart); - return ret; + return uart_register_driver(&sc16is7xx_uart); } module_init(sc16is7xx_init); static void __exit sc16is7xx_exit(void) { -#ifdef CONFIG_SERIAL_SC16IS7XX_I2C - i2c_del_driver(&sc16is7xx_i2c_uart_driver); -#endif - -#ifdef CONFIG_SERIAL_SC16IS7XX_SPI - spi_unregister_driver(&sc16is7xx_spi_uart_driver); -#endif uart_unregister_driver(&sc16is7xx_uart); } module_exit(sc16is7xx_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jon Ringle <jringle@gridpoint.com>"); -MODULE_DESCRIPTION("SC16IS7XX serial driver"); +MODULE_DESCRIPTION("SC16IS7xx tty serial core driver"); diff --git a/drivers/tty/serial/sc16is7xx.h b/drivers/tty/serial/sc16is7xx.h new file mode 100644 index 000000000000..afb784eaee45 --- /dev/null +++ b/drivers/tty/serial/sc16is7xx.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* SC16IS7xx SPI/I2C tty serial driver */ + +#ifndef _SC16IS7XX_H_ +#define _SC16IS7XX_H_ + +#include <linux/mod_devicetable.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#define SC16IS7XX_NAME "sc16is7xx" +#define SC16IS7XX_MAX_PORTS 2 /* Maximum number of UART ports per IC. */ + +struct device; + +struct sc16is7xx_devtype { + char name[10]; + int nr_gpio; + int nr_uart; +}; + +extern const struct regmap_config sc16is7xx_regcfg; + +extern const struct of_device_id sc16is7xx_dt_ids[]; + +extern const struct sc16is7xx_devtype sc16is74x_devtype; +extern const struct sc16is7xx_devtype sc16is750_devtype; +extern const struct sc16is7xx_devtype sc16is752_devtype; +extern const struct sc16is7xx_devtype sc16is760_devtype; +extern const struct sc16is7xx_devtype sc16is762_devtype; + +const char *sc16is7xx_regmap_name(u8 port_id); + +unsigned int sc16is7xx_regmap_port_mask(unsigned int port_id); + +int sc16is7xx_probe(struct device *dev, const struct sc16is7xx_devtype *devtype, + struct regmap *regmaps[], int irq); + +void sc16is7xx_remove(struct device *dev); + +#endif /* _SC16IS7XX_H_ */ diff --git a/drivers/tty/serial/sc16is7xx_i2c.c b/drivers/tty/serial/sc16is7xx_i2c.c new file mode 100644 index 000000000000..cd7de9e057b8 --- /dev/null +++ b/drivers/tty/serial/sc16is7xx_i2c.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* SC16IS7xx I2C interface driver */ + +#include <linux/dev_printk.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/string.h> + +#include "sc16is7xx.h" + +static int sc16is7xx_i2c_probe(struct i2c_client *i2c) +{ + const struct sc16is7xx_devtype *devtype; + struct regmap *regmaps[SC16IS7XX_MAX_PORTS]; + struct regmap_config regcfg; + unsigned int i; + + devtype = i2c_get_match_data(i2c); + if (!devtype) + return dev_err_probe(&i2c->dev, -ENODEV, "Failed to match device\n"); + + memcpy(®cfg, &sc16is7xx_regcfg, sizeof(struct regmap_config)); + + for (i = 0; i < devtype->nr_uart; i++) { + regcfg.name = sc16is7xx_regmap_name(i); + regcfg.read_flag_mask = sc16is7xx_regmap_port_mask(i); + regcfg.write_flag_mask = sc16is7xx_regmap_port_mask(i); + regmaps[i] = devm_regmap_init_i2c(i2c, ®cfg); + } + + return sc16is7xx_probe(&i2c->dev, devtype, regmaps, i2c->irq); +} + +static void sc16is7xx_i2c_remove(struct i2c_client *client) +{ + sc16is7xx_remove(&client->dev); +} + +static const struct i2c_device_id sc16is7xx_i2c_id_table[] = { + { "sc16is74x", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is740", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is741", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is750", (kernel_ulong_t)&sc16is750_devtype, }, + { "sc16is752", (kernel_ulong_t)&sc16is752_devtype, }, + { "sc16is760", (kernel_ulong_t)&sc16is760_devtype, }, + { "sc16is762", (kernel_ulong_t)&sc16is762_devtype, }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sc16is7xx_i2c_id_table); + +static struct i2c_driver sc16is7xx_i2c_driver = { + .driver = { + .name = SC16IS7XX_NAME, + .of_match_table = sc16is7xx_dt_ids, + }, + .probe = sc16is7xx_i2c_probe, + .remove = sc16is7xx_i2c_remove, + .id_table = sc16is7xx_i2c_id_table, +}; + +module_i2c_driver(sc16is7xx_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SC16IS7xx I2C interface driver"); +MODULE_IMPORT_NS("SERIAL_NXP_SC16IS7XX"); diff --git a/drivers/tty/serial/sc16is7xx_spi.c b/drivers/tty/serial/sc16is7xx_spi.c new file mode 100644 index 000000000000..20d736b657b1 --- /dev/null +++ b/drivers/tty/serial/sc16is7xx_spi.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* SC16IS7xx SPI interface driver */ + +#include <linux/dev_printk.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> +#include <linux/string.h> +#include <linux/units.h> + +#include "sc16is7xx.h" + +/* SPI definitions */ +#define SC16IS7XX_SPI_READ_BIT BIT(7) + +static int sc16is7xx_spi_probe(struct spi_device *spi) +{ + const struct sc16is7xx_devtype *devtype; + struct regmap *regmaps[SC16IS7XX_MAX_PORTS]; + struct regmap_config regcfg; + unsigned int i; + int ret; + + /* Setup SPI bus */ + spi->bits_per_word = 8; + /* For all variants, only mode 0 is supported */ + if ((spi->mode & SPI_MODE_X_MASK) != SPI_MODE_0) + return dev_err_probe(&spi->dev, -EINVAL, "Unsupported SPI mode\n"); + + spi->mode = spi->mode ? : SPI_MODE_0; + spi->max_speed_hz = spi->max_speed_hz ? : 4 * HZ_PER_MHZ; + ret = spi_setup(spi); + if (ret) + return ret; + + devtype = spi_get_device_match_data(spi); + if (!devtype) + return dev_err_probe(&spi->dev, -ENODEV, "Failed to match device\n"); + + memcpy(®cfg, &sc16is7xx_regcfg, sizeof(struct regmap_config)); + + for (i = 0; i < devtype->nr_uart; i++) { + regcfg.name = sc16is7xx_regmap_name(i); + /* + * If read_flag_mask is 0, the regmap code sets it to a default + * of 0x80. Since we specify our own mask, we must add the READ + * bit ourselves: + */ + regcfg.read_flag_mask = sc16is7xx_regmap_port_mask(i) | + SC16IS7XX_SPI_READ_BIT; + regcfg.write_flag_mask = sc16is7xx_regmap_port_mask(i); + regmaps[i] = devm_regmap_init_spi(spi, ®cfg); + } + + return sc16is7xx_probe(&spi->dev, devtype, regmaps, spi->irq); +} + +static void sc16is7xx_spi_remove(struct spi_device *spi) +{ + sc16is7xx_remove(&spi->dev); +} + +static const struct spi_device_id sc16is7xx_spi_id_table[] = { + { "sc16is74x", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is740", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is741", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is750", (kernel_ulong_t)&sc16is750_devtype, }, + { "sc16is752", (kernel_ulong_t)&sc16is752_devtype, }, + { "sc16is760", (kernel_ulong_t)&sc16is760_devtype, }, + { "sc16is762", (kernel_ulong_t)&sc16is762_devtype, }, + { } +}; +MODULE_DEVICE_TABLE(spi, sc16is7xx_spi_id_table); + +static struct spi_driver sc16is7xx_spi_driver = { + .driver = { + .name = SC16IS7XX_NAME, + .of_match_table = sc16is7xx_dt_ids, + }, + .probe = sc16is7xx_spi_probe, + .remove = sc16is7xx_spi_remove, + .id_table = sc16is7xx_spi_id_table, +}; + +module_spi_driver(sc16is7xx_spi_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SC16IS7xx SPI interface driver"); +MODULE_IMPORT_NS("SERIAL_NXP_SC16IS7XX"); diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index f24217a560d7..4ceca11ce600 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -439,7 +439,7 @@ static void sccnxp_handle_rx(struct uart_port *port) static void sccnxp_handle_tx(struct uart_port *port) { u8 sr; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; struct sccnxp_port *s = dev_get_drvdata(port->dev); if (unlikely(port->x_char)) { @@ -449,7 +449,7 @@ static void sccnxp_handle_tx(struct uart_port *port) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { /* Disable TX if FIFO is empty */ if (sccnxp_port_read(port, SCCNXP_SR_REG) & SR_TXEMT) { sccnxp_disable_irq(port, IMR_TXRDY); @@ -461,16 +461,20 @@ static void sccnxp_handle_tx(struct uart_port *port) return; } - while (!uart_circ_empty(xmit)) { + while (1) { + unsigned char ch; + sr = sccnxp_port_read(port, SCCNXP_SR_REG); if (!(sr & SR_TXRDY)) break; - sccnxp_port_write(port, SCCNXP_THR_REG, xmit->buf[xmit->tail]); - uart_xmit_advance(port, 1); + if (!uart_fifo_get(port, &ch)) + break; + + sccnxp_port_write(port, SCCNXP_THR_REG, ch); } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); } @@ -496,7 +500,7 @@ static void sccnxp_handle_events(struct sccnxp_port *s) static void sccnxp_timer(struct timer_list *t) { - struct sccnxp_port *s = from_timer(s, t, timer); + struct sccnxp_port *s = timer_container_of(s, t, timer); unsigned long flags; spin_lock_irqsave(&s->lock, flags); @@ -1029,7 +1033,7 @@ static void sccnxp_remove(struct platform_device *pdev) if (!s->poll) devm_free_irq(&pdev->dev, s->irq, s); else - del_timer_sync(&s->timer); + timer_delete_sync(&s->timer); for (i = 0; i < s->uart.nr; i++) uart_remove_one_port(&s->uart, &s->port[i]); @@ -1048,7 +1052,7 @@ static struct platform_driver sccnxp_uart_driver = { .name = SCCNXP_NAME, }, .probe = sccnxp_probe, - .remove_new = sccnxp_remove, + .remove = sccnxp_remove, .id_table = sccnxp_id_table, }; module_platform_driver(sccnxp_uart_driver); diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 525f3a2f7bd4..8004fc00fb9c 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -484,18 +484,18 @@ static void tegra_uart_release_port(struct uart_port *u) static void tegra_uart_fill_tx_fifo(struct tegra_uart_port *tup, int max_bytes) { - struct circ_buf *xmit = &tup->uport.state->xmit; + unsigned char ch; int i; for (i = 0; i < max_bytes; i++) { - BUG_ON(uart_circ_empty(xmit)); if (tup->cdata->tx_fifo_full_status) { unsigned long lsr = tegra_uart_read(tup, UART_LSR); if ((lsr & TEGRA_UART_LSR_TXFIFO_FULL)) break; } - tegra_uart_write(tup, xmit->buf[xmit->tail], UART_TX); - uart_xmit_advance(&tup->uport, 1); + if (WARN_ON_ONCE(!uart_fifo_get(&tup->uport, &ch))) + break; + tegra_uart_write(tup, ch, UART_TX); } } @@ -514,7 +514,7 @@ static void tegra_uart_start_pio_tx(struct tegra_uart_port *tup, static void tegra_uart_tx_dma_complete(void *args) { struct tegra_uart_port *tup = args; - struct circ_buf *xmit = &tup->uport.state->xmit; + struct tty_port *tport = &tup->uport.state->port; struct dma_tx_state state; unsigned long flags; unsigned int count; @@ -525,7 +525,7 @@ static void tegra_uart_tx_dma_complete(void *args) uart_port_lock_irqsave(&tup->uport, &flags); uart_xmit_advance(&tup->uport, count); tup->tx_in_progress = 0; - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&tup->uport); tegra_uart_start_next_tx(tup); uart_port_unlock_irqrestore(&tup->uport, flags); @@ -534,11 +534,14 @@ static void tegra_uart_tx_dma_complete(void *args) static int tegra_uart_start_tx_dma(struct tegra_uart_port *tup, unsigned long count) { - struct circ_buf *xmit = &tup->uport.state->xmit; + struct tty_port *tport = &tup->uport.state->port; dma_addr_t tx_phys_addr; + unsigned int tail; tup->tx_bytes = count & ~(0xF); - tx_phys_addr = tup->tx_dma_buf_phys + xmit->tail; + WARN_ON_ONCE(kfifo_out_linear(&tport->xmit_fifo, &tail, + UART_XMIT_SIZE) < count); + tx_phys_addr = tup->tx_dma_buf_phys + tail; dma_sync_single_for_device(tup->uport.dev, tx_phys_addr, tup->tx_bytes, DMA_TO_DEVICE); @@ -562,18 +565,21 @@ static int tegra_uart_start_tx_dma(struct tegra_uart_port *tup, static void tegra_uart_start_next_tx(struct tegra_uart_port *tup) { + struct tty_port *tport = &tup->uport.state->port; + unsigned char *tail_ptr; unsigned long tail; - unsigned long count; - struct circ_buf *xmit = &tup->uport.state->xmit; + unsigned int count; if (!tup->current_baud) return; - tail = (unsigned long)&xmit->buf[xmit->tail]; - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + count = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail_ptr, + UART_XMIT_SIZE); if (!count) return; + tail = (unsigned long)tail_ptr; + if (tup->use_tx_pio || count < TEGRA_UART_MIN_DMA) tegra_uart_start_pio_tx(tup, count); else if (BYTES_TO_ALIGN(tail) > 0) @@ -586,9 +592,9 @@ static void tegra_uart_start_next_tx(struct tegra_uart_port *tup) static void tegra_uart_start_tx(struct uart_port *u) { struct tegra_uart_port *tup = to_tegra_uport(u); - struct circ_buf *xmit = &u->state->xmit; + struct tty_port *tport = &u->state->port; - if (!uart_circ_empty(xmit) && !tup->tx_in_progress) + if (!kfifo_is_empty(&tport->xmit_fifo) && !tup->tx_in_progress) tegra_uart_start_next_tx(tup); } @@ -628,11 +634,11 @@ static void tegra_uart_stop_tx(struct uart_port *u) static void tegra_uart_handle_tx_pio(struct tegra_uart_port *tup) { - struct circ_buf *xmit = &tup->uport.state->xmit; + struct tty_port *tport = &tup->uport.state->port; tegra_uart_fill_tx_fifo(tup, tup->tx_bytes); tup->tx_in_progress = 0; - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&tup->uport); tegra_uart_start_next_tx(tup); } @@ -1169,15 +1175,14 @@ static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup, tup->rx_dma_buf_virt = dma_buf; tup->rx_dma_buf_phys = dma_phys; } else { + dma_buf = tup->uport.state->port.xmit_buf; dma_phys = dma_map_single(tup->uport.dev, - tup->uport.state->xmit.buf, UART_XMIT_SIZE, - DMA_TO_DEVICE); + dma_buf, UART_XMIT_SIZE, DMA_TO_DEVICE); if (dma_mapping_error(tup->uport.dev, dma_phys)) { dev_err(tup->uport.dev, "dma_map_single tx failed\n"); dma_release_channel(dma_chan); return -ENOMEM; } - dma_buf = tup->uport.state->xmit.buf; dma_sconfig.dst_addr = tup->uport.mapbase; dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; dma_sconfig.dst_maxburst = 16; @@ -1643,7 +1648,7 @@ static const struct dev_pm_ops tegra_uart_pm_ops = { static struct platform_driver tegra_uart_platform_driver = { .probe = tegra_uart_probe, - .remove_new = tegra_uart_remove, + .remove = tegra_uart_remove, .driver = { .name = "serial-tegra", .of_match_table = tegra_uart_of_match, diff --git a/drivers/tty/serial/serial_base.h b/drivers/tty/serial/serial_base.h index c74c548f0db6..0d50db5b660b 100644 --- a/drivers/tty/serial/serial_base.h +++ b/drivers/tty/serial/serial_base.h @@ -22,6 +22,7 @@ struct serial_ctrl_device { struct serial_port_device { struct device dev; struct uart_port *port; + unsigned int tx_enabled:1; }; int serial_base_ctrl_init(void); @@ -30,6 +31,9 @@ void serial_base_ctrl_exit(void); int serial_base_port_init(void); void serial_base_port_exit(void); +void serial_base_port_startup(struct uart_port *port); +void serial_base_port_shutdown(struct uart_port *port); + int serial_base_driver_register(struct device_driver *driver); void serial_base_driver_unregister(struct device_driver *driver); @@ -45,3 +49,19 @@ void serial_ctrl_unregister_port(struct uart_driver *drv, struct uart_port *port int serial_core_register_port(struct uart_driver *drv, struct uart_port *port); void serial_core_unregister_port(struct uart_driver *drv, struct uart_port *port); + +#ifdef CONFIG_SERIAL_CORE_CONSOLE + +int serial_base_match_and_update_preferred_console(struct uart_driver *drv, + struct uart_port *port); + +#else + +static inline +int serial_base_match_and_update_preferred_console(struct uart_driver *drv, + struct uart_port *port) +{ + return 0; +} + +#endif diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c index 4df2a4b10445..22749ab0428a 100644 --- a/drivers/tty/serial/serial_base_bus.c +++ b/drivers/tty/serial/serial_base_bus.c @@ -8,10 +8,12 @@ * The serial core bus manages the serial core controller instances. */ +#include <linux/cleanup.h> #include <linux/container_of.h> #include <linux/device.h> #include <linux/idr.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/serial_core.h> #include <linux/slab.h> #include <linux/spinlock.h> @@ -28,7 +30,7 @@ static const struct device_type serial_port_type = { .name = "port", }; -static int serial_base_match(struct device *dev, struct device_driver *drv) +static int serial_base_match(struct device *dev, const struct device_driver *drv) { if (dev->type == &serial_ctrl_type && str_has_prefix(drv->name, serial_ctrl_type.name)) @@ -71,6 +73,7 @@ static int serial_base_device_init(struct uart_port *port, dev->parent = parent_dev; dev->bus = &serial_base_bus_type; dev->release = release; + device_set_of_node_from_dev(dev, parent_dev); if (!serial_base_initialized) { dev_dbg(port->dev, "uart_add_one_port() called before arch_initcall()?\n"); @@ -91,6 +94,7 @@ static void serial_base_ctrl_release(struct device *dev) { struct serial_ctrl_device *ctrl_dev = to_serial_base_ctrl_device(dev); + of_node_put(dev->of_node); kfree(ctrl_dev); } @@ -138,6 +142,7 @@ static void serial_base_port_release(struct device *dev) { struct serial_port_device *port_dev = to_serial_base_port_device(dev); + of_node_put(dev->of_node); kfree(port_dev); } @@ -204,6 +209,42 @@ void serial_base_port_device_remove(struct serial_port_device *port_dev) put_device(&port_dev->dev); } +#ifdef CONFIG_SERIAL_CORE_CONSOLE + +/** + * serial_base_match_and_update_preferred_console - Match and update a preferred console + * @drv: Serial port device driver + * @port: Serial port instance + * + * Tries to match and update the preferred console for a serial port for + * the kernel command line option console=DEVNAME:0.0. + * + * Cannot be called early for ISA ports, depends on struct device. + * + * Return: 0 on success, negative error code on failure. + */ +int serial_base_match_and_update_preferred_console(struct uart_driver *drv, + struct uart_port *port) +{ + const char *port_match __free(kfree) = NULL; + int ret; + + port_match = kasprintf(GFP_KERNEL, "%s:%d.%d", dev_name(port->dev), + port->ctrl_id, port->port_id); + if (!port_match) + return -ENOMEM; + + ret = match_devname_and_update_preferred_console(port_match, + drv->dev_name, + port->line); + if (ret == -ENOENT) + return 0; + + return ret; +} + +#endif + static int serial_base_init(void) { int ret; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index ff85ebd3a007..1f7708a91fc6 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -75,22 +75,23 @@ static inline void uart_port_deref(struct uart_port *uport) wake_up(&uport->state->remove_wait); } -#define uart_port_lock(state, flags) \ - ({ \ - struct uart_port *__uport = uart_port_ref(state); \ - if (__uport) \ - uart_port_lock_irqsave(__uport, &flags); \ - __uport; \ - }) - -#define uart_port_unlock(uport, flags) \ - ({ \ - struct uart_port *__uport = uport; \ - if (__uport) { \ - uart_port_unlock_irqrestore(__uport, flags); \ - uart_port_deref(__uport); \ - } \ - }) +static inline struct uart_port *uart_port_ref_lock(struct uart_state *state, unsigned long *flags) +{ + struct uart_port *uport = uart_port_ref(state); + + if (uport) + uart_port_lock_irqsave(uport, flags); + + return uport; +} + +static inline void uart_port_unlock_deref(struct uart_port *uport, unsigned long flags) +{ + if (uport) { + uart_port_unlock_irqrestore(uport, flags); + uart_port_deref(uport); + } +} static inline struct uart_port *uart_port_check(struct uart_state *state) { @@ -127,10 +128,10 @@ static void uart_stop(struct tty_struct *tty) struct uart_port *port; unsigned long flags; - port = uart_port_lock(state, flags); + port = uart_port_ref_lock(state, &flags); if (port) port->ops->stop_tx(port); - uart_port_unlock(port, flags); + uart_port_unlock_deref(port, flags); } static void __uart_start(struct uart_state *state) @@ -156,7 +157,7 @@ static void __uart_start(struct uart_state *state) * enabled, serial_port_runtime_resume() calls start_tx() again * after enabling the device. */ - if (pm_runtime_active(&port_dev->dev)) + if (!pm_runtime_enabled(port->dev) || pm_runtime_active(&port_dev->dev)) port->ops->start_tx(port); pm_runtime_mark_last_busy(&port_dev->dev); pm_runtime_put_autosuspend(&port_dev->dev); @@ -168,9 +169,9 @@ static void uart_start(struct tty_struct *tty) struct uart_port *port; unsigned long flags; - port = uart_port_lock(state, flags); + port = uart_port_ref_lock(state, &flags); __uart_start(state); - uart_port_unlock(port, flags); + uart_port_unlock_deref(port, flags); } static void @@ -243,25 +244,12 @@ static void uart_change_line_settings(struct tty_struct *tty, struct uart_state uart_port_unlock_irq(uport); } -/* - * Startup the port. This will be called once per open. All calls - * will be serialised by the per-port mutex. - */ -static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, - bool init_hw) +static int uart_alloc_xmit_buf(struct tty_port *port) { - struct uart_port *uport = uart_port_check(state); + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport; unsigned long flags; unsigned long page; - int retval = 0; - - if (uport->type == PORT_UNKNOWN) - return 1; - - /* - * Make sure the device is in D0 state. - */ - uart_change_pm(state, UART_PM_STATE_ON); /* * Initialise and allocate the transmit and temporary @@ -271,20 +259,68 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, if (!page) return -ENOMEM; - uart_port_lock(state, flags); - if (!state->xmit.buf) { - state->xmit.buf = (unsigned char *) page; - uart_circ_clear(&state->xmit); - uart_port_unlock(uport, flags); + uport = uart_port_ref_lock(state, &flags); + if (!state->port.xmit_buf) { + state->port.xmit_buf = (unsigned char *)page; + kfifo_init(&state->port.xmit_fifo, state->port.xmit_buf, + PAGE_SIZE); + uart_port_unlock_deref(uport, flags); } else { - uart_port_unlock(uport, flags); + uart_port_unlock_deref(uport, flags); /* * Do not free() the page under the port lock, see - * uart_shutdown(). + * uart_free_xmit_buf(). */ free_page(page); } + return 0; +} + +static void uart_free_xmit_buf(struct tty_port *port) +{ + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport; + unsigned long flags; + char *xmit_buf; + + /* + * Do not free() the transmit buffer page under the port lock since + * this can create various circular locking scenarios. For instance, + * console driver may need to allocate/free a debug object, which + * can end up in printk() recursion. + */ + uport = uart_port_ref_lock(state, &flags); + xmit_buf = port->xmit_buf; + port->xmit_buf = NULL; + INIT_KFIFO(port->xmit_fifo); + uart_port_unlock_deref(uport, flags); + + free_page((unsigned long)xmit_buf); +} + +/* + * Startup the port. This will be called once per open. All calls + * will be serialised by the per-port mutex. + */ +static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, + bool init_hw) +{ + struct uart_port *uport = uart_port_check(state); + int retval; + + if (uport->type == PORT_UNKNOWN) + return 1; + + /* + * Make sure the device is in D0 state. + */ + uart_change_pm(state, UART_PM_STATE_ON); + + retval = uart_alloc_xmit_buf(&state->port); + if (retval) + return retval; + retval = uport->ops->startup(uport); if (retval == 0) { if (uart_console(uport) && uport->cons->cflag) { @@ -323,16 +359,26 @@ static int uart_startup(struct tty_struct *tty, struct uart_state *state, bool init_hw) { struct tty_port *port = &state->port; + struct uart_port *uport; int retval; if (tty_port_initialized(port)) - return 0; + goto out_base_port_startup; retval = uart_port_startup(tty, state, init_hw); - if (retval) + if (retval) { set_bit(TTY_IO_ERROR, &tty->flags); + return retval; + } - return retval; +out_base_port_startup: + uport = uart_port_check(state); + if (!uport) + return -EIO; + + serial_base_port_startup(uport); + + return 0; } /* @@ -346,8 +392,6 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) { struct uart_port *uport = uart_port_check(state); struct tty_port *port = &state->port; - unsigned long flags; - char *xmit_buf = NULL; /* * Set the TTY IO error marker @@ -355,20 +399,25 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) if (tty) set_bit(TTY_IO_ERROR, &tty->flags); + if (uport) + serial_base_port_shutdown(uport); + if (tty_port_initialized(port)) { tty_port_set_initialized(port, false); /* * Turn off DTR and RTS early. */ - if (uport && uart_console(uport) && tty) { - uport->cons->cflag = tty->termios.c_cflag; - uport->cons->ispeed = tty->termios.c_ispeed; - uport->cons->ospeed = tty->termios.c_ospeed; - } + if (uport) { + if (uart_console(uport) && tty) { + uport->cons->cflag = tty->termios.c_cflag; + uport->cons->ispeed = tty->termios.c_ispeed; + uport->cons->ospeed = tty->termios.c_ospeed; + } - if (!tty || C_HUPCL(tty)) - uart_port_dtr_rts(uport, false); + if (!tty || C_HUPCL(tty)) + uart_port_dtr_rts(uport, false); + } uart_port_shutdown(port); } @@ -380,18 +429,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) */ tty_port_set_suspended(port, false); - /* - * Do not free() the transmit buffer page under the port lock since - * this can create various circular locking scenarios. For instance, - * console driver may need to allocate/free a debug object, which - * can endup in printk() recursion. - */ - uart_port_lock(state, flags); - xmit_buf = state->xmit.buf; - state->xmit.buf = NULL; - uart_port_unlock(uport, flags); - - free_page((unsigned long)xmit_buf); + uart_free_xmit_buf(port); } /** @@ -552,23 +590,18 @@ static int uart_put_char(struct tty_struct *tty, u8 c) { struct uart_state *state = tty->driver_data; struct uart_port *port; - struct circ_buf *circ; unsigned long flags; int ret = 0; - circ = &state->xmit; - port = uart_port_lock(state, flags); - if (!circ->buf) { - uart_port_unlock(port, flags); + port = uart_port_ref_lock(state, &flags); + if (!state->port.xmit_buf) { + uart_port_unlock_deref(port, flags); return 0; } - if (port && uart_circ_chars_free(circ) != 0) { - circ->buf[circ->head] = c; - circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); - ret = 1; - } - uart_port_unlock(port, flags); + if (port) + ret = kfifo_put(&state->port.xmit_fifo, c); + uart_port_unlock_deref(port, flags); return ret; } @@ -581,9 +614,8 @@ static ssize_t uart_write(struct tty_struct *tty, const u8 *buf, size_t count) { struct uart_state *state = tty->driver_data; struct uart_port *port; - struct circ_buf *circ; unsigned long flags; - int c, ret = 0; + int ret = 0; /* * This means you called this function _after_ the port was @@ -592,28 +624,17 @@ static ssize_t uart_write(struct tty_struct *tty, const u8 *buf, size_t count) if (WARN_ON(!state)) return -EL3HLT; - port = uart_port_lock(state, flags); - circ = &state->xmit; - if (!circ->buf) { - uart_port_unlock(port, flags); + port = uart_port_ref_lock(state, &flags); + if (!state->port.xmit_buf) { + uart_port_unlock_deref(port, flags); return 0; } - while (port) { - c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); - if (count < c) - c = count; - if (c <= 0) - break; - memcpy(circ->buf + circ->head, buf, c); - circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); - buf += c; - count -= c; - ret += c; - } + if (port) + ret = kfifo_in(&state->port.xmit_fifo, buf, count); __uart_start(state); - uart_port_unlock(port, flags); + uart_port_unlock_deref(port, flags); return ret; } @@ -624,9 +645,9 @@ static unsigned int uart_write_room(struct tty_struct *tty) unsigned long flags; unsigned int ret; - port = uart_port_lock(state, flags); - ret = uart_circ_chars_free(&state->xmit); - uart_port_unlock(port, flags); + port = uart_port_ref_lock(state, &flags); + ret = kfifo_avail(&state->port.xmit_fifo); + uart_port_unlock_deref(port, flags); return ret; } @@ -637,9 +658,9 @@ static unsigned int uart_chars_in_buffer(struct tty_struct *tty) unsigned long flags; unsigned int ret; - port = uart_port_lock(state, flags); - ret = uart_circ_chars_pending(&state->xmit); - uart_port_unlock(port, flags); + port = uart_port_ref_lock(state, &flags); + ret = kfifo_len(&state->port.xmit_fifo); + uart_port_unlock_deref(port, flags); return ret; } @@ -658,13 +679,13 @@ static void uart_flush_buffer(struct tty_struct *tty) pr_debug("uart_flush_buffer(%d) called\n", tty->index); - port = uart_port_lock(state, flags); + port = uart_port_ref_lock(state, &flags); if (!port) return; - uart_circ_clear(&state->xmit); + kfifo_reset(&state->port.xmit_fifo); if (port->ops->flush_buffer) port->ops->flush_buffer(port); - uart_port_unlock(port, flags); + uart_port_unlock_deref(port, flags); tty_port_tty_wakeup(&state->port); } @@ -770,7 +791,6 @@ static int uart_get_info(struct tty_port *port, struct serial_struct *retinfo) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport; - int ret = -ENODEV; /* Initialize structure in case we error out later to prevent any stack info leakage. */ *retinfo = (struct serial_struct){}; @@ -779,10 +799,10 @@ static int uart_get_info(struct tty_port *port, struct serial_struct *retinfo) * Ensure the state we copy is consistent and no hardware changes * occur as we go */ - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); uport = uart_port_check(state); if (!uport) - goto out; + return -ENODEV; retinfo->type = uport->type; retinfo->line = uport->line; @@ -803,10 +823,7 @@ static int uart_get_info(struct tty_port *port, struct serial_struct *retinfo) retinfo->iomem_reg_shift = uport->regshift; retinfo->iomem_base = (void *)(unsigned long)uport->mapbase; - ret = 0; -out: - mutex_unlock(&port->mutex); - return ret; + return 0; } static int uart_get_info_user(struct tty_struct *tty, @@ -818,16 +835,71 @@ static int uart_get_info_user(struct tty_struct *tty, return uart_get_info(port, ss) < 0 ? -EIO : 0; } +static int uart_change_port(struct uart_port *uport, + const struct serial_struct *new_info, + unsigned long new_port) +{ + unsigned long old_iobase, old_mapbase; + unsigned int old_type, old_iotype, old_hub6, old_shift; + int retval; + + old_iobase = uport->iobase; + old_mapbase = uport->mapbase; + old_type = uport->type; + old_hub6 = uport->hub6; + old_iotype = uport->iotype; + old_shift = uport->regshift; + + if (old_type != PORT_UNKNOWN && uport->ops->release_port) + uport->ops->release_port(uport); + + uport->iobase = new_port; + uport->type = new_info->type; + uport->hub6 = new_info->hub6; + uport->iotype = new_info->io_type; + uport->regshift = new_info->iomem_reg_shift; + uport->mapbase = (unsigned long)new_info->iomem_base; + + if (uport->type == PORT_UNKNOWN || !uport->ops->request_port) + return 0; + + retval = uport->ops->request_port(uport); + if (retval == 0) + return 0; /* succeeded => done */ + + /* + * If we fail to request resources for the new port, try to restore the + * old settings. + */ + uport->iobase = old_iobase; + uport->type = old_type; + uport->hub6 = old_hub6; + uport->iotype = old_iotype; + uport->regshift = old_shift; + uport->mapbase = old_mapbase; + + if (old_type == PORT_UNKNOWN) + return retval; + + retval = uport->ops->request_port(uport); + /* If we failed to restore the old settings, we fail like this. */ + if (retval) + uport->type = PORT_UNKNOWN; + + /* We failed anyway. */ + return -EBUSY; +} + static int uart_set_info(struct tty_struct *tty, struct tty_port *port, struct uart_state *state, struct serial_struct *new_info) { struct uart_port *uport = uart_port_check(state); unsigned long new_port; - unsigned int change_irq, change_port, closing_wait; - unsigned int old_custom_divisor, close_delay; + unsigned int old_custom_divisor, close_delay, closing_wait; + bool change_irq, change_port; upf_t old_flags, new_flags; - int retval = 0; + int retval; if (!uport) return -EIO; @@ -863,8 +935,13 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, new_flags = (__force upf_t)new_info->flags; old_custom_divisor = uport->custom_divisor; + if (!(uport->flags & UPF_FIXED_PORT)) { + unsigned int uartclk = new_info->baud_base * 16; + /* check needs to be done here before other settings made */ + if (uartclk == 0) + return -EINVAL; + } if (!capable(CAP_SYS_ADMIN)) { - retval = -EPERM; if (change_irq || change_port || (new_info->baud_base != uport->uartclk / 16) || (close_delay != port->close_delay) || @@ -872,7 +949,7 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, (new_info->xmit_fifo_size && new_info->xmit_fifo_size != uport->fifosize) || (((new_flags ^ old_flags) & ~UPF_USR_MASK) != 0)) - goto exit; + return -EPERM; uport->flags = ((uport->flags & ~UPF_USR_MASK) | (new_flags & UPF_USR_MASK)); uport->custom_divisor = new_info->custom_divisor; @@ -882,30 +959,24 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, if (change_irq || change_port) { retval = security_locked_down(LOCKDOWN_TIOCSSERIAL); if (retval) - goto exit; + return retval; } - /* - * Ask the low level driver to verify the settings. - */ - if (uport->ops->verify_port) + /* Ask the low level driver to verify the settings. */ + if (uport->ops->verify_port) { retval = uport->ops->verify_port(uport, new_info); + if (retval) + return retval; + } - if ((new_info->irq >= nr_irqs) || (new_info->irq < 0) || + if ((new_info->irq >= irq_get_nr_irqs()) || (new_info->irq < 0) || (new_info->baud_base < 9600)) - retval = -EINVAL; - - if (retval) - goto exit; + return -EINVAL; if (change_port || change_irq) { - retval = -EBUSY; - - /* - * Make sure that we are the sole user of this port. - */ + /* Make sure that we are the sole user of this port. */ if (tty_port_users(port) > 1) - goto exit; + return -EBUSY; /* * We need to shutdown the serial port at the old @@ -915,69 +986,9 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, } if (change_port) { - unsigned long old_iobase, old_mapbase; - unsigned int old_type, old_iotype, old_hub6, old_shift; - - old_iobase = uport->iobase; - old_mapbase = uport->mapbase; - old_type = uport->type; - old_hub6 = uport->hub6; - old_iotype = uport->iotype; - old_shift = uport->regshift; - - /* - * Free and release old regions - */ - if (old_type != PORT_UNKNOWN && uport->ops->release_port) - uport->ops->release_port(uport); - - uport->iobase = new_port; - uport->type = new_info->type; - uport->hub6 = new_info->hub6; - uport->iotype = new_info->io_type; - uport->regshift = new_info->iomem_reg_shift; - uport->mapbase = (unsigned long)new_info->iomem_base; - - /* - * Claim and map the new regions - */ - if (uport->type != PORT_UNKNOWN && uport->ops->request_port) { - retval = uport->ops->request_port(uport); - } else { - /* Always success - Jean II */ - retval = 0; - } - - /* - * If we fail to request resources for the - * new port, try to restore the old settings. - */ - if (retval) { - uport->iobase = old_iobase; - uport->type = old_type; - uport->hub6 = old_hub6; - uport->iotype = old_iotype; - uport->regshift = old_shift; - uport->mapbase = old_mapbase; - - if (old_type != PORT_UNKNOWN) { - retval = uport->ops->request_port(uport); - /* - * If we failed to restore the old settings, - * we fail like this. - */ - if (retval) - uport->type = PORT_UNKNOWN; - - /* - * We failed anyway. - */ - retval = -EBUSY; - } - - /* Added to return the correct error -Ram Gupta */ - goto exit; - } + retval = uart_change_port(uport, new_info, new_port); + if (retval) + return retval; } if (change_irq) @@ -993,9 +1004,9 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, uport->fifosize = new_info->xmit_fifo_size; check_and_exit: - retval = 0; if (uport->type == PORT_UNKNOWN) - goto exit; + return 0; + if (tty_port_initialized(port)) { if (((old_flags ^ uport->flags) & UPF_SPD_MASK) || old_custom_divisor != uport->custom_divisor) { @@ -1011,15 +1022,17 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, } uart_change_line_settings(tty, state, NULL); } - } else { - retval = uart_startup(tty, state, true); - if (retval == 0) - tty_port_set_initialized(port, true); - if (retval > 0) - retval = 0; + + return 0; } - exit: - return retval; + + retval = uart_startup(tty, state, true); + if (retval < 0) + return retval; + if (retval == 0) + tty_port_set_initialized(port, true); + + return 0; } static int uart_set_info_user(struct tty_struct *tty, struct serial_struct *ss) @@ -1064,7 +1077,7 @@ static int uart_get_lsr_info(struct tty_struct *tty, * interrupt happens). */ if (uport->x_char || - ((uart_circ_chars_pending(&state->xmit) > 0) && + (!kfifo_is_empty(&state->port.xmit_fifo) && !uart_tx_stopped(uport))) result &= ~TIOCSER_TEMT; @@ -1076,21 +1089,19 @@ static int uart_tiocmget(struct tty_struct *tty) struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; struct uart_port *uport; - int result = -EIO; + int result; + + guard(mutex)(&port->mutex); - mutex_lock(&port->mutex); uport = uart_port_check(state); - if (!uport) - goto out; + if (!uport || tty_io_error(tty)) + return -EIO; + + uart_port_lock_irq(uport); + result = uport->mctrl; + result |= uport->ops->get_mctrl(uport); + uart_port_unlock_irq(uport); - if (!tty_io_error(tty)) { - uart_port_lock_irq(uport); - result = uport->mctrl; - result |= uport->ops->get_mctrl(uport); - uart_port_unlock_irq(uport); - } -out: - mutex_unlock(&port->mutex); return result; } @@ -1100,20 +1111,16 @@ uart_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; struct uart_port *uport; - int ret = -EIO; - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); + uport = uart_port_check(state); - if (!uport) - goto out; + if (!uport || tty_io_error(tty)) + return -EIO; - if (!tty_io_error(tty)) { - uart_update_mctrl(uport, set, clear); - ret = 0; - } -out: - mutex_unlock(&port->mutex); - return ret; + uart_update_mctrl(uport, set, clear); + + return 0; } static int uart_break_ctl(struct tty_struct *tty, int break_state) @@ -1121,19 +1128,17 @@ static int uart_break_ctl(struct tty_struct *tty, int break_state) struct uart_state *state = tty->driver_data; struct tty_port *port = &state->port; struct uart_port *uport; - int ret = -EIO; - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); + uport = uart_port_check(state); if (!uport) - goto out; + return -EIO; if (uport->type != PORT_UNKNOWN && uport->ops->break_ctl) uport->ops->break_ctl(uport, break_state); - ret = 0; -out: - mutex_unlock(&port->mutex); - return ret; + + return 0; } static int uart_do_autoconfig(struct tty_struct *tty, struct uart_state *state) @@ -1150,17 +1155,14 @@ static int uart_do_autoconfig(struct tty_struct *tty, struct uart_state *state) * changing, and hence any extra opens of the port while * we're auto-configuring. */ - if (mutex_lock_interruptible(&port->mutex)) - return -ERESTARTSYS; + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &port->mutex) { + uport = uart_port_check(state); + if (!uport) + return -EIO; - uport = uart_port_check(state); - if (!uport) { - ret = -EIO; - goto out; - } + if (tty_port_users(port) != 1) + return -EBUSY; - ret = -EBUSY; - if (tty_port_users(port) == 1) { uart_shutdown(tty, state); /* @@ -1181,14 +1183,15 @@ static int uart_do_autoconfig(struct tty_struct *tty, struct uart_state *state) uport->ops->config_port(uport, flags); ret = uart_startup(tty, state, true); - if (ret == 0) - tty_port_set_initialized(port, true); + if (ret < 0) + return ret; if (ret > 0) - ret = 0; + return 0; + + tty_port_set_initialized(port, true); } -out: - mutex_unlock(&port->mutex); - return ret; + + return 0; } static void uart_enable_ms(struct uart_port *uport) @@ -1273,14 +1276,13 @@ static int uart_get_icount(struct tty_struct *tty, struct uart_state *state = tty->driver_data; struct uart_icount cnow; struct uart_port *uport; + unsigned long flags; - uport = uart_port_ref(state); + uport = uart_port_ref_lock(state, &flags); if (!uport) return -EIO; - uart_port_lock_irq(uport); memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); - uart_port_unlock_irq(uport); - uart_port_deref(uport); + uart_port_unlock_deref(uport, flags); icount->cts = cnow.cts; icount->dsr = cnow.dsr; @@ -1683,10 +1685,11 @@ static void uart_set_termios(struct tty_struct *tty, unsigned int iflag_mask = IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK; bool sw_changed = false; - mutex_lock(&state->port.mutex); + guard(mutex)(&state->port.mutex); + uport = uart_port_check(state); if (!uport) - goto out; + return; /* * Drivers doing software flow control also need to know @@ -1709,9 +1712,8 @@ static void uart_set_termios(struct tty_struct *tty, tty->termios.c_ospeed == old_termios->c_ospeed && tty->termios.c_ispeed == old_termios->c_ispeed && ((tty->termios.c_iflag ^ old_termios->c_iflag) & iflag_mask) == 0 && - !sw_changed) { - goto out; - } + !sw_changed) + return; uart_change_line_settings(tty, state, old_termios); /* reload cflag from termios; port driver may have overridden flags */ @@ -1728,8 +1730,6 @@ static void uart_set_termios(struct tty_struct *tty, mask |= TIOCM_RTS; uart_set_mctrl(uport, mask); } -out: - mutex_unlock(&state->port.mutex); } /* @@ -1762,7 +1762,6 @@ static void uart_tty_port_shutdown(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport = uart_port_check(state); - char *buf; /* * At this point, we stop accepting input. To do this, we @@ -1775,6 +1774,7 @@ static void uart_tty_port_shutdown(struct tty_port *port) uport->ops->stop_rx(uport); uart_port_unlock_irq(uport); + serial_base_port_shutdown(uport); uart_port_shutdown(port); /* @@ -1784,15 +1784,7 @@ static void uart_tty_port_shutdown(struct tty_port *port) */ tty_port_set_suspended(port, false); - /* - * Free the transmit buffer. - */ - uart_port_lock_irq(uport); - buf = state->xmit.buf; - state->xmit.buf = NULL; - uart_port_unlock_irq(uport); - - free_page((unsigned long)buf); + uart_free_xmit_buf(port); uart_change_pm(state, UART_PM_STATE_OFF); } @@ -1922,9 +1914,10 @@ static bool uart_carrier_raised(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport; + unsigned long flags; int mctrl; - uport = uart_port_ref(state); + uport = uart_port_ref_lock(state, &flags); /* * Should never observe uport == NULL since checks for hangup should * abort the tty_port_block_til_ready() loop before checking for carrier @@ -1933,11 +1926,9 @@ static bool uart_carrier_raised(struct tty_port *port) */ if (WARN_ON(!uport)) return true; - uart_port_lock_irq(uport); uart_enable_ms(uport); mctrl = uport->ops->get_mctrl(uport); - uart_port_unlock_irq(uport); - uart_port_deref(uport); + uart_port_unlock_deref(uport, flags); return mctrl & TIOCM_CAR; } @@ -2021,9 +2012,8 @@ static const char *uart_type(struct uart_port *port) #ifdef CONFIG_PROC_FS -static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) +static void uart_line_info(struct seq_file *m, struct uart_state *state) { - struct uart_state *state = drv->state + i; struct tty_port *port = &state->port; enum uart_pm_state pm_state; struct uart_port *uport; @@ -2031,10 +2021,11 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) unsigned int status; int mmio; - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); + uport = uart_port_check(state); if (!uport) - goto out; + return; mmio = uport->iotype >= UPIO_MEM; seq_printf(m, "%d: uart:%s %s%08llX irq:%d", @@ -2046,7 +2037,7 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) if (uport->type == PORT_UNKNOWN) { seq_putc(m, '\n'); - goto out; + return; } if (capable(CAP_SYS_ADMIN)) { @@ -2097,8 +2088,6 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) seq_putc(m, '\n'); #undef STATBIT #undef INFOBIT -out: - mutex_unlock(&port->mutex); } static int uart_proc_show(struct seq_file *m, void *v) @@ -2109,7 +2098,7 @@ static int uart_proc_show(struct seq_file *m, void *v) seq_printf(m, "serinfo:1.0 driver%s%s revision:%s\n", "", "", ""); for (i = 0; i < drv->nr; i++) - uart_line_info(m, drv, i); + uart_line_info(m, drv->state + i); return 0; } #endif @@ -2188,8 +2177,8 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co) * * Returns: 0 on success or -%EINVAL on failure */ -int uart_parse_earlycon(char *p, unsigned char *iotype, resource_size_t *addr, - char **options) +int uart_parse_earlycon(char *p, enum uart_iotype *iotype, + resource_size_t *addr, char **options) { if (strncmp(p, "mmio,", 5) == 0) { *iotype = UPIO_MEM; @@ -2358,9 +2347,9 @@ struct uart_match { struct uart_driver *driver; }; -static int serial_match_port(struct device *dev, void *data) +static int serial_match_port(struct device *dev, const void *data) { - struct uart_match *match = data; + const struct uart_match *match = data; struct tty_driver *tty_drv = match->driver->tty_driver; dev_t devt = MKDEV(tty_drv->major, tty_drv->minor_start) + match->port->line; @@ -2375,13 +2364,12 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) struct device *tty_dev; struct uart_match match = {uport, drv}; - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); tty_dev = device_find_child(&uport->port_dev->dev, &match, serial_match_port); if (tty_dev && device_may_wakeup(tty_dev)) { enable_irq_wake(uport->irq); put_device(tty_dev); - mutex_unlock(&port->mutex); return 0; } put_device(tty_dev); @@ -2398,7 +2386,8 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) uport->ops->stop_rx(uport); uart_port_unlock_irq(uport); } - goto unlock; + device_set_awake_path(uport->dev); + return 0; } uport->suspended = 1; @@ -2435,14 +2424,12 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) } /* - * Disable the console device before suspending. + * Suspend the console device before suspending the port. */ if (uart_console(uport)) - console_stop(uport->cons); + console_suspend(uport->cons); uart_change_pm(state, UART_PM_STATE_OFF); -unlock: - mutex_unlock(&port->mutex); return 0; } @@ -2456,14 +2443,13 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) struct uart_match match = {uport, drv}; struct ktermios termios; - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); tty_dev = device_find_child(&uport->port_dev->dev, &match, serial_match_port); if (!uport->suspended && device_may_wakeup(tty_dev)) { if (irqd_is_wakeup_set(irq_get_irq_data((uport->irq)))) disable_irq_wake(uport->irq); put_device(tty_dev); - mutex_unlock(&port->mutex); return 0; } put_device(tty_dev); @@ -2496,7 +2482,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) uart_port_unlock_irq(uport); } if (console_suspend_enabled) - console_start(uport->cons); + console_resume(uport->cons); } if (tty_port_suspended(port)) { @@ -2536,8 +2522,6 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) tty_port_set_suspended(port, false); } - mutex_unlock(&port->mutex); - return 0; } EXPORT_SYMBOL(uart_resume_port); @@ -2677,14 +2661,13 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options) int ret = 0; tport = &state->port; - mutex_lock(&tport->mutex); + + guard(mutex)(&tport->mutex); port = uart_port_check(state); if (!port || port->type == PORT_UNKNOWN || - !(port->ops->poll_get_char && port->ops->poll_put_char)) { - ret = -1; - goto out; - } + !(port->ops->poll_get_char && port->ops->poll_put_char)) + return -1; pm_state = state->pm_state; uart_change_pm(state, UART_PM_STATE_ON); @@ -2704,10 +2687,10 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options) ret = uart_set_options(port, NULL, baud, parity, bits, flow); console_list_unlock(); } -out: + if (ret) uart_change_pm(state, pm_state); - mutex_unlock(&tport->mutex); + return ret; } @@ -3060,26 +3043,25 @@ static ssize_t console_store(struct device *dev, if (ret) return ret; - mutex_lock(&port->mutex); + guard(mutex)(&port->mutex); uport = uart_port_check(state); - if (uport) { - oldconsole = uart_console_registered(uport); - if (oldconsole && !newconsole) { - ret = unregister_console(uport->cons); - } else if (!oldconsole && newconsole) { - if (uart_console(uport)) { - uport->console_reinit = 1; - register_console(uport->cons); - } else { - ret = -ENOENT; - } - } - } else { - ret = -ENXIO; + if (!uport) + return -ENXIO; + + oldconsole = uart_console_registered(uport); + if (oldconsole && !newconsole) { + ret = unregister_console(uport->cons); + if (ret < 0) + return ret; + } else if (!oldconsole && newconsole) { + if (!uart_console(uport)) + return -ENOENT; + + uport->console_reinit = 1; + register_console(uport->cons); } - mutex_unlock(&port->mutex); - return ret < 0 ? ret : count; + return count; } static DEVICE_ATTR_RO(uartclk); @@ -3135,7 +3117,6 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u { struct uart_state *state; struct tty_port *port; - int ret = 0; struct device *tty_dev; int num_groups; @@ -3145,11 +3126,9 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u state = drv->state + uport->line; port = &state->port; - mutex_lock(&port->mutex); - if (state->uart_port) { - ret = -EINVAL; - goto out; - } + guard(mutex)(&port->mutex); + if (state->uart_port) + return -EINVAL; /* Link the port to the driver state table and vice versa */ atomic_set(&state->refcount, 1); @@ -3157,16 +3136,6 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u state->uart_port = uport; uport->state = state; - state->pm_state = UART_PM_STATE_UNDEFINED; - uport->cons = drv->cons; - uport->minor = drv->tty_driver->minor_start + uport->line; - uport->name = kasprintf(GFP_KERNEL, "%s%d", drv->dev_name, - drv->tty_driver->name_base + uport->line); - if (!uport->name) { - ret = -ENOMEM; - goto out; - } - /* * If this port is in use as a console then the spinlock is already * initialised. @@ -3174,10 +3143,17 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u if (!uart_console_registered(uport)) uart_port_spin_lock_init(uport); + state->pm_state = UART_PM_STATE_UNDEFINED; + uart_port_set_cons(uport, drv->cons); + uport->minor = drv->tty_driver->minor_start + uport->line; + uport->name = kasprintf(GFP_KERNEL, "%s%d", drv->dev_name, + drv->tty_driver->name_base + uport->line); + if (!uport->name) + return -ENOMEM; + if (uport->cons && uport->dev) of_console_check(uport->dev->of_node, uport->cons->name, uport->line); - tty_port_link_device(port, drv->tty_driver, uport->line); uart_configure_port(drv, state, uport); port->console = uart_console(uport); @@ -3188,14 +3164,16 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u uport->tty_groups = kcalloc(num_groups, sizeof(*uport->tty_groups), GFP_KERNEL); - if (!uport->tty_groups) { - ret = -ENOMEM; - goto out; - } + if (!uport->tty_groups) + return -ENOMEM; + uport->tty_groups[0] = &tty_dev_attr_group; if (uport->attr_group) uport->tty_groups[1] = uport->attr_group; + /* Ensure serdev drivers can call serdev_device_open() right away */ + uport->flags &= ~UPF_DEAD; + /* * Register the port whether it's detected or not. This allows * setserial to be used to alter this port's parameters. @@ -3206,14 +3184,12 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u if (!IS_ERR(tty_dev)) { device_set_wakeup_capable(tty_dev, 1); } else { + uport->flags |= UPF_DEAD; dev_err(uport->dev, "Cannot register tty device on line %d\n", uport->line); } - out: - mutex_unlock(&port->mutex); - - return ret; + return 0; } /** @@ -3312,9 +3288,9 @@ bool uart_match_port(const struct uart_port *port1, case UPIO_AU: case UPIO_TSI: return port1->mapbase == port2->mapbase; + default: + return false; } - - return false; } EXPORT_SYMBOL(uart_match_port); @@ -3379,7 +3355,7 @@ int serial_core_register_port(struct uart_driver *drv, struct uart_port *port) struct serial_ctrl_device *ctrl_dev, *new_ctrl_dev = NULL; int ret; - mutex_lock(&port_mutex); + guard(mutex)(&port_mutex); /* * Prevent serial_port_runtime_resume() from trying to use the port @@ -3391,10 +3367,8 @@ int serial_core_register_port(struct uart_driver *drv, struct uart_port *port) ctrl_dev = serial_core_ctrl_find(drv, port->dev, port->ctrl_id); if (!ctrl_dev) { new_ctrl_dev = serial_core_ctrl_device_add(port); - if (IS_ERR(new_ctrl_dev)) { - ret = PTR_ERR(new_ctrl_dev); - goto err_unlock; - } + if (IS_ERR(new_ctrl_dev)) + return PTR_ERR(new_ctrl_dev); ctrl_dev = new_ctrl_dev; } @@ -3407,13 +3381,13 @@ int serial_core_register_port(struct uart_driver *drv, struct uart_port *port) if (ret) goto err_unregister_ctrl_dev; - ret = serial_core_add_one_port(drv, port); + ret = serial_base_match_and_update_preferred_console(drv, port); if (ret) goto err_unregister_port_dev; - port->flags &= ~UPF_DEAD; - - mutex_unlock(&port_mutex); + ret = serial_core_add_one_port(drv, port); + if (ret) + goto err_unregister_port_dev; return 0; @@ -3423,9 +3397,6 @@ err_unregister_port_dev: err_unregister_ctrl_dev: serial_base_ctrl_device_remove(new_ctrl_dev); -err_unlock: - mutex_unlock(&port_mutex); - return ret; } diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c index e51ca593ab86..7b02c5ca4afd 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.c +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -217,7 +217,7 @@ static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context) * * This will get the {cts,rts,...}-gpios from device tree if they are present * and request them, set direction etc, and return an allocated structure. - * `devm_*` functions are used, so there's no need to call mctrl_gpio_free(). + * `devm_*` functions are used, so there's no need to explicitly free. * As this sets up the irq handling, make sure to not handle changes to the * gpio input lines in your driver, too. */ @@ -268,32 +268,6 @@ struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx) EXPORT_SYMBOL_GPL(mctrl_gpio_init); /** - * mctrl_gpio_free - explicitly free uart gpios - * @dev: uart port's device - * @gpios: gpios structure to be freed - * - * This will free the requested gpios in mctrl_gpio_init(). As `devm_*` - * functions are used, there's generally no need to call this function. - */ -void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios) -{ - enum mctrl_gpio_idx i; - - if (gpios == NULL) - return; - - for (i = 0; i < UART_GPIO_MAX; i++) { - if (gpios->irq[i]) - devm_free_irq(gpios->port->dev, gpios->irq[i], gpios); - - if (gpios->gpio[i]) - devm_gpiod_put(dev, gpios->gpio[i]); - } - devm_kfree(dev, gpios); -} -EXPORT_SYMBOL_GPL(mctrl_gpio_free); - -/** * mctrl_gpio_enable_ms - enable irqs and handling of changes to the ms lines * @gpios: gpios to enable */ @@ -322,11 +296,7 @@ void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios) } EXPORT_SYMBOL_GPL(mctrl_gpio_enable_ms); -/** - * mctrl_gpio_disable_ms - disable irqs and handling of changes to the ms lines - * @gpios: gpios to disable - */ -void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios) +static void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios, bool sync) { enum mctrl_gpio_idx i; @@ -342,10 +312,34 @@ void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios) if (!gpios->irq[i]) continue; - disable_irq(gpios->irq[i]); + if (sync) + disable_irq(gpios->irq[i]); + else + disable_irq_nosync(gpios->irq[i]); } } -EXPORT_SYMBOL_GPL(mctrl_gpio_disable_ms); + +/** + * mctrl_gpio_disable_ms_sync - disable irqs and handling of changes to the ms + * lines, and wait for any pending IRQ to be processed + * @gpios: gpios to disable + */ +void mctrl_gpio_disable_ms_sync(struct mctrl_gpios *gpios) +{ + mctrl_gpio_disable_ms(gpios, true); +} +EXPORT_SYMBOL_GPL(mctrl_gpio_disable_ms_sync); + +/** + * mctrl_gpio_disable_ms_no_sync - disable irqs and handling of changes to the + * ms lines, and return immediately + * @gpios: gpios to disable + */ +void mctrl_gpio_disable_ms_no_sync(struct mctrl_gpios *gpios) +{ + mctrl_gpio_disable_ms(gpios, false); +} +EXPORT_SYMBOL_GPL(mctrl_gpio_disable_ms_no_sync); void mctrl_gpio_enable_irq_wake(struct mctrl_gpios *gpios) { @@ -385,4 +379,5 @@ void mctrl_gpio_disable_irq_wake(struct mctrl_gpios *gpios) } EXPORT_SYMBOL_GPL(mctrl_gpio_disable_irq_wake); +MODULE_DESCRIPTION("Helpers for controlling modem lines via GPIO"); MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/serial_mctrl_gpio.h b/drivers/tty/serial/serial_mctrl_gpio.h index fc76910fb105..961e4ba0c6f8 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.h +++ b/drivers/tty/serial/serial_mctrl_gpio.h @@ -59,7 +59,7 @@ struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, /* * Request and set direction of modem control line GPIOs and set up irq * handling. - * devm_* functions are used, so there's no need to call mctrl_gpio_free(). + * devm_* functions are used, so there's no need to explicitly free. * Returns a pointer to the allocated mctrl structure if ok, -ENOMEM on * allocation error. */ @@ -67,7 +67,7 @@ struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx); /* * Request and set direction of modem control line GPIOs. - * devm_* functions are used, so there's no need to call mctrl_gpio_free(). + * devm_* functions are used, so there's no need to explicitly free. * Returns a pointer to the allocated mctrl structure if ok, -ENOMEM on * allocation error. */ @@ -75,21 +75,21 @@ struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx); /* - * Free the mctrl_gpios structure. - * Normally, this function will not be called, as the GPIOs will - * be disposed of by the resource management code. + * Enable gpio interrupts to report status line changes. */ -void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios); +void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios); /* - * Enable gpio interrupts to report status line changes. + * Disable gpio interrupts to report status line changes, and block until + * any corresponding IRQ is processed */ -void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios); +void mctrl_gpio_disable_ms_sync(struct mctrl_gpios *gpios); /* - * Disable gpio interrupts to report status line changes. + * Disable gpio interrupts to report status line changes, and return + * immediately */ -void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios); +void mctrl_gpio_disable_ms_no_sync(struct mctrl_gpios *gpios); /* * Enable gpio wakeup interrupts to enable wake up source. @@ -139,16 +139,15 @@ struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx) return NULL; } -static inline -void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios) +static inline void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios) { } -static inline void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios) +static inline void mctrl_gpio_disable_ms_sync(struct mctrl_gpios *gpios) { } -static inline void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios) +static inline void mctrl_gpio_disable_ms_no_sync(struct mctrl_gpios *gpios) { } diff --git a/drivers/tty/serial/serial_port.c b/drivers/tty/serial/serial_port.c index 22b9eeb23e68..2fc48cd63f6c 100644 --- a/drivers/tty/serial/serial_port.c +++ b/drivers/tty/serial/serial_port.c @@ -11,6 +11,7 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/pnp.h> #include <linux/property.h> #include <linux/serial_core.h> #include <linux/spinlock.h> @@ -23,7 +24,7 @@ static int __serial_port_busy(struct uart_port *port) { return !uart_tx_stopped(port) && - uart_circ_chars_pending(&port->state->xmit); + !kfifo_is_empty(&port->state->port.xmit_fifo); } static int serial_port_runtime_resume(struct device *dev) @@ -39,8 +40,12 @@ static int serial_port_runtime_resume(struct device *dev) /* Flush any pending TX for the port */ uart_port_lock_irqsave(port, &flags); + if (!port_dev->tx_enabled) + goto unlock; if (__serial_port_busy(port)) port->ops->start_tx(port); + +unlock: uart_port_unlock_irqrestore(port, flags); out: @@ -59,7 +64,19 @@ static int serial_port_runtime_suspend(struct device *dev) if (port->flags & UPF_DEAD) return 0; + /* + * Nothing to do on pm_runtime_force_suspend(), see + * DEFINE_RUNTIME_DEV_PM_OPS. + */ + if (!pm_runtime_enabled(dev)) + return 0; + uart_port_lock_irqsave(port, &flags); + if (!port_dev->tx_enabled) { + uart_port_unlock_irqrestore(port, flags); + return 0; + } + busy = __serial_port_busy(port); if (busy) port->ops->start_tx(port); @@ -71,6 +88,31 @@ static int serial_port_runtime_suspend(struct device *dev) return busy ? -EBUSY : 0; } +static void serial_base_port_set_tx(struct uart_port *port, + struct serial_port_device *port_dev, + bool enabled) +{ + unsigned long flags; + + uart_port_lock_irqsave(port, &flags); + port_dev->tx_enabled = enabled; + uart_port_unlock_irqrestore(port, flags); +} + +void serial_base_port_startup(struct uart_port *port) +{ + struct serial_port_device *port_dev = port->port_dev; + + serial_base_port_set_tx(port, port_dev, true); +} + +void serial_base_port_shutdown(struct uart_port *port) +{ + struct serial_port_device *port_dev = port->port_dev; + + serial_base_port_set_tx(port, port_dev, false); +} + static DEFINE_RUNTIME_DEV_PM_OPS(serial_port_pm, serial_port_runtime_suspend, serial_port_runtime_resume, NULL); @@ -131,6 +173,7 @@ EXPORT_SYMBOL(uart_remove_one_port); * The caller is responsible to initialize the following fields of the @port * ->dev (must be valid) * ->flags + * ->iobase * ->mapbase * ->mapsize * ->regshift (if @use_defaults is false) @@ -172,7 +215,7 @@ static int __uart_read_properties(struct uart_port *port, bool use_defaults) /* Read the registers I/O access type (default: MMIO 8-bit) */ ret = device_property_read_u32(dev, "reg-io-width", &value); if (ret) { - port->iotype = UPIO_MEM; + port->iotype = port->iobase ? UPIO_PORT : UPIO_MEM; } else { switch (value) { case 1: @@ -185,15 +228,16 @@ static int __uart_read_properties(struct uart_port *port, bool use_defaults) port->iotype = device_is_big_endian(dev) ? UPIO_MEM32BE : UPIO_MEM32; break; default: - if (!use_defaults) { - dev_err(dev, "Unsupported reg-io-width (%u)\n", value); - return -EINVAL; - } port->iotype = UPIO_UNKNOWN; break; } } + if (!use_defaults && port->iotype == UPIO_UNKNOWN) { + dev_err(dev, "Unsupported reg-io-width (%u)\n", value); + return -EINVAL; + } + /* Read the address mapping base offset (default: no offset) */ ret = device_property_read_u32(dev, "reg-offset", &value); if (ret) @@ -221,7 +265,11 @@ static int __uart_read_properties(struct uart_port *port, bool use_defaults) if (dev_is_platform(dev)) ret = platform_get_irq(to_platform_device(dev), 0); - else + else if (dev_is_pnp(dev)) { + ret = pnp_irq(to_pnp_dev(dev), 0); + if (ret < 0) + ret = -ENXIO; + } else ret = fwnode_irq_get(dev_fwnode(dev), 0); if (ret == -EPROBE_DEFER) return ret; diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c index abba39722958..436a559234df 100644 --- a/drivers/tty/serial/serial_txx9.c +++ b/drivers/tty/serial/serial_txx9.c @@ -1097,7 +1097,7 @@ static int serial_txx9_resume(struct platform_device *dev) static struct platform_driver serial_txx9_plat_driver = { .probe = serial_txx9_probe, - .remove_new = serial_txx9_remove, + .remove = serial_txx9_remove, #ifdef CONFIG_PM .suspend = serial_txx9_suspend, .resume = serial_txx9_resume, diff --git a/drivers/tty/serial/sh-sci-common.h b/drivers/tty/serial/sh-sci-common.h new file mode 100644 index 000000000000..bd9d9cfac1c8 --- /dev/null +++ b/drivers/tty/serial/sh-sci-common.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __SH_SCI_COMMON_H__ +#define __SH_SCI_COMMON_H__ + +#include <linux/serial_core.h> + +enum SCI_CLKS { + SCI_FCK, /* Functional Clock */ + SCI_SCK, /* Optional External Clock */ + SCI_BRG_INT, /* Optional BRG Internal Clock Source */ + SCI_SCIF_CLK, /* Optional BRG External Clock Source */ + SCI_NUM_CLKS +}; + +/* Offsets into the sci_port->irqs array */ +enum { + SCIx_ERI_IRQ, + SCIx_RXI_IRQ, + SCIx_TXI_IRQ, + SCIx_BRI_IRQ, + SCIx_DRI_IRQ, + SCIx_TEI_IRQ, + SCIx_NR_IRQS, + + SCIx_MUX_IRQ = SCIx_NR_IRQS, /* special case */ +}; + +/* Bit x set means sampling rate x + 1 is supported */ +#define SCI_SR(x) BIT((x) - 1) +#define SCI_SR_RANGE(x, y) GENMASK((y) - 1, (x) - 1) + +void sci_release_port(struct uart_port *port); +int sci_request_port(struct uart_port *port); +void sci_config_port(struct uart_port *port, int flags); +int sci_verify_port(struct uart_port *port, struct serial_struct *ser); +void sci_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate); + +struct plat_sci_reg { + u8 offset; + u8 size; +}; + +struct sci_port_params_bits { + unsigned int rxtx_enable; + unsigned int te_clear; + unsigned int poll_sent_bits; +}; + +struct sci_common_regs { + unsigned int status; + unsigned int control; +}; + +/* The actual number of needed registers. This is used by sci only */ +#define SCI_NR_REGS 20 + +struct sci_port_params { + const struct plat_sci_reg regs[SCI_NR_REGS]; + const struct sci_common_regs *common_regs; + const struct sci_port_params_bits *param_bits; + unsigned int fifosize; + unsigned int overrun_reg; + unsigned int overrun_mask; + unsigned int sampling_rate_mask; + unsigned int error_mask; + unsigned int error_clear; +}; + +struct sci_port_ops { + u32 (*read_reg)(struct uart_port *port, int reg); + void (*write_reg)(struct uart_port *port, int reg, int value); + void (*clear_SCxSR)(struct uart_port *port, unsigned int mask); + + void (*transmit_chars)(struct uart_port *port); + void (*receive_chars)(struct uart_port *port); + + void (*poll_put_char)(struct uart_port *port, unsigned char c); + + int (*set_rtrg)(struct uart_port *port, int rx_trig); + int (*rtrg_enabled)(struct uart_port *port); + + void (*shutdown_complete)(struct uart_port *port); + + void (*prepare_console_write)(struct uart_port *port, u32 ctrl); + void (*console_save)(struct uart_port *port); + void (*console_restore)(struct uart_port *port); + size_t (*suspend_regs_size)(void); +}; + +struct sci_of_data { + const struct sci_port_params *params; + const struct uart_ops *uart_ops; + const struct sci_port_ops *ops; + unsigned short regtype; + unsigned short type; +}; + +struct sci_port { + struct uart_port port; + + /* Platform configuration */ + const struct sci_port_params *params; + const struct plat_sci_port *cfg; + + unsigned int sampling_rate_mask; + resource_size_t reg_size; + struct mctrl_gpios *gpios; + + /* Clocks */ + struct clk *clks[SCI_NUM_CLKS]; + unsigned long clk_rates[SCI_NUM_CLKS]; + + int irqs[SCIx_NR_IRQS]; + char *irqstr[SCIx_NR_IRQS]; + + struct dma_chan *chan_tx; + struct dma_chan *chan_rx; + + struct reset_control *rstc; + struct sci_suspend_regs *suspend_regs; + +#ifdef CONFIG_SERIAL_SH_SCI_DMA + struct dma_chan *chan_tx_saved; + struct dma_chan *chan_rx_saved; + dma_cookie_t cookie_tx; + dma_cookie_t cookie_rx[2]; + dma_cookie_t active_rx; + dma_addr_t tx_dma_addr; + unsigned int tx_dma_len; + struct scatterlist sg_rx[2]; + void *rx_buf[2]; + size_t buf_len_rx; + struct work_struct work_tx; + struct hrtimer rx_timer; + unsigned int rx_timeout; /* microseconds */ +#endif + unsigned int rx_frame; + int rx_trigger; + struct timer_list rx_fifo_timer; + int rx_fifo_timeout; + u16 hscif_tot; + + const struct sci_port_ops *ops; + + bool has_rtscts; + bool autorts; + bool tx_occurred; +}; + +#define to_sci_port(uart) container_of((uart), struct sci_port, port) + +void sci_port_disable(struct sci_port *sci_port); +void sci_port_enable(struct sci_port *sci_port); + +int sci_startup(struct uart_port *port); +void sci_shutdown(struct uart_port *port); + +#define min_sr(_port) ffs((_port)->sampling_rate_mask) +#define max_sr(_port) fls((_port)->sampling_rate_mask) + +#ifdef CONFIG_SERIAL_SH_SCI_EARLYCON +int __init scix_early_console_setup(struct earlycon_device *device, const struct sci_of_data *data); +#endif + +#endif /* __SH_SCI_COMMON_H__ */ diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index e512eaa57ed5..1c356544a832 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -56,19 +56,7 @@ #include "serial_mctrl_gpio.h" #include "sh-sci.h" - -/* Offsets into the sci_port->irqs array */ -enum { - SCIx_ERI_IRQ, - SCIx_RXI_IRQ, - SCIx_TXI_IRQ, - SCIx_BRI_IRQ, - SCIx_DRI_IRQ, - SCIx_TEI_IRQ, - SCIx_NR_IRQS, - - SCIx_MUX_IRQ = SCIx_NR_IRQS, /* special case */ -}; +#include "sh-sci-common.h" #define SCIx_IRQ_IS_MUXED(port) \ ((port)->irqs[SCIx_ERI_IRQ] == \ @@ -76,99 +64,57 @@ enum { ((port)->irqs[SCIx_ERI_IRQ] && \ ((port)->irqs[SCIx_RXI_IRQ] < 0)) -enum SCI_CLKS { - SCI_FCK, /* Functional Clock */ - SCI_SCK, /* Optional External Clock */ - SCI_BRG_INT, /* Optional BRG Internal Clock Source */ - SCI_SCIF_CLK, /* Optional BRG External Clock Source */ - SCI_NUM_CLKS -}; - -/* Bit x set means sampling rate x + 1 is supported */ -#define SCI_SR(x) BIT((x) - 1) -#define SCI_SR_RANGE(x, y) GENMASK((y) - 1, (x) - 1) - #define SCI_SR_SCIFAB SCI_SR(5) | SCI_SR(7) | SCI_SR(11) | \ SCI_SR(13) | SCI_SR(16) | SCI_SR(17) | \ SCI_SR(19) | SCI_SR(27) -#define min_sr(_port) ffs((_port)->sampling_rate_mask) -#define max_sr(_port) fls((_port)->sampling_rate_mask) - /* Iterate over all supported sampling rates, from high to low */ #define for_each_sr(_sr, _port) \ for ((_sr) = max_sr(_port); (_sr) >= min_sr(_port); (_sr)--) \ if ((_port)->sampling_rate_mask & SCI_SR((_sr))) -struct plat_sci_reg { - u8 offset, size; -}; - -struct sci_port_params { - const struct plat_sci_reg regs[SCIx_NR_REGS]; - unsigned int fifosize; - unsigned int overrun_reg; - unsigned int overrun_mask; - unsigned int sampling_rate_mask; - unsigned int error_mask; - unsigned int error_clear; -}; - -struct sci_port { - struct uart_port port; - - /* Platform configuration */ - const struct sci_port_params *params; - const struct plat_sci_port *cfg; - unsigned int sampling_rate_mask; - resource_size_t reg_size; - struct mctrl_gpios *gpios; - - /* Clocks */ - struct clk *clks[SCI_NUM_CLKS]; - unsigned long clk_rates[SCI_NUM_CLKS]; +#define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS - int irqs[SCIx_NR_IRQS]; - char *irqstr[SCIx_NR_IRQS]; +static struct sci_port sci_ports[SCI_NPORTS]; +static unsigned long sci_ports_in_use; +static struct uart_driver sci_uart_driver; +static bool sci_uart_earlycon; +static bool sci_uart_earlycon_dev_probing; - struct dma_chan *chan_tx; - struct dma_chan *chan_rx; +static const struct sci_port_params_bits sci_sci_port_params_bits = { + .rxtx_enable = SCSCR_RE | SCSCR_TE, + .te_clear = SCSCR_TE | SCSCR_TEIE, + .poll_sent_bits = SCI_TDRE | SCI_TEND +}; -#ifdef CONFIG_SERIAL_SH_SCI_DMA - struct dma_chan *chan_tx_saved; - struct dma_chan *chan_rx_saved; - dma_cookie_t cookie_tx; - dma_cookie_t cookie_rx[2]; - dma_cookie_t active_rx; - dma_addr_t tx_dma_addr; - unsigned int tx_dma_len; - struct scatterlist sg_rx[2]; - void *rx_buf[2]; - size_t buf_len_rx; - struct work_struct work_tx; - struct hrtimer rx_timer; - unsigned int rx_timeout; /* microseconds */ -#endif - unsigned int rx_frame; - int rx_trigger; - struct timer_list rx_fifo_timer; - int rx_fifo_timeout; - u16 hscif_tot; - - bool has_rtscts; - bool autorts; +static const struct sci_port_params_bits sci_scif_port_params_bits = { + .rxtx_enable = SCSCR_RE | SCSCR_TE, + .te_clear = SCSCR_TE | SCSCR_TEIE, + .poll_sent_bits = SCIF_TDFE | SCIF_TEND }; -#define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS +static const struct sci_common_regs sci_common_regs = { + .status = SCxSR, + .control = SCSCR, +}; -static struct sci_port sci_ports[SCI_NPORTS]; -static unsigned long sci_ports_in_use; -static struct uart_driver sci_uart_driver; +struct sci_suspend_regs { + u16 scdl; + u16 sccks; + u16 scsmr; + u16 scscr; + u16 scfcr; + u16 scsptr; + u16 hssrr; + u16 scpcr; + u16 scpdr; + u8 scbrr; + u8 semr; +}; -static inline struct sci_port * -to_sci_port(struct uart_port *uart) +static size_t sci_suspend_regs_size(void) { - return container_of(uart, struct sci_port, port); + return sizeof(struct sci_suspend_regs); } static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { @@ -191,6 +137,8 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { .sampling_rate_mask = SCI_SR(32), .error_mask = SCI_DEFAULT_ERROR_MASK | SCI_ORER, .error_clear = SCI_ERROR_CLEAR & ~SCI_ORER, + .param_bits = &sci_sci_port_params_bits, + .common_regs = &sci_common_regs, }, /* @@ -213,6 +161,8 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { .sampling_rate_mask = SCI_SR(32), .error_mask = SCI_DEFAULT_ERROR_MASK | SCI_ORER, .error_clear = SCI_ERROR_CLEAR & ~SCI_ORER, + .param_bits = &sci_scif_port_params_bits, + .common_regs = &sci_common_regs, }, /* @@ -237,6 +187,8 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { .sampling_rate_mask = SCI_SR_SCIFAB, .error_mask = SCIF_DEFAULT_ERROR_MASK | SCIFA_ORER, .error_clear = SCIF_ERROR_CLEAR & ~SCIFA_ORER, + .param_bits = &sci_scif_port_params_bits, + .common_regs = &sci_common_regs, }, /* @@ -262,6 +214,8 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { .sampling_rate_mask = SCI_SR_SCIFAB, .error_mask = SCIF_DEFAULT_ERROR_MASK | SCIFA_ORER, .error_clear = SCIF_ERROR_CLEAR & ~SCIFA_ORER, + .param_bits = &sci_scif_port_params_bits, + .common_regs = &sci_common_regs, }, /* @@ -287,10 +241,12 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { .sampling_rate_mask = SCI_SR(32), .error_mask = SCIF_DEFAULT_ERROR_MASK, .error_clear = SCIF_ERROR_CLEAR, + .param_bits = &sci_scif_port_params_bits, + .common_regs = &sci_common_regs, }, /* - * The "SCIFA" that is in RZ/A2, RZ/G2L and RZ/T. + * The "SCIFA" that is in RZ/A2, RZ/G2L and RZ/T1. * It looks like a normal SCIF with FIFO data, but with a * compressed address space. Also, the break out of interrupts * are different: ERI/BRI, RXI, TXI, TEI, DRI. @@ -315,6 +271,41 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { .sampling_rate_mask = SCI_SR(32), .error_mask = SCIF_DEFAULT_ERROR_MASK, .error_clear = SCIF_ERROR_CLEAR, + .param_bits = &sci_scif_port_params_bits, + .common_regs = &sci_common_regs, + }, + + /* + * The "SCIF" that is in RZ/V2H(P) SoC is similar to one found on RZ/G2L SoC + * with below differences, + * - Break out of interrupts are different: ERI, BRI, RXI, TXI, TEI, DRI, + * TEI-DRI, RXI-EDGE and TXI-EDGE. + * - SCSMR register does not have CM bit (BIT(7)) ie it does not support synchronous mode. + * - SCFCR register does not have SCFCR_MCE bit. + * - SCSPTR register has only bits SCSPTR_SPB2DT and SCSPTR_SPB2IO. + */ + [SCIx_RZV2H_SCIF_REGTYPE] = { + .regs = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x02, 8 }, + [SCSCR] = { 0x04, 16 }, + [SCxTDR] = { 0x06, 8 }, + [SCxSR] = { 0x08, 16 }, + [SCxRDR] = { 0x0a, 8 }, + [SCFCR] = { 0x0c, 16 }, + [SCFDR] = { 0x0e, 16 }, + [SCSPTR] = { 0x10, 16 }, + [SCLSR] = { 0x12, 16 }, + [SEMR] = { 0x14, 8 }, + }, + .fifosize = 16, + .overrun_reg = SCLSR, + .overrun_mask = SCLSR_ORER, + .sampling_rate_mask = SCI_SR(32), + .error_mask = SCIF_DEFAULT_ERROR_MASK, + .error_clear = SCIF_ERROR_CLEAR, + .param_bits = &sci_scif_port_params_bits, + .common_regs = &sci_common_regs, }, /* @@ -337,6 +328,8 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { .sampling_rate_mask = SCI_SR(32), .error_mask = SCIF_DEFAULT_ERROR_MASK, .error_clear = SCIF_ERROR_CLEAR, + .param_bits = &sci_scif_port_params_bits, + .common_regs = &sci_common_regs, }, /* @@ -361,6 +354,8 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { .sampling_rate_mask = SCI_SR(32), .error_mask = SCIF_DEFAULT_ERROR_MASK, .error_clear = SCIF_ERROR_CLEAR, + .param_bits = &sci_scif_port_params_bits, + .common_regs = &sci_common_regs, }, /* @@ -388,6 +383,8 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { .sampling_rate_mask = SCI_SR(32), .error_mask = SCIF_DEFAULT_ERROR_MASK, .error_clear = SCIF_ERROR_CLEAR, + .param_bits = &sci_scif_port_params_bits, + .common_regs = &sci_common_regs, }, /* @@ -417,6 +414,8 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { .sampling_rate_mask = SCI_SR_RANGE(8, 32), .error_mask = SCIF_DEFAULT_ERROR_MASK, .error_clear = SCIF_ERROR_CLEAR, + .param_bits = &sci_scif_port_params_bits, + .common_regs = &sci_common_regs, }, /* @@ -441,6 +440,8 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { .sampling_rate_mask = SCI_SR(32), .error_mask = SCIF_DEFAULT_ERROR_MASK, .error_clear = SCIF_ERROR_CLEAR, + .param_bits = &sci_scif_port_params_bits, + .common_regs = &sci_common_regs, }, /* @@ -468,6 +469,8 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { .sampling_rate_mask = SCI_SR(32), .error_mask = SCIF_DEFAULT_ERROR_MASK, .error_clear = SCIF_ERROR_CLEAR, + .param_bits = &sci_scif_port_params_bits, + .common_regs = &sci_common_regs, }, /* @@ -491,6 +494,8 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { .sampling_rate_mask = SCI_SR(16), .error_mask = SCIF_DEFAULT_ERROR_MASK | SCIFA_ORER, .error_clear = SCIF_ERROR_CLEAR & ~SCIFA_ORER, + .param_bits = &sci_scif_port_params_bits, + .common_regs = &sci_common_regs, }, }; @@ -528,7 +533,7 @@ static void sci_serial_out(struct uart_port *p, int offset, int value) WARN(1, "Invalid register access\n"); } -static void sci_port_enable(struct sci_port *sci_port) +void sci_port_enable(struct sci_port *sci_port) { unsigned int i; @@ -544,7 +549,7 @@ static void sci_port_enable(struct sci_port *sci_port) sci_port->port.uartclk = sci_port->clk_rates[SCI_FCK]; } -static void sci_port_disable(struct sci_port *sci_port) +void sci_port_disable(struct sci_port *sci_port) { unsigned int i; @@ -585,7 +590,7 @@ static void sci_start_tx(struct uart_port *port) sci_serial_out(port, SCSCR, new); } - if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) && + if (s->chan_tx && !kfifo_is_empty(&port->state->port.xmit_fifo) && dma_submit_error(s->cookie_tx)) { if (s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) /* Switch irq from SCIF to DMA */ @@ -684,12 +689,13 @@ static void sci_clear_SCxSR(struct uart_port *port, unsigned int mask) static int sci_poll_get_char(struct uart_port *port) { unsigned short status; + struct sci_port *s = to_sci_port(port); int c; do { status = sci_serial_in(port, SCxSR); if (status & SCxSR_ERRORS(port)) { - sci_clear_SCxSR(port, SCxSR_ERROR_CLEAR(port)); + s->ops->clear_SCxSR(port, SCxSR_ERROR_CLEAR(port)); continue; } break; @@ -702,7 +708,7 @@ static int sci_poll_get_char(struct uart_port *port) /* Dummy read */ sci_serial_in(port, SCxSR); - sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port)); + s->ops->clear_SCxSR(port, SCxSR_RDxF_CLEAR(port)); return c; } @@ -710,14 +716,16 @@ static int sci_poll_get_char(struct uart_port *port) static void sci_poll_put_char(struct uart_port *port, unsigned char c) { - unsigned short status; + struct sci_port *s = to_sci_port(port); + const struct sci_common_regs *regs = s->params->common_regs; + unsigned int status; do { - status = sci_serial_in(port, SCxSR); + status = s->ops->read_reg(port, regs->status); } while (!(status & SCxSR_TDxE(port))); sci_serial_out(port, SCxTDR, c); - sci_clear_SCxSR(port, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port)); + s->ops->clear_SCxSR(port, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port)); } #endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE || CONFIG_SERIAL_SH_SCI_EARLYCON */ @@ -757,7 +765,7 @@ static void sci_init_pins(struct uart_port *port, unsigned int cflag) } sci_serial_out(port, SCPDR, data); sci_serial_out(port, SCPCR, ctrl); - } else if (sci_getreg(port, SCSPTR)->size) { + } else if (sci_getreg(port, SCSPTR)->size && s->cfg->regtype != SCIx_RZV2H_SCIF_REGTYPE) { u16 status = sci_serial_in(port, SCSPTR); /* RTS# is always output; and active low, unless autorts */ @@ -817,8 +825,9 @@ static int sci_rxfill(struct uart_port *port) static void sci_transmit_chars(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; unsigned int stopped = uart_tx_stopped(port); + struct sci_port *s = to_sci_port(port); unsigned short status; unsigned short ctrl; int count; @@ -826,7 +835,7 @@ static void sci_transmit_chars(struct uart_port *port) status = sci_serial_in(port, SCxSR); if (!(status & SCxSR_TDxE(port))) { ctrl = sci_serial_in(port, SCSCR); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) ctrl &= ~SCSCR_TIE; else ctrl |= SCSCR_TIE; @@ -842,28 +851,28 @@ static void sci_transmit_chars(struct uart_port *port) if (port->x_char) { c = port->x_char; port->x_char = 0; - } else if (!uart_circ_empty(xmit) && !stopped) { - c = xmit->buf[xmit->tail]; - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - } else if (port->type == PORT_SCI && uart_circ_empty(xmit)) { - ctrl = sci_serial_in(port, SCSCR); - ctrl &= ~SCSCR_TE; - sci_serial_out(port, SCSCR, ctrl); - return; - } else { + } else if (stopped || !kfifo_get(&tport->xmit_fifo, &c)) { + if (port->type == PORT_SCI && + kfifo_is_empty(&tport->xmit_fifo)) { + ctrl = sci_serial_in(port, SCSCR); + ctrl &= ~SCSCR_TE; + sci_serial_out(port, SCSCR, ctrl); + return; + } break; } sci_serial_out(port, SCxTDR, c); + s->tx_occurred = true; port->icount.tx++; } while (--count > 0); - sci_clear_SCxSR(port, SCxSR_TDxE_CLEAR(port)); + s->ops->clear_SCxSR(port, SCxSR_TDxE_CLEAR(port)); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) { + if (kfifo_is_empty(&tport->xmit_fifo)) { if (port->type == PORT_SCI) { ctrl = sci_serial_in(port, SCSCR); ctrl &= ~SCSCR_TIE; @@ -878,6 +887,7 @@ static void sci_transmit_chars(struct uart_port *port) static void sci_receive_chars(struct uart_port *port) { struct tty_port *tport = &port->state->port; + struct sci_port *s = to_sci_port(port); int i, count, copied = 0; unsigned short status; unsigned char flag; @@ -932,7 +942,7 @@ static void sci_receive_chars(struct uart_port *port) } sci_serial_in(port, SCxSR); /* dummy read */ - sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port)); + s->ops->clear_SCxSR(port, SCxSR_RDxF_CLEAR(port)); copied += count; port->icount.rx += count; @@ -945,16 +955,17 @@ static void sci_receive_chars(struct uart_port *port) /* TTY buffers full; read from RX reg to prevent lockup */ sci_serial_in(port, SCxRDR); sci_serial_in(port, SCxSR); /* dummy read */ - sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port)); + s->ops->clear_SCxSR(port, SCxSR_RDxF_CLEAR(port)); } } static int sci_handle_errors(struct uart_port *port) { int copied = 0; - unsigned short status = sci_serial_in(port, SCxSR); - struct tty_port *tport = &port->state->port; struct sci_port *s = to_sci_port(port); + const struct sci_common_regs *regs = s->params->common_regs; + unsigned int status = s->ops->read_reg(port, regs->status); + struct tty_port *tport = &port->state->port; /* Handle overruns */ if (status & s->params->overrun_mask) { @@ -1109,11 +1120,11 @@ static int scif_rtrg_enabled(struct uart_port *port) static void rx_fifo_timer_fn(struct timer_list *t) { - struct sci_port *s = from_timer(s, t, rx_fifo_timer); + struct sci_port *s = timer_container_of(s, t, rx_fifo_timer); struct uart_port *port = &s->port; dev_dbg(port->dev, "Rx timed out\n"); - scif_set_rtrg(port, 1); + s->ops->set_rtrg(port, 1); } static ssize_t rx_fifo_trigger_show(struct device *dev, @@ -1138,9 +1149,9 @@ static ssize_t rx_fifo_trigger_store(struct device *dev, if (ret) return ret; - sci->rx_trigger = scif_set_rtrg(port, r); + sci->rx_trigger = sci->ops->set_rtrg(port, r); if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) - scif_set_rtrg(port, 1); + sci->ops->set_rtrg(port, 1); return count; } @@ -1183,7 +1194,7 @@ static ssize_t rx_fifo_timeout_store(struct device *dev, sci->hscif_tot = r << HSSCR_TOT_SHIFT; } else { sci->rx_fifo_timeout = r; - scif_set_rtrg(port, 1); + sci->ops->set_rtrg(port, 1); if (r > 0) timer_setup(&sci->rx_fifo_timer, rx_fifo_timer_fn, 0); } @@ -1199,7 +1210,7 @@ static void sci_dma_tx_complete(void *arg) { struct sci_port *s = arg; struct uart_port *port = &s->port; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; unsigned long flags; dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); @@ -1208,10 +1219,12 @@ static void sci_dma_tx_complete(void *arg) uart_xmit_advance(port, s->tx_dma_len); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (!uart_circ_empty(xmit)) { + s->tx_occurred = true; + + if (!kfifo_is_empty(&tport->xmit_fifo)) { s->cookie_tx = 0; schedule_work(&s->work_tx); } else { @@ -1258,6 +1271,7 @@ static int sci_dma_rx_find_active(struct sci_port *s) return -1; } +/* Must only be called with uart_port_lock taken */ static void sci_dma_rx_chan_invalidate(struct sci_port *s) { unsigned int i; @@ -1271,9 +1285,14 @@ static void sci_dma_rx_chan_invalidate(struct sci_port *s) static void sci_dma_rx_release(struct sci_port *s) { struct dma_chan *chan = s->chan_rx_saved; + struct uart_port *port = &s->port; + unsigned long flags; + uart_port_lock_irqsave(port, &flags); s->chan_rx_saved = NULL; sci_dma_rx_chan_invalidate(s); + uart_port_unlock_irqrestore(port, flags); + dmaengine_terminate_sync(chan); dma_free_coherent(chan->device->dev, s->buf_len_rx * 2, s->rx_buf[0], sg_dma_address(&s->sg_rx[0])); @@ -1300,7 +1319,7 @@ static void sci_dma_rx_reenable_irq(struct sci_port *s) s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) { enable_irq(s->irqs[SCIx_RXI_IRQ]); if (s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) - scif_set_rtrg(port, s->rx_trigger); + s->ops->set_rtrg(port, s->rx_trigger); else scr &= ~SCSCR_RDRQE; } @@ -1319,14 +1338,14 @@ static void sci_dma_rx_complete(void *arg) dev_dbg(port->dev, "%s(%d) active cookie %d\n", __func__, port->line, s->active_rx); + hrtimer_cancel(&s->rx_timer); + uart_port_lock_irqsave(port, &flags); active = sci_dma_rx_find_active(s); if (active >= 0) count = sci_dma_rx_push(s, s->rx_buf[active], s->buf_len_rx); - start_hrtimer_us(&s->rx_timer, s->rx_timeout); - if (count) tty_flip_buffer_push(&port->state->port); @@ -1349,17 +1368,18 @@ static void sci_dma_rx_complete(void *arg) uart_port_unlock_irqrestore(port, flags); dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n", __func__, s->cookie_rx[active], active, s->active_rx); + + start_hrtimer_us(&s->rx_timer, s->rx_timeout); + return; fail: - uart_port_unlock_irqrestore(port, flags); - dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); /* Switch to PIO */ - uart_port_lock_irqsave(port, &flags); dmaengine_terminate_async(chan); sci_dma_rx_chan_invalidate(s); sci_dma_rx_reenable_irq(s); uart_port_unlock_irqrestore(port, flags); + dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); } static void sci_dma_tx_release(struct sci_port *s) @@ -1424,10 +1444,10 @@ static void sci_dma_tx_work_fn(struct work_struct *work) struct dma_async_tx_descriptor *desc; struct dma_chan *chan = s->chan_tx; struct uart_port *port = &s->port; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; unsigned long flags; + unsigned int tail; dma_addr_t buf; - int head, tail; /* * DMA is idle now. @@ -1437,10 +1457,9 @@ static void sci_dma_tx_work_fn(struct work_struct *work) * consistent xmit buffer state. */ uart_port_lock_irq(port); - head = xmit->head; - tail = xmit->tail; + s->tx_dma_len = kfifo_out_linear(&tport->xmit_fifo, &tail, + UART_XMIT_SIZE); buf = s->tx_dma_addr + tail; - s->tx_dma_len = CIRC_CNT_TO_END(head, tail, UART_XMIT_SIZE); if (!s->tx_dma_len) { /* Transmit buffer has been flushed */ uart_port_unlock_irq(port); @@ -1469,8 +1488,8 @@ static void sci_dma_tx_work_fn(struct work_struct *work) } uart_port_unlock_irq(port); - dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", - __func__, xmit->buf, tail, head, s->cookie_tx); + dev_dbg(port->dev, "%s: %p: %u, cookie %d\n", + __func__, tport->xmit_buf, tail, s->cookie_tx); dma_async_issue_pending(chan); return; @@ -1585,6 +1604,7 @@ static struct dma_chan *sci_request_dma_chan(struct uart_port *port, static void sci_request_dma(struct uart_port *port) { struct sci_port *s = to_sci_port(port); + struct tty_port *tport = &port->state->port; struct dma_chan *chan; dev_dbg(port->dev, "%s: port %d\n", __func__, port->line); @@ -1613,7 +1633,7 @@ static void sci_request_dma(struct uart_port *port) if (chan) { /* UART circular tx buffer is an aligned page. */ s->tx_dma_addr = dma_map_single(chan->device->dev, - port->state->xmit.buf, + tport->xmit_buf, UART_XMIT_SIZE, DMA_TO_DEVICE); if (dma_mapping_error(chan->device->dev, s->tx_dma_addr)) { @@ -1622,7 +1642,7 @@ static void sci_request_dma(struct uart_port *port) } else { dev_dbg(port->dev, "%s: mapped %lu@%p to %pad\n", __func__, UART_XMIT_SIZE, - port->state->xmit.buf, &s->tx_dma_addr); + tport->xmit_buf, &s->tx_dma_addr); INIT_WORK(&s->work_tx, sci_dma_tx_work_fn); s->chan_tx_saved = s->chan_tx = chan; @@ -1658,8 +1678,7 @@ static void sci_request_dma(struct uart_port *port) dma += s->buf_len_rx; } - hrtimer_init(&s->rx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - s->rx_timer.function = sci_dma_rx_timer_fn; + hrtimer_setup(&s->rx_timer, sci_dma_rx_timer_fn, CLOCK_MONOTONIC, HRTIMER_MODE_REL); s->chan_rx_saved = s->chan_rx = chan; @@ -1694,6 +1713,19 @@ static void sci_flush_buffer(struct uart_port *port) s->cookie_tx = -EINVAL; } } + +static void sci_dma_check_tx_occurred(struct sci_port *s) +{ + struct dma_tx_state state; + enum dma_status status; + + if (!s->chan_tx) + return; + + status = dmaengine_tx_status(s->chan_tx, s->cookie_tx, &state); + if (status == DMA_COMPLETE || status == DMA_IN_PROGRESS) + s->tx_occurred = true; +} #else /* !CONFIG_SERIAL_SH_SCI_DMA */ static inline void sci_request_dma(struct uart_port *port) { @@ -1703,6 +1735,10 @@ static inline void sci_free_dma(struct uart_port *port) { } +static void sci_dma_check_tx_occurred(struct sci_port *s) +{ +} + #define sci_flush_buffer NULL #endif /* !CONFIG_SERIAL_SH_SCI_DMA */ @@ -1721,7 +1757,7 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) { disable_irq_nosync(s->irqs[SCIx_RXI_IRQ]); if (s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) { - scif_set_rtrg(port, 1); + s->ops->set_rtrg(port, 1); scr |= SCSCR_RIE; } else { scr |= SCSCR_RDRQE; @@ -1747,8 +1783,8 @@ handle_pio: #endif if (s->rx_trigger > 1 && s->rx_fifo_timeout > 0) { - if (!scif_rtrg_enabled(port)) - scif_set_rtrg(port, s->rx_trigger); + if (!s->ops->rtrg_enabled(port)) + s->ops->set_rtrg(port, s->rx_trigger); mod_timer(&s->rx_fifo_timer, jiffies + DIV_ROUND_UP( s->rx_frame * HZ * s->rx_fifo_timeout, 1000000)); @@ -1758,7 +1794,7 @@ handle_pio: * of whether the I_IXOFF is set, otherwise, how is the interrupt * to be disabled? */ - sci_receive_chars(port); + s->ops->receive_chars(port); return IRQ_HANDLED; } @@ -1767,9 +1803,10 @@ static irqreturn_t sci_tx_interrupt(int irq, void *ptr) { struct uart_port *port = ptr; unsigned long flags; + struct sci_port *s = to_sci_port(port); uart_port_lock_irqsave(port, &flags); - sci_transmit_chars(port); + s->ops->transmit_chars(port); uart_port_unlock_irqrestore(port, flags); return IRQ_HANDLED; @@ -1778,16 +1815,18 @@ static irqreturn_t sci_tx_interrupt(int irq, void *ptr) static irqreturn_t sci_tx_end_interrupt(int irq, void *ptr) { struct uart_port *port = ptr; + struct sci_port *s = to_sci_port(port); + const struct sci_common_regs *regs = s->params->common_regs; unsigned long flags; - unsigned short ctrl; + u32 ctrl; if (port->type != PORT_SCI) return sci_tx_interrupt(irq, ptr); uart_port_lock_irqsave(port, &flags); - ctrl = sci_serial_in(port, SCSCR); - ctrl &= ~(SCSCR_TE | SCSCR_TEIE); - sci_serial_out(port, SCSCR, ctrl); + ctrl = s->ops->read_reg(port, regs->control) & + ~(s->params->param_bits->te_clear); + s->ops->write_reg(port, regs->control, ctrl); uart_port_unlock_irqrestore(port, flags); return IRQ_HANDLED; @@ -1796,6 +1835,7 @@ static irqreturn_t sci_tx_end_interrupt(int irq, void *ptr) static irqreturn_t sci_br_interrupt(int irq, void *ptr) { struct uart_port *port = ptr; + struct sci_port *s = to_sci_port(port); /* Handle BREAKs */ sci_handle_breaks(port); @@ -1803,7 +1843,7 @@ static irqreturn_t sci_br_interrupt(int irq, void *ptr) /* drop invalid character received before break was detected */ sci_serial_in(port, SCxRDR); - sci_clear_SCxSR(port, SCxSR_BREAK_CLEAR(port)); + s->ops->clear_SCxSR(port, SCxSR_BREAK_CLEAR(port)); return IRQ_HANDLED; } @@ -1831,15 +1871,15 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr) if (sci_handle_errors(port)) { /* discard character in rx buffer */ sci_serial_in(port, SCxSR); - sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port)); + s->ops->clear_SCxSR(port, SCxSR_RDxF_CLEAR(port)); } } else { sci_handle_fifo_overrun(port); if (!s->chan_rx) - sci_receive_chars(port); + s->ops->receive_chars(port); } - sci_clear_SCxSR(port, SCxSR_ERROR_CLEAR(port)); + s->ops->clear_SCxSR(port, SCxSR_ERROR_CLEAR(port)); /* Kick the transmission */ if (!s->chan_tx) @@ -2039,6 +2079,12 @@ static unsigned int sci_tx_empty(struct uart_port *port) { unsigned short status = sci_serial_in(port, SCxSR); unsigned short in_tx_fifo = sci_txfill(port); + struct sci_port *s = to_sci_port(port); + + sci_dma_check_tx_occurred(s); + + if (!s->tx_occurred) + return TIOCSER_TEMT; return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0; } @@ -2118,8 +2164,9 @@ static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) if (!(mctrl & TIOCM_RTS)) { /* Disable Auto RTS */ - sci_serial_out(port, SCFCR, - sci_serial_in(port, SCFCR) & ~SCFCR_MCE); + if (s->cfg->regtype != SCIx_RZV2H_SCIF_REGTYPE) + sci_serial_out(port, SCFCR, + sci_serial_in(port, SCFCR) & ~SCFCR_MCE); /* Clear RTS */ sci_set_rts(port, 0); @@ -2131,8 +2178,9 @@ static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) } /* Enable Auto RTS */ - sci_serial_out(port, SCFCR, - sci_serial_in(port, SCFCR) | SCFCR_MCE); + if (s->cfg->regtype != SCIx_RZV2H_SCIF_REGTYPE) + sci_serial_out(port, SCFCR, + sci_serial_in(port, SCFCR) | SCFCR_MCE); } else { /* Set RTS */ sci_set_rts(port, 1); @@ -2201,13 +2249,24 @@ static void sci_break_ctl(struct uart_port *port, int break_state) uart_port_unlock_irqrestore(port, flags); } -static int sci_startup(struct uart_port *port) +static void sci_shutdown_complete(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + u16 scr; + + scr = sci_serial_in(port, SCSCR); + sci_serial_out(port, SCSCR, + scr & (SCSCR_CKE1 | SCSCR_CKE0 | s->hscif_tot)); +} + +int sci_startup(struct uart_port *port) { struct sci_port *s = to_sci_port(port); int ret; dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + s->tx_occurred = false; sci_request_dma(port); ret = sci_request_irq(s); @@ -2219,27 +2278,20 @@ static int sci_startup(struct uart_port *port) return 0; } -static void sci_shutdown(struct uart_port *port) +void sci_shutdown(struct uart_port *port) { struct sci_port *s = to_sci_port(port); unsigned long flags; - u16 scr; dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); s->autorts = false; - mctrl_gpio_disable_ms(to_sci_port(port)->gpios); + mctrl_gpio_disable_ms_sync(to_sci_port(port)->gpios); uart_port_lock_irqsave(port, &flags); sci_stop_rx(port); sci_stop_tx(port); - /* - * Stop RX and TX, disable related interrupts, keep clock source - * and HSCIF TOT bits - */ - scr = sci_serial_in(port, SCSCR); - sci_serial_out(port, SCSCR, - scr & (SCSCR_CKE1 | SCSCR_CKE0 | s->hscif_tot)); + s->ops->shutdown_complete(port); uart_port_unlock_irqrestore(port, flags); #ifdef CONFIG_SERIAL_SH_SCI_DMA @@ -2251,7 +2303,7 @@ static void sci_shutdown(struct uart_port *port) #endif if (s->rx_trigger > 1 && s->rx_fifo_timeout > 0) - del_timer_sync(&s->rx_fifo_timer); + timer_delete_sync(&s->rx_fifo_timer); sci_free_irq(s); sci_free_dma(port); } @@ -2316,8 +2368,8 @@ static int sci_brg_calc(struct sci_port *s, unsigned int bps, /* calculate sample rate, BRR, and clock select */ static int sci_scbrr_calc(struct sci_port *s, unsigned int bps, - unsigned int *brr, unsigned int *srr, - unsigned int *cks) + unsigned int *brr, unsigned int *srr, + unsigned int *cks) { unsigned long freq = s->clk_rates[SCI_FCK]; unsigned int sr, br, prediv, scrate, c; @@ -2394,9 +2446,9 @@ static void sci_reset(struct uart_port *port) if (reg->size) sci_serial_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST); - sci_clear_SCxSR(port, - SCxSR_RDxF_CLEAR(port) & SCxSR_ERROR_CLEAR(port) & - SCxSR_BREAK_CLEAR(port)); + s->ops->clear_SCxSR(port, + SCxSR_RDxF_CLEAR(port) & SCxSR_ERROR_CLEAR(port) & + SCxSR_BREAK_CLEAR(port)); if (sci_getreg(port, SCLSR)->size) { status = sci_serial_in(port, SCLSR); status &= ~(SCLSR_TO | SCLSR_ORER); @@ -2405,14 +2457,14 @@ static void sci_reset(struct uart_port *port) if (s->rx_trigger > 1) { if (s->rx_fifo_timeout) { - scif_set_rtrg(port, 1); + s->ops->set_rtrg(port, 1); timer_setup(&s->rx_fifo_timer, rx_fifo_timer_fn, 0); } else { if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) - scif_set_rtrg(port, 1); + s->ops->set_rtrg(port, 1); else - scif_set_rtrg(port, s->rx_trigger); + s->ops->set_rtrg(port, s->rx_trigger); } } } @@ -2672,7 +2724,7 @@ done: sci_enable_ms(port); } -static void sci_pm(struct uart_port *port, unsigned int state, +void sci_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) { struct sci_port *sci_port = to_sci_port(port); @@ -2735,7 +2787,7 @@ static int sci_remap_port(struct uart_port *port) return 0; } -static void sci_release_port(struct uart_port *port) +void sci_release_port(struct uart_port *port) { struct sci_port *sport = to_sci_port(port); @@ -2747,7 +2799,7 @@ static void sci_release_port(struct uart_port *port) release_mem_region(port->mapbase, sport->reg_size); } -static int sci_request_port(struct uart_port *port) +int sci_request_port(struct uart_port *port) { struct resource *res; struct sci_port *sport = to_sci_port(port); @@ -2769,7 +2821,7 @@ static int sci_request_port(struct uart_port *port) return 0; } -static void sci_config_port(struct uart_port *port, int flags) +void sci_config_port(struct uart_port *port, int flags) { if (flags & UART_CONFIG_TYPE) { struct sci_port *sport = to_sci_port(port); @@ -2779,7 +2831,7 @@ static void sci_config_port(struct uart_port *port, int flags) } } -static int sci_verify_port(struct uart_port *port, struct serial_struct *ser) +int sci_verify_port(struct uart_port *port, struct serial_struct *ser) { if (ser->baud_base < 2400) /* No paper tape reader for Mitch.. */ @@ -2788,6 +2840,75 @@ static int sci_verify_port(struct uart_port *port, struct serial_struct *ser) return 0; } +static void sci_prepare_console_write(struct uart_port *port, u32 ctrl) +{ + struct sci_port *s = to_sci_port(port); + u32 ctrl_temp = + s->params->param_bits->rxtx_enable | + (s->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0)) | + (ctrl & (SCSCR_CKE1 | SCSCR_CKE0)) | + s->hscif_tot; + sci_serial_out(port, SCSCR, ctrl_temp); +} + +static void sci_console_save(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + struct sci_suspend_regs *regs = s->suspend_regs; + + if (sci_getreg(port, SCDL)->size) + regs->scdl = sci_serial_in(port, SCDL); + if (sci_getreg(port, SCCKS)->size) + regs->sccks = sci_serial_in(port, SCCKS); + if (sci_getreg(port, SCSMR)->size) + regs->scsmr = sci_serial_in(port, SCSMR); + if (sci_getreg(port, SCSCR)->size) + regs->scscr = sci_serial_in(port, SCSCR); + if (sci_getreg(port, SCFCR)->size) + regs->scfcr = sci_serial_in(port, SCFCR); + if (sci_getreg(port, SCSPTR)->size) + regs->scsptr = sci_serial_in(port, SCSPTR); + if (sci_getreg(port, SCBRR)->size) + regs->scbrr = sci_serial_in(port, SCBRR); + if (sci_getreg(port, HSSRR)->size) + regs->hssrr = sci_serial_in(port, HSSRR); + if (sci_getreg(port, SCPCR)->size) + regs->scpcr = sci_serial_in(port, SCPCR); + if (sci_getreg(port, SCPDR)->size) + regs->scpdr = sci_serial_in(port, SCPDR); + if (sci_getreg(port, SEMR)->size) + regs->semr = sci_serial_in(port, SEMR); +} + +static void sci_console_restore(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + struct sci_suspend_regs *regs = s->suspend_regs; + + if (sci_getreg(port, SCDL)->size) + sci_serial_out(port, SCDL, regs->scdl); + if (sci_getreg(port, SCCKS)->size) + sci_serial_out(port, SCCKS, regs->sccks); + if (sci_getreg(port, SCSMR)->size) + sci_serial_out(port, SCSMR, regs->scsmr); + if (sci_getreg(port, SCSCR)->size) + sci_serial_out(port, SCSCR, regs->scscr); + if (sci_getreg(port, SCFCR)->size) + sci_serial_out(port, SCFCR, regs->scfcr); + if (sci_getreg(port, SCSPTR)->size) + sci_serial_out(port, SCSPTR, regs->scsptr); + if (sci_getreg(port, SCBRR)->size) + sci_serial_out(port, SCBRR, regs->scbrr); + if (sci_getreg(port, HSSRR)->size) + sci_serial_out(port, HSSRR, regs->hssrr); + if (sci_getreg(port, SCPCR)->size) + sci_serial_out(port, SCPCR, regs->scpcr); + if (sci_getreg(port, SCPDR)->size) + sci_serial_out(port, SCPDR, regs->scpdr); + if (sci_getreg(port, SEMR)->size) + sci_serial_out(port, SEMR, regs->semr); +} + static const struct uart_ops sci_uart_ops = { .tx_empty = sci_tx_empty, .set_mctrl = sci_set_mctrl, @@ -2813,6 +2934,25 @@ static const struct uart_ops sci_uart_ops = { #endif }; +static const struct sci_port_ops sci_port_ops = { + .read_reg = sci_serial_in, + .write_reg = sci_serial_out, + .clear_SCxSR = sci_clear_SCxSR, + .transmit_chars = sci_transmit_chars, + .receive_chars = sci_receive_chars, +#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) || \ + defined(CONFIG_SERIAL_SH_SCI_EARLYCON) + .poll_put_char = sci_poll_put_char, +#endif + .set_rtrg = scif_set_rtrg, + .rtrg_enabled = scif_rtrg_enabled, + .shutdown_complete = sci_shutdown_complete, + .prepare_console_write = sci_prepare_console_write, + .console_save = sci_console_save, + .console_restore = sci_console_restore, + .suspend_regs_size = sci_suspend_regs_size, +}; + static int sci_init_clocks(struct sci_port *sci_port, struct device *dev) { const char *clk_names[] = { @@ -2856,10 +2996,13 @@ static int sci_init_clocks(struct sci_port *sci_port, struct device *dev) } static const struct sci_port_params * -sci_probe_regmap(const struct plat_sci_port *cfg) +sci_probe_regmap(const struct plat_sci_port *cfg, struct sci_port *sci_port) { unsigned int regtype; + sci_port->ops = &sci_port_ops; + sci_port->port.ops = &sci_uart_ops; + if (cfg->regtype != SCIx_PROBE_REGTYPE) return &sci_port_params[cfg->regtype]; @@ -2907,7 +3050,6 @@ static int sci_init_single(struct platform_device *dev, sci_port->cfg = p; - port->ops = &sci_uart_ops; port->iotype = UPIO_MEM; port->line = index; port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_SH_SCI_CONSOLE); @@ -2947,10 +3089,6 @@ static int sci_init_single(struct platform_device *dev, for (i = 1; i < ARRAY_SIZE(sci_port->irqs); i++) sci_port->irqs[i] = sci_port->irqs[0]; - sci_port->params = sci_probe_regmap(p); - if (unlikely(sci_port->params == NULL)) - return -EINVAL; - switch (p->type) { case PORT_SCIFB: sci_port->rx_trigger = 48; @@ -2988,10 +3126,6 @@ static int sci_init_single(struct platform_device *dev, ret = sci_init_clocks(sci_port, &dev->dev); if (ret < 0) return ret; - - port->dev = &dev->dev; - - pm_runtime_enable(&dev->dev); } port->type = p->type; @@ -3018,16 +3152,11 @@ static int sci_init_single(struct platform_device *dev, return 0; } -static void sci_cleanup_single(struct sci_port *port) -{ - pm_runtime_disable(port->port.dev); -} - #if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) || \ defined(CONFIG_SERIAL_SH_SCI_EARLYCON) static void serial_console_putchar(struct uart_port *port, unsigned char ch) { - sci_poll_put_char(port, ch); + to_sci_port(port)->ops->poll_put_char(port, ch); } /* @@ -3039,7 +3168,9 @@ static void serial_console_write(struct console *co, const char *s, { struct sci_port *sci_port = &sci_ports[co->index]; struct uart_port *port = &sci_port->port; - unsigned short bits, ctrl, ctrl_temp; + const struct sci_common_regs *regs = sci_port->params->common_regs; + unsigned int bits; + u32 ctrl; unsigned long flags; int locked = 1; @@ -3051,21 +3182,21 @@ static void serial_console_write(struct console *co, const char *s, uart_port_lock_irqsave(port, &flags); /* first save SCSCR then disable interrupts, keep clock source */ - ctrl = sci_serial_in(port, SCSCR); - ctrl_temp = SCSCR_RE | SCSCR_TE | - (sci_port->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0)) | - (ctrl & (SCSCR_CKE1 | SCSCR_CKE0)); - sci_serial_out(port, SCSCR, ctrl_temp | sci_port->hscif_tot); + + ctrl = sci_port->ops->read_reg(port, regs->control); + sci_port->ops->prepare_console_write(port, ctrl); uart_console_write(port, s, count, serial_console_putchar); /* wait until fifo is empty and last bit has been transmitted */ - bits = SCxSR_TDxE(port) | SCxSR_TEND(port); - while ((sci_serial_in(port, SCxSR) & bits) != bits) + + bits = sci_port->params->param_bits->poll_sent_bits; + + while ((sci_port->ops->read_reg(port, regs->status) & bits) != bits) cpu_relax(); /* restore the SCSCR */ - sci_serial_out(port, SCSCR, ctrl); + sci_port->ops->write_reg(port, regs->control, ctrl); if (locked) uart_port_unlock_irqrestore(port, flags); @@ -3143,13 +3274,18 @@ static struct console early_serial_console = { static int sci_probe_earlyprintk(struct platform_device *pdev) { const struct plat_sci_port *cfg = dev_get_platdata(&pdev->dev); + struct sci_port *sp = &sci_ports[pdev->id]; if (early_serial_console.data) return -EEXIST; early_serial_console.index = pdev->id; - sci_init_single(pdev, &sci_ports[pdev->id], pdev->id, cfg, true); + sp->params = sci_probe_regmap(cfg, sp); + if (!sp->params) + return -ENODEV; + + sci_init_single(pdev, sp, pdev->id, cfg, true); if (!strstr(early_serial_buf, "keep")) early_serial_console.flags |= CON_BOOT; @@ -3192,63 +3328,132 @@ static void sci_remove(struct platform_device *dev) sci_ports_in_use &= ~BIT(port->port.line); uart_remove_one_port(&sci_uart_driver, &port->port); - sci_cleanup_single(port); - if (port->port.fifosize > 1) device_remove_file(&dev->dev, &dev_attr_rx_fifo_trigger); if (type == PORT_SCIFA || type == PORT_SCIFB || type == PORT_HSCIF) device_remove_file(&dev->dev, &dev_attr_rx_fifo_timeout); } +static const struct sci_of_data of_sci_scif_sh2 = { + .type = PORT_SCIF, + .regtype = SCIx_SH2_SCIF_FIFODATA_REGTYPE, + .ops = &sci_port_ops, + .uart_ops = &sci_uart_ops, + .params = &sci_port_params[SCIx_SH2_SCIF_FIFODATA_REGTYPE], +}; + +static const struct sci_of_data of_sci_scif_rz_scifa = { + .type = PORT_SCIF, + .regtype = SCIx_RZ_SCIFA_REGTYPE, + .ops = &sci_port_ops, + .uart_ops = &sci_uart_ops, + .params = &sci_port_params[SCIx_RZ_SCIFA_REGTYPE], +}; + +static const struct sci_of_data of_sci_scif_rzv2h = { + .type = PORT_SCIF, + .regtype = SCIx_RZV2H_SCIF_REGTYPE, + .ops = &sci_port_ops, + .uart_ops = &sci_uart_ops, + .params = &sci_port_params[SCIx_RZV2H_SCIF_REGTYPE], +}; + +static const struct sci_of_data of_sci_rcar_scif = { + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_BRG_REGTYPE, + .ops = &sci_port_ops, + .uart_ops = &sci_uart_ops, + .params = &sci_port_params[SCIx_SH4_SCIF_BRG_REGTYPE], +}; + +static const struct sci_of_data of_sci_scif_sh4 = { + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_REGTYPE, + .ops = &sci_port_ops, + .uart_ops = &sci_uart_ops, + .params = &sci_port_params[SCIx_SH4_SCIF_REGTYPE], +}; + +static const struct sci_of_data of_sci_scifa = { + .type = PORT_SCIFA, + .regtype = SCIx_SCIFA_REGTYPE, + .ops = &sci_port_ops, + .uart_ops = &sci_uart_ops, + .params = &sci_port_params[SCIx_SCIFA_REGTYPE], +}; + +static const struct sci_of_data of_sci_scifb = { + .type = PORT_SCIFB, + .regtype = SCIx_SCIFB_REGTYPE, + .ops = &sci_port_ops, + .uart_ops = &sci_uart_ops, + .params = &sci_port_params[SCIx_SCIFB_REGTYPE], +}; + +static const struct sci_of_data of_sci_hscif = { + .type = PORT_HSCIF, + .regtype = SCIx_HSCIF_REGTYPE, + .ops = &sci_port_ops, + .uart_ops = &sci_uart_ops, + .params = &sci_port_params[SCIx_HSCIF_REGTYPE], +}; -#define SCI_OF_DATA(type, regtype) (void *)((type) << 16 | (regtype)) -#define SCI_OF_TYPE(data) ((unsigned long)(data) >> 16) -#define SCI_OF_REGTYPE(data) ((unsigned long)(data) & 0xffff) +static const struct sci_of_data of_sci_sci = { + .type = PORT_SCI, + .regtype = SCIx_SCI_REGTYPE, + .ops = &sci_port_ops, + .uart_ops = &sci_uart_ops, + .params = &sci_port_params[SCIx_SCI_REGTYPE], +}; static const struct of_device_id of_sci_match[] __maybe_unused = { /* SoC-specific types */ { .compatible = "renesas,scif-r7s72100", - .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH2_SCIF_FIFODATA_REGTYPE), + .data = &of_sci_scif_sh2, }, { .compatible = "renesas,scif-r7s9210", - .data = SCI_OF_DATA(PORT_SCIF, SCIx_RZ_SCIFA_REGTYPE), + .data = &of_sci_scif_rz_scifa, }, { .compatible = "renesas,scif-r9a07g044", - .data = SCI_OF_DATA(PORT_SCIF, SCIx_RZ_SCIFA_REGTYPE), + .data = &of_sci_scif_rz_scifa, + }, + { + .compatible = "renesas,scif-r9a09g057", + .data = &of_sci_scif_rzv2h, }, /* Family-specific types */ { .compatible = "renesas,rcar-gen1-scif", - .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_BRG_REGTYPE), + .data = &of_sci_rcar_scif, }, { .compatible = "renesas,rcar-gen2-scif", - .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_BRG_REGTYPE), + .data = &of_sci_rcar_scif, }, { .compatible = "renesas,rcar-gen3-scif", - .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_BRG_REGTYPE), + .data = &of_sci_rcar_scif }, { .compatible = "renesas,rcar-gen4-scif", - .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_BRG_REGTYPE), + .data = &of_sci_rcar_scif }, /* Generic types */ { .compatible = "renesas,scif", - .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_REGTYPE), + .data = &of_sci_scif_sh4, }, { .compatible = "renesas,scifa", - .data = SCI_OF_DATA(PORT_SCIFA, SCIx_SCIFA_REGTYPE), + .data = &of_sci_scifa, }, { .compatible = "renesas,scifb", - .data = SCI_OF_DATA(PORT_SCIFB, SCIx_SCIFB_REGTYPE), + .data = &of_sci_scifb, }, { .compatible = "renesas,hscif", - .data = SCI_OF_DATA(PORT_HSCIF, SCIx_HSCIF_REGTYPE), + .data = &of_sci_hscif, }, { .compatible = "renesas,sci", - .data = SCI_OF_DATA(PORT_SCI, SCIx_SCI_REGTYPE), + .data = &of_sci_sci, }, { /* Terminator */ }, @@ -3267,7 +3472,7 @@ static struct plat_sci_port *sci_parse_dt(struct platform_device *pdev, struct reset_control *rstc; struct plat_sci_port *p; struct sci_port *sp; - const void *data; + const struct sci_of_data *data; int id, ret; if (!IS_ENABLED(CONFIG_OF) || !np) @@ -3311,10 +3516,15 @@ static struct plat_sci_port *sci_parse_dt(struct platform_device *pdev, } sp = &sci_ports[id]; + sp->rstc = rstc; *dev_id = id; - p->type = SCI_OF_TYPE(data); - p->regtype = SCI_OF_REGTYPE(data); + p->type = data->type; + p->regtype = data->regtype; + + sp->ops = data->ops; + sp->port.ops = data->uart_ops; + sp->params = data->params; sp->has_rtscts = of_property_read_bool(np, "uart-has-rtscts"); @@ -3324,7 +3534,8 @@ static struct plat_sci_port *sci_parse_dt(struct platform_device *pdev, static int sci_probe_single(struct platform_device *dev, unsigned int index, struct plat_sci_port *p, - struct sci_port *sciport) + struct sci_port *sciport, + struct resource *sci_res) { int ret; @@ -3353,6 +3564,11 @@ static int sci_probe_single(struct platform_device *dev, if (ret) return ret; + sciport->port.dev = &dev->dev; + ret = devm_pm_runtime_enable(&dev->dev); + if (ret) + return ret; + sciport->gpios = mctrl_gpio_init(&sciport->port, 0); if (IS_ERR(sciport->gpios)) return PTR_ERR(sciport->gpios); @@ -3366,18 +3582,37 @@ static int sci_probe_single(struct platform_device *dev, sciport->port.flags |= UPF_HARD_FLOW; } - ret = uart_add_one_port(&sci_uart_driver, &sciport->port); - if (ret) { - sci_cleanup_single(sciport); - return ret; + if (sci_uart_earlycon && sci_ports[0].port.mapbase == sci_res->start) { + /* + * In case: + * - this is the earlycon port (mapped on index 0 in sci_ports[]) and + * - it now maps to an alias other than zero and + * - the earlycon is still alive (e.g., "earlycon keep_bootcon" is + * available in bootargs) + * + * we need to avoid disabling clocks and PM domains through the runtime + * PM APIs called in __device_attach(). For this, increment the runtime + * PM reference counter (the clocks and PM domains were already enabled + * by the bootloader). Otherwise the earlycon may access the HW when it + * has no clocks enabled leading to failures (infinite loop in + * sci_poll_put_char()). + */ + pm_runtime_get_noresume(&dev->dev); + + /* + * Skip cleanup the sci_port[0] in early_console_exit(), this + * port is the same as the earlycon one. + */ + sci_uart_earlycon_dev_probing = true; } - return 0; + return uart_add_one_port(&sci_uart_driver, &sciport->port); } static int sci_probe(struct platform_device *dev) { struct plat_sci_port *p; + struct resource *res; struct sci_port *sp; unsigned int dev_id; int ret; @@ -3396,6 +3631,7 @@ static int sci_probe(struct platform_device *dev) p = sci_parse_dt(dev, &dev_id); if (IS_ERR(p)) return PTR_ERR(p); + sp = &sci_ports[dev_id]; } else { p = dev->dev.platform_data; if (p == NULL) { @@ -3404,12 +3640,40 @@ static int sci_probe(struct platform_device *dev) } dev_id = dev->id; + sp = &sci_ports[dev_id]; + sp->params = sci_probe_regmap(p, sp); + if (!sp->params) + return -ENODEV; } - sp = &sci_ports[dev_id]; + sp->suspend_regs = devm_kzalloc(&dev->dev, + sp->ops->suspend_regs_size(), + GFP_KERNEL); + if (!sp->suspend_regs) + return -ENOMEM; + + /* + * In case: + * - the probed port alias is zero (as the one used by earlycon), and + * - the earlycon is still active (e.g., "earlycon keep_bootcon" in + * bootargs) + * + * defer the probe of this serial. This is a debug scenario and the user + * must be aware of it. + * + * Except when the probed port is the same as the earlycon port. + */ + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + if (sci_uart_earlycon && sp == &sci_ports[0] && sp->port.mapbase != res->start) + return dev_err_probe(&dev->dev, -EBUSY, "sci_port[0] is used by earlycon!\n"); + platform_set_drvdata(dev, sp); - ret = sci_probe_single(dev, dev_id, p, sp); + ret = sci_probe_single(dev, dev_id, p, sp, res); if (ret) return ret; @@ -3442,9 +3706,17 @@ static __maybe_unused int sci_suspend(struct device *dev) { struct sci_port *sport = dev_get_drvdata(dev); - if (sport) + if (sport) { uart_suspend_port(&sci_uart_driver, &sport->port); + if (!console_suspend_enabled && uart_console(&sport->port)) { + if (sport->ops->console_save) + sport->ops->console_save(&sport->port); + } + else + return reset_control_assert(sport->rstc); + } + return 0; } @@ -3452,8 +3724,19 @@ static __maybe_unused int sci_resume(struct device *dev) { struct sci_port *sport = dev_get_drvdata(dev); - if (sport) + if (sport) { + if (!console_suspend_enabled && uart_console(&sport->port)) { + if (sport->ops->console_restore) + sport->ops->console_restore(&sport->port); + } else { + int ret = reset_control_deassert(sport->rstc); + + if (ret) + return ret; + } + uart_resume_port(&sci_uart_driver, &sport->port); + } return 0; } @@ -3462,7 +3745,7 @@ static SIMPLE_DEV_PM_OPS(sci_dev_pm_ops, sci_suspend, sci_resume); static struct platform_driver sci_driver = { .probe = sci_probe, - .remove_new = sci_remove, + .remove = sci_remove, .driver = { .name = "sh-sci", .pm = &sci_dev_pm_ops, @@ -3490,63 +3773,98 @@ sh_early_platform_init_buffer("earlyprintk", &sci_driver, early_serial_buf, ARRAY_SIZE(early_serial_buf)); #endif #ifdef CONFIG_SERIAL_SH_SCI_EARLYCON -static struct plat_sci_port port_cfg __initdata; +static struct plat_sci_port port_cfg; + +static int early_console_exit(struct console *co) +{ + struct sci_port *sci_port = &sci_ports[0]; + + /* + * Clean the slot used by earlycon. A new SCI device might + * map to this slot. + */ + if (!sci_uart_earlycon_dev_probing) { + memset(sci_port, 0, sizeof(*sci_port)); + sci_uart_earlycon = false; + } -static int __init early_console_setup(struct earlycon_device *device, - int type) + return 0; +} + +int __init scix_early_console_setup(struct earlycon_device *device, + const struct sci_of_data *data) { + const struct sci_common_regs *regs; + if (!device->port.membase) return -ENODEV; - device->port.type = type; - memcpy(&sci_ports[0].port, &device->port, sizeof(struct uart_port)); - port_cfg.type = type; + device->port.type = data->type; + sci_ports[0].port = device->port; + + port_cfg.type = data->type; + port_cfg.regtype = data->regtype; + sci_ports[0].cfg = &port_cfg; - sci_ports[0].params = sci_probe_regmap(&port_cfg); - port_cfg.scscr = sci_serial_in(&sci_ports[0].port, SCSCR); - sci_serial_out(&sci_ports[0].port, SCSCR, - SCSCR_RE | SCSCR_TE | port_cfg.scscr); + sci_ports[0].params = data->params; + sci_ports[0].ops = data->ops; + sci_ports[0].port.ops = data->uart_ops; + sci_uart_earlycon = true; + regs = sci_ports[0].params->common_regs; + + port_cfg.scscr = sci_ports[0].ops->read_reg(&sci_ports[0].port, regs->control); + sci_ports[0].ops->write_reg(&sci_ports[0].port, + regs->control, + sci_ports[0].params->param_bits->rxtx_enable | port_cfg.scscr); device->con->write = serial_console_write; + device->con->exit = early_console_exit; + return 0; } static int __init sci_early_console_setup(struct earlycon_device *device, const char *opt) { - return early_console_setup(device, PORT_SCI); + return scix_early_console_setup(device, &of_sci_sci); } static int __init scif_early_console_setup(struct earlycon_device *device, const char *opt) { - return early_console_setup(device, PORT_SCIF); + return scix_early_console_setup(device, &of_sci_scif_sh4); } static int __init rzscifa_early_console_setup(struct earlycon_device *device, const char *opt) { - port_cfg.regtype = SCIx_RZ_SCIFA_REGTYPE; - return early_console_setup(device, PORT_SCIF); + return scix_early_console_setup(device, &of_sci_scif_rz_scifa); +} + +static int __init rzv2hscif_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + return scix_early_console_setup(device, &of_sci_scif_rzv2h); } static int __init scifa_early_console_setup(struct earlycon_device *device, const char *opt) { - return early_console_setup(device, PORT_SCIFA); + return scix_early_console_setup(device, &of_sci_scifa); } static int __init scifb_early_console_setup(struct earlycon_device *device, const char *opt) { - return early_console_setup(device, PORT_SCIFB); + return scix_early_console_setup(device, &of_sci_scifb); } static int __init hscif_early_console_setup(struct earlycon_device *device, const char *opt) { - return early_console_setup(device, PORT_HSCIF); + return scix_early_console_setup(device, &of_sci_hscif); } OF_EARLYCON_DECLARE(sci, "renesas,sci", sci_early_console_setup); OF_EARLYCON_DECLARE(scif, "renesas,scif", scif_early_console_setup); OF_EARLYCON_DECLARE(scif, "renesas,scif-r7s9210", rzscifa_early_console_setup); OF_EARLYCON_DECLARE(scif, "renesas,scif-r9a07g044", rzscifa_early_console_setup); +OF_EARLYCON_DECLARE(scif, "renesas,scif-r9a09g057", rzv2hscif_early_console_setup); OF_EARLYCON_DECLARE(scifa, "renesas,scifa", scifa_early_console_setup); OF_EARLYCON_DECLARE(scifb, "renesas,scifb", scifb_early_console_setup); OF_EARLYCON_DECLARE(hscif, "renesas,hscif", hscif_early_console_setup); diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 0b65563c4e9e..951681aba586 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -32,8 +32,6 @@ enum { HSRTRGR, /* Rx FIFO Data Count Trigger Register */ HSTTRGR, /* Tx FIFO Data Count Trigger Register */ SEMR, /* Serial extended mode register */ - - SCIx_NR_REGS, }; diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c index 0670fd9f8496..110d67613192 100644 --- a/drivers/tty/serial/sifive.c +++ b/drivers/tty/serial/sifive.c @@ -141,6 +141,7 @@ * @baud_rate: UART serial line rate (e.g., 115200 baud) * @clk: reference to this device's clock * @clk_notifier: clock rate change notifier for upstream clock changes + * @console_line_ended: indicate that the console line is fully written * * Configuration data specific to this SiFive UART. */ @@ -151,6 +152,7 @@ struct sifive_serial_port { unsigned long baud_rate; struct clk *clk; struct notifier_block clk_notifier; + bool console_line_ended; }; /* @@ -563,8 +565,11 @@ static void sifive_serial_break_ctl(struct uart_port *port, int break_state) static int sifive_serial_startup(struct uart_port *port) { struct sifive_serial_port *ssp = port_to_sifive_serial_port(port); + unsigned long flags; + uart_port_lock_irqsave(&ssp->port, &flags); __ssp_enable_rxwm(ssp); + uart_port_unlock_irqrestore(&ssp->port, flags); return 0; } @@ -572,9 +577,12 @@ static int sifive_serial_startup(struct uart_port *port) static void sifive_serial_shutdown(struct uart_port *port) { struct sifive_serial_port *ssp = port_to_sifive_serial_port(port); + unsigned long flags; + uart_port_lock_irqsave(&ssp->port, &flags); __ssp_disable_rxwm(ssp); __ssp_disable_txwm(ssp); + uart_port_unlock_irqrestore(&ssp->port, flags); } /** @@ -761,7 +769,7 @@ static int __init early_sifive_serial_setup(struct earlycon_device *dev, } OF_EARLYCON_DECLARE(sifive, "sifive,uart0", early_sifive_serial_setup); -OF_EARLYCON_DECLARE(sifive, "sifive,fu540-c000-uart0", +OF_EARLYCON_DECLARE(sifive, "sifive,fu540-c000-uart", early_sifive_serial_setup); #endif /* CONFIG_SERIAL_EARLYCON */ @@ -779,33 +787,88 @@ static void sifive_serial_console_putchar(struct uart_port *port, unsigned char __ssp_wait_for_xmitr(ssp); __ssp_transmit_char(ssp, ch); + + ssp->console_line_ended = (ch == '\n'); +} + +static void sifive_serial_device_lock(struct console *co, unsigned long *flags) +{ + struct uart_port *up = &sifive_serial_console_ports[co->index]->port; + + __uart_port_lock_irqsave(up, flags); } -static void sifive_serial_console_write(struct console *co, const char *s, - unsigned int count) +static void sifive_serial_device_unlock(struct console *co, unsigned long flags) +{ + struct uart_port *up = &sifive_serial_console_ports[co->index]->port; + + __uart_port_unlock_irqrestore(up, flags); +} + +static void sifive_serial_console_write_atomic(struct console *co, + struct nbcon_write_context *wctxt) { struct sifive_serial_port *ssp = sifive_serial_console_ports[co->index]; - unsigned long flags; + struct uart_port *port = &ssp->port; unsigned int ier; - int locked = 1; if (!ssp) return; - if (oops_in_progress) - locked = uart_port_trylock_irqsave(&ssp->port, &flags); - else - uart_port_lock_irqsave(&ssp->port, &flags); + if (!nbcon_enter_unsafe(wctxt)) + return; ier = __ssp_readl(ssp, SIFIVE_SERIAL_IE_OFFS); __ssp_writel(0, SIFIVE_SERIAL_IE_OFFS, ssp); - uart_console_write(&ssp->port, s, count, sifive_serial_console_putchar); + if (!ssp->console_line_ended) + uart_console_write(port, "\n", 1, sifive_serial_console_putchar); + uart_console_write(port, wctxt->outbuf, wctxt->len, + sifive_serial_console_putchar); __ssp_writel(ier, SIFIVE_SERIAL_IE_OFFS, ssp); - if (locked) - uart_port_unlock_irqrestore(&ssp->port, flags); + nbcon_exit_unsafe(wctxt); +} + +static void sifive_serial_console_write_thread(struct console *co, + struct nbcon_write_context *wctxt) +{ + struct sifive_serial_port *ssp = sifive_serial_console_ports[co->index]; + struct uart_port *port = &ssp->port; + unsigned int ier; + + if (!ssp) + return; + + if (!nbcon_enter_unsafe(wctxt)) + return; + + ier = __ssp_readl(ssp, SIFIVE_SERIAL_IE_OFFS); + __ssp_writel(0, SIFIVE_SERIAL_IE_OFFS, ssp); + + if (nbcon_exit_unsafe(wctxt)) { + int len = READ_ONCE(wctxt->len); + int i; + + for (i = 0; i < len; i++) { + if (!nbcon_enter_unsafe(wctxt)) + break; + + uart_console_write(port, wctxt->outbuf + i, 1, + sifive_serial_console_putchar); + + if (!nbcon_exit_unsafe(wctxt)) + break; + } + } + + while (!nbcon_enter_unsafe(wctxt)) + nbcon_reacquire_nobuf(wctxt); + + __ssp_writel(ier, SIFIVE_SERIAL_IE_OFFS, ssp); + + nbcon_exit_unsafe(wctxt); } static int sifive_serial_console_setup(struct console *co, char *options) @@ -823,6 +886,8 @@ static int sifive_serial_console_setup(struct console *co, char *options) if (!ssp) return -ENODEV; + ssp->console_line_ended = true; + if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); @@ -833,10 +898,13 @@ static struct uart_driver sifive_serial_uart_driver; static struct console sifive_serial_console = { .name = SIFIVE_TTY_PREFIX, - .write = sifive_serial_console_write, + .write_atomic = sifive_serial_console_write_atomic, + .write_thread = sifive_serial_console_write_thread, + .device_lock = sifive_serial_device_lock, + .device_unlock = sifive_serial_device_unlock, .device = uart_console_device, .setup = sifive_serial_console_setup, - .flags = CON_PRINTBUFFER, + .flags = CON_PRINTBUFFER | CON_NBCON, .index = -1, .data = &sifive_serial_uart_driver, }; @@ -1032,7 +1100,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(sifive_uart_pm_ops, sifive_serial_suspend, sifive_serial_resume); static const struct of_device_id sifive_serial_of_match[] = { - { .compatible = "sifive,fu540-c000-uart0" }, + { .compatible = "sifive,fu540-c000-uart" }, { .compatible = "sifive,uart0" }, {}, }; @@ -1040,7 +1108,7 @@ MODULE_DEVICE_TABLE(of, sifive_serial_of_match); static struct platform_driver sifive_serial_platform_driver = { .probe = sifive_serial_probe, - .remove_new = sifive_serial_remove, + .remove = sifive_serial_remove, .driver = { .name = SIFIVE_SERIAL_NAME, .pm = pm_sleep_ptr(&sifive_uart_pm_ops), diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 15f14fa593da..8c9366321f8e 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -53,10 +53,12 @@ #define SPRD_IEN_TX_EMPTY BIT(1) #define SPRD_IEN_BREAK_DETECT BIT(7) #define SPRD_IEN_TIMEOUT BIT(13) +#define SPRD_IEN_DATA_TIMEOUT BIT(17) /* interrupt clear register */ #define SPRD_ICLR 0x0014 #define SPRD_ICLR_TIMEOUT BIT(13) +#define SPRD_ICLR_DATA_TIMEOUT BIT(17) /* line control register */ #define SPRD_LCR 0x0018 @@ -102,6 +104,7 @@ #define SPRD_IMSR_TX_FIFO_EMPTY BIT(1) #define SPRD_IMSR_BREAK_DETECT BIT(7) #define SPRD_IMSR_TIMEOUT BIT(13) +#define SPRD_IMSR_DATA_TIMEOUT BIT(17) #define SPRD_DEFAULT_SOURCE_CLK 26000000 #define SPRD_RX_DMA_STEP 1 @@ -118,6 +121,12 @@ struct sprd_uart_dma { bool enable; }; +struct sprd_uart_data { + unsigned int timeout_ien; + unsigned int timeout_iclr; + unsigned int timeout_imsr; +}; + struct sprd_uart_port { struct uart_port port; char name[16]; @@ -126,6 +135,7 @@ struct sprd_uart_port { struct sprd_uart_dma rx_dma; dma_addr_t pos; unsigned char *rx_buf_tail; + const struct sprd_uart_data *pdata; }; static struct sprd_uart_port *sprd_port[UART_NR_MAX]; @@ -134,6 +144,18 @@ static int sprd_ports_num; static int sprd_start_dma_rx(struct uart_port *port); static int sprd_tx_dma_config(struct uart_port *port); +static const struct sprd_uart_data sc9836_data = { + .timeout_ien = SPRD_IEN_TIMEOUT, + .timeout_iclr = SPRD_ICLR_TIMEOUT, + .timeout_imsr = SPRD_IMSR_TIMEOUT, +}; + +static const struct sprd_uart_data sc9632_data = { + .timeout_ien = SPRD_IEN_DATA_TIMEOUT, + .timeout_iclr = SPRD_ICLR_DATA_TIMEOUT, + .timeout_imsr = SPRD_IMSR_DATA_TIMEOUT, +}; + static inline unsigned int serial_in(struct uart_port *port, unsigned int offset) { @@ -227,13 +249,13 @@ static int sprd_tx_buf_remap(struct uart_port *port) { struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port, port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; + unsigned char *tail; - sp->tx_dma.trans_len = - CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + sp->tx_dma.trans_len = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, + UART_XMIT_SIZE); - sp->tx_dma.phys_addr = dma_map_single(port->dev, - (void *)&(xmit->buf[xmit->tail]), + sp->tx_dma.phys_addr = dma_map_single(port->dev, tail, sp->tx_dma.trans_len, DMA_TO_DEVICE); return dma_mapping_error(port->dev, sp->tx_dma.phys_addr); @@ -244,7 +266,7 @@ static void sprd_complete_tx_dma(void *data) struct uart_port *port = (struct uart_port *)data; struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port, port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; unsigned long flags; uart_port_lock_irqsave(port, &flags); @@ -253,10 +275,10 @@ static void sprd_complete_tx_dma(void *data) uart_xmit_advance(port, sp->tx_dma.trans_len); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit) || sprd_tx_buf_remap(port) || + if (kfifo_is_empty(&tport->xmit_fifo) || sprd_tx_buf_remap(port) || sprd_tx_dma_config(port)) sp->tx_dma.trans_len = 0; @@ -319,7 +341,7 @@ static void sprd_start_tx_dma(struct uart_port *port) { struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port, port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; if (port->x_char) { serial_out(port, SPRD_TXD, port->x_char); @@ -328,7 +350,7 @@ static void sprd_start_tx_dma(struct uart_port *port) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { sprd_stop_tx_dma(port); return; } @@ -637,6 +659,8 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id) { struct uart_port *port = dev_id; unsigned int ims; + struct sprd_uart_port *sp = + container_of(port, struct sprd_uart_port, port); uart_port_lock(port); @@ -647,14 +671,14 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id) return IRQ_NONE; } - if (ims & SPRD_IMSR_TIMEOUT) - serial_out(port, SPRD_ICLR, SPRD_ICLR_TIMEOUT); + if (ims & sp->pdata->timeout_imsr) + serial_out(port, SPRD_ICLR, sp->pdata->timeout_iclr); if (ims & SPRD_IMSR_BREAK_DETECT) serial_out(port, SPRD_ICLR, SPRD_IMSR_BREAK_DETECT); if (ims & (SPRD_IMSR_RX_FIFO_FULL | SPRD_IMSR_BREAK_DETECT | - SPRD_IMSR_TIMEOUT)) + sp->pdata->timeout_imsr)) sprd_rx(port); if (ims & SPRD_IMSR_TX_FIFO_EMPTY) @@ -729,7 +753,7 @@ static int sprd_startup(struct uart_port *port) /* enable interrupt */ uart_port_lock_irqsave(port, &flags); ien = serial_in(port, SPRD_IEN); - ien |= SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT; + ien |= SPRD_IEN_BREAK_DETECT | sp->pdata->timeout_ien; if (!sp->rx_dma.enable) ien |= SPRD_IEN_RX_FULL; serial_out(port, SPRD_IEN, ien); @@ -1184,6 +1208,12 @@ static int sprd_probe(struct platform_device *pdev) up->mapbase = res->start; + sport->pdata = of_device_get_match_data(&pdev->dev); + if (!sport->pdata) { + dev_err(&pdev->dev, "get match data failed!\n"); + return -EINVAL; + } + irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; @@ -1248,14 +1278,15 @@ static int sprd_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(sprd_pm_ops, sprd_suspend, sprd_resume); static const struct of_device_id serial_ids[] = { - {.compatible = "sprd,sc9836-uart",}, + {.compatible = "sprd,sc9836-uart", .data = &sc9836_data}, + {.compatible = "sprd,sc9632-uart", .data = &sc9632_data}, {} }; MODULE_DEVICE_TABLE(of, serial_ids); static struct platform_driver sprd_platform_driver = { .probe = sprd_probe, - .remove_new = sprd_remove, + .remove = sprd_remove, .driver = { .name = "sprd_serial", .of_match_table = serial_ids, diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c index a23e59551848..6ed9a327702b 100644 --- a/drivers/tty/serial/st-asc.c +++ b/drivers/tty/serial/st-asc.c @@ -387,9 +387,9 @@ static unsigned int asc_get_mctrl(struct uart_port *port) /* There are probably characters waiting to be transmitted. */ static void asc_start_tx(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; - if (!uart_circ_empty(xmit)) + if (!kfifo_is_empty(&tport->xmit_fifo)) asc_enable_tx_interrupts(port); } @@ -808,7 +808,6 @@ static void asc_serial_remove(struct platform_device *pdev) uart_remove_one_port(&asc_uart_driver, port); } -#ifdef CONFIG_PM_SLEEP static int asc_serial_suspend(struct device *dev) { struct uart_port *port = dev_get_drvdata(dev); @@ -823,8 +822,6 @@ static int asc_serial_resume(struct device *dev) return uart_resume_port(&asc_uart_driver, port); } -#endif /* CONFIG_PM_SLEEP */ - /*----------------------------------------------------------------------*/ #ifdef CONFIG_SERIAL_ST_ASC_CONSOLE @@ -932,16 +929,15 @@ static struct uart_driver asc_uart_driver = { .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 DEFINE_SIMPLE_DEV_PM_OPS(asc_serial_pm_ops, asc_serial_suspend, + asc_serial_resume); static struct platform_driver asc_serial_driver = { .probe = asc_serial_probe, - .remove_new = asc_serial_remove, + .remove = asc_serial_remove, .driver = { .name = DRIVER_NAME, - .pm = &asc_serial_pm_ops, + .pm = pm_sleep_ptr(&asc_serial_pm_ops), .of_match_table = of_match_ptr(asc_match), }, }; diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 58d169e5c1db..ad06b760cfca 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -696,18 +696,23 @@ static void stm32_usart_transmit_chars_pio(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; + + while (1) { + unsigned char ch; - while (!uart_circ_empty(xmit)) { /* Check that TDR is empty before filling FIFO */ if (!(readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE)) break; - writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr); - uart_xmit_advance(port, 1); + + if (!uart_fifo_get(port, &ch)) + break; + + writel_relaxed(ch, port->membase + ofs->tdr); } /* rely on TXE irq (mask or unmask) for sending remaining data */ - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) stm32_usart_tx_interrupt_disable(port); else stm32_usart_tx_interrupt_enable(port); @@ -716,7 +721,7 @@ static void stm32_usart_transmit_chars_pio(struct uart_port *port) static void stm32_usart_transmit_chars_dma(struct uart_port *port) { struct stm32_port *stm32port = to_stm32_port(port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; struct dma_async_tx_descriptor *desc = NULL; unsigned int count; int ret; @@ -728,25 +733,8 @@ static void stm32_usart_transmit_chars_dma(struct uart_port *port) return; } - count = uart_circ_chars_pending(xmit); - - if (count > TX_BUF_L) - count = TX_BUF_L; - - if (xmit->tail < xmit->head) { - memcpy(&stm32port->tx_buf[0], &xmit->buf[xmit->tail], count); - } else { - size_t one = UART_XMIT_SIZE - xmit->tail; - size_t two; - - if (one > count) - one = count; - two = count - one; - - memcpy(&stm32port->tx_buf[0], &xmit->buf[xmit->tail], one); - if (two) - memcpy(&stm32port->tx_buf[one], &xmit->buf[0], two); - } + count = kfifo_out_peek(&tport->xmit_fifo, &stm32port->tx_buf[0], + TX_BUF_L); desc = dmaengine_prep_slave_single(stm32port->tx_ch, stm32port->tx_dma_buf, @@ -792,14 +780,14 @@ static void stm32_usart_transmit_chars(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; u32 isr; int ret; if (!stm32_port->hw_flow_control && port->rs485.flags & SER_RS485_ENABLED && (port->x_char || - !(uart_circ_empty(xmit) || uart_tx_stopped(port)))) { + !(kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)))) { stm32_usart_tc_interrupt_disable(port); stm32_usart_rs485_rts_enable(port); } @@ -826,7 +814,7 @@ static void stm32_usart_transmit_chars(struct uart_port *port) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { stm32_usart_tx_interrupt_disable(port); return; } @@ -841,10 +829,10 @@ static void stm32_usart_transmit_chars(struct uart_port *port) else stm32_usart_transmit_chars_pio(port); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) { + if (kfifo_is_empty(&tport->xmit_fifo)) { stm32_usart_tx_interrupt_disable(port); if (!stm32_port->hw_flow_control && port->rs485.flags & SER_RS485_ENABLED) { @@ -861,6 +849,7 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; u32 sr; unsigned int size; + irqreturn_t ret = IRQ_NONE; sr = readl_relaxed(port->membase + ofs->isr); @@ -869,11 +858,14 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) (sr & USART_SR_TC)) { stm32_usart_tc_interrupt_disable(port); stm32_usart_rs485_rts_disable(port); + ret = IRQ_HANDLED; } - if ((sr & USART_SR_RTOF) && ofs->icr != UNDEF_REG) + if ((sr & USART_SR_RTOF) && ofs->icr != UNDEF_REG) { writel_relaxed(USART_ICR_RTOCF, port->membase + ofs->icr); + ret = IRQ_HANDLED; + } if ((sr & USART_SR_WUF) && ofs->icr != UNDEF_REG) { /* Clear wake up flag and disable wake up interrupt */ @@ -882,6 +874,7 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_WUFIE); if (irqd_is_wakeup_set(irq_get_irq_data(port->irq))) pm_wakeup_event(tport->tty->dev, 0); + ret = IRQ_HANDLED; } /* @@ -896,6 +889,7 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) uart_unlock_and_check_sysrq(port); if (size) tty_flip_buffer_push(tport); + ret = IRQ_HANDLED; } } @@ -903,6 +897,7 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) uart_port_lock(port); stm32_usart_transmit_chars(port); uart_port_unlock(port); + ret = IRQ_HANDLED; } /* Receiver timeout irq for DMA RX */ @@ -912,9 +907,10 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) uart_unlock_and_check_sysrq(port); if (size) tty_flip_buffer_push(tport); + ret = IRQ_HANDLED; } - return IRQ_HANDLED; + return ret; } static void stm32_usart_set_mctrl(struct uart_port *port, unsigned int mctrl) @@ -948,7 +944,7 @@ static void stm32_usart_enable_ms(struct uart_port *port) static void stm32_usart_disable_ms(struct uart_port *port) { - mctrl_gpio_disable_ms(to_stm32_port(port)->gpios); + mctrl_gpio_disable_ms_sync(to_stm32_port(port)->gpios); } /* Transmit stop */ @@ -967,12 +963,10 @@ static void stm32_usart_stop_tx(struct uart_port *port) /* There are probably characters waiting to be transmitted. */ static void stm32_usart_start_tx(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; - if (uart_circ_empty(xmit) && !port->x_char) { - stm32_usart_rs485_rts_disable(port); + if (kfifo_is_empty(&tport->xmit_fifo) && !port->x_char) return; - } stm32_usart_rs485_rts_enable(port); @@ -1055,14 +1049,14 @@ static void stm32_usart_break_ctl(struct uart_port *port, int break_state) const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); if (break_state) stm32_usart_set_bits(port, ofs->rqr, USART_RQR_SBKRQ); else stm32_usart_clr_bits(port, ofs->rqr, USART_RQR_SBKRQ); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static int stm32_usart_startup(struct uart_port *port) @@ -1084,6 +1078,7 @@ static int stm32_usart_startup(struct uart_port *port) val |= USART_CR2_SWAP; writel_relaxed(val, port->membase + ofs->cr2); } + stm32_port->throttled = false; /* RX FIFO Flush */ if (ofs->rqr != UNDEF_REG) @@ -2191,7 +2186,7 @@ static const struct dev_pm_ops stm32_serial_pm_ops = { static struct platform_driver stm32_serial_driver = { .probe = stm32_usart_serial_probe, - .remove_new = stm32_usart_serial_remove, + .remove = stm32_usart_serial_remove, .driver = { .name = DRIVER_NAME, .pm = &stm32_serial_pm_ops, diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index 8d612ab80680..2b3ec65d595d 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -39,10 +39,13 @@ static char *con_read_page; static int hung_up = 0; -static void transmit_chars_putchar(struct uart_port *port, struct circ_buf *xmit) +static void transmit_chars_putchar(struct uart_port *port, + struct tty_port *tport) { - while (!uart_circ_empty(xmit)) { - long status = sun4v_con_putchar(xmit->buf[xmit->tail]); + unsigned char ch; + + while (kfifo_peek(&tport->xmit_fifo, &ch)) { + long status = sun4v_con_putchar(ch); if (status != HV_EOK) break; @@ -51,14 +54,16 @@ static void transmit_chars_putchar(struct uart_port *port, struct circ_buf *xmit } } -static void transmit_chars_write(struct uart_port *port, struct circ_buf *xmit) +static void transmit_chars_write(struct uart_port *port, struct tty_port *tport) { - while (!uart_circ_empty(xmit)) { - unsigned long ra = __pa(xmit->buf + xmit->tail); - unsigned long len, status, sent; + while (!kfifo_is_empty(&tport->xmit_fifo)) { + unsigned long len, ra, status, sent; + unsigned char *tail; + + len = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, + UART_XMIT_SIZE); + ra = __pa(tail); - len = CIRC_CNT_TO_END(xmit->head, xmit->tail, - UART_XMIT_SIZE); status = sun4v_con_write(ra, len, &sent); if (status != HV_EOK) break; @@ -165,7 +170,7 @@ static int receive_chars_read(struct uart_port *port) } struct sunhv_ops { - void (*transmit_chars)(struct uart_port *port, struct circ_buf *xmit); + void (*transmit_chars)(struct uart_port *port, struct tty_port *tport); int (*receive_chars)(struct uart_port *port); }; @@ -196,18 +201,18 @@ static struct tty_port *receive_chars(struct uart_port *port) static void transmit_chars(struct uart_port *port) { - struct circ_buf *xmit; + struct tty_port *tport; if (!port->state) return; - xmit = &port->state->xmit; - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + tport = &port->state->port; + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) return; - sunhv_ops->transmit_chars(port, xmit); + sunhv_ops->transmit_chars(port, tport); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); } @@ -628,7 +633,7 @@ static struct platform_driver hv_driver = { .of_match_table = hv_match, }, .probe = hv_probe, - .remove_new = hv_remove, + .remove = hv_remove, }; static int __init sunhv_init(void) diff --git a/drivers/tty/serial/sunplus-uart.c b/drivers/tty/serial/sunplus-uart.c index f5e29eb4a4ce..38deee571b0d 100644 --- a/drivers/tty/serial/sunplus-uart.c +++ b/drivers/tty/serial/sunplus-uart.c @@ -200,7 +200,7 @@ static void sunplus_break_ctl(struct uart_port *port, int ctl) static void transmit_chars(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; if (port->x_char) { sp_uart_put_char(port, port->x_char); @@ -209,22 +209,24 @@ static void transmit_chars(struct uart_port *port) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { sunplus_stop_tx(port); return; } do { - sp_uart_put_char(port, xmit->buf[xmit->tail]); - uart_xmit_advance(port, 1); - if (uart_circ_empty(xmit)) + unsigned char ch; + + if (!uart_fifo_get(port, &ch)) break; + + sp_uart_put_char(port, ch); } while (sunplus_tx_buf_not_full(port)); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) sunplus_stop_tx(port); } @@ -695,7 +697,7 @@ MODULE_DEVICE_TABLE(of, sp_uart_of_match); static struct platform_driver sunplus_uart_platform_driver = { .probe = sunplus_uart_probe, - .remove_new = sunplus_uart_remove, + .remove = sunplus_uart_remove, .driver = { .name = "sunplus_uart", .of_match_table = sp_uart_of_match, diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index 1ea2f33a07a7..df906ccf2e8a 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -232,7 +232,7 @@ static void sunsab_tx_idle(struct uart_sunsab_port *); static void transmit_chars(struct uart_sunsab_port *up, union sab82532_irq_status *stat) { - struct circ_buf *xmit = &up->port.state->xmit; + struct tty_port *tport = &up->port.state->port; int i; if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) { @@ -252,7 +252,7 @@ static void transmit_chars(struct uart_sunsab_port *up, set_bit(SAB82532_XPR, &up->irqflags); sunsab_tx_idle(up); - if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(&up->port)) { up->interrupt_mask1 |= SAB82532_IMR1_XPR; writeb(up->interrupt_mask1, &up->regs->w.imr1); return; @@ -265,21 +265,22 @@ static void transmit_chars(struct uart_sunsab_port *up, /* Stuff 32 bytes into Transmit FIFO. */ clear_bit(SAB82532_XPR, &up->irqflags); for (i = 0; i < up->port.fifosize; i++) { - writeb(xmit->buf[xmit->tail], - &up->regs->w.xfifo[i]); - uart_xmit_advance(&up->port, 1); - if (uart_circ_empty(xmit)) + unsigned char ch; + + if (!uart_fifo_get(&up->port, &ch)) break; + + writeb(ch, &up->regs->w.xfifo[i]); } /* Issue a Transmit Frame command. */ sunsab_cec_wait(up); writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&up->port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) sunsab_stop_tx(&up->port); } @@ -435,10 +436,10 @@ static void sunsab_start_tx(struct uart_port *port) { struct uart_sunsab_port *up = container_of(port, struct uart_sunsab_port, port); - struct circ_buf *xmit = &up->port.state->xmit; + struct tty_port *tport = &up->port.state->port; int i; - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) return; up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR); @@ -451,11 +452,12 @@ static void sunsab_start_tx(struct uart_port *port) clear_bit(SAB82532_XPR, &up->irqflags); for (i = 0; i < up->port.fifosize; i++) { - writeb(xmit->buf[xmit->tail], - &up->regs->w.xfifo[i]); - uart_xmit_advance(&up->port, 1); - if (uart_circ_empty(xmit)) + unsigned char ch; + + if (!uart_fifo_get(&up->port, &ch)) break; + + writeb(ch, &up->regs->w.xfifo[i]); } /* Issue a Transmit Frame command. */ @@ -1098,7 +1100,7 @@ static struct platform_driver sab_driver = { .of_match_table = sab_match, }, .probe = sab_probe, - .remove_new = sab_remove, + .remove = sab_remove, }; static int __init sunsab_init(void) diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index c8b65f4b2710..383141fe7ba0 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -151,16 +151,6 @@ static void serial_out(struct uart_sunsu_port *up, int offset, int value) } /* - * We used to support using pause I/O for certain machines. We - * haven't supported this for a while, but just in case it's badly - * needed for certain old 386 machines, I've left these #define's - * in.... - */ -#define serial_inp(up, offset) serial_in(up, offset) -#define serial_outp(up, offset, value) serial_out(up, offset, value) - - -/* * For the 16C950 */ static void serial_icr_write(struct uart_sunsu_port *up, int offset, int value) @@ -169,20 +159,6 @@ static void serial_icr_write(struct uart_sunsu_port *up, int offset, int value) serial_out(up, UART_ICR, value); } -#if 0 /* Unused currently */ -static unsigned int serial_icr_read(struct uart_sunsu_port *up, int offset) -{ - unsigned int value; - - serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD); - serial_out(up, UART_SCR, offset); - value = serial_in(up, UART_ICR); - serial_icr_write(up, UART_ACR, up->acr); - - return value; -} -#endif - #ifdef CONFIG_SERIAL_8250_RSA /* * Attempts to turn on the RSA FIFO. Returns zero on failure. @@ -193,12 +169,12 @@ static int __enable_rsa(struct uart_sunsu_port *up) unsigned char mode; int result; - mode = serial_inp(up, UART_RSA_MSR); + mode = serial_in(up, UART_RSA_MSR); result = mode & UART_RSA_MSR_FIFO; if (!result) { - serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); - mode = serial_inp(up, UART_RSA_MSR); + serial_out(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); + mode = serial_in(up, UART_RSA_MSR); result = mode & UART_RSA_MSR_FIFO; } @@ -217,7 +193,7 @@ static void enable_rsa(struct uart_sunsu_port *up) uart_port_unlock_irq(&up->port); } if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) - serial_outp(up, UART_RSA_FRR, 0); + serial_out(up, UART_RSA_FRR, 0); } } @@ -236,12 +212,12 @@ static void disable_rsa(struct uart_sunsu_port *up) up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) { uart_port_lock_irq(&up->port); - mode = serial_inp(up, UART_RSA_MSR); + mode = serial_in(up, UART_RSA_MSR); result = !(mode & UART_RSA_MSR_FIFO); if (!result) { - serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); - mode = serial_inp(up, UART_RSA_MSR); + serial_out(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); + mode = serial_in(up, UART_RSA_MSR); result = !(mode & UART_RSA_MSR_FIFO); } @@ -326,7 +302,7 @@ receive_chars(struct uart_sunsu_port *up, unsigned char *status) int saw_console_brk = 0; do { - ch = serial_inp(up, UART_RX); + ch = serial_in(up, UART_RX); flag = TTY_NORMAL; up->port.icount.rx++; @@ -387,7 +363,7 @@ receive_chars(struct uart_sunsu_port *up, unsigned char *status) */ tty_insert_flip_char(port, 0, TTY_OVERRUN); ignore_char: - *status = serial_inp(up, UART_LSR); + *status = serial_in(up, UART_LSR); } while ((*status & UART_LSR_DR) && (max_count-- > 0)); if (saw_console_brk) @@ -396,11 +372,12 @@ receive_chars(struct uart_sunsu_port *up, unsigned char *status) static void transmit_chars(struct uart_sunsu_port *up) { - struct circ_buf *xmit = &up->port.state->xmit; + struct tty_port *tport = &up->port.state->port; + unsigned char ch; int count; if (up->port.x_char) { - serial_outp(up, UART_TX, up->port.x_char); + serial_out(up, UART_TX, up->port.x_char); up->port.icount.tx++; up->port.x_char = 0; return; @@ -409,23 +386,23 @@ static void transmit_chars(struct uart_sunsu_port *up) sunsu_stop_tx(&up->port); return; } - if (uart_circ_empty(xmit)) { + if (kfifo_is_empty(&tport->xmit_fifo)) { __stop_tx(up); return; } count = up->port.fifosize; do { - serial_out(up, UART_TX, xmit->buf[xmit->tail]); - uart_xmit_advance(&up->port, 1); - if (uart_circ_empty(xmit)) + if (!uart_fifo_get(&up->port, &ch)) break; + + serial_out(up, UART_TX, ch); } while (--count > 0); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&up->port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) __stop_tx(up); } @@ -459,7 +436,7 @@ static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id) uart_port_lock_irqsave(&up->port, &flags); do { - status = serial_inp(up, UART_LSR); + status = serial_in(up, UART_LSR); if (status & UART_LSR_DR) receive_chars(up, &status); check_modem_status(up); @@ -497,7 +474,7 @@ static void sunsu_change_mouse_baud(struct uart_sunsu_port *up) static void receive_kbd_ms_chars(struct uart_sunsu_port *up, int is_break) { do { - unsigned char ch = serial_inp(up, UART_RX); + unsigned char ch = serial_in(up, UART_RX); /* Stop-A is handled by drivers/char/keyboard.c now. */ if (up->su_type == SU_PORT_KBD) { @@ -529,7 +506,7 @@ static irqreturn_t sunsu_kbd_ms_interrupt(int irq, void *dev_id) struct uart_sunsu_port *up = dev_id; if (!(serial_in(up, UART_IIR) & UART_IIR_NO_INT)) { - unsigned char status = serial_inp(up, UART_LSR); + unsigned char status = serial_in(up, UART_LSR); if ((status & UART_LSR_DR) || (status & UART_LSR_BI)) receive_kbd_ms_chars(up, (status & UART_LSR_BI) != 0); @@ -618,14 +595,14 @@ static int sunsu_startup(struct uart_port *port) if (up->port.type == PORT_16C950) { /* Wake up and initialize UART */ up->acr = 0; - serial_outp(up, UART_LCR, 0xBF); - serial_outp(up, UART_EFR, UART_EFR_ECB); - serial_outp(up, UART_IER, 0); - serial_outp(up, UART_LCR, 0); + serial_out(up, UART_LCR, 0xBF); + serial_out(up, UART_EFR, UART_EFR_ECB); + serial_out(up, UART_IER, 0); + serial_out(up, UART_LCR, 0); serial_icr_write(up, UART_CSR, 0); /* Reset the UART */ - serial_outp(up, UART_LCR, 0xBF); - serial_outp(up, UART_EFR, UART_EFR_ECB); - serial_outp(up, UART_LCR, 0); + serial_out(up, UART_LCR, 0xBF); + serial_out(up, UART_EFR, UART_EFR_ECB); + serial_out(up, UART_LCR, 0); } #ifdef CONFIG_SERIAL_8250_RSA @@ -641,19 +618,19 @@ static int sunsu_startup(struct uart_port *port) * (they will be reenabled in set_termios()) */ if (uart_config[up->port.type].flags & UART_CLEAR_FIFO) { - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - serial_outp(up, UART_FCR, 0); + serial_out(up, UART_FCR, 0); } /* * Clear the interrupt registers. */ - (void) serial_inp(up, UART_LSR); - (void) serial_inp(up, UART_RX); - (void) serial_inp(up, UART_IIR); - (void) serial_inp(up, UART_MSR); + (void) serial_in(up, UART_LSR); + (void) serial_in(up, UART_RX); + (void) serial_in(up, UART_IIR); + (void) serial_in(up, UART_MSR); /* * At this point, there's no way the LSR could still be 0xff; @@ -661,7 +638,7 @@ static int sunsu_startup(struct uart_port *port) * here. */ if (!(up->port.flags & UPF_BUGGY_UART) && - (serial_inp(up, UART_LSR) == 0xff)) { + (serial_in(up, UART_LSR) == 0xff)) { printk("ttyS%d: LSR safety check engaged!\n", up->port.line); return -ENODEV; } @@ -681,7 +658,7 @@ static int sunsu_startup(struct uart_port *port) /* * Now, initialize the UART */ - serial_outp(up, UART_LCR, UART_LCR_WLEN8); + serial_out(up, UART_LCR, UART_LCR_WLEN8); uart_port_lock_irqsave(&up->port, &flags); @@ -696,7 +673,7 @@ static int sunsu_startup(struct uart_port *port) * anyway, so we don't enable them here. */ up->ier = UART_IER_RLSI | UART_IER_RDI; - serial_outp(up, UART_IER, up->ier); + serial_out(up, UART_IER, up->ier); if (up->port.flags & UPF_FOURPORT) { unsigned int icp; @@ -711,10 +688,10 @@ static int sunsu_startup(struct uart_port *port) /* * And clear the interrupt registers again for luck. */ - (void) serial_inp(up, UART_LSR); - (void) serial_inp(up, UART_RX); - (void) serial_inp(up, UART_IIR); - (void) serial_inp(up, UART_MSR); + (void) serial_in(up, UART_LSR); + (void) serial_in(up, UART_RX); + (void) serial_in(up, UART_IIR); + (void) serial_in(up, UART_MSR); return 0; } @@ -729,7 +706,7 @@ static void sunsu_shutdown(struct uart_port *port) * Disable interrupts from this port */ up->ier = 0; - serial_outp(up, UART_IER, 0); + serial_out(up, UART_IER, 0); uart_port_lock_irqsave(&up->port, &flags); if (up->port.flags & UPF_FOURPORT) { @@ -745,11 +722,11 @@ static void sunsu_shutdown(struct uart_port *port) /* * Disable break condition and FIFOs */ - serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC); - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | + serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - serial_outp(up, UART_FCR, 0); + serial_out(up, UART_FCR, 0); #ifdef CONFIG_SERIAL_8250_RSA /* @@ -871,22 +848,22 @@ sunsu_change_speed(struct uart_port *port, unsigned int cflag, serial_out(up, UART_IER, up->ier); if (uart_config[up->port.type].flags & UART_STARTECH) { - serial_outp(up, UART_LCR, 0xBF); - serial_outp(up, UART_EFR, cflag & CRTSCTS ? UART_EFR_CTS :0); + serial_out(up, UART_LCR, 0xBF); + serial_out(up, UART_EFR, cflag & CRTSCTS ? UART_EFR_CTS :0); } - serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ - serial_outp(up, UART_DLL, quot & 0xff); /* LS of divisor */ - serial_outp(up, UART_DLM, quot >> 8); /* MS of divisor */ + serial_out(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ + serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */ if (up->port.type == PORT_16750) - serial_outp(up, UART_FCR, fcr); /* set fcr */ - serial_outp(up, UART_LCR, cval); /* reset DLAB */ + serial_out(up, UART_FCR, fcr); /* set fcr */ + serial_out(up, UART_LCR, cval); /* reset DLAB */ up->lcr = cval; /* Save LCR */ if (up->port.type != PORT_16750) { if (fcr & UART_FCR_ENABLE_FIFO) { /* emulated UARTs (Lucent Venus 167x) need two steps */ - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); } - serial_outp(up, UART_FCR, fcr); /* set fcr */ + serial_out(up, UART_FCR, fcr); /* set fcr */ } up->cflag = cflag; @@ -1050,18 +1027,18 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up) * 0x80 is a non-existent port; which should be safe since * include/asm/io.h also makes this assumption. */ - scratch = serial_inp(up, UART_IER); - serial_outp(up, UART_IER, 0); + scratch = serial_in(up, UART_IER); + serial_out(up, UART_IER, 0); #ifdef __i386__ outb(0xff, 0x080); #endif - scratch2 = serial_inp(up, UART_IER); - serial_outp(up, UART_IER, 0x0f); + scratch2 = serial_in(up, UART_IER); + serial_out(up, UART_IER, 0x0f); #ifdef __i386__ outb(0, 0x080); #endif - scratch3 = serial_inp(up, UART_IER); - serial_outp(up, UART_IER, scratch); + scratch3 = serial_in(up, UART_IER); + serial_out(up, UART_IER, scratch); if (scratch2 != 0 || scratch3 != 0x0F) goto out; /* We failed; there's nothing here */ } @@ -1079,16 +1056,16 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up) * that conflicts with COM 1-4 --- we hope! */ if (!(up->port.flags & UPF_SKIP_TEST)) { - serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A); - status1 = serial_inp(up, UART_MSR) & 0xF0; - serial_outp(up, UART_MCR, save_mcr); + serial_out(up, UART_MCR, UART_MCR_LOOP | 0x0A); + status1 = serial_in(up, UART_MSR) & 0xF0; + serial_out(up, UART_MCR, save_mcr); if (status1 != 0x90) goto out; /* We failed loopback test */ } - serial_outp(up, UART_LCR, 0xBF); /* set up for StarTech test */ - serial_outp(up, UART_EFR, 0); /* EFR is the same as FCR */ - serial_outp(up, UART_LCR, 0); - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(up, UART_LCR, 0xBF); /* set up for StarTech test */ + serial_out(up, UART_EFR, 0); /* EFR is the same as FCR */ + serial_out(up, UART_LCR, 0); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); scratch = serial_in(up, UART_IIR) >> 6; switch (scratch) { case 0: @@ -1106,19 +1083,19 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up) } if (up->port.type == PORT_16550A) { /* Check for Startech UART's */ - serial_outp(up, UART_LCR, UART_LCR_DLAB); + serial_out(up, UART_LCR, UART_LCR_DLAB); if (serial_in(up, UART_EFR) == 0) { up->port.type = PORT_16650; } else { - serial_outp(up, UART_LCR, 0xBF); + serial_out(up, UART_LCR, 0xBF); if (serial_in(up, UART_EFR) == 0) up->port.type = PORT_16650V2; } } if (up->port.type == PORT_16550A) { /* Check for TI 16750 */ - serial_outp(up, UART_LCR, save_lcr | UART_LCR_DLAB); - serial_outp(up, UART_FCR, + serial_out(up, UART_LCR, save_lcr | UART_LCR_DLAB); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); scratch = serial_in(up, UART_IIR) >> 5; if (scratch == 7) { @@ -1128,24 +1105,24 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up) * mode if the UART_FCR7_64BYTE bit was set * while UART_LCR_DLAB was latched. */ - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_outp(up, UART_LCR, 0); - serial_outp(up, UART_FCR, + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(up, UART_LCR, 0); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); scratch = serial_in(up, UART_IIR) >> 5; if (scratch == 6) up->port.type = PORT_16750; } - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); } - serial_outp(up, UART_LCR, save_lcr); + serial_out(up, UART_LCR, save_lcr); if (up->port.type == PORT_16450) { scratch = serial_in(up, UART_SCR); - serial_outp(up, UART_SCR, 0xa5); + serial_out(up, UART_SCR, 0xa5); status1 = serial_in(up, UART_SCR); - serial_outp(up, UART_SCR, 0x5a); + serial_out(up, UART_SCR, 0x5a); status2 = serial_in(up, UART_SCR); - serial_outp(up, UART_SCR, scratch); + serial_out(up, UART_SCR, scratch); if ((status1 != 0xa5) || (status2 != 0x5a)) up->port.type = PORT_8250; @@ -1162,15 +1139,15 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up) */ #ifdef CONFIG_SERIAL_8250_RSA if (up->port.type == PORT_RSA) - serial_outp(up, UART_RSA_FRR, 0); + serial_out(up, UART_RSA_FRR, 0); #endif - serial_outp(up, UART_MCR, save_mcr); - serial_outp(up, UART_FCR, (UART_FCR_ENABLE_FIFO | + serial_out(up, UART_MCR, save_mcr); + serial_out(up, UART_FCR, (UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); - serial_outp(up, UART_FCR, 0); + serial_out(up, UART_FCR, 0); (void)serial_in(up, UART_RX); - serial_outp(up, UART_IER, 0); + serial_out(up, UART_IER, 0); out: uart_port_unlock_irqrestore(&up->port, flags); @@ -1381,44 +1358,29 @@ static inline struct console *SUNSU_CONSOLE(void) static enum su_type su_get_type(struct device_node *dp) { - struct device_node *ap = of_find_node_by_path("/aliases"); - enum su_type rc = SU_PORT_PORT; + struct device_node *ap __free(device_node) = + of_find_node_by_path("/aliases"); if (ap) { const char *keyb = of_get_property(ap, "keyboard", NULL); const char *ms = of_get_property(ap, "mouse", NULL); - struct device_node *match; if (keyb) { - match = of_find_node_by_path(keyb); + struct device_node *match __free(device_node) = + of_find_node_by_path(keyb); - /* - * The pointer is used as an identifier not - * as a pointer, we can drop the refcount on - * the of__node immediately after getting it. - */ - of_node_put(match); - - if (dp == match) { - rc = SU_PORT_KBD; - goto out; - } + if (dp == match) + return SU_PORT_KBD; } if (ms) { - match = of_find_node_by_path(ms); - - of_node_put(match); + struct device_node *match __free(device_node) = + of_find_node_by_path(ms); - if (dp == match) { - rc = SU_PORT_MS; - goto out; - } + if (dp == match) + return SU_PORT_MS; } } - -out: - of_node_put(ap); - return rc; + return SU_PORT_PORT; } static int su_probe(struct platform_device *op) @@ -1563,7 +1525,7 @@ static struct platform_driver su_driver = { .of_match_table = su_match, }, .probe = su_probe, - .remove_new = su_remove, + .remove = su_remove, }; static int __init sunsu_init(void) diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c index c99289c6c8f8..0551c24c06f5 100644 --- a/drivers/tty/serial/sunzilog.c +++ b/drivers/tty/serial/sunzilog.c @@ -453,7 +453,8 @@ static void sunzilog_status_handle(struct uart_sunzilog_port *up, static void sunzilog_transmit_chars(struct uart_sunzilog_port *up, struct zilog_channel __iomem *channel) { - struct circ_buf *xmit; + struct tty_port *tport; + unsigned char ch; if (ZS_IS_CONS(up)) { unsigned char status = readb(&channel->control); @@ -496,21 +497,20 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up, if (up->port.state == NULL) goto ack_tx_int; - xmit = &up->port.state->xmit; - if (uart_circ_empty(xmit)) - goto ack_tx_int; + tport = &up->port.state->port; if (uart_tx_stopped(&up->port)) goto ack_tx_int; + if (!uart_fifo_get(&up->port, &ch)) + goto ack_tx_int; + up->flags |= SUNZILOG_FLAG_TX_ACTIVE; - writeb(xmit->buf[xmit->tail], &channel->data); + writeb(ch, &channel->data); ZSDELAY(); ZS_WSYNC(channel); - uart_xmit_advance(&up->port, 1); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&up->port); return; @@ -700,17 +700,16 @@ static void sunzilog_start_tx(struct uart_port *port) port->icount.tx++; port->x_char = 0; } else { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; + unsigned char ch; - if (uart_circ_empty(xmit)) + if (!uart_fifo_get(&up->port, &ch)) return; - writeb(xmit->buf[xmit->tail], &channel->data); + writeb(ch, &channel->data); ZSDELAY(); ZS_WSYNC(channel); - uart_xmit_advance(port, 1); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&up->port); } } @@ -1539,7 +1538,7 @@ static struct platform_driver zs_driver = { .of_match_table = zs_match, }, .probe = zs_probe, - .remove_new = zs_remove, + .remove = zs_remove, }; static int __init sunzilog_init(void) diff --git a/drivers/tty/serial/tegra-tcu.c b/drivers/tty/serial/tegra-tcu.c index d9c78320eb02..7033dbfe8ba1 100644 --- a/drivers/tty/serial/tegra-tcu.c +++ b/drivers/tty/serial/tegra-tcu.c @@ -91,15 +91,17 @@ static void tegra_tcu_write(struct tegra_tcu *tcu, const char *s, static void tegra_tcu_uart_start_tx(struct uart_port *port) { struct tegra_tcu *tcu = port->private_data; - struct circ_buf *xmit = &port->state->xmit; - unsigned long count; + struct tty_port *tport = &port->state->port; + unsigned char *tail; + unsigned int count; for (;;) { - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + count = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, + UART_XMIT_SIZE); if (!count) break; - tegra_tcu_write(tcu, &xmit->buf[xmit->tail], count); + tegra_tcu_write(tcu, tail, count); uart_xmit_advance(port, count); } @@ -291,7 +293,7 @@ static struct platform_driver tegra_tcu_driver = { .of_match_table = tegra_tcu_match, }, .probe = tegra_tcu_probe, - .remove_new = tegra_tcu_remove, + .remove = tegra_tcu_remove, }; module_platform_driver(tegra_tcu_driver); diff --git a/drivers/tty/serial/tegra-utc.c b/drivers/tty/serial/tegra-utc.c new file mode 100644 index 000000000000..0c70d3e7b9b9 --- /dev/null +++ b/drivers/tty/serial/tegra-utc.c @@ -0,0 +1,625 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// NVIDIA Tegra UTC (UART Trace Controller) driver. + +#include <linux/bits.h> +#include <linux/console.h> +#include <linux/container_of.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/iopoll.h> +#include <linux/kfifo.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> +#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/types.h> + +#define TEGRA_UTC_ENABLE 0x000 +#define TEGRA_UTC_ENABLE_CLIENT_ENABLE BIT(0) + +#define TEGRA_UTC_FIFO_THRESHOLD 0x008 + +#define TEGRA_UTC_COMMAND 0x00c +#define TEGRA_UTC_COMMAND_RESET BIT(0) +#define TEGRA_UTC_COMMAND_FLUSH BIT(1) + +#define TEGRA_UTC_DATA 0x020 + +#define TEGRA_UTC_FIFO_STATUS 0x100 +#define TEGRA_UTC_FIFO_EMPTY BIT(0) +#define TEGRA_UTC_FIFO_FULL BIT(1) +#define TEGRA_UTC_FIFO_REQ BIT(2) +#define TEGRA_UTC_FIFO_OVERFLOW BIT(3) +#define TEGRA_UTC_FIFO_TIMEOUT BIT(4) + +#define TEGRA_UTC_FIFO_OCCUPANCY 0x104 + +#define TEGRA_UTC_INTR_STATUS 0x108 +#define TEGRA_UTC_INTR_SET 0x10c +#define TEGRA_UTC_INTR_MASK 0x110 +#define TEGRA_UTC_INTR_CLEAR 0x114 +#define TEGRA_UTC_INTR_EMPTY BIT(0) +#define TEGRA_UTC_INTR_FULL BIT(1) +#define TEGRA_UTC_INTR_REQ BIT(2) +#define TEGRA_UTC_INTR_OVERFLOW BIT(3) +#define TEGRA_UTC_INTR_TIMEOUT BIT(4) + +#define TEGRA_UTC_UART_NR 16 + +#define TEGRA_UTC_INTR_COMMON (TEGRA_UTC_INTR_REQ | TEGRA_UTC_INTR_FULL | TEGRA_UTC_INTR_EMPTY) + +struct tegra_utc_port { +#if IS_ENABLED(CONFIG_SERIAL_TEGRA_UTC_CONSOLE) + struct console console; +#endif + struct uart_port port; + + void __iomem *rx_base; + void __iomem *tx_base; + + u32 tx_irqmask; + u32 rx_irqmask; + + unsigned int fifosize; + u32 tx_threshold; + u32 rx_threshold; +}; + +static u32 tegra_utc_rx_readl(struct tegra_utc_port *tup, unsigned int offset) +{ + void __iomem *addr = tup->rx_base + offset; + + return readl_relaxed(addr); +} + +static void tegra_utc_rx_writel(struct tegra_utc_port *tup, u32 val, unsigned int offset) +{ + void __iomem *addr = tup->rx_base + offset; + + writel_relaxed(val, addr); +} + +static u32 tegra_utc_tx_readl(struct tegra_utc_port *tup, unsigned int offset) +{ + void __iomem *addr = tup->tx_base + offset; + + return readl_relaxed(addr); +} + +static void tegra_utc_tx_writel(struct tegra_utc_port *tup, u32 val, unsigned int offset) +{ + void __iomem *addr = tup->tx_base + offset; + + writel_relaxed(val, addr); +} + +static void tegra_utc_enable_tx_irq(struct tegra_utc_port *tup) +{ + tup->tx_irqmask = TEGRA_UTC_INTR_REQ; + + tegra_utc_tx_writel(tup, tup->tx_irqmask, TEGRA_UTC_INTR_MASK); + tegra_utc_tx_writel(tup, tup->tx_irqmask, TEGRA_UTC_INTR_SET); +} + +static void tegra_utc_disable_tx_irq(struct tegra_utc_port *tup) +{ + tup->tx_irqmask = 0x0; + + tegra_utc_tx_writel(tup, tup->tx_irqmask, TEGRA_UTC_INTR_MASK); + tegra_utc_tx_writel(tup, tup->tx_irqmask, TEGRA_UTC_INTR_SET); +} + +static void tegra_utc_stop_tx(struct uart_port *port) +{ + struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port); + + tegra_utc_disable_tx_irq(tup); +} + +static void tegra_utc_init_tx(struct tegra_utc_port *tup) +{ + /* Disable TX. */ + tegra_utc_tx_writel(tup, 0x0, TEGRA_UTC_ENABLE); + + /* Update the FIFO Threshold. */ + tegra_utc_tx_writel(tup, tup->tx_threshold, TEGRA_UTC_FIFO_THRESHOLD); + + /* Clear and mask all the interrupts. */ + tegra_utc_tx_writel(tup, TEGRA_UTC_INTR_COMMON, TEGRA_UTC_INTR_CLEAR); + tegra_utc_disable_tx_irq(tup); + + /* Enable TX. */ + tegra_utc_tx_writel(tup, TEGRA_UTC_ENABLE_CLIENT_ENABLE, TEGRA_UTC_ENABLE); +} + +static void tegra_utc_init_rx(struct tegra_utc_port *tup) +{ + tup->rx_irqmask = TEGRA_UTC_INTR_REQ | TEGRA_UTC_INTR_TIMEOUT; + + tegra_utc_rx_writel(tup, TEGRA_UTC_COMMAND_RESET, TEGRA_UTC_COMMAND); + tegra_utc_rx_writel(tup, tup->rx_threshold, TEGRA_UTC_FIFO_THRESHOLD); + + /* Clear all the pending interrupts. */ + tegra_utc_rx_writel(tup, TEGRA_UTC_INTR_TIMEOUT | TEGRA_UTC_INTR_OVERFLOW | + TEGRA_UTC_INTR_COMMON, TEGRA_UTC_INTR_CLEAR); + tegra_utc_rx_writel(tup, tup->rx_irqmask, TEGRA_UTC_INTR_MASK); + tegra_utc_rx_writel(tup, tup->rx_irqmask, TEGRA_UTC_INTR_SET); + + /* Enable RX. */ + tegra_utc_rx_writel(tup, TEGRA_UTC_ENABLE_CLIENT_ENABLE, TEGRA_UTC_ENABLE); +} + +static bool tegra_utc_tx_chars(struct tegra_utc_port *tup) +{ + struct uart_port *port = &tup->port; + unsigned int pending; + u8 c; + + pending = uart_port_tx(port, c, + !(tegra_utc_tx_readl(tup, TEGRA_UTC_FIFO_STATUS) & TEGRA_UTC_FIFO_FULL), + tegra_utc_tx_writel(tup, c, TEGRA_UTC_DATA)); + + return pending; +} + +static void tegra_utc_rx_chars(struct tegra_utc_port *tup) +{ + struct tty_port *port = &tup->port.state->port; + unsigned int max_chars = 256; + u32 status; + int sysrq; + u32 ch; + + while (max_chars--) { + status = tegra_utc_rx_readl(tup, TEGRA_UTC_FIFO_STATUS); + if (status & TEGRA_UTC_FIFO_EMPTY) + break; + + ch = tegra_utc_rx_readl(tup, TEGRA_UTC_DATA); + tup->port.icount.rx++; + + if (status & TEGRA_UTC_FIFO_OVERFLOW) + tup->port.icount.overrun++; + + uart_port_unlock(&tup->port); + sysrq = uart_handle_sysrq_char(&tup->port, ch); + uart_port_lock(&tup->port); + + if (!sysrq) + tty_insert_flip_char(port, ch, TTY_NORMAL); + } + + tty_flip_buffer_push(port); +} + +static irqreturn_t tegra_utc_isr(int irq, void *dev_id) +{ + struct tegra_utc_port *tup = dev_id; + unsigned int handled = 0; + u32 status; + + uart_port_lock(&tup->port); + + /* Process RX_REQ and RX_TIMEOUT interrupts. */ + do { + status = tegra_utc_rx_readl(tup, TEGRA_UTC_INTR_STATUS) & tup->rx_irqmask; + if (status) { + tegra_utc_rx_writel(tup, tup->rx_irqmask, TEGRA_UTC_INTR_CLEAR); + tegra_utc_rx_chars(tup); + handled = 1; + } + } while (status); + + /* Process TX_REQ interrupt. */ + do { + status = tegra_utc_tx_readl(tup, TEGRA_UTC_INTR_STATUS) & tup->tx_irqmask; + if (status) { + tegra_utc_tx_writel(tup, tup->tx_irqmask, TEGRA_UTC_INTR_CLEAR); + tegra_utc_tx_chars(tup); + handled = 1; + } + } while (status); + + uart_port_unlock(&tup->port); + + return IRQ_RETVAL(handled); +} + +static unsigned int tegra_utc_tx_empty(struct uart_port *port) +{ + struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port); + + return tegra_utc_tx_readl(tup, TEGRA_UTC_FIFO_OCCUPANCY) ? 0 : TIOCSER_TEMT; +} + +static void tegra_utc_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static unsigned int tegra_utc_get_mctrl(struct uart_port *port) +{ + return 0; +} + +static void tegra_utc_start_tx(struct uart_port *port) +{ + struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port); + + if (tegra_utc_tx_chars(tup)) + tegra_utc_enable_tx_irq(tup); +} + +static void tegra_utc_stop_rx(struct uart_port *port) +{ + struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port); + + tup->rx_irqmask = 0x0; + tegra_utc_rx_writel(tup, tup->rx_irqmask, TEGRA_UTC_INTR_MASK); + tegra_utc_rx_writel(tup, tup->rx_irqmask, TEGRA_UTC_INTR_SET); +} + +static void tegra_utc_hw_init(struct tegra_utc_port *tup) +{ + tegra_utc_init_tx(tup); + tegra_utc_init_rx(tup); +} + +static int tegra_utc_startup(struct uart_port *port) +{ + struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port); + int ret; + + tegra_utc_hw_init(tup); + + /* Interrupt is dedicated to this UTC client. */ + ret = request_irq(port->irq, tegra_utc_isr, 0, dev_name(port->dev), tup); + if (ret < 0) + dev_err(port->dev, "failed to register interrupt handler\n"); + + return ret; +} + +static void tegra_utc_shutdown(struct uart_port *port) +{ + struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port); + + tegra_utc_rx_writel(tup, 0x0, TEGRA_UTC_ENABLE); + free_irq(port->irq, tup); +} + +static void tegra_utc_set_termios(struct uart_port *port, struct ktermios *termios, + const struct ktermios *old) +{ + /* The Tegra UTC clients supports only 8-N-1 configuration without HW flow control */ + termios->c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD); + termios->c_cflag &= ~(CMSPAR | CRTSCTS); + termios->c_cflag |= CS8 | CLOCAL; +} + +#ifdef CONFIG_CONSOLE_POLL + +static int tegra_utc_poll_init(struct uart_port *port) +{ + struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port); + + tegra_utc_hw_init(tup); + return 0; +} + +static int tegra_utc_get_poll_char(struct uart_port *port) +{ + struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port); + + if (tegra_utc_rx_readl(tup, TEGRA_UTC_FIFO_STATUS) & TEGRA_UTC_FIFO_EMPTY) + return NO_POLL_CHAR; + + return tegra_utc_rx_readl(tup, TEGRA_UTC_DATA); +} + +static void tegra_utc_put_poll_char(struct uart_port *port, unsigned char ch) +{ + struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port); + u32 val; + + read_poll_timeout_atomic(tegra_utc_tx_readl, val, !(val & TEGRA_UTC_FIFO_FULL), + 0, USEC_PER_SEC, false, tup, TEGRA_UTC_FIFO_STATUS); + + tegra_utc_tx_writel(tup, ch, TEGRA_UTC_DATA); +} + +#endif + +static const struct uart_ops tegra_utc_uart_ops = { + .tx_empty = tegra_utc_tx_empty, + .set_mctrl = tegra_utc_set_mctrl, + .get_mctrl = tegra_utc_get_mctrl, + .stop_tx = tegra_utc_stop_tx, + .start_tx = tegra_utc_start_tx, + .stop_rx = tegra_utc_stop_rx, + .startup = tegra_utc_startup, + .shutdown = tegra_utc_shutdown, + .set_termios = tegra_utc_set_termios, +#ifdef CONFIG_CONSOLE_POLL + .poll_init = tegra_utc_poll_init, + .poll_get_char = tegra_utc_get_poll_char, + .poll_put_char = tegra_utc_put_poll_char, +#endif +}; + +#if IS_ENABLED(CONFIG_SERIAL_TEGRA_UTC_CONSOLE) +#define TEGRA_UTC_DEFAULT_FIFO_THRESHOLD 4 +#define TEGRA_UTC_EARLYCON_MAX_BURST_SIZE 128 + +static void tegra_utc_putc(struct uart_port *port, unsigned char c) +{ + writel(c, port->membase + TEGRA_UTC_DATA); +} + +static void tegra_utc_early_write(struct console *con, const char *s, unsigned int n) +{ + struct earlycon_device *dev = con->data; + + while (n) { + u32 burst_size = TEGRA_UTC_EARLYCON_MAX_BURST_SIZE; + + burst_size -= readl(dev->port.membase + TEGRA_UTC_FIFO_OCCUPANCY); + if (n < burst_size) + burst_size = n; + + uart_console_write(&dev->port, s, burst_size, tegra_utc_putc); + + n -= burst_size; + s += burst_size; + } +} + +static int __init tegra_utc_early_console_setup(struct earlycon_device *device, const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + /* Configure TX */ + writel(TEGRA_UTC_COMMAND_FLUSH | TEGRA_UTC_COMMAND_RESET, + device->port.membase + TEGRA_UTC_COMMAND); + writel(TEGRA_UTC_DEFAULT_FIFO_THRESHOLD, device->port.membase + TEGRA_UTC_FIFO_THRESHOLD); + + /* Clear and mask all the interrupts. */ + writel(TEGRA_UTC_INTR_COMMON, device->port.membase + TEGRA_UTC_INTR_CLEAR); + + writel(0x0, device->port.membase + TEGRA_UTC_INTR_MASK); + writel(0x0, device->port.membase + TEGRA_UTC_INTR_SET); + + /* Enable TX. */ + writel(TEGRA_UTC_ENABLE_CLIENT_ENABLE, device->port.membase + TEGRA_UTC_ENABLE); + + device->con->write = tegra_utc_early_write; + + return 0; +} +OF_EARLYCON_DECLARE(tegra_utc, "nvidia,tegra264-utc", tegra_utc_early_console_setup); + +static void tegra_utc_console_putchar(struct uart_port *port, unsigned char ch) +{ + struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port); + + tegra_utc_tx_writel(tup, ch, TEGRA_UTC_DATA); +} + +static void tegra_utc_console_write_atomic(struct console *cons, struct nbcon_write_context *wctxt) +{ + struct tegra_utc_port *tup = container_of(cons, struct tegra_utc_port, console); + unsigned int len; + char *outbuf; + + if (!nbcon_enter_unsafe(wctxt)) + return; + + outbuf = wctxt->outbuf; + len = wctxt->len; + + while (len) { + u32 burst_size = tup->fifosize; + + burst_size -= tegra_utc_tx_readl(tup, TEGRA_UTC_FIFO_OCCUPANCY); + if (len < burst_size) + burst_size = len; + + uart_console_write(&tup->port, outbuf, burst_size, tegra_utc_console_putchar); + + outbuf += burst_size; + len -= burst_size; + } + + nbcon_exit_unsafe(wctxt); +} + +static void tegra_utc_console_write_thread(struct console *cons, struct nbcon_write_context *wctxt) +{ + struct tegra_utc_port *tup = container_of(cons, struct tegra_utc_port, console); + unsigned int len = READ_ONCE(wctxt->len); + unsigned int i; + u32 val; + + for (i = 0; i < len; i++) { + if (!nbcon_enter_unsafe(wctxt)) + break; + + read_poll_timeout_atomic(tegra_utc_tx_readl, val, !(val & TEGRA_UTC_FIFO_FULL), + 0, USEC_PER_SEC, false, tup, TEGRA_UTC_FIFO_STATUS); + uart_console_write(&tup->port, wctxt->outbuf + i, 1, tegra_utc_console_putchar); + + if (!nbcon_exit_unsafe(wctxt)) + break; + } +} + +static void tegra_utc_console_device_lock(struct console *cons, unsigned long *flags) +{ + struct tegra_utc_port *tup = container_of(cons, struct tegra_utc_port, console); + struct uart_port *port = &tup->port; + + __uart_port_lock_irqsave(port, flags); +} + +static void tegra_utc_console_device_unlock(struct console *cons, unsigned long flags) +{ + struct tegra_utc_port *tup = container_of(cons, struct tegra_utc_port, console); + struct uart_port *port = &tup->port; + + __uart_port_unlock_irqrestore(port, flags); +} + +static int tegra_utc_console_setup(struct console *cons, char *options) +{ + struct tegra_utc_port *tup = container_of(cons, struct tegra_utc_port, console); + + tegra_utc_init_tx(tup); + + return 0; +} +#endif + +static struct uart_driver tegra_utc_driver = { + .driver_name = "tegra-utc", + .dev_name = "ttyUTC", + .nr = TEGRA_UTC_UART_NR, +}; + +static int tegra_utc_setup_port(struct device *dev, struct tegra_utc_port *tup) +{ + tup->port.dev = dev; + tup->port.fifosize = tup->fifosize; + tup->port.flags = UPF_BOOT_AUTOCONF; + tup->port.iotype = UPIO_MEM; + tup->port.ops = &tegra_utc_uart_ops; + tup->port.type = PORT_TEGRA_TCU; + tup->port.private_data = tup; + +#if IS_ENABLED(CONFIG_SERIAL_TEGRA_UTC_CONSOLE) + strscpy(tup->console.name, "ttyUTC", sizeof(tup->console.name)); + tup->console.write_atomic = tegra_utc_console_write_atomic; + tup->console.write_thread = tegra_utc_console_write_thread; + tup->console.device_lock = tegra_utc_console_device_lock; + tup->console.device_unlock = tegra_utc_console_device_unlock; + tup->console.device = uart_console_device; + tup->console.setup = tegra_utc_console_setup; + tup->console.flags = CON_PRINTBUFFER | CON_NBCON; + tup->console.data = &tegra_utc_driver; +#endif + + return uart_read_port_properties(&tup->port); +} + +static int tegra_utc_register_port(struct tegra_utc_port *tup) +{ + int ret; + + ret = uart_add_one_port(&tegra_utc_driver, &tup->port); + if (ret) + return ret; + +#if IS_ENABLED(CONFIG_SERIAL_TEGRA_UTC_CONSOLE) + register_console(&tup->console); +#endif + + return 0; +} + +static int tegra_utc_probe(struct platform_device *pdev) +{ + const unsigned int *soc_fifosize; + struct device *dev = &pdev->dev; + struct tegra_utc_port *tup; + int ret; + + tup = devm_kzalloc(dev, sizeof(*tup), GFP_KERNEL); + if (!tup) + return -ENOMEM; + + ret = device_property_read_u32(dev, "tx-threshold", &tup->tx_threshold); + if (ret) + return dev_err_probe(dev, ret, "missing %s property\n", "tx-threshold"); + + ret = device_property_read_u32(dev, "rx-threshold", &tup->rx_threshold); + if (ret) + return dev_err_probe(dev, ret, "missing %s property\n", "rx-threshold"); + + soc_fifosize = device_get_match_data(dev); + tup->fifosize = *soc_fifosize; + + tup->tx_base = devm_platform_ioremap_resource_byname(pdev, "tx"); + if (IS_ERR(tup->tx_base)) + return PTR_ERR(tup->tx_base); + + tup->rx_base = devm_platform_ioremap_resource_byname(pdev, "rx"); + if (IS_ERR(tup->rx_base)) + return PTR_ERR(tup->rx_base); + + ret = tegra_utc_setup_port(dev, tup); + if (ret) + dev_err_probe(dev, ret, "failed to setup uart port\n"); + + platform_set_drvdata(pdev, tup); + + return tegra_utc_register_port(tup); +} + +static void tegra_utc_remove(struct platform_device *pdev) +{ + struct tegra_utc_port *tup = platform_get_drvdata(pdev); + +#if IS_ENABLED(CONFIG_SERIAL_TEGRA_UTC_CONSOLE) + unregister_console(&tup->console); +#endif + uart_remove_one_port(&tegra_utc_driver, &tup->port); +} + +static const unsigned int tegra264_utc_soc = 128; + +static const struct of_device_id tegra_utc_of_match[] = { + { .compatible = "nvidia,tegra264-utc", .data = &tegra264_utc_soc }, + {} +}; +MODULE_DEVICE_TABLE(of, tegra_utc_of_match); + +static struct platform_driver tegra_utc_platform_driver = { + .probe = tegra_utc_probe, + .remove = tegra_utc_remove, + .driver = { + .name = "tegra-utc", + .of_match_table = tegra_utc_of_match, + }, +}; + +static int __init tegra_utc_init(void) +{ + int ret; + + ret = uart_register_driver(&tegra_utc_driver); + if (ret) + return ret; + + ret = platform_driver_register(&tegra_utc_platform_driver); + if (ret) + uart_unregister_driver(&tegra_utc_driver); + + return ret; +} +module_init(tegra_utc_init); + +static void __exit tegra_utc_exit(void) +{ + platform_driver_unregister(&tegra_utc_platform_driver); + uart_unregister_driver(&tegra_utc_driver); +} +module_exit(tegra_utc_exit); + +MODULE_AUTHOR("Kartik Rajput <kkartik@nvidia.com>"); +MODULE_DESCRIPTION("Tegra UART Trace Controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c index 4bc89a9b380a..6fa93c3872a7 100644 --- a/drivers/tty/serial/timbuart.c +++ b/drivers/tty/serial/timbuart.c @@ -95,14 +95,11 @@ static void timbuart_rx_chars(struct uart_port *port) static void timbuart_tx_chars(struct uart_port *port) { - struct circ_buf *xmit = &port->state->xmit; + unsigned char ch; while (!(ioread32(port->membase + TIMBUART_ISR) & TXBF) && - !uart_circ_empty(xmit)) { - iowrite8(xmit->buf[xmit->tail], - port->membase + TIMBUART_TXFIFO); - uart_xmit_advance(port, 1); - } + uart_fifo_get(port, &ch)) + iowrite8(ch, port->membase + TIMBUART_TXFIFO); dev_dbg(port->dev, "%s - total written %d bytes, CTL: %x, RTS: %x, baud: %x\n", @@ -117,9 +114,9 @@ static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier) { struct timbuart_port *uart = container_of(port, struct timbuart_port, port); - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) return; if (port->x_char) @@ -130,7 +127,7 @@ static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier) /* clear all TX interrupts */ iowrite32(TXFLAGS, port->membase + TIMBUART_ISR); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); } else /* Re-enable any tx interrupt */ @@ -141,7 +138,7 @@ static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier) * we wake up the upper layer later when we got the interrupt * to give it some time to go out... */ - if (!uart_circ_empty(xmit)) + if (!kfifo_is_empty(&tport->xmit_fifo)) *ier |= TXBAE; dev_dbg(port->dev, "%s - leaving\n", __func__); @@ -488,7 +485,7 @@ static struct platform_driver timbuart_platform_driver = { .name = "timb-uart", }, .probe = timbuart_probe, - .remove_new = timbuart_remove, + .remove = timbuart_remove, }; module_platform_driver(timbuart_platform_driver); diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 10ba41b7be99..39c1fd1ff9ce 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -189,7 +189,8 @@ static int ulite_receive(struct uart_port *port, int stat) static int ulite_transmit(struct uart_port *port, int stat) { - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; + unsigned char ch; if (stat & ULITE_STATUS_TXFULL) return 0; @@ -201,14 +202,16 @@ static int ulite_transmit(struct uart_port *port, int stat) return 1; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + if (uart_tx_stopped(port)) + return 0; + + if (!uart_fifo_get(port, &ch)) return 0; - uart_out32(xmit->buf[xmit->tail], ULITE_TX, port); - uart_xmit_advance(port, 1); + uart_out32(ch, ULITE_TX, port); /* wake up */ - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); return 1; @@ -877,16 +880,6 @@ of_err: pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); - if (!ulite_uart_driver.state) { - dev_dbg(&pdev->dev, "uartlite: calling uart_register_driver()\n"); - ret = uart_register_driver(&ulite_uart_driver); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to register driver\n"); - clk_disable_unprepare(pdata->clk); - return ret; - } - } - ret = ulite_assign(&pdev->dev, id, res->start, irq, pdata); pm_runtime_mark_last_busy(&pdev->dev); @@ -912,7 +905,7 @@ MODULE_ALIAS("platform:uartlite"); static struct platform_driver ulite_platform_driver = { .probe = ulite_probe, - .remove_new = ulite_remove, + .remove = ulite_remove, .driver = { .name = "uartlite", .of_match_table = of_match_ptr(ulite_of_match), @@ -926,16 +919,25 @@ static struct platform_driver ulite_platform_driver = { static int __init ulite_init(void) { + int ret; + + pr_debug("uartlite: calling uart_register_driver()\n"); + ret = uart_register_driver(&ulite_uart_driver); + if (ret) + return ret; pr_debug("uartlite: calling platform_driver_register()\n"); - return platform_driver_register(&ulite_platform_driver); + ret = platform_driver_register(&ulite_platform_driver); + if (ret) + uart_unregister_driver(&ulite_uart_driver); + + return ret; } static void __exit ulite_exit(void) { platform_driver_unregister(&ulite_platform_driver); - if (ulite_uart_driver.state) - uart_unregister_driver(&ulite_uart_driver); + uart_unregister_driver(&ulite_uart_driver); } module_init(ulite_init); diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index 397b95dff7ed..0613f8c11ab1 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -334,7 +334,7 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port) unsigned char *p; unsigned int count; struct uart_port *port = &qe_port->port; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; /* Handle xon/xoff */ if (port->x_char) { @@ -358,7 +358,7 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port) return 1; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { qe_uart_stop_tx(port); return 0; } @@ -366,16 +366,10 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port) /* Pick next descriptor and fill from buffer */ bdp = qe_port->tx_cur; - while (!(ioread16be(&bdp->status) & BD_SC_READY) && !uart_circ_empty(xmit)) { - count = 0; + while (!(ioread16be(&bdp->status) & BD_SC_READY) && + !kfifo_is_empty(&tport->xmit_fifo)) { p = qe2cpu_addr(ioread32be(&bdp->buf), qe_port); - while (count < qe_port->tx_fifosize) { - *p++ = xmit->buf[xmit->tail]; - uart_xmit_advance(port, 1); - count++; - if (uart_circ_empty(xmit)) - break; - } + count = uart_fifo_out(port, p, qe_port->tx_fifosize); iowrite16be(count, &bdp->length); qe_setbits_be16(&bdp->status, BD_SC_READY); @@ -388,10 +382,10 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port) } qe_port->tx_cur = bdp; - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) { + if (kfifo_is_empty(&tport->xmit_fifo)) { /* The kernel buffer is empty, so turn off TX interrupts. We don't need to be told when the QE is finished transmitting the data. */ @@ -1051,7 +1045,7 @@ static int qe_uart_verify_port(struct uart_port *port, if (ser->type != PORT_UNKNOWN && ser->type != PORT_CPM) return -EINVAL; - if (ser->irq < 0 || ser->irq >= nr_irqs) + if (ser->irq < 0 || ser->irq >= irq_get_nr_irqs()) return -EINVAL; if (ser->baud_base < 9600) @@ -1490,7 +1484,7 @@ static struct platform_driver ucc_uart_of_driver = { .of_match_table = ucc_uart_match, }, .probe = ucc_uart_probe, - .remove_new = ucc_uart_remove, + .remove = ucc_uart_remove, }; static int __init ucc_uart_init(void) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 5f48ec37cb25..fe457bf1e15b 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -25,6 +25,7 @@ #include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/delay.h> +#include <linux/reset.h> #define CDNS_UART_TTY_NAME "ttyPS" #define CDNS_UART_NAME "xuartps" @@ -198,6 +199,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); * @gpiod_rts: Pointer to the gpio descriptor * @rs485_tx_started: RS485 tx state * @tx_timer: Timer for tx + * @rstc: Pointer to the reset control */ struct cdns_uart { struct uart_port *port; @@ -211,12 +213,13 @@ struct cdns_uart { struct gpio_desc *gpiod_rts; bool rs485_tx_started; struct hrtimer tx_timer; + struct reset_control *rstc; }; struct cdns_platform_data { u32 quirks; }; -struct serial_rs485 cdns_rs485_supported = { +static struct serial_rs485 cdns_rs485_supported = { .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, .delay_rts_before_send = 1, @@ -284,7 +287,7 @@ static void cdns_uart_handle_rx(void *dev_id, unsigned int isrstatus) continue; } - if (uart_handle_sysrq_char(port, data)) + if (uart_prepare_sysrq_char(port, data)) continue; if (is_rxbs_support) { @@ -425,33 +428,33 @@ static void cdns_uart_handle_tx(void *dev_id) { struct uart_port *port = (struct uart_port *)dev_id; struct cdns_uart *cdns_uart = port->private_data; - struct circ_buf *xmit = &port->state->xmit; + struct tty_port *tport = &port->state->port; unsigned int numbytes; + unsigned char ch; - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { /* Disable the TX Empty interrupt */ writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IDR); return; } numbytes = port->fifosize; - while (numbytes && !uart_circ_empty(xmit) && - !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) { - - writel(xmit->buf[xmit->tail], port->membase + CDNS_UART_FIFO); - uart_xmit_advance(port, 1); + while (numbytes && + !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL) && + uart_fifo_get(port, &ch)) { + writel(ch, port->membase + CDNS_UART_FIFO); numbytes--; } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); /* Enable the TX Empty interrupt */ writel(CDNS_UART_IXR_TXEMPTY, cdns_uart->port->membase + CDNS_UART_IER); if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED && - (uart_circ_empty(xmit) || uart_tx_stopped(port))) { - cdns_uart->tx_timer.function = &cdns_rs485_rx_callback; + (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port))) { + hrtimer_update_function(&cdns_uart->tx_timer, cdns_rs485_rx_callback); hrtimer_start(&cdns_uart->tx_timer, ns_to_ktime(cdns_calc_after_tx_delay(cdns_uart)), HRTIMER_MODE_REL); } @@ -492,7 +495,7 @@ static irqreturn_t cdns_uart_isr(int irq, void *dev_id) !(readl(port->membase + CDNS_UART_CR) & CDNS_UART_CR_RX_DIS)) cdns_uart_handle_rx(dev_id, isrstatus); - uart_port_unlock(port); + uart_unlock_and_check_sysrq(port); return IRQ_HANDLED; } @@ -723,7 +726,7 @@ static void cdns_uart_start_tx(struct uart_port *port) status |= CDNS_UART_CR_TX_EN; writel(status, port->membase + CDNS_UART_CR); - if (uart_circ_empty(&port->state->xmit)) + if (kfifo_is_empty(&port->state->port.xmit_fifo)) return; /* Clear the TX Empty interrupt */ @@ -731,7 +734,7 @@ static void cdns_uart_start_tx(struct uart_port *port) if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED) { if (!cdns_uart->rs485_tx_started) { - cdns_uart->tx_timer.function = &cdns_rs485_tx_callback; + hrtimer_update_function(&cdns_uart->tx_timer, cdns_rs485_tx_callback); cdns_rs485_tx_setup(cdns_uart); return hrtimer_start(&cdns_uart->tx_timer, ms_to_ktime(port->rs485.delay_rts_before_send), @@ -948,6 +951,10 @@ static int cdns_uart_startup(struct uart_port *port) is_brk_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT; + ret = reset_control_deassert(cdns_uart->rstc); + if (ret) + return ret; + uart_port_lock_irqsave(port, &flags); /* Disable the TX and RX */ @@ -1373,9 +1380,7 @@ static void cdns_uart_console_write(struct console *co, const char *s, unsigned int imr, ctrl; int locked = 1; - if (port->sysrq) - locked = 0; - else if (oops_in_progress) + if (oops_in_progress) locked = uart_port_trylock_irqsave(port, &flags); else uart_port_lock_irqsave(port, &flags); @@ -1621,8 +1626,8 @@ static int cdns_rs485_config(struct uart_port *port, struct ktermios *termios, writel(val, port->membase + CDNS_UART_MODEMCR); /* Timer setup */ - hrtimer_init(&cdns_uart->tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - cdns_uart->tx_timer.function = &cdns_rs485_tx_callback; + hrtimer_setup(&cdns_uart->tx_timer, &cdns_rs485_tx_callback, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); /* Disable transmitter and make Rx setup*/ cdns_uart_stop_tx(port); @@ -1721,6 +1726,13 @@ static int cdns_uart_probe(struct platform_device *pdev) dev_err(&pdev->dev, "clock name 'ref_clk' is deprecated.\n"); } + cdns_uart_data->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); + if (IS_ERR(cdns_uart_data->rstc)) { + rc = PTR_ERR(cdns_uart_data->rstc); + dev_err_probe(&pdev->dev, rc, "Cannot get UART reset\n"); + goto err_out_unregister_driver; + } + rc = clk_prepare_enable(cdns_uart_data->pclk); if (rc) { dev_err(&pdev->dev, "Unable to enable pclk clock.\n"); @@ -1881,6 +1893,7 @@ static void cdns_uart_remove(struct platform_device *pdev) if (console_port == port) console_port = NULL; #endif + reset_control_assert(cdns_uart_data->rstc); if (!--instances) uart_unregister_driver(cdns_uart_data->cdns_uart_driver); @@ -1888,7 +1901,7 @@ static void cdns_uart_remove(struct platform_device *pdev) static struct platform_driver cdns_uart_platform_driver = { .probe = cdns_uart_probe, - .remove_new = cdns_uart_remove, + .remove = cdns_uart_remove, .driver = { .name = CDNS_UART_NAME, .of_match_table = cdns_uart_of_match, diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c index 65ca4da6e368..79ea7108a0f3 100644 --- a/drivers/tty/serial/zs.c +++ b/drivers/tty/serial/zs.c @@ -606,7 +606,8 @@ static void zs_receive_chars(struct zs_port *zport) static void zs_raw_transmit_chars(struct zs_port *zport) { - struct circ_buf *xmit = &zport->port.state->xmit; + struct tty_port *tport = &zport->port.state->port; + unsigned char ch; /* XON/XOFF chars. */ if (zport->port.x_char) { @@ -617,20 +618,20 @@ static void zs_raw_transmit_chars(struct zs_port *zport) } /* If nothing to do or stopped or hardware stopped. */ - if (uart_circ_empty(xmit) || uart_tx_stopped(&zport->port)) { + if (uart_tx_stopped(&zport->port) || + !uart_fifo_get(&zport->port, &ch)) { zs_raw_stop_tx(zport); return; } /* Send char. */ - write_zsdata(zport, xmit->buf[xmit->tail]); - uart_xmit_advance(&zport->port, 1); + write_zsdata(zport, ch); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(&zport->port); /* Are we are done? */ - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) zs_raw_stop_tx(zport); } diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index 8112d9d5a0d8..3865b10d2d43 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -89,6 +89,7 @@ */ static const char driver_name[] = "SyncLink GT"; static const char tty_dev_prefix[] = "ttySLG"; +MODULE_DESCRIPTION("Device driver for Microgate SyncLink GT serial adapters"); MODULE_LICENSE("GPL"); #define MAX_DEVICES 32 @@ -2219,7 +2220,7 @@ static void isr_txeom(struct slgt_info *info, unsigned short status) } info->tx_active = false; - del_timer(&info->tx_timer); + timer_delete(&info->tx_timer); if (info->params.mode != MGSL_MODE_ASYNC && info->drop_rts_on_tx_done) { info->signals &= ~SerialSignal_RTS; @@ -2374,8 +2375,8 @@ static void shutdown(struct slgt_info *info) wake_up_interruptible(&info->status_event_wait_q); wake_up_interruptible(&info->event_wait_q); - del_timer_sync(&info->tx_timer); - del_timer_sync(&info->rx_timer); + timer_delete_sync(&info->tx_timer); + timer_delete_sync(&info->rx_timer); kfree(info->tx_buf); info->tx_buf = NULL; @@ -3954,7 +3955,7 @@ static void tx_stop(struct slgt_info *info) { unsigned short val; - del_timer(&info->tx_timer); + timer_delete(&info->tx_timer); tdma_reset(info); @@ -5001,7 +5002,7 @@ static int adapter_test(struct slgt_info *info) */ static void tx_timeout(struct timer_list *t) { - struct slgt_info *info = from_timer(info, t, tx_timer); + struct slgt_info *info = timer_container_of(info, t, tx_timer); unsigned long flags; DBGINFO(("%s tx_timeout\n", info->device_name)); @@ -5025,7 +5026,7 @@ static void tx_timeout(struct timer_list *t) */ static void rx_timeout(struct timer_list *t) { - struct slgt_info *info = from_timer(info, t, rx_timer); + struct slgt_info *info = timer_container_of(info, t, rx_timer); unsigned long flags; DBGINFO(("%s rx_timeout\n", info->device_name)); diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 02217e3c916b..d77c03d22227 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -450,6 +450,17 @@ static const struct sysrq_key_op sysrq_unrt_op = { .enable_mask = SYSRQ_ENABLE_RTNICE, }; +static void sysrq_handle_replay_logs(u8 key) +{ + console_try_replay_all(); +} +static struct sysrq_key_op sysrq_replay_logs_op = { + .handler = sysrq_handle_replay_logs, + .help_msg = "replay-kernel-logs(R)", + .action_msg = "Replay kernel logs on consoles", + .enable_mask = SYSRQ_ENABLE_DUMP, +}; + /* Key Operations table and lock */ static DEFINE_SPINLOCK(sysrq_key_table_lock); @@ -519,7 +530,8 @@ static const struct sysrq_key_op *sysrq_key_table[62] = { NULL, /* O */ NULL, /* P */ NULL, /* Q */ - NULL, /* R */ + &sysrq_replay_logs_op, /* R */ + /* S: May be registered by sched_ext for resetting */ NULL, /* S */ NULL, /* T */ NULL, /* U */ @@ -571,7 +583,6 @@ static void __sysrq_put_key_op(u8 key, const struct sysrq_key_op *op_p) void __handle_sysrq(u8 key, bool check_mask) { const struct sysrq_key_op *op_p; - int orig_log_level; int orig_suppress_printk; int i; @@ -581,13 +592,12 @@ void __handle_sysrq(u8 key, bool check_mask) rcu_sysrq_start(); rcu_read_lock(); /* - * Raise the apparent loglevel to maximum so that the sysrq header - * is shown to provide the user with positive feedback. We do not - * simply emit this at KERN_EMERG as that would change message - * routing in the consumers of /proc/kmsg. + * Enter in the force_console context so that sysrq header is shown to + * provide the user with positive feedback. We do not simply emit this + * at KERN_EMERG as that would change message routing in the consumers + * of /proc/kmsg. */ - orig_log_level = console_loglevel; - console_loglevel = CONSOLE_LOGLEVEL_DEFAULT; + printk_force_console_enter(); op_p = __sysrq_get_key_op(key); if (op_p) { @@ -597,11 +607,11 @@ void __handle_sysrq(u8 key, bool check_mask) */ if (!check_mask || sysrq_on_mask(op_p->enable_mask)) { pr_info("%s\n", op_p->action_msg); - console_loglevel = orig_log_level; + printk_force_console_exit(); op_p->handler(key); } else { pr_info("This sysrq operation is disabled.\n"); - console_loglevel = orig_log_level; + printk_force_console_exit(); } } else { pr_info("HELP : "); @@ -619,7 +629,7 @@ void __handle_sysrq(u8 key, bool check_mask) } } pr_cont("\n"); - console_loglevel = orig_log_level; + printk_force_console_exit(); } rcu_read_unlock(); rcu_sysrq_end(); @@ -702,7 +712,8 @@ static void sysrq_parse_reset_sequence(struct sysrq_state *state) static void sysrq_do_reset(struct timer_list *t) { - struct sysrq_state *state = from_timer(state, t, keyreset_timer); + struct sysrq_state *state = timer_container_of(state, t, + keyreset_timer); state->reset_requested = true; @@ -733,7 +744,7 @@ static void sysrq_detect_reset_sequence(struct sysrq_state *state, */ if (value && state->reset_seq_cnt) { state->reset_canceled = true; - del_timer(&state->keyreset_timer); + timer_delete(&state->keyreset_timer); } } else if (value == 0) { /* @@ -741,7 +752,7 @@ static void sysrq_detect_reset_sequence(struct sysrq_state *state, * to be pressed and held for the reset timeout * to hold. */ - del_timer(&state->keyreset_timer); + timer_delete(&state->keyreset_timer); if (--state->reset_seq_cnt == 0) state->reset_canceled = false; @@ -759,8 +770,6 @@ static void sysrq_of_get_keyreset_config(void) { u32 key; struct device_node *np; - struct property *prop; - const __be32 *p; np = of_find_node_by_path("/chosen/linux,sysrq-reset-seq"); if (!np) { @@ -771,7 +780,7 @@ static void sysrq_of_get_keyreset_config(void) /* Reset in case a __weak definition was present */ sysrq_reset_seq_len = 0; - of_property_for_each_u32(np, "keyset", prop, p, key) { + of_property_for_each_u32(np, "keyset", key) { if (key == KEY_RESERVED || key > KEY_MAX || sysrq_reset_seq_len == SYSRQ_KEY_RESET_MAX) break; diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c index 1d81eeefb068..75542333c54a 100644 --- a/drivers/tty/tty_audit.c +++ b/drivers/tty/tty_audit.c @@ -12,12 +12,14 @@ #include <linux/tty.h> #include "tty.h" +#define TTY_AUDIT_BUF_SIZE 4096 + struct tty_audit_buf { struct mutex mutex; /* Protects all data below */ dev_t dev; /* The TTY which the data is from */ bool icanon; size_t valid; - u8 *data; /* Allocated size N_TTY_BUF_SIZE */ + u8 *data; /* Allocated size TTY_AUDIT_BUF_SIZE */ }; static struct tty_audit_buf *tty_audit_buf_ref(void) @@ -37,7 +39,7 @@ static struct tty_audit_buf *tty_audit_buf_alloc(void) if (!buf) goto err; - buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL); + buf->data = kmalloc(TTY_AUDIT_BUF_SIZE, GFP_KERNEL); if (!buf->data) goto err_buf; @@ -235,14 +237,14 @@ void tty_audit_add_data(const struct tty_struct *tty, const void *data, do { size_t run; - run = N_TTY_BUF_SIZE - buf->valid; + run = TTY_AUDIT_BUF_SIZE - buf->valid; if (run > size) run = size; memcpy(buf->data + buf->valid, data, run); buf->valid += run; data += run; size -= run; - if (buf->valid == N_TTY_BUF_SIZE) + if (buf->valid == TTY_AUDIT_BUF_SIZE) tty_audit_buf_push(buf); } while (size != 0); mutex_unlock(&buf->mutex); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 407b0d87b7c1..e2d92cf70eb7 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -276,11 +276,10 @@ static void check_tty_count(struct tty_struct *tty, const char *routine) struct list_head *p; int count = 0, kopen_count = 0; - spin_lock(&tty->files_lock); - list_for_each(p, &tty->tty_files) { - count++; - } - spin_unlock(&tty->files_lock); + scoped_guard(spinlock, &tty->files_lock) + list_for_each(p, &tty->tty_files) + count++; + if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_SLAVE && tty->link && tty->link->count) @@ -350,22 +349,19 @@ int tty_dev_name_to_number(const char *name, dev_t *number) return ret; prefix_length = str - name; - mutex_lock(&tty_mutex); + + guard(mutex)(&tty_mutex); list_for_each_entry(p, &tty_drivers, tty_drivers) if (prefix_length == strlen(p->name) && strncmp(name, p->name, prefix_length) == 0) { if (index < p->num) { *number = MKDEV(p->major, p->minor_start + index); - goto out; + return 0; } } - /* if here then driver wasn't found */ - ret = -ENODEV; -out: - mutex_unlock(&tty_mutex); - return ret; + return -ENODEV; } EXPORT_SYMBOL_GPL(tty_dev_name_to_number); @@ -381,7 +377,7 @@ EXPORT_SYMBOL_GPL(tty_dev_name_to_number); */ struct tty_driver *tty_find_polling_driver(char *name, int *line) { - struct tty_driver *p, *res = NULL; + struct tty_driver *p; int tty_line = 0; int len; char *str, *stp; @@ -395,7 +391,8 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line) len = str - name; tty_line = simple_strtoul(str, &str, 10); - mutex_lock(&tty_mutex); + guard(mutex)(&tty_mutex); + /* Search through the tty devices to look for a match */ list_for_each_entry(p, &tty_drivers, tty_drivers) { if (!len || strncmp(name, p->name, len) != 0) @@ -408,14 +405,12 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line) if (tty_line >= 0 && tty_line < p->num && p->ops && p->ops->poll_init && !p->ops->poll_init(p, tty_line, stp)) { - res = tty_driver_kref_get(p); *line = tty_line; - break; + return tty_driver_kref_get(p); } } - mutex_unlock(&tty_mutex); - return res; + return NULL; } EXPORT_SYMBOL_GPL(tty_find_polling_driver); #endif @@ -462,7 +457,6 @@ static void tty_show_fdinfo(struct seq_file *m, struct file *file) } static const struct file_operations tty_fops = { - .llseek = no_llseek, .read_iter = tty_read, .write_iter = tty_write, .splice_read = copy_splice_read, @@ -477,7 +471,6 @@ static const struct file_operations tty_fops = { }; static const struct file_operations console_fops = { - .llseek = no_llseek, .read_iter = tty_read, .write_iter = redirected_tty_write, .splice_read = copy_splice_read, @@ -491,7 +484,6 @@ static const struct file_operations console_fops = { }; static const struct file_operations hung_up_tty_fops = { - .llseek = no_llseek, .read_iter = hung_up_tty_read, .write_iter = hung_up_tty_write, .poll = hung_up_tty_poll, @@ -537,16 +529,15 @@ EXPORT_SYMBOL_GPL(tty_wakeup); */ static struct file *tty_release_redirect(struct tty_struct *tty) { - struct file *f = NULL; + guard(spinlock)(&redirect_lock); - spin_lock(&redirect_lock); if (redirect && file_tty(redirect) == tty) { - f = redirect; + struct file *f = redirect; redirect = NULL; + return f; } - spin_unlock(&redirect_lock); - return f; + return NULL; } /** @@ -771,11 +762,8 @@ void __stop_tty(struct tty_struct *tty) */ void stop_tty(struct tty_struct *tty) { - unsigned long flags; - - spin_lock_irqsave(&tty->flow.lock, flags); + guard(spinlock_irqsave)(&tty->flow.lock); __stop_tty(tty); - spin_unlock_irqrestore(&tty->flow.lock, flags); } EXPORT_SYMBOL(stop_tty); @@ -802,11 +790,8 @@ void __start_tty(struct tty_struct *tty) */ void start_tty(struct tty_struct *tty) { - unsigned long flags; - - spin_lock_irqsave(&tty->flow.lock, flags); + guard(spinlock_irqsave)(&tty->flow.lock); __start_tty(tty); - spin_unlock_irqrestore(&tty->flow.lock, flags); } EXPORT_SYMBOL(start_tty); @@ -815,7 +800,8 @@ static void tty_update_time(struct tty_struct *tty, bool mtime) time64_t sec = ktime_get_real_seconds(); struct tty_file_private *priv; - spin_lock(&tty->files_lock); + guard(spinlock)(&tty->files_lock); + list_for_each_entry(priv, &tty->tty_files, list) { struct inode *inode = file_inode(priv->file); struct timespec64 time = mtime ? inode_get_mtime(inode) : inode_get_atime(inode); @@ -833,7 +819,6 @@ static void tty_update_time(struct tty_struct *tty, bool mtime) inode_set_atime(inode, sec, 0); } } - spin_unlock(&tty->files_lock); } /* @@ -2225,6 +2210,12 @@ static int __tty_fasync(int fd, struct file *filp, int on) if (tty_paranoia_check(tty, file_inode(filp), "tty_fasync")) goto out; + if (on) { + retval = file_f_owner_allocate(filp); + if (retval) + goto out; + } + retval = fasync_helper(fd, filp, on, &tty->fasync); if (retval <= 0) goto out; @@ -2314,13 +2305,12 @@ static int tiocsti(struct tty_struct *tty, u8 __user *p) */ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg) { - int err; + guard(mutex)(&tty->winsize_mutex); - mutex_lock(&tty->winsize_mutex); - err = copy_to_user(arg, &tty->winsize, sizeof(*arg)); - mutex_unlock(&tty->winsize_mutex); + if (copy_to_user(arg, &tty->winsize, sizeof(*arg))) + return -EFAULT; - return err ? -EFAULT : 0; + return 0; } /** @@ -2335,10 +2325,10 @@ int tty_do_resize(struct tty_struct *tty, struct winsize *ws) { struct pid *pgrp; - /* Lock the tty */ - mutex_lock(&tty->winsize_mutex); + guard(mutex)(&tty->winsize_mutex); + if (!memcmp(ws, &tty->winsize, sizeof(*ws))) - goto done; + return 0; /* Signal the foreground process group */ pgrp = tty_get_pgrp(tty); @@ -2347,8 +2337,7 @@ int tty_do_resize(struct tty_struct *tty, struct winsize *ws) put_pid(pgrp); tty->winsize = *ws; -done: - mutex_unlock(&tty->winsize_mutex); + return 0; } EXPORT_SYMBOL(tty_do_resize); @@ -2409,13 +2398,14 @@ static int tioccons(struct file *file) return -EBADF; if (!(file->f_mode & FMODE_CAN_WRITE)) return -EINVAL; - spin_lock(&redirect_lock); - if (redirect) { - spin_unlock(&redirect_lock); + + guard(spinlock)(&redirect_lock); + + if (redirect) return -EBUSY; - } + redirect = get_file(file); - spin_unlock(&redirect_lock); + return 0; } @@ -2622,14 +2612,13 @@ static int tty_tiocgicount(struct tty_struct *tty, void __user *arg) static int tty_set_serial(struct tty_struct *tty, struct serial_struct *ss) { - char comm[TASK_COMM_LEN]; int flags; flags = ss->flags & ASYNC_DEPRECATED; if (flags) pr_warn_ratelimited("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n", - __func__, get_task_comm(comm, current), flags); + __func__, current->comm, flags); if (!tty->ops->set_serial) return -ENOTTY; @@ -3029,11 +3018,9 @@ void __do_SAK(struct tty_struct *tty) struct task_struct *g, *p; struct pid *session; int i; - unsigned long flags; - spin_lock_irqsave(&tty->ctrl.lock, flags); - session = get_pid(tty->ctrl.session); - spin_unlock_irqrestore(&tty->ctrl.lock, flags); + scoped_guard(spinlock_irqsave, &tty->ctrl.lock) + session = get_pid(tty->ctrl.session); tty_ldisc_flush(tty); @@ -3056,7 +3043,7 @@ void __do_SAK(struct tty_struct *tty) PIDTYPE_SID); continue; } - task_lock(p); + guard(task_lock)(p); i = iterate_fd(p->files, 0, this_tty, tty); if (i != 0) { tty_notice(tty, "SAK: killed process %d (%s): by fd#%d\n", @@ -3064,7 +3051,6 @@ void __do_SAK(struct tty_struct *tty) group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p, PIDTYPE_SID); } - task_unlock(p); } read_unlock(&tasklist_lock); put_pid(session); @@ -3330,10 +3316,12 @@ EXPORT_SYMBOL(tty_unregister_device); * __tty_alloc_driver - allocate tty driver * @lines: count of lines this driver can handle at most * @owner: module which is responsible for this driver - * @flags: some of %TTY_DRIVER_ flags, will be set in driver->flags + * @flags: some of enum tty_driver_flag, will be set in driver->flags + * + * This should not be called directly, tty_alloc_driver() should be used + * instead. * - * This should not be called directly, some of the provided macros should be - * used instead. Use IS_ERR() and friends on @retval. + * Returns: struct tty_driver or a PTR-encoded error (use IS_ERR() and friends). */ struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner, unsigned long flags) @@ -3464,9 +3452,8 @@ int tty_register_driver(struct tty_driver *driver) goto err_unreg_char; } - mutex_lock(&tty_mutex); - list_add(&driver->tty_drivers, &tty_drivers); - mutex_unlock(&tty_mutex); + scoped_guard(mutex, &tty_mutex) + list_add(&driver->tty_drivers, &tty_drivers); if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) { for (i = 0; i < driver->num; i++) { @@ -3485,9 +3472,8 @@ err_unreg_devs: for (i--; i >= 0; i--) tty_unregister_device(driver, i); - mutex_lock(&tty_mutex); - list_del(&driver->tty_drivers); - mutex_unlock(&tty_mutex); + scoped_guard(mutex, &tty_mutex) + list_del(&driver->tty_drivers); err_unreg_char: unregister_chrdev_region(dev, driver->num); @@ -3506,9 +3492,8 @@ void tty_unregister_driver(struct tty_driver *driver) { unregister_chrdev_region(MKDEV(driver->major, driver->minor_start), driver->num); - mutex_lock(&tty_mutex); - list_del(&driver->tty_drivers); - mutex_unlock(&tty_mutex); + scoped_guard(mutex, &tty_mutex) + list_del(&driver->tty_drivers); } EXPORT_SYMBOL(tty_unregister_driver); @@ -3567,7 +3552,7 @@ static ssize_t show_cons_active(struct device *dev, for_each_console(c) { if (!c->device) continue; - if (!c->write) + if (!(c->flags & CON_NBCON) && !c->write) continue; if ((c->flags & CON_ENABLED) == 0) continue; @@ -3618,7 +3603,7 @@ void console_sysfs_notify(void) sysfs_notify(&consdev->kobj, NULL, "active"); } -static struct ctl_table tty_table[] = { +static const struct ctl_table tty_table[] = { { .procname = "legacy_tiocsti", .data = &tty_legacy_tiocsti, @@ -3631,7 +3616,7 @@ static struct ctl_table tty_table[] = { .data = &tty_ldisc_autoload, .maxlen = sizeof(tty_ldisc_autoload), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, .extra1 = SYSCTL_ZERO, .extra2 = SYSCTL_ONE, }, diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 85de90eebc7b..90c70d8d14e3 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -122,21 +122,19 @@ EXPORT_SYMBOL(tty_unthrottle); */ bool tty_throttle_safe(struct tty_struct *tty) { - bool ret = true; - - mutex_lock(&tty->throttle_mutex); - if (!tty_throttled(tty)) { - if (tty->flow_change != TTY_THROTTLE_SAFE) - ret = false; - else { - set_bit(TTY_THROTTLED, &tty->flags); - if (tty->ops->throttle) - tty->ops->throttle(tty); - } - } - mutex_unlock(&tty->throttle_mutex); + guard(mutex)(&tty->throttle_mutex); - return ret; + if (tty_throttled(tty)) + return true; + + if (tty->flow_change != TTY_THROTTLE_SAFE) + return false; + + set_bit(TTY_THROTTLED, &tty->flags); + if (tty->ops->throttle) + tty->ops->throttle(tty); + + return true; } /** @@ -152,21 +150,19 @@ bool tty_throttle_safe(struct tty_struct *tty) */ bool tty_unthrottle_safe(struct tty_struct *tty) { - bool ret = true; + guard(mutex)(&tty->throttle_mutex); - mutex_lock(&tty->throttle_mutex); - if (tty_throttled(tty)) { - if (tty->flow_change != TTY_UNTHROTTLE_SAFE) - ret = false; - else { - clear_bit(TTY_THROTTLED, &tty->flags); - if (tty->ops->unthrottle) - tty->ops->unthrottle(tty); - } - } - mutex_unlock(&tty->throttle_mutex); + if (!tty_throttled(tty)) + return true; - return ret; + if (tty->flow_change != TTY_UNTHROTTLE_SAFE) + return false; + + clear_bit(TTY_THROTTLED, &tty->flags); + if (tty->ops->unthrottle) + tty->ops->unthrottle(tty); + + return true; } /** diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 3f68e213df1f..d80e9d4c974b 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -545,6 +545,12 @@ int tty_set_ldisc(struct tty_struct *tty, int disc) goto out; } + if (tty->ops->ldisc_ok) { + retval = tty->ops->ldisc_ok(tty, disc); + if (retval) + goto out; + } + old_ldisc = tty->ldisc; /* Shutdown the old discipline. */ diff --git a/drivers/tty/tty_ldsem.c b/drivers/tty/tty_ldsem.c index 3be428c16260..4e18031a5ca3 100644 --- a/drivers/tty/tty_ldsem.c +++ b/drivers/tty/tty_ldsem.c @@ -367,23 +367,6 @@ int __sched ldsem_down_write(struct ld_semaphore *sem, long timeout) } /* - * trylock for writing -- returns 1 if successful, 0 if contention - */ -int ldsem_down_write_trylock(struct ld_semaphore *sem) -{ - long count = atomic_long_read(&sem->count); - - while ((count & LDSEM_ACTIVE_MASK) == 0) { - if (atomic_long_try_cmpxchg(&sem->count, &count, count + LDSEM_WRITE_BIAS)) { - rwsem_acquire(&sem->dep_map, 0, 1, _RET_IP_); - lock_acquired(&sem->dep_map, _RET_IP_); - return 1; - } - } - return 0; -} - -/* * release a read lock */ void ldsem_up_read(struct ld_semaphore *sem) diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 14cca33d2269..4af1fbf73f51 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -200,26 +200,6 @@ struct device *tty_port_register_device_attr_serdev(struct tty_port *port, EXPORT_SYMBOL_GPL(tty_port_register_device_attr_serdev); /** - * tty_port_register_device_serdev - register tty or serdev device - * @port: tty_port of the device - * @driver: tty_driver for this device - * @index: index of the tty - * @host: serial port hardware controller device - * @parent: parent if exists, otherwise NULL - * - * Register a serdev or tty device depending on if the parent device has any - * defined serdev clients or not. - */ -struct device *tty_port_register_device_serdev(struct tty_port *port, - struct tty_driver *driver, unsigned index, - struct device *host, struct device *parent) -{ - return tty_port_register_device_attr_serdev(port, driver, index, - host, parent, NULL, NULL); -} -EXPORT_SYMBOL_GPL(tty_port_register_device_serdev); - -/** * tty_port_unregister_device - deregister a tty or serdev device * @port: tty_port of the device * @driver: tty_driver for this device diff --git a/drivers/tty/ttynull.c b/drivers/tty/ttynull.c index e4c4273993bc..6b2f7208b564 100644 --- a/drivers/tty/ttynull.c +++ b/drivers/tty/ttynull.c @@ -106,4 +106,5 @@ static void __exit ttynull_exit(void) module_init(ttynull_init); module_exit(ttynull_exit); +MODULE_DESCRIPTION("NULL TTY driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index 5b625f20233b..2960031ace72 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -356,7 +356,7 @@ done: static void vcc_rx_timer(struct timer_list *t) { - struct vcc_port *port = from_timer(port, t, rx_timer); + struct vcc_port *port = timer_container_of(port, t, rx_timer); struct vio_driver_state *vio; unsigned long flags; int rv; @@ -382,7 +382,7 @@ done: static void vcc_tx_timer(struct timer_list *t) { - struct vcc_port *port = from_timer(port, t, tx_timer); + struct vcc_port *port = timer_container_of(port, t, tx_timer); struct vio_vcc *pkt; unsigned long flags; size_t tosend = 0; @@ -683,8 +683,8 @@ static void vcc_remove(struct vio_dev *vdev) { struct vcc_port *port = dev_get_drvdata(&vdev->dev); - del_timer_sync(&port->rx_timer); - del_timer_sync(&port->tx_timer); + timer_delete_sync(&port->rx_timer); + timer_delete_sync(&port->tx_timer); /* If there's a process with the device open, do a synchronous * hangup of the TTY. This *may* cause the process to call close @@ -700,7 +700,7 @@ static void vcc_remove(struct vio_dev *vdev) tty_unregister_device(vcc_tty_driver, port->index); - del_timer_sync(&port->vio.timer); + timer_delete_sync(&port->vio.timer); vio_ldc_free(&port->vio); sysfs_remove_group(&vdev->dev.kobj, &vcc_attribute_group); dev_set_drvdata(&vdev->dev, NULL); diff --git a/drivers/tty/vt/.gitignore b/drivers/tty/vt/.gitignore index 0221709b177d..a74859bab862 100644 --- a/drivers/tty/vt/.gitignore +++ b/drivers/tty/vt/.gitignore @@ -2,3 +2,6 @@ /conmakehash /consolemap_deftbl.c /defkeymap.c +/ucs_fallback_table.h +/ucs_recompose_table.h +/ucs_width_table.h diff --git a/drivers/tty/vt/Makefile b/drivers/tty/vt/Makefile index 2c8ce8b592ed..ae746dcdeec8 100644 --- a/drivers/tty/vt/Makefile +++ b/drivers/tty/vt/Makefile @@ -7,10 +7,12 @@ FONTMAPFILE = cp437.uni obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o \ selection.o keyboard.o \ vt.o defkeymap.o -obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o +obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o \ + ucs.o # Files generated that shall be removed upon make clean -clean-files := consolemap_deftbl.c defkeymap.c +clean-files := consolemap_deftbl.c defkeymap.c \ + ucs_width_table.h ucs_recompose_table.h ucs_fallback_table.h hostprogs += conmakehash @@ -33,3 +35,31 @@ $(obj)/defkeymap.c: $(obj)/%.c: $(src)/%.map loadkeys --mktable --unicode $< > $@ endif + +$(obj)/ucs.o: $(src)/ucs.c $(obj)/ucs_width_table.h \ + $(obj)/ucs_recompose_table.h $(obj)/ucs_fallback_table.h + +# You may uncomment one of those to have the UCS tables be regenerated +# during the build process. By default the _shipped versions are used. +# +#GENERATE_UCS_TABLES := 1 +#GENERATE_UCS_TABLES := 2 # invokes gen_ucs_recompose_table.py with --full + +ifdef GENERATE_UCS_TABLES + +$(obj)/ucs_width_table.h: $(src)/gen_ucs_width_table.py + $(PYTHON3) $< -o $@ + +ifeq ($(GENERATE_UCS_TABLES),2) +gen_recomp_arg := --full +else +gen_recomp_arg := +endif + +$(obj)/ucs_recompose_table.h: $(src)/gen_ucs_recompose_table.py + $(PYTHON3) $< -o $@ $(gen_recomp_arg) + +$(obj)/ucs_fallback_table.h: $(src)/gen_ucs_fallback_table.py + $(PYTHON3) $< -o $@ + +endif diff --git a/drivers/tty/vt/conmakehash.c b/drivers/tty/vt/conmakehash.c index cddd789fe46e..a931fcde7ad9 100644 --- a/drivers/tty/vt/conmakehash.c +++ b/drivers/tty/vt/conmakehash.c @@ -76,7 +76,7 @@ static void addpair(int fp, int un) int main(int argc, char *argv[]) { FILE *ctbl; - char *tblname; + const char *tblname; char buffer[65536]; int fontlen; int i, nuni, nent; @@ -244,16 +244,13 @@ int main(int argc, char *argv[]) printf("\ /*\n\ - * Do not edit this file; it was automatically generated by\n\ - *\n\ - * conmakehash %s > [this file]\n\ - *\n\ + * Automatically generated file; Do not edit.\n\ */\n\ \n\ #include <linux/types.h>\n\ \n\ u8 dfont_unicount[%d] = \n\ -{\n\t", argv[1], fontlen); +{\n\t", fontlen); for ( i = 0 ; i < fontlen ; i++ ) { diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 82d70083fead..bb4bb272ebec 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -870,8 +870,6 @@ int conv_uni_to_pc(struct vc_data *conp, long ucs) return -4; /* Not found */ else if (ucs < 0x20) return -1; /* Not a printable character */ - else if (ucs == 0xfeff || (ucs >= 0x200b && ucs <= 0x200f)) - return -2; /* Zero-width space */ /* * UNI_DIRECT_BASE indicates the start of the region in the User Zone * which always has a 1:1 mapping to the currently loaded font. The diff --git a/drivers/tty/vt/gen_ucs_fallback_table.py b/drivers/tty/vt/gen_ucs_fallback_table.py new file mode 100755 index 000000000000..6e09c1cb6d4b --- /dev/null +++ b/drivers/tty/vt/gen_ucs_fallback_table.py @@ -0,0 +1,360 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# +# Leverage Python's unidecode module to generate ucs_fallback_table.h +# +# The generated table maps complex characters to their simpler fallback forms +# for a terminal display when corresponding glyphs are unavailable. +# +# Usage: +# python3 gen_ucs_fallback_table.py # Generate fallback tables +# python3 gen_ucs_fallback_table.py -o FILE # Specify output file + +import unicodedata +from unidecode import unidecode +import sys +import argparse +from collections import defaultdict + +# Try to get unidecode version +try: + from importlib.metadata import version + unidecode_version = version('unidecode') +except: + unidecode_version = 'unknown' + +# This script's file name +from pathlib import Path +this_file = Path(__file__).name + +# Default output file name +DEFAULT_OUT_FILE = "ucs_fallback_table.h" + +# Define the range marker value +RANGE_MARKER = 0x00 + +def generate_fallback_map(): + """Generate a fallback map using unidecode for all relevant Unicode points.""" + fallback_map = {} + + # Process BMP characters (0x0000 - 0xFFFF) to keep table size manageable + for cp in range(0x0080, 0x10000): # Skip ASCII range (0x00-0x7F) + char = chr(cp) + + # Skip unassigned/control characters + try: + if not unicodedata.name(char, ''): + continue + except ValueError: + continue + + # Get the unidecode transliteration + ascii_version = unidecode(char) + + # Only store if it results in a single character mapping + if len(ascii_version) == 1: + fallback_map[cp] = ord(ascii_version) + + # Apply manual overrides for special cases + fallback_map.update(get_special_overrides()) + + return fallback_map + +def get_special_overrides(): + """Get special case overrides that need different handling than unidecode + provides... or doesn't provide at all.""" + + overrides = {} + + # Multi-character unidecode output + # These map to single chars instead of unidecode's multiple-char mappings + # In a terminal fallback context, we need a single character rather than multiple + overrides[0x00C6] = ord('E') # Æ LATIN CAPITAL LETTER AE -> E (unidecode: "AE") + overrides[0x00E6] = ord('e') # æ LATIN SMALL LETTER AE -> e (unidecode: "ae") + overrides[0x0152] = ord('E') # Œ LATIN CAPITAL LIGATURE OE -> E (unidecode: "OE") + overrides[0x0153] = ord('e') # œ LATIN SMALL LETTER LIGATURE OE -> e (unidecode: "oe") + overrides[0x00DF] = ord('s') # ß LATIN SMALL LETTER SHARP S -> s (unidecode: "ss") + + # Comparison operators that unidecode renders as multiple characters + overrides[0x2264] = ord('<') # ≤ LESS-THAN OR EQUAL TO -> < (unidecode: "<=") + overrides[0x2265] = ord('>') # ≥ GREATER-THAN OR EQUAL TO -> > (unidecode: ">=") + + # Unidecode returns an empty string for these + overrides[0x2260] = ord('#') # ≠ NOT EQUAL TO -> # (unidecode: empty string) + + # Quadrant block characters that unidecode doesn't map + for cp in range(0x2596, 0x259F+1): + overrides[cp] = ord('#') # ▖ ▗ ▘ ▙ etc. - map to # (unidecode: empty string) + + # Directional arrows + # These provide better semantic meaning than unidecode's mappings + overrides[0x2192] = ord('>') # → RIGHTWARDS ARROW -> > (unidecode: "-") + overrides[0x2190] = ord('<') # ← LEFTWARDS ARROW -> < (unidecode: "-") + overrides[0x2191] = ord('^') # ↑ UPWARDS ARROW -> ^ (unidecode: "|") + overrides[0x2193] = ord('v') # ↓ DOWNWARDS ARROW -> v (unidecode: "|") + + # Double arrows with their directional semantic mappings + overrides[0x21D0] = ord('<') # ⇐ LEFTWARDS DOUBLE ARROW -> < + overrides[0x21D1] = ord('^') # ⇑ UPWARDS DOUBLE ARROW -> ^ + overrides[0x21D2] = ord('>') # ⇒ RIGHTWARDS DOUBLE ARROW -> > + overrides[0x21D3] = ord('v') # ⇓ DOWNWARDS DOUBLE ARROW -> v + + # Halfwidth arrows + # These need the same treatment as their normal-width counterparts + overrides[0xFFE9] = ord('<') # ← HALFWIDTH LEFTWARDS ARROW -> < (unidecode: "-") + overrides[0xFFEA] = ord('^') # ↑ HALFWIDTH UPWARDS ARROW -> ^ (unidecode: "|") + overrides[0xFFEB] = ord('>') # → HALFWIDTH RIGHTWARDS ARROW -> > (unidecode: "-") + overrides[0xFFEC] = ord('v') # ↓ HALFWIDTH DOWNWARDS ARROW -> v (unidecode: "|") + + # Currency symbols - each mapped to a representative letter + overrides[0x00A2] = ord('c') # ¢ CENT SIGN -> c + overrides[0x00A3] = ord('L') # £ POUND SIGN -> L + overrides[0x00A5] = ord('Y') # ¥ YEN SIGN -> Y + overrides[0x20AC] = ord('E') # € EURO SIGN -> E + + # Symbols mapped to letters + overrides[0x00A7] = ord('S') # § SECTION SIGN -> S + overrides[0x00A9] = ord('C') # © COPYRIGHT SIGN -> C + overrides[0x00AE] = ord('R') # ® REGISTERED SIGN -> R + overrides[0x2122] = ord('T') # ™ TRADE MARK SIGN -> T + + # Degree-related symbols + overrides[0x00B0] = ord('o') # ° DEGREE SIGN -> o + overrides[0x2103] = ord('C') # ℃ DEGREE CELSIUS -> C + overrides[0x2109] = ord('F') # ℉ DEGREE FAHRENHEIT -> F + + # Angle quotation marks + overrides[0x00AB] = ord('<') # « LEFT-POINTING DOUBLE ANGLE QUOTATION MARK -> < + overrides[0x00BB] = ord('>') # » RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK -> > + + # Operators with circular shape + overrides[0x2218] = ord('o') # ∘ RING OPERATOR -> o + overrides[0x2219] = ord('.') # ∙ BULLET OPERATOR -> . + + # Negated mathematical symbols (preserving the negation semantics) + # Negated symbols mapped to exclamation mark (semantically "not") + for cp in (0x2204, 0x2209, 0x220C, 0x2224, 0x2226, 0x226E, 0x226F, 0x2280, 0x2281, 0x2284, 0x2285): + overrides[cp] = ord('!') # Negated math symbols -> ! (not) + + # Negated symbols mapped to hash sign (semantically "not equal") + for cp in (0x2241, 0x2244, 0x2249, 0x2262, 0x2268, 0x2269, 0x226D, 0x228A, 0x228B): + overrides[cp] = ord('#') # Negated equality symbols -> # (not equal) + + # Negated arrows - all mapped to exclamation mark + for cp in (0x219A, 0x219B, 0x21AE, 0x21CD, 0x21CE, 0x21CF): + overrides[cp] = ord('!') # Negated arrows -> ! (not) + + # Dashes and hyphens + for cp in (0x2010, 0x2011, 0x2012, 0x2013, 0x2014, 0x2015, 0x2043, 0x2052): + overrides[cp] = ord('-') # Dashes and hyphens -> - + + # Question mark punctuation + for cp in (0x203D, 0x2047, 0x2048): + overrides[cp] = ord('?') # Question marks -> ? + + # Exclamation mark punctuation + for cp in (0x203C, 0x2049): + overrides[cp] = ord('!') # Exclamation marks -> ! + + # Asterisk-like symbols + for cp in (0x2042, 0x2051, 0x2055): + overrides[cp] = ord('*') + + # Other specific punctuation with unique mappings + overrides[0x201E] = ord('"') # „ DOUBLE LOW-9 QUOTATION MARK + overrides[0x2023] = ord('>') # ‣ TRIANGULAR BULLET + overrides[0x2026] = ord('.') # … HORIZONTAL ELLIPSIS + overrides[0x2033] = ord('"') # ″ DOUBLE PRIME + overrides[0x204B] = ord('P') # ⁋ REVERSED PILCROW SIGN + overrides[0x204C] = ord('<') # ⁌ BLACK LEFTWARDS BULLET + overrides[0x204D] = ord('>') # ⁍ BLACK RIGHTWARDS BULLET + overrides[0x204F] = ord(';') # ⁏ REVERSED SEMICOLON + overrides[0x205B] = ord(':') # ⁛ FOUR DOT MARK + + # Check marks + overrides[0x2713] = ord('v') # ✓ CHECK MARK + overrides[0x2714] = ord('V') # ✔ HEAVY CHECK MARK + + # X marks - lowercase for regular, uppercase for heavy + for cp in (0x2715, 0x2717): + overrides[cp] = ord('x') # Regular X marks -> x + for cp in (0x2716, 0x2718): + overrides[cp] = ord('X') # Heavy X marks -> X + + # Stars and asterisk-like symbols mapped to '*' + for cp in (0x2605, 0x2606, 0x262A, 0x269D, 0x2698): + overrides[cp] = ord('*') # All star and asterisk symbols -> * + for cp in range(0x2721, 0x2746+1): + overrides[cp] = ord('*') # All star and asterisk symbols -> * + for cp in range(0x2749, 0x274B+1): + overrides[cp] = ord('*') # Last set of asterisk symbols -> * + for cp in (0x229B, 0x22C6, 0x235F, 0x2363): + overrides[cp] = ord('*') # Star operators -> * + + # Special exclusions with fallback value of 0 + # These will be filtered out in organize_by_pages() + + # Exclude U+2028 (LINE SEPARATOR) + overrides[0x2028] = 0 # LINE SEPARATOR (unidecode: '\n') + + # Full-width to ASCII mapping (covering all printable ASCII 33-126) + # 0xFF01 (!) to 0xFF5E (~) -> ASCII 33 (!) to 126 (~) + # Those are excluded here to reduce the table size. + # It is more efficient to process them programmatically in + # ucs.c:ucs_get_fallback(). + for cp in range(0xFF01, 0xFF5E + 1): + overrides[cp] = 0 # Double-width ASCII characters + + return overrides + +def organize_by_pages(fallback_map): + """Organize the fallback mappings by their high byte (page).""" + # Group by high byte (page) + page_groups = defaultdict(list) + for code, fallback in fallback_map.items(): + # Skip characters with fallback value of 0 (excluded characters) + if fallback == 0: + continue + + page = code >> 8 # Get the high byte (page) + offset = code & 0xFF # Get the low byte (offset within page) + page_groups[page].append((offset, fallback)) + + # Sort each page's entries by offset + for page in page_groups: + page_groups[page].sort() + + return page_groups + +def compress_ranges(page_groups): + """Compress consecutive entries with the same fallback character into ranges. + A range is only compressed if it contains 3 or more consecutive entries.""" + + compressed_pages = {} + + for page, entries in page_groups.items(): + compressed_entries = [] + i = 0 + while i < len(entries): + start_offset, fallback = entries[i] + + # Look ahead to find consecutive entries with the same fallback + j = i + 1 + while (j < len(entries) and + entries[j][0] == entries[j-1][0] + 1 and # consecutive offsets + entries[j][1] == fallback): # same fallback + j += 1 + + # Calculate the range end + end_offset = entries[j-1][0] + + # If we found a range with 3 or more entries (worth compressing) + if j - i >= 3: + # Add a range entry + compressed_entries.append((start_offset, RANGE_MARKER)) + compressed_entries.append((end_offset, fallback)) + else: + # Add the individual entries as is + for k in range(i, j): + compressed_entries.append(entries[k]) + + i = j + + compressed_pages[page] = compressed_entries + + return compressed_pages + +def cp_name(cp): + """Get the Unicode character name for a code point.""" + try: + return unicodedata.name(chr(cp)) + except: + return f"U+{cp:04X}" + +def generate_fallback_tables(out_file=DEFAULT_OUT_FILE): + """Generate the fallback character tables.""" + # Generate fallback map using unidecode + fallback_map = generate_fallback_map() + print(f"Generated {len(fallback_map)} total fallback mappings") + + # Organize by pages + page_groups = organize_by_pages(fallback_map) + print(f"Organized into {len(page_groups)} pages") + + # Compress ranges + compressed_pages = compress_ranges(page_groups) + total_compressed_entries = sum(len(entries) for entries in compressed_pages.values()) + print(f"Total compressed entries: {total_compressed_entries}") + + # Create output file + with open(out_file, 'w') as f: + f.write(f"""\ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * {out_file} - Unicode character fallback table + * + * Auto-generated by {this_file} + * + * Unicode Version: {unicodedata.unidata_version} + * Unidecode Version: {unidecode_version} + * + * This file contains optimized tables that map complex Unicode characters + * to simpler fallback characters for terminal display when corresponding + * glyphs are unavailable. + */ + +static const struct ucs_page_desc ucs_fallback_pages[] = {{ +""") + + # Convert compressed_pages to a sorted list of (page, entries) tuples + sorted_pages = sorted(compressed_pages.items()) + + # Track the start index for each page + start_index = 0 + + # Write page descriptors + for page, entries in sorted_pages: + count = len(entries) + f.write(f"\t{{ 0x{page:02X}, {count}, {start_index} }},\n") + start_index += count + + # Write entries array + f.write("""\ +}; + +/* Page entries array (referenced by page descriptors) */ +static const struct ucs_page_entry ucs_fallback_entries[] = { +""") + + # Write all entries + for page, entries in sorted_pages: + page_hex = f"0x{page:02X}" + f.write(f"\t/* Entries for page {page_hex} */\n") + + for i, (offset, fallback) in enumerate(entries): + # Convert to hex for better readability + offset_hex = f"0x{offset:02X}" + fallback_hex = f"0x{fallback:02X}" + + # Handle comments + codepoint = (page << 8) | offset + + if fallback == RANGE_MARKER: + comment = f"{cp_name(codepoint)} -> ..." + else: + comment = f"{cp_name(codepoint)} -> '{chr(fallback)}'" + f.write(f"\t{{ 0x{offset:02X}, 0x{fallback:02X} }}, /* {comment} */\n") + + f.write(f"""\ +}}; + +#define UCS_PAGE_ENTRY_RANGE_MARKER {RANGE_MARKER} +""") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Generate Unicode fallback character tables") + parser.add_argument("-o", "--output", dest="output_file", default=DEFAULT_OUT_FILE, + help=f"Output file name (default: {DEFAULT_OUT_FILE})") + args = parser.parse_args() + + generate_fallback_tables(out_file=args.output_file) diff --git a/drivers/tty/vt/gen_ucs_recompose_table.py b/drivers/tty/vt/gen_ucs_recompose_table.py new file mode 100755 index 000000000000..4434a436ac9e --- /dev/null +++ b/drivers/tty/vt/gen_ucs_recompose_table.py @@ -0,0 +1,257 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# +# Leverage Python's unicodedata module to generate ucs_recompose_table.h +# +# The generated table maps base character + combining mark pairs to their +# precomposed equivalents. +# +# Usage: +# python3 gen_ucs_recompose_table.py # Generate with common recomposition pairs +# python3 gen_ucs_recompose_table.py --full # Generate with all recomposition pairs + +import unicodedata +import sys +import argparse +import textwrap + +# This script's file name +from pathlib import Path +this_file = Path(__file__).name + +# Default output file name +DEFAULT_OUT_FILE = "ucs_recompose_table.h" + +common_recompose_description = "most commonly used Latin, Greek, and Cyrillic recomposition pairs only" +COMMON_RECOMPOSITION_PAIRS = [ + # Latin letters with accents - uppercase + (0x0041, 0x0300, 0x00C0), # A + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER A WITH GRAVE + (0x0041, 0x0301, 0x00C1), # A + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER A WITH ACUTE + (0x0041, 0x0302, 0x00C2), # A + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER A WITH CIRCUMFLEX + (0x0041, 0x0303, 0x00C3), # A + COMBINING TILDE = LATIN CAPITAL LETTER A WITH TILDE + (0x0041, 0x0308, 0x00C4), # A + COMBINING DIAERESIS = LATIN CAPITAL LETTER A WITH DIAERESIS + (0x0041, 0x030A, 0x00C5), # A + COMBINING RING ABOVE = LATIN CAPITAL LETTER A WITH RING ABOVE + (0x0043, 0x0327, 0x00C7), # C + COMBINING CEDILLA = LATIN CAPITAL LETTER C WITH CEDILLA + (0x0045, 0x0300, 0x00C8), # E + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER E WITH GRAVE + (0x0045, 0x0301, 0x00C9), # E + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER E WITH ACUTE + (0x0045, 0x0302, 0x00CA), # E + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER E WITH CIRCUMFLEX + (0x0045, 0x0308, 0x00CB), # E + COMBINING DIAERESIS = LATIN CAPITAL LETTER E WITH DIAERESIS + (0x0049, 0x0300, 0x00CC), # I + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER I WITH GRAVE + (0x0049, 0x0301, 0x00CD), # I + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER I WITH ACUTE + (0x0049, 0x0302, 0x00CE), # I + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER I WITH CIRCUMFLEX + (0x0049, 0x0308, 0x00CF), # I + COMBINING DIAERESIS = LATIN CAPITAL LETTER I WITH DIAERESIS + (0x004E, 0x0303, 0x00D1), # N + COMBINING TILDE = LATIN CAPITAL LETTER N WITH TILDE + (0x004F, 0x0300, 0x00D2), # O + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER O WITH GRAVE + (0x004F, 0x0301, 0x00D3), # O + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER O WITH ACUTE + (0x004F, 0x0302, 0x00D4), # O + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER O WITH CIRCUMFLEX + (0x004F, 0x0303, 0x00D5), # O + COMBINING TILDE = LATIN CAPITAL LETTER O WITH TILDE + (0x004F, 0x0308, 0x00D6), # O + COMBINING DIAERESIS = LATIN CAPITAL LETTER O WITH DIAERESIS + (0x0055, 0x0300, 0x00D9), # U + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER U WITH GRAVE + (0x0055, 0x0301, 0x00DA), # U + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER U WITH ACUTE + (0x0055, 0x0302, 0x00DB), # U + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER U WITH CIRCUMFLEX + (0x0055, 0x0308, 0x00DC), # U + COMBINING DIAERESIS = LATIN CAPITAL LETTER U WITH DIAERESIS + (0x0059, 0x0301, 0x00DD), # Y + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER Y WITH ACUTE + + # Latin letters with accents - lowercase + (0x0061, 0x0300, 0x00E0), # a + COMBINING GRAVE ACCENT = LATIN SMALL LETTER A WITH GRAVE + (0x0061, 0x0301, 0x00E1), # a + COMBINING ACUTE ACCENT = LATIN SMALL LETTER A WITH ACUTE + (0x0061, 0x0302, 0x00E2), # a + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER A WITH CIRCUMFLEX + (0x0061, 0x0303, 0x00E3), # a + COMBINING TILDE = LATIN SMALL LETTER A WITH TILDE + (0x0061, 0x0308, 0x00E4), # a + COMBINING DIAERESIS = LATIN SMALL LETTER A WITH DIAERESIS + (0x0061, 0x030A, 0x00E5), # a + COMBINING RING ABOVE = LATIN SMALL LETTER A WITH RING ABOVE + (0x0063, 0x0327, 0x00E7), # c + COMBINING CEDILLA = LATIN SMALL LETTER C WITH CEDILLA + (0x0065, 0x0300, 0x00E8), # e + COMBINING GRAVE ACCENT = LATIN SMALL LETTER E WITH GRAVE + (0x0065, 0x0301, 0x00E9), # e + COMBINING ACUTE ACCENT = LATIN SMALL LETTER E WITH ACUTE + (0x0065, 0x0302, 0x00EA), # e + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER E WITH CIRCUMFLEX + (0x0065, 0x0308, 0x00EB), # e + COMBINING DIAERESIS = LATIN SMALL LETTER E WITH DIAERESIS + (0x0069, 0x0300, 0x00EC), # i + COMBINING GRAVE ACCENT = LATIN SMALL LETTER I WITH GRAVE + (0x0069, 0x0301, 0x00ED), # i + COMBINING ACUTE ACCENT = LATIN SMALL LETTER I WITH ACUTE + (0x0069, 0x0302, 0x00EE), # i + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER I WITH CIRCUMFLEX + (0x0069, 0x0308, 0x00EF), # i + COMBINING DIAERESIS = LATIN SMALL LETTER I WITH DIAERESIS + (0x006E, 0x0303, 0x00F1), # n + COMBINING TILDE = LATIN SMALL LETTER N WITH TILDE + (0x006F, 0x0300, 0x00F2), # o + COMBINING GRAVE ACCENT = LATIN SMALL LETTER O WITH GRAVE + (0x006F, 0x0301, 0x00F3), # o + COMBINING ACUTE ACCENT = LATIN SMALL LETTER O WITH ACUTE + (0x006F, 0x0302, 0x00F4), # o + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER O WITH CIRCUMFLEX + (0x006F, 0x0303, 0x00F5), # o + COMBINING TILDE = LATIN SMALL LETTER O WITH TILDE + (0x006F, 0x0308, 0x00F6), # o + COMBINING DIAERESIS = LATIN SMALL LETTER O WITH DIAERESIS + (0x0075, 0x0300, 0x00F9), # u + COMBINING GRAVE ACCENT = LATIN SMALL LETTER U WITH GRAVE + (0x0075, 0x0301, 0x00FA), # u + COMBINING ACUTE ACCENT = LATIN SMALL LETTER U WITH ACUTE + (0x0075, 0x0302, 0x00FB), # u + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER U WITH CIRCUMFLEX + (0x0075, 0x0308, 0x00FC), # u + COMBINING DIAERESIS = LATIN SMALL LETTER U WITH DIAERESIS + (0x0079, 0x0301, 0x00FD), # y + COMBINING ACUTE ACCENT = LATIN SMALL LETTER Y WITH ACUTE + (0x0079, 0x0308, 0x00FF), # y + COMBINING DIAERESIS = LATIN SMALL LETTER Y WITH DIAERESIS + + # Common Greek characters + (0x0391, 0x0301, 0x0386), # Α + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER ALPHA WITH TONOS + (0x0395, 0x0301, 0x0388), # Ε + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER EPSILON WITH TONOS + (0x0397, 0x0301, 0x0389), # Η + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER ETA WITH TONOS + (0x0399, 0x0301, 0x038A), # Ι + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER IOTA WITH TONOS + (0x039F, 0x0301, 0x038C), # Ο + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER OMICRON WITH TONOS + (0x03A5, 0x0301, 0x038E), # Υ + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER UPSILON WITH TONOS + (0x03A9, 0x0301, 0x038F), # Ω + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER OMEGA WITH TONOS + (0x03B1, 0x0301, 0x03AC), # α + COMBINING ACUTE ACCENT = GREEK SMALL LETTER ALPHA WITH TONOS + (0x03B5, 0x0301, 0x03AD), # ε + COMBINING ACUTE ACCENT = GREEK SMALL LETTER EPSILON WITH TONOS + (0x03B7, 0x0301, 0x03AE), # η + COMBINING ACUTE ACCENT = GREEK SMALL LETTER ETA WITH TONOS + (0x03B9, 0x0301, 0x03AF), # ι + COMBINING ACUTE ACCENT = GREEK SMALL LETTER IOTA WITH TONOS + (0x03BF, 0x0301, 0x03CC), # ο + COMBINING ACUTE ACCENT = GREEK SMALL LETTER OMICRON WITH TONOS + (0x03C5, 0x0301, 0x03CD), # υ + COMBINING ACUTE ACCENT = GREEK SMALL LETTER UPSILON WITH TONOS + (0x03C9, 0x0301, 0x03CE), # ω + COMBINING ACUTE ACCENT = GREEK SMALL LETTER OMEGA WITH TONOS + + # Common Cyrillic characters + (0x0418, 0x0306, 0x0419), # И + COMBINING BREVE = CYRILLIC CAPITAL LETTER SHORT I + (0x0438, 0x0306, 0x0439), # и + COMBINING BREVE = CYRILLIC SMALL LETTER SHORT I + (0x0423, 0x0306, 0x040E), # У + COMBINING BREVE = CYRILLIC CAPITAL LETTER SHORT U + (0x0443, 0x0306, 0x045E), # у + COMBINING BREVE = CYRILLIC SMALL LETTER SHORT U +] + +full_recompose_description = "all possible recomposition pairs from the Unicode BMP" +def collect_all_recomposition_pairs(): + """Collect all possible recomposition pairs from the Unicode data.""" + # Map to store recomposition pairs: (base, combining) -> recomposed + recompose_map = {} + + # Process all assigned Unicode code points in BMP (Basic Multilingual Plane) + # We limit to BMP (0x0000-0xFFFF) to keep our table smaller with uint16_t + for cp in range(0, 0x10000): + try: + char = chr(cp) + + # Skip unassigned or control characters + if not unicodedata.name(char, ''): + continue + + # Find decomposition + decomp = unicodedata.decomposition(char) + if not decomp or '<' in decomp: # Skip compatibility decompositions + continue + + # Parse the decomposition + parts = decomp.split() + if len(parts) == 2: # Simple base + combining mark + base = int(parts[0], 16) + combining = int(parts[1], 16) + + # Only store if both are in BMP + if base < 0x10000 and combining < 0x10000: + recompose_map[(base, combining)] = cp + + except (ValueError, TypeError): + continue + + # Convert to a list of tuples and sort for binary search + recompose_list = [(base, combining, recomposed) + for (base, combining), recomposed in recompose_map.items()] + recompose_list.sort() + + return recompose_list + +def validate_common_pairs(full_list): + """Validate that all common pairs are in the full list. + + Raises: + ValueError: If any common pair is missing or has a different recomposition + value than what's in the full table. + """ + full_pairs = {(base, combining): recomposed for base, combining, recomposed in full_list} + for base, combining, recomposed in COMMON_RECOMPOSITION_PAIRS: + full_recomposed = full_pairs.get((base, combining)) + if full_recomposed is None: + error_msg = f"Error: Common pair (0x{base:04X}, 0x{combining:04X}) not found in full data" + print(error_msg) + raise ValueError(error_msg) + elif full_recomposed != recomposed: + error_msg = (f"Error: Common pair (0x{base:04X}, 0x{combining:04X}) has different recomposition: " + f"0x{recomposed:04X} vs 0x{full_recomposed:04X}") + print(error_msg) + raise ValueError(error_msg) + +def generate_recomposition_table(use_full_list=False, out_file=DEFAULT_OUT_FILE): + """Generate the recomposition C table.""" + + # Collect all recomposition pairs for validation + full_recompose_list = collect_all_recomposition_pairs() + + # Decide which list to use + if use_full_list: + print("Using full recomposition list...") + recompose_list = full_recompose_list + table_description = full_recompose_description + alt_list = COMMON_RECOMPOSITION_PAIRS + alt_description = common_recompose_description + else: + print("Using common recomposition list...") + # Validate that all common pairs are in the full list + validate_common_pairs(full_recompose_list) + recompose_list = sorted(COMMON_RECOMPOSITION_PAIRS) + table_description = common_recompose_description + alt_list = full_recompose_list + alt_description = full_recompose_description + generation_mode = " --full" if use_full_list else "" + alternative_mode = " --full" if not use_full_list else "" + table_description_detail = f"{table_description} ({len(recompose_list)} entries)" + alt_description_detail = f"{alt_description} ({len(alt_list)} entries)" + + # Calculate min/max values for boundary checks + min_base = min(base for base, _, _ in recompose_list) + max_base = max(base for base, _, _ in recompose_list) + min_combining = min(combining for _, combining, _ in recompose_list) + max_combining = max(combining for _, combining, _ in recompose_list) + + # Generate implementation file + with open(out_file, 'w') as f: + f.write(f"""\ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * {out_file} - Unicode character recomposition + * + * Auto-generated by {this_file}{generation_mode} + * + * Unicode Version: {unicodedata.unidata_version} + * +{textwrap.fill( + f"This file contains a table with {table_description_detail}. " + + f"To generate a table with {alt_description_detail} instead, run:", + width=75, initial_indent=" * ", subsequent_indent=" * ")} + * + * python3 {this_file}{alternative_mode} + */ + +/* + * Table of {table_description} + * Sorted by base character and then combining mark for binary search + */ +static const struct ucs_recomposition ucs_recomposition_table[] = {{ +""") + + for base, combining, recomposed in recompose_list: + try: + base_name = unicodedata.name(chr(base)) + combining_name = unicodedata.name(chr(combining)) + recomposed_name = unicodedata.name(chr(recomposed)) + comment = f"/* {base_name} + {combining_name} = {recomposed_name} */" + except ValueError: + comment = f"/* U+{base:04X} + U+{combining:04X} = U+{recomposed:04X} */" + f.write(f"\t{{ 0x{base:04X}, 0x{combining:04X}, 0x{recomposed:04X} }}, {comment}\n") + + f.write(f"""\ +}}; + +/* + * Boundary values for quick rejection + * These are calculated by analyzing the table during generation + */ +#define UCS_RECOMPOSE_MIN_BASE 0x{min_base:04X} +#define UCS_RECOMPOSE_MAX_BASE 0x{max_base:04X} +#define UCS_RECOMPOSE_MIN_MARK 0x{min_combining:04X} +#define UCS_RECOMPOSE_MAX_MARK 0x{max_combining:04X} +""") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Generate Unicode recomposition table") + parser.add_argument("--full", action="store_true", + help="Generate a full recomposition table (default: common pairs only)") + parser.add_argument("-o", "--output", dest="output_file", default=DEFAULT_OUT_FILE, + help=f"Output file name (default: {DEFAULT_OUT_FILE})") + args = parser.parse_args() + + generate_recomposition_table(use_full_list=args.full, out_file=args.output_file) diff --git a/drivers/tty/vt/gen_ucs_width_table.py b/drivers/tty/vt/gen_ucs_width_table.py new file mode 100755 index 000000000000..76e80ebeff13 --- /dev/null +++ b/drivers/tty/vt/gen_ucs_width_table.py @@ -0,0 +1,307 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# +# Leverage Python's unicodedata module to generate ucs_width_table.h + +import unicodedata +import sys +import argparse + +# This script's file name +from pathlib import Path +this_file = Path(__file__).name + +# Default output file name +DEFAULT_OUT_FILE = "ucs_width_table.h" + +# --- Global Constants for Width Assignments --- + +# Known zero-width characters +KNOWN_ZERO_WIDTH = ( + 0x200B, # ZERO WIDTH SPACE + 0x200C, # ZERO WIDTH NON-JOINER + 0x200D, # ZERO WIDTH JOINER + 0x2060, # WORD JOINER + 0xFEFF # ZERO WIDTH NO-BREAK SPACE (BOM) +) + +# Zero-width emoji modifiers and components +# NOTE: Some of these characters would normally be single-width according to +# East Asian Width properties, but we deliberately override them to be +# zero-width because they function as modifiers in emoji sequences. +EMOJI_ZERO_WIDTH = [ + # Skin tone modifiers + (0x1F3FB, 0x1F3FF), # Emoji modifiers (skin tones) + + # Variation selectors (note: VS16 is treated specially in vt.c) + (0xFE00, 0xFE0F), # Variation Selectors 1-16 + + # Gender and hair style modifiers + # These would be single-width by Unicode properties, but are zero-width + # when part of emoji + (0x2640, 0x2640), # Female sign + (0x2642, 0x2642), # Male sign + (0x26A7, 0x26A7), # Transgender symbol + (0x1F9B0, 0x1F9B3), # Hair components (red, curly, white, bald) + + # Tag characters + (0xE0020, 0xE007E), # Tags +] + +# Regional indicators (flag components) +REGIONAL_INDICATORS = (0x1F1E6, 0x1F1FF) # Regional indicator symbols A-Z + +# Double-width emoji ranges +# +# Many emoji characters are classified as single-width according to Unicode +# Standard Annex #11 East Asian Width property (N or Neutral), but we +# deliberately override them to be double-width. References: +# 1. Unicode Technical Standard #51: Unicode Emoji +# (https://www.unicode.org/reports/tr51/) +# 2. Principle of "emoji presentation" in WHATWG CSS Text specification +# (https://drafts.csswg.org/css-text-3/#character-properties) +# 3. Terminal emulator implementations (iTerm2, Windows Terminal, etc.) which +# universally render emoji as double-width characters regardless of their +# Unicode EAW property +# 4. W3C Work Item: Requirements for Japanese Text Layout - Section 3.8.1 +# Emoji width (https://www.w3.org/TR/jlreq/) +EMOJI_RANGES = [ + (0x1F000, 0x1F02F), # Mahjong Tiles (EAW: N, but displayed as double-width) + (0x1F0A0, 0x1F0FF), # Playing Cards (EAW: N, but displayed as double-width) + (0x1F300, 0x1F5FF), # Miscellaneous Symbols and Pictographs + (0x1F600, 0x1F64F), # Emoticons + (0x1F680, 0x1F6FF), # Transport and Map Symbols + (0x1F700, 0x1F77F), # Alchemical Symbols + (0x1F780, 0x1F7FF), # Geometric Shapes Extended + (0x1F800, 0x1F8FF), # Supplemental Arrows-C + (0x1F900, 0x1F9FF), # Supplemental Symbols and Pictographs + (0x1FA00, 0x1FA6F), # Chess Symbols + (0x1FA70, 0x1FAFF), # Symbols and Pictographs Extended-A +] + +def create_width_tables(): + """ + Creates Unicode character width tables and returns the data structures. + + Returns: + tuple: (zero_width_ranges, double_width_ranges) + """ + + # Width data mapping + width_map = {} # Maps code points to width (0, 1, 2) + + # Mark emoji modifiers as zero-width + for start, end in EMOJI_ZERO_WIDTH: + for cp in range(start, end + 1): + width_map[cp] = 0 + + # Mark all regional indicators as single-width as they are usually paired + # providing a combined width of 2 when displayed together. + start, end = REGIONAL_INDICATORS + for cp in range(start, end + 1): + width_map[cp] = 1 + + # Process all assigned Unicode code points (Basic Multilingual Plane + + # Supplementary Planes) Range 0x0 to 0x10FFFF (the full Unicode range) + for block_start in range(0, 0x110000, 0x1000): + block_end = block_start + 0x1000 + for cp in range(block_start, block_end): + try: + char = chr(cp) + + # Skip if already processed + if cp in width_map: + continue + + # Check for combining marks and a format characters + category = unicodedata.category(char) + + # Combining marks + if category.startswith('M'): + width_map[cp] = 0 + continue + + # Format characters + # Since we have no support for bidirectional text, all format + # characters (category Cf) can be treated with width 0 (zero) + # for simplicity, as they don't need to occupy visual space + # in a non-bidirectional text environment. + if category == 'Cf': + width_map[cp] = 0 + continue + + # Known zero-width characters + if cp in KNOWN_ZERO_WIDTH: + width_map[cp] = 0 + continue + + # Use East Asian Width property + eaw = unicodedata.east_asian_width(char) + if eaw in ('F', 'W'): # Fullwidth or Wide + width_map[cp] = 2 + elif eaw in ('Na', 'H', 'N', 'A'): # Narrow, Halfwidth, Neutral, Ambiguous + width_map[cp] = 1 + else: + # Default to single-width for unknown + width_map[cp] = 1 + + except (ValueError, OverflowError): + # Skip invalid code points + continue + + # Process Emoji - generally double-width + for start, end in EMOJI_RANGES: + for cp in range(start, end + 1): + if cp not in width_map or width_map[cp] != 0: # Don't override zero-width + try: + char = chr(cp) + width_map[cp] = 2 + except (ValueError, OverflowError): + continue + + # Optimize to create range tables + def ranges_optimize(width_data, target_width): + points = sorted([cp for cp, width in width_data.items() if width == target_width]) + if not points: + return [] + + # Group consecutive code points into ranges + ranges = [] + start = points[0] + prev = start + + for cp in points[1:]: + if cp > prev + 1: + ranges.append((start, prev)) + start = cp + prev = cp + + # Add the last range + ranges.append((start, prev)) + return ranges + + # Extract ranges for each width + zero_width_ranges = ranges_optimize(width_map, 0) + double_width_ranges = ranges_optimize(width_map, 2) + + return zero_width_ranges, double_width_ranges + +def write_tables(zero_width_ranges, double_width_ranges, out_file=DEFAULT_OUT_FILE): + """ + Write the generated tables to C header file. + + Args: + zero_width_ranges: List of (start, end) ranges for zero-width characters + double_width_ranges: List of (start, end) ranges for double-width characters + out_file: Output file name (default: DEFAULT_OUT_FILE) + """ + + # Function to split ranges into BMP (16-bit) and non-BMP (above 16-bit) + def split_ranges_by_size(ranges): + bmp_ranges = [] + non_bmp_ranges = [] + + for start, end in ranges: + if end <= 0xFFFF: + bmp_ranges.append((start, end)) + elif start > 0xFFFF: + non_bmp_ranges.append((start, end)) + else: + # Split the range at 0xFFFF + bmp_ranges.append((start, 0xFFFF)) + non_bmp_ranges.append((0x10000, end)) + + return bmp_ranges, non_bmp_ranges + + # Split ranges into BMP and non-BMP + zero_width_bmp, zero_width_non_bmp = split_ranges_by_size(zero_width_ranges) + double_width_bmp, double_width_non_bmp = split_ranges_by_size(double_width_ranges) + + # Function to generate code point description comments + def get_code_point_comment(start, end): + try: + start_char_desc = unicodedata.name(chr(start)) + if start == end: + return f"/* {start_char_desc} */" + else: + end_char_desc = unicodedata.name(chr(end)) + return f"/* {start_char_desc} - {end_char_desc} */" + except: + if start == end: + return f"/* U+{start:04X} */" + else: + return f"/* U+{start:04X} - U+{end:04X} */" + + # Generate C tables + with open(out_file, 'w') as f: + f.write(f"""\ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * {out_file} - Unicode character width + * + * Auto-generated by {this_file} + * + * Unicode Version: {unicodedata.unidata_version} + */ + +/* Zero-width character ranges (BMP - Basic Multilingual Plane, U+0000 to U+FFFF) */ +static const struct ucs_interval16 ucs_zero_width_bmp_ranges[] = {{ +""") + + for start, end in zero_width_bmp: + comment = get_code_point_comment(start, end) + f.write(f"\t{{ 0x{start:04X}, 0x{end:04X} }}, {comment}\n") + + f.write("""\ +}; + +/* Zero-width character ranges (non-BMP, U+10000 and above) */ +static const struct ucs_interval32 ucs_zero_width_non_bmp_ranges[] = { +""") + + for start, end in zero_width_non_bmp: + comment = get_code_point_comment(start, end) + f.write(f"\t{{ 0x{start:05X}, 0x{end:05X} }}, {comment}\n") + + f.write("""\ +}; + +/* Double-width character ranges (BMP - Basic Multilingual Plane, U+0000 to U+FFFF) */ +static const struct ucs_interval16 ucs_double_width_bmp_ranges[] = { +""") + + for start, end in double_width_bmp: + comment = get_code_point_comment(start, end) + f.write(f"\t{{ 0x{start:04X}, 0x{end:04X} }}, {comment}\n") + + f.write("""\ +}; + +/* Double-width character ranges (non-BMP, U+10000 and above) */ +static const struct ucs_interval32 ucs_double_width_non_bmp_ranges[] = { +""") + + for start, end in double_width_non_bmp: + comment = get_code_point_comment(start, end) + f.write(f"\t{{ 0x{start:05X}, 0x{end:05X} }}, {comment}\n") + + f.write("};\n") + +if __name__ == "__main__": + # Parse command line arguments + parser = argparse.ArgumentParser(description="Generate Unicode width tables") + parser.add_argument("-o", "--output", dest="output_file", default=DEFAULT_OUT_FILE, + help=f"Output file name (default: {DEFAULT_OUT_FILE})") + args = parser.parse_args() + + # Write tables to header file + zero_width_ranges, double_width_ranges = create_width_tables() + write_tables(zero_width_ranges, double_width_ranges, out_file=args.output_file) + + # Print summary + zero_width_count = sum(end - start + 1 for start, end in zero_width_ranges) + double_width_count = sum(end - start + 1 for start, end in double_width_ranges) + print(f"Generated {args.output_file} with:") + print(f"- {len(zero_width_ranges)} zero-width ranges covering ~{zero_width_count} code points") + print(f"- {len(double_width_ranges)} double-width ranges covering ~{double_width_count} code points") + print(f"- Unicode Version: {unicodedata.unidata_version}") diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index a2116e135a82..dc585079c2fb 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -275,7 +275,7 @@ static DEFINE_TIMER(kd_mksound_timer, kd_nosound); void kd_mksound(unsigned int hz, unsigned int ticks) { - del_timer_sync(&kd_mksound_timer); + timer_delete_sync(&kd_mksound_timer); input_handler_for_each_handle(&kbd_handler, &hz, kd_sound_helper); @@ -376,6 +376,17 @@ static void to_utf8(struct vc_data *vc, uint c) } } +static void put_queue_utf8(struct vc_data *vc, u32 value) +{ + if (kbd->kbdmode == VC_UNICODE) + to_utf8(vc, value); + else { + int c = conv_uni_to_8bit(value); + if (c != -1) + put_queue(vc, c); + } +} + /* FIXME: review locking for vt.c callers */ static void set_leds(void) { @@ -454,13 +465,7 @@ static unsigned int handle_diacr(struct vc_data *vc, unsigned int ch) if (ch == ' ' || ch == (BRL_UC_ROW|0) || ch == d) return d; - if (kbd->kbdmode == VC_UNICODE) - to_utf8(vc, d); - else { - int c = conv_uni_to_8bit(d); - if (c != -1) - put_queue(vc, c); - } + put_queue_utf8(vc, d); return ch; } @@ -471,13 +476,7 @@ static unsigned int handle_diacr(struct vc_data *vc, unsigned int ch) static void fn_enter(struct vc_data *vc) { if (diacr) { - if (kbd->kbdmode == VC_UNICODE) - to_utf8(vc, diacr); - else { - int c = conv_uni_to_8bit(diacr); - if (c != -1) - put_queue(vc, c); - } + put_queue_utf8(vc, diacr); diacr = 0; } @@ -685,13 +684,7 @@ static void k_unicode(struct vc_data *vc, unsigned int value, char up_flag) diacr = value; return; } - if (kbd->kbdmode == VC_UNICODE) - to_utf8(vc, value); - else { - int c = conv_uni_to_8bit(value); - if (c != -1) - put_queue(vc, c); - } + put_queue_utf8(vc, value); } /* @@ -1033,9 +1026,7 @@ static int kbd_led_trigger_activate(struct led_classdev *cdev) tasklet_disable(&keyboard_tasklet); if (ledstate != -1U) - led_trigger_event(&trigger->trigger, - ledstate & trigger->mask ? - LED_FULL : LED_OFF); + led_set_brightness(cdev, ledstate & trigger->mask ? LED_FULL : LED_OFF); tasklet_enable(&keyboard_tasklet); return 0; @@ -1521,7 +1512,7 @@ static void kbd_keycode(unsigned int keycode, int down, bool hw_raw) if ((raw_mode || kbd->kbdmode == VC_OFF) && type != KT_SPEC && type != KT_SHIFT) return; - (*k_handler[type])(vc, keysym & 0xff, !down); + (*k_handler[type])(vc, KVAL(keysym), !down); param.ledstate = kbd->ledflagstate; atomic_notifier_call_chain(&keyboard_notifier_list, KBD_POST_KEYSYM, ¶m); diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 564341f1a74f..24b0a53e5a79 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -192,6 +192,19 @@ int set_selection_user(const struct tiocl_selection __user *sel, if (copy_from_user(&v, sel, sizeof(*sel))) return -EFAULT; + /* + * TIOCL_SELCLEAR and TIOCL_SELPOINTER are OK to use without + * CAP_SYS_ADMIN as they do not modify the selection. + */ + switch (v.sel_mode) { + case TIOCL_SELCLEAR: + case TIOCL_SELPOINTER: + break; + default: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + } + return set_selection_kernel(&v, tty); } @@ -390,6 +403,12 @@ int paste_selection(struct tty_struct *tty) DECLARE_WAITQUEUE(wait, current); int ret = 0; + bool bp = vc->vc_bracketed_paste; + static const char bracketed_paste_start[] = "\033[200~"; + static const char bracketed_paste_end[] = "\033[201~"; + const char *bps = bp ? bracketed_paste_start : NULL; + const char *bpe = bp ? bracketed_paste_end : NULL; + console_lock(); poke_blanked_console(); console_unlock(); @@ -401,7 +420,7 @@ int paste_selection(struct tty_struct *tty) add_wait_queue(&vc->paste_wait, &wait); mutex_lock(&vc_sel.lock); - while (vc_sel.buffer && vc_sel.buf_len > pasted) { + while (vc_sel.buffer && (vc_sel.buf_len > pasted || bpe)) { set_current_state(TASK_INTERRUPTIBLE); if (signal_pending(current)) { ret = -EINTR; @@ -414,10 +433,27 @@ int paste_selection(struct tty_struct *tty) continue; } __set_current_state(TASK_RUNNING); + + if (bps) { + bps += tty_ldisc_receive_buf(ld, bps, NULL, strlen(bps)); + if (*bps != '\0') + continue; + bps = NULL; + } + count = vc_sel.buf_len - pasted; - count = tty_ldisc_receive_buf(ld, vc_sel.buffer + pasted, NULL, - count); - pasted += count; + if (count) { + pasted += tty_ldisc_receive_buf(ld, vc_sel.buffer + pasted, + NULL, count); + if (vc_sel.buf_len > pasted) + continue; + } + + if (bpe) { + bpe += tty_ldisc_receive_buf(ld, bpe, NULL, strlen(bpe)); + if (*bpe == '\0') + bpe = NULL; + } } mutex_unlock(&vc_sel.lock); remove_wait_queue(&vc->paste_wait, &wait); diff --git a/drivers/tty/vt/ucs.c b/drivers/tty/vt/ucs.c new file mode 100644 index 000000000000..03877485dfb7 --- /dev/null +++ b/drivers/tty/vt/ucs.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ucs.c - Universal Character Set processing + */ + +#include <linux/array_size.h> +#include <linux/bsearch.h> +#include <linux/consolemap.h> +#include <linux/minmax.h> + +struct ucs_interval16 { + u16 first; + u16 last; +}; + +struct ucs_interval32 { + u32 first; + u32 last; +}; + +#include "ucs_width_table.h" + +static int interval16_cmp(const void *key, const void *element) +{ + u16 cp = *(u16 *)key; + const struct ucs_interval16 *entry = element; + + if (cp < entry->first) + return -1; + if (cp > entry->last) + return 1; + return 0; +} + +static int interval32_cmp(const void *key, const void *element) +{ + u32 cp = *(u32 *)key; + const struct ucs_interval32 *entry = element; + + if (cp < entry->first) + return -1; + if (cp > entry->last) + return 1; + return 0; +} + +static bool cp_in_range16(u16 cp, const struct ucs_interval16 *ranges, size_t size) +{ + if (cp < ranges[0].first || cp > ranges[size - 1].last) + return false; + + return __inline_bsearch(&cp, ranges, size, sizeof(*ranges), + interval16_cmp) != NULL; +} + +static bool cp_in_range32(u32 cp, const struct ucs_interval32 *ranges, size_t size) +{ + if (cp < ranges[0].first || cp > ranges[size - 1].last) + return false; + + return __inline_bsearch(&cp, ranges, size, sizeof(*ranges), + interval32_cmp) != NULL; +} + +#define UCS_IS_BMP(cp) ((cp) <= 0xffff) + +/** + * ucs_is_zero_width() - Determine if a Unicode code point is zero-width. + * @cp: Unicode code point (UCS-4) + * + * Return: true if the character is zero-width, false otherwise + */ +bool ucs_is_zero_width(u32 cp) +{ + if (UCS_IS_BMP(cp)) + return cp_in_range16(cp, ucs_zero_width_bmp_ranges, + ARRAY_SIZE(ucs_zero_width_bmp_ranges)); + else + return cp_in_range32(cp, ucs_zero_width_non_bmp_ranges, + ARRAY_SIZE(ucs_zero_width_non_bmp_ranges)); +} + +/** + * ucs_is_double_width() - Determine if a Unicode code point is double-width. + * @cp: Unicode code point (UCS-4) + * + * Return: true if the character is double-width, false otherwise + */ +bool ucs_is_double_width(u32 cp) +{ + if (UCS_IS_BMP(cp)) + return cp_in_range16(cp, ucs_double_width_bmp_ranges, + ARRAY_SIZE(ucs_double_width_bmp_ranges)); + else + return cp_in_range32(cp, ucs_double_width_non_bmp_ranges, + ARRAY_SIZE(ucs_double_width_non_bmp_ranges)); +} + +/* + * Structure for base with combining mark pairs and resulting recompositions. + * Using u16 to save space since all values are within BMP range. + */ +struct ucs_recomposition { + u16 base; /* base character */ + u16 mark; /* combining mark */ + u16 recomposed; /* corresponding recomposed character */ +}; + +#include "ucs_recompose_table.h" + +struct compare_key { + u16 base; + u16 mark; +}; + +static int recomposition_cmp(const void *key, const void *element) +{ + const struct compare_key *search_key = key; + const struct ucs_recomposition *entry = element; + + /* Compare base character first */ + if (search_key->base < entry->base) + return -1; + if (search_key->base > entry->base) + return 1; + + /* Base characters match, now compare combining character */ + if (search_key->mark < entry->mark) + return -1; + if (search_key->mark > entry->mark) + return 1; + + /* Both match */ + return 0; +} + +/** + * ucs_recompose() - Attempt to recompose two Unicode characters into a single character. + * @base: Base Unicode code point (UCS-4) + * @mark: Combining mark Unicode code point (UCS-4) + * + * Return: Recomposed Unicode code point, or 0 if no recomposition is possible + */ +u32 ucs_recompose(u32 base, u32 mark) +{ + /* Check if characters are within the range of our table */ + if (base < UCS_RECOMPOSE_MIN_BASE || base > UCS_RECOMPOSE_MAX_BASE || + mark < UCS_RECOMPOSE_MIN_MARK || mark > UCS_RECOMPOSE_MAX_MARK) + return 0; + + struct compare_key key = { base, mark }; + struct ucs_recomposition *result = + __inline_bsearch(&key, ucs_recomposition_table, + ARRAY_SIZE(ucs_recomposition_table), + sizeof(*ucs_recomposition_table), + recomposition_cmp); + + return result ? result->recomposed : 0; +} + +/* + * The fallback table structures implement a 2-level lookup. + */ + +struct ucs_page_desc { + u8 page; /* Page index (high byte of code points) */ + u8 count; /* Number of entries in this page */ + u16 start; /* Start index in entries array */ +}; + +struct ucs_page_entry { + u8 offset; /* Offset within page (0-255) */ + u8 fallback; /* Fallback character or range start marker */ +}; + +#include "ucs_fallback_table.h" + +static int ucs_page_desc_cmp(const void *key, const void *element) +{ + u8 page = *(u8 *)key; + const struct ucs_page_desc *entry = element; + + if (page < entry->page) + return -1; + if (page > entry->page) + return 1; + return 0; +} + +static int ucs_page_entry_cmp(const void *key, const void *element) +{ + u8 offset = *(u8 *)key; + const struct ucs_page_entry *entry = element; + + if (offset < entry->offset) + return -1; + if (entry->fallback == UCS_PAGE_ENTRY_RANGE_MARKER) { + if (offset > entry[1].offset) + return 1; + } else { + if (offset > entry->offset) + return 1; + } + return 0; +} + +/** + * ucs_get_fallback() - Get a substitution for the provided Unicode character + * @cp: Unicode code point (UCS-4) + * + * Get a simpler fallback character for the provided Unicode character. + * This is used for terminal display when corresponding glyph is unavailable. + * The substitution may not be as good as the actual glyph for the original + * character but still way more helpful than a squared question mark. + * + * Return: Fallback Unicode code point, or 0 if none is available + */ +u32 ucs_get_fallback(u32 cp) +{ + const struct ucs_page_desc *page; + const struct ucs_page_entry *entry; + u8 page_idx = cp >> 8, offset = cp; + + if (!UCS_IS_BMP(cp)) + return 0; + + /* + * Full-width to ASCII mapping (covering all printable ASCII 33-126) + * 0xFF01 (!) to 0xFF5E (~) -> ASCII 33 (!) to 126 (~) + * We process them programmatically to reduce the table size. + */ + if (cp >= 0xFF01 && cp <= 0xFF5E) + return cp - 0xFF01 + 33; + + page = __inline_bsearch(&page_idx, ucs_fallback_pages, + ARRAY_SIZE(ucs_fallback_pages), + sizeof(*ucs_fallback_pages), + ucs_page_desc_cmp); + if (!page) + return 0; + + entry = __inline_bsearch(&offset, ucs_fallback_entries + page->start, + page->count, sizeof(*ucs_fallback_entries), + ucs_page_entry_cmp); + if (!entry) + return 0; + + if (entry->fallback == UCS_PAGE_ENTRY_RANGE_MARKER) + entry++; + return entry->fallback; +} diff --git a/drivers/tty/vt/ucs_fallback_table.h_shipped b/drivers/tty/vt/ucs_fallback_table.h_shipped new file mode 100644 index 000000000000..2da5a8fe1cf1 --- /dev/null +++ b/drivers/tty/vt/ucs_fallback_table.h_shipped @@ -0,0 +1,3346 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * ucs_fallback_table.h - Unicode character fallback table + * + * Auto-generated by gen_ucs_fallback_table.py + * + * Unicode Version: 16.0.0 + * Unidecode Version: 1.3.8 + * + * This file contains optimized tables that map complex Unicode characters + * to simpler fallback characters for terminal display when corresponding + * glyphs are unavailable. + */ + +static const struct ucs_page_desc ucs_fallback_pages[] = { + { 0x00, 62, 0 }, + { 0x01, 218, 62 }, + { 0x02, 196, 280 }, + { 0x03, 96, 476 }, + { 0x04, 113, 572 }, + { 0x05, 100, 685 }, + { 0x06, 119, 785 }, + { 0x07, 91, 904 }, + { 0x09, 99, 995 }, + { 0x0A, 78, 1094 }, + { 0x0B, 79, 1172 }, + { 0x0C, 85, 1251 }, + { 0x0D, 73, 1336 }, + { 0x0E, 83, 1409 }, + { 0x0F, 69, 1492 }, + { 0x10, 93, 1561 }, + { 0x11, 51, 1654 }, + { 0x13, 22, 1705 }, + { 0x14, 30, 1727 }, + { 0x15, 17, 1757 }, + { 0x16, 81, 1774 }, + { 0x17, 47, 1855 }, + { 0x18, 96, 1902 }, + { 0x1D, 105, 1998 }, + { 0x1E, 246, 2103 }, + { 0x1F, 94, 2349 }, + { 0x20, 107, 2443 }, + { 0x21, 136, 2550 }, + { 0x22, 34, 2686 }, + { 0x23, 4, 2720 }, + { 0x24, 72, 2724 }, + { 0x25, 60, 2796 }, + { 0x26, 6, 2856 }, + { 0x27, 18, 2862 }, + { 0x28, 64, 2880 }, + { 0x29, 1, 2944 }, + { 0x2C, 15, 2945 }, + { 0x2E, 29, 2960 }, + { 0x30, 53, 2989 }, + { 0x31, 50, 3042 }, + { 0x32, 5, 3092 }, + { 0xA0, 4, 3097 }, + { 0xC5, 2, 3101 }, + { 0xC6, 2, 3103 }, + { 0xC7, 1, 3105 }, + { 0xFB, 35, 3106 }, + { 0xFE, 37, 3141 }, + { 0xFF, 50, 3178 }, +}; + +/* Page entries array (referenced by page descriptors) */ +static const struct ucs_page_entry ucs_fallback_entries[] = { + /* Entries for page 0x00 */ + { 0xA0, 0x20 }, /* NO-BREAK SPACE -> ' ' */ + { 0xA1, 0x21 }, /* INVERTED EXCLAMATION MARK -> '!' */ + { 0xA2, 0x63 }, /* CENT SIGN -> 'c' */ + { 0xA3, 0x4C }, /* POUND SIGN -> 'L' */ + { 0xA5, 0x59 }, /* YEN SIGN -> 'Y' */ + { 0xA6, 0x7C }, /* BROKEN BAR -> '|' */ + { 0xA7, 0x53 }, /* SECTION SIGN -> 'S' */ + { 0xA8, 0x22 }, /* DIAERESIS -> '"' */ + { 0xA9, 0x43 }, /* COPYRIGHT SIGN -> 'C' */ + { 0xAA, 0x61 }, /* FEMININE ORDINAL INDICATOR -> 'a' */ + { 0xAB, 0x3C }, /* LEFT-POINTING DOUBLE ANGLE QUOTATION MARK -> '<' */ + { 0xAC, 0x21 }, /* NOT SIGN -> '!' */ + { 0xAE, 0x52 }, /* REGISTERED SIGN -> 'R' */ + { 0xAF, 0x2D }, /* MACRON -> '-' */ + { 0xB0, 0x6F }, /* DEGREE SIGN -> 'o' */ + { 0xB2, 0x32 }, /* SUPERSCRIPT TWO -> '2' */ + { 0xB3, 0x33 }, /* SUPERSCRIPT THREE -> '3' */ + { 0xB4, 0x27 }, /* ACUTE ACCENT -> ''' */ + { 0xB5, 0x75 }, /* MICRO SIGN -> 'u' */ + { 0xB6, 0x50 }, /* PILCROW SIGN -> 'P' */ + { 0xB7, 0x2A }, /* MIDDLE DOT -> '*' */ + { 0xB8, 0x2C }, /* CEDILLA -> ',' */ + { 0xB9, 0x31 }, /* SUPERSCRIPT ONE -> '1' */ + { 0xBA, 0x6F }, /* MASCULINE ORDINAL INDICATOR -> 'o' */ + { 0xBB, 0x3E }, /* RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK -> '>' */ + { 0xBF, 0x3F }, /* INVERTED QUESTION MARK -> '?' */ + { 0xC0, 0x00 }, /* LATIN CAPITAL LETTER A WITH GRAVE -> ... */ + { 0xC5, 0x41 }, /* LATIN CAPITAL LETTER A WITH RING ABOVE -> 'A' */ + { 0xC6, 0x45 }, /* LATIN CAPITAL LETTER AE -> 'E' */ + { 0xC7, 0x43 }, /* LATIN CAPITAL LETTER C WITH CEDILLA -> 'C' */ + { 0xC8, 0x00 }, /* LATIN CAPITAL LETTER E WITH GRAVE -> ... */ + { 0xCB, 0x45 }, /* LATIN CAPITAL LETTER E WITH DIAERESIS -> 'E' */ + { 0xCC, 0x00 }, /* LATIN CAPITAL LETTER I WITH GRAVE -> ... */ + { 0xCF, 0x49 }, /* LATIN CAPITAL LETTER I WITH DIAERESIS -> 'I' */ + { 0xD0, 0x44 }, /* LATIN CAPITAL LETTER ETH -> 'D' */ + { 0xD1, 0x4E }, /* LATIN CAPITAL LETTER N WITH TILDE -> 'N' */ + { 0xD2, 0x00 }, /* LATIN CAPITAL LETTER O WITH GRAVE -> ... */ + { 0xD6, 0x4F }, /* LATIN CAPITAL LETTER O WITH DIAERESIS -> 'O' */ + { 0xD7, 0x78 }, /* MULTIPLICATION SIGN -> 'x' */ + { 0xD8, 0x4F }, /* LATIN CAPITAL LETTER O WITH STROKE -> 'O' */ + { 0xD9, 0x00 }, /* LATIN CAPITAL LETTER U WITH GRAVE -> ... */ + { 0xDC, 0x55 }, /* LATIN CAPITAL LETTER U WITH DIAERESIS -> 'U' */ + { 0xDD, 0x59 }, /* LATIN CAPITAL LETTER Y WITH ACUTE -> 'Y' */ + { 0xDF, 0x73 }, /* LATIN SMALL LETTER SHARP S -> 's' */ + { 0xE0, 0x00 }, /* LATIN SMALL LETTER A WITH GRAVE -> ... */ + { 0xE5, 0x61 }, /* LATIN SMALL LETTER A WITH RING ABOVE -> 'a' */ + { 0xE6, 0x65 }, /* LATIN SMALL LETTER AE -> 'e' */ + { 0xE7, 0x63 }, /* LATIN SMALL LETTER C WITH CEDILLA -> 'c' */ + { 0xE8, 0x00 }, /* LATIN SMALL LETTER E WITH GRAVE -> ... */ + { 0xEB, 0x65 }, /* LATIN SMALL LETTER E WITH DIAERESIS -> 'e' */ + { 0xEC, 0x00 }, /* LATIN SMALL LETTER I WITH GRAVE -> ... */ + { 0xEF, 0x69 }, /* LATIN SMALL LETTER I WITH DIAERESIS -> 'i' */ + { 0xF0, 0x64 }, /* LATIN SMALL LETTER ETH -> 'd' */ + { 0xF1, 0x6E }, /* LATIN SMALL LETTER N WITH TILDE -> 'n' */ + { 0xF2, 0x00 }, /* LATIN SMALL LETTER O WITH GRAVE -> ... */ + { 0xF6, 0x6F }, /* LATIN SMALL LETTER O WITH DIAERESIS -> 'o' */ + { 0xF7, 0x2F }, /* DIVISION SIGN -> '/' */ + { 0xF8, 0x6F }, /* LATIN SMALL LETTER O WITH STROKE -> 'o' */ + { 0xF9, 0x00 }, /* LATIN SMALL LETTER U WITH GRAVE -> ... */ + { 0xFC, 0x75 }, /* LATIN SMALL LETTER U WITH DIAERESIS -> 'u' */ + { 0xFD, 0x79 }, /* LATIN SMALL LETTER Y WITH ACUTE -> 'y' */ + { 0xFF, 0x79 }, /* LATIN SMALL LETTER Y WITH DIAERESIS -> 'y' */ + /* Entries for page 0x01 */ + { 0x00, 0x41 }, /* LATIN CAPITAL LETTER A WITH MACRON -> 'A' */ + { 0x01, 0x61 }, /* LATIN SMALL LETTER A WITH MACRON -> 'a' */ + { 0x02, 0x41 }, /* LATIN CAPITAL LETTER A WITH BREVE -> 'A' */ + { 0x03, 0x61 }, /* LATIN SMALL LETTER A WITH BREVE -> 'a' */ + { 0x04, 0x41 }, /* LATIN CAPITAL LETTER A WITH OGONEK -> 'A' */ + { 0x05, 0x61 }, /* LATIN SMALL LETTER A WITH OGONEK -> 'a' */ + { 0x06, 0x43 }, /* LATIN CAPITAL LETTER C WITH ACUTE -> 'C' */ + { 0x07, 0x63 }, /* LATIN SMALL LETTER C WITH ACUTE -> 'c' */ + { 0x08, 0x43 }, /* LATIN CAPITAL LETTER C WITH CIRCUMFLEX -> 'C' */ + { 0x09, 0x63 }, /* LATIN SMALL LETTER C WITH CIRCUMFLEX -> 'c' */ + { 0x0A, 0x43 }, /* LATIN CAPITAL LETTER C WITH DOT ABOVE -> 'C' */ + { 0x0B, 0x63 }, /* LATIN SMALL LETTER C WITH DOT ABOVE -> 'c' */ + { 0x0C, 0x43 }, /* LATIN CAPITAL LETTER C WITH CARON -> 'C' */ + { 0x0D, 0x63 }, /* LATIN SMALL LETTER C WITH CARON -> 'c' */ + { 0x0E, 0x44 }, /* LATIN CAPITAL LETTER D WITH CARON -> 'D' */ + { 0x0F, 0x64 }, /* LATIN SMALL LETTER D WITH CARON -> 'd' */ + { 0x10, 0x44 }, /* LATIN CAPITAL LETTER D WITH STROKE -> 'D' */ + { 0x11, 0x64 }, /* LATIN SMALL LETTER D WITH STROKE -> 'd' */ + { 0x12, 0x45 }, /* LATIN CAPITAL LETTER E WITH MACRON -> 'E' */ + { 0x13, 0x65 }, /* LATIN SMALL LETTER E WITH MACRON -> 'e' */ + { 0x14, 0x45 }, /* LATIN CAPITAL LETTER E WITH BREVE -> 'E' */ + { 0x15, 0x65 }, /* LATIN SMALL LETTER E WITH BREVE -> 'e' */ + { 0x16, 0x45 }, /* LATIN CAPITAL LETTER E WITH DOT ABOVE -> 'E' */ + { 0x17, 0x65 }, /* LATIN SMALL LETTER E WITH DOT ABOVE -> 'e' */ + { 0x18, 0x45 }, /* LATIN CAPITAL LETTER E WITH OGONEK -> 'E' */ + { 0x19, 0x65 }, /* LATIN SMALL LETTER E WITH OGONEK -> 'e' */ + { 0x1A, 0x45 }, /* LATIN CAPITAL LETTER E WITH CARON -> 'E' */ + { 0x1B, 0x65 }, /* LATIN SMALL LETTER E WITH CARON -> 'e' */ + { 0x1C, 0x47 }, /* LATIN CAPITAL LETTER G WITH CIRCUMFLEX -> 'G' */ + { 0x1D, 0x67 }, /* LATIN SMALL LETTER G WITH CIRCUMFLEX -> 'g' */ + { 0x1E, 0x47 }, /* LATIN CAPITAL LETTER G WITH BREVE -> 'G' */ + { 0x1F, 0x67 }, /* LATIN SMALL LETTER G WITH BREVE -> 'g' */ + { 0x20, 0x47 }, /* LATIN CAPITAL LETTER G WITH DOT ABOVE -> 'G' */ + { 0x21, 0x67 }, /* LATIN SMALL LETTER G WITH DOT ABOVE -> 'g' */ + { 0x22, 0x47 }, /* LATIN CAPITAL LETTER G WITH CEDILLA -> 'G' */ + { 0x23, 0x67 }, /* LATIN SMALL LETTER G WITH CEDILLA -> 'g' */ + { 0x24, 0x48 }, /* LATIN CAPITAL LETTER H WITH CIRCUMFLEX -> 'H' */ + { 0x25, 0x68 }, /* LATIN SMALL LETTER H WITH CIRCUMFLEX -> 'h' */ + { 0x26, 0x48 }, /* LATIN CAPITAL LETTER H WITH STROKE -> 'H' */ + { 0x27, 0x68 }, /* LATIN SMALL LETTER H WITH STROKE -> 'h' */ + { 0x28, 0x49 }, /* LATIN CAPITAL LETTER I WITH TILDE -> 'I' */ + { 0x29, 0x69 }, /* LATIN SMALL LETTER I WITH TILDE -> 'i' */ + { 0x2A, 0x49 }, /* LATIN CAPITAL LETTER I WITH MACRON -> 'I' */ + { 0x2B, 0x69 }, /* LATIN SMALL LETTER I WITH MACRON -> 'i' */ + { 0x2C, 0x49 }, /* LATIN CAPITAL LETTER I WITH BREVE -> 'I' */ + { 0x2D, 0x69 }, /* LATIN SMALL LETTER I WITH BREVE -> 'i' */ + { 0x2E, 0x49 }, /* LATIN CAPITAL LETTER I WITH OGONEK -> 'I' */ + { 0x2F, 0x69 }, /* LATIN SMALL LETTER I WITH OGONEK -> 'i' */ + { 0x30, 0x49 }, /* LATIN CAPITAL LETTER I WITH DOT ABOVE -> 'I' */ + { 0x31, 0x69 }, /* LATIN SMALL LETTER DOTLESS I -> 'i' */ + { 0x34, 0x4A }, /* LATIN CAPITAL LETTER J WITH CIRCUMFLEX -> 'J' */ + { 0x35, 0x6A }, /* LATIN SMALL LETTER J WITH CIRCUMFLEX -> 'j' */ + { 0x36, 0x4B }, /* LATIN CAPITAL LETTER K WITH CEDILLA -> 'K' */ + { 0x37, 0x6B }, /* LATIN SMALL LETTER K WITH CEDILLA -> 'k' */ + { 0x38, 0x6B }, /* LATIN SMALL LETTER KRA -> 'k' */ + { 0x39, 0x4C }, /* LATIN CAPITAL LETTER L WITH ACUTE -> 'L' */ + { 0x3A, 0x6C }, /* LATIN SMALL LETTER L WITH ACUTE -> 'l' */ + { 0x3B, 0x4C }, /* LATIN CAPITAL LETTER L WITH CEDILLA -> 'L' */ + { 0x3C, 0x6C }, /* LATIN SMALL LETTER L WITH CEDILLA -> 'l' */ + { 0x3D, 0x4C }, /* LATIN CAPITAL LETTER L WITH CARON -> 'L' */ + { 0x3E, 0x6C }, /* LATIN SMALL LETTER L WITH CARON -> 'l' */ + { 0x3F, 0x4C }, /* LATIN CAPITAL LETTER L WITH MIDDLE DOT -> 'L' */ + { 0x40, 0x6C }, /* LATIN SMALL LETTER L WITH MIDDLE DOT -> 'l' */ + { 0x41, 0x4C }, /* LATIN CAPITAL LETTER L WITH STROKE -> 'L' */ + { 0x42, 0x6C }, /* LATIN SMALL LETTER L WITH STROKE -> 'l' */ + { 0x43, 0x4E }, /* LATIN CAPITAL LETTER N WITH ACUTE -> 'N' */ + { 0x44, 0x6E }, /* LATIN SMALL LETTER N WITH ACUTE -> 'n' */ + { 0x45, 0x4E }, /* LATIN CAPITAL LETTER N WITH CEDILLA -> 'N' */ + { 0x46, 0x6E }, /* LATIN SMALL LETTER N WITH CEDILLA -> 'n' */ + { 0x47, 0x4E }, /* LATIN CAPITAL LETTER N WITH CARON -> 'N' */ + { 0x48, 0x6E }, /* LATIN SMALL LETTER N WITH CARON -> 'n' */ + { 0x4C, 0x4F }, /* LATIN CAPITAL LETTER O WITH MACRON -> 'O' */ + { 0x4D, 0x6F }, /* LATIN SMALL LETTER O WITH MACRON -> 'o' */ + { 0x4E, 0x4F }, /* LATIN CAPITAL LETTER O WITH BREVE -> 'O' */ + { 0x4F, 0x6F }, /* LATIN SMALL LETTER O WITH BREVE -> 'o' */ + { 0x50, 0x4F }, /* LATIN CAPITAL LETTER O WITH DOUBLE ACUTE -> 'O' */ + { 0x51, 0x6F }, /* LATIN SMALL LETTER O WITH DOUBLE ACUTE -> 'o' */ + { 0x52, 0x45 }, /* LATIN CAPITAL LIGATURE OE -> 'E' */ + { 0x53, 0x65 }, /* LATIN SMALL LIGATURE OE -> 'e' */ + { 0x54, 0x52 }, /* LATIN CAPITAL LETTER R WITH ACUTE -> 'R' */ + { 0x55, 0x72 }, /* LATIN SMALL LETTER R WITH ACUTE -> 'r' */ + { 0x56, 0x52 }, /* LATIN CAPITAL LETTER R WITH CEDILLA -> 'R' */ + { 0x57, 0x72 }, /* LATIN SMALL LETTER R WITH CEDILLA -> 'r' */ + { 0x58, 0x52 }, /* LATIN CAPITAL LETTER R WITH CARON -> 'R' */ + { 0x59, 0x72 }, /* LATIN SMALL LETTER R WITH CARON -> 'r' */ + { 0x5A, 0x53 }, /* LATIN CAPITAL LETTER S WITH ACUTE -> 'S' */ + { 0x5B, 0x73 }, /* LATIN SMALL LETTER S WITH ACUTE -> 's' */ + { 0x5C, 0x53 }, /* LATIN CAPITAL LETTER S WITH CIRCUMFLEX -> 'S' */ + { 0x5D, 0x73 }, /* LATIN SMALL LETTER S WITH CIRCUMFLEX -> 's' */ + { 0x5E, 0x53 }, /* LATIN CAPITAL LETTER S WITH CEDILLA -> 'S' */ + { 0x5F, 0x73 }, /* LATIN SMALL LETTER S WITH CEDILLA -> 's' */ + { 0x60, 0x53 }, /* LATIN CAPITAL LETTER S WITH CARON -> 'S' */ + { 0x61, 0x73 }, /* LATIN SMALL LETTER S WITH CARON -> 's' */ + { 0x62, 0x54 }, /* LATIN CAPITAL LETTER T WITH CEDILLA -> 'T' */ + { 0x63, 0x74 }, /* LATIN SMALL LETTER T WITH CEDILLA -> 't' */ + { 0x64, 0x54 }, /* LATIN CAPITAL LETTER T WITH CARON -> 'T' */ + { 0x65, 0x74 }, /* LATIN SMALL LETTER T WITH CARON -> 't' */ + { 0x66, 0x54 }, /* LATIN CAPITAL LETTER T WITH STROKE -> 'T' */ + { 0x67, 0x74 }, /* LATIN SMALL LETTER T WITH STROKE -> 't' */ + { 0x68, 0x55 }, /* LATIN CAPITAL LETTER U WITH TILDE -> 'U' */ + { 0x69, 0x75 }, /* LATIN SMALL LETTER U WITH TILDE -> 'u' */ + { 0x6A, 0x55 }, /* LATIN CAPITAL LETTER U WITH MACRON -> 'U' */ + { 0x6B, 0x75 }, /* LATIN SMALL LETTER U WITH MACRON -> 'u' */ + { 0x6C, 0x55 }, /* LATIN CAPITAL LETTER U WITH BREVE -> 'U' */ + { 0x6D, 0x75 }, /* LATIN SMALL LETTER U WITH BREVE -> 'u' */ + { 0x6E, 0x55 }, /* LATIN CAPITAL LETTER U WITH RING ABOVE -> 'U' */ + { 0x6F, 0x75 }, /* LATIN SMALL LETTER U WITH RING ABOVE -> 'u' */ + { 0x70, 0x55 }, /* LATIN CAPITAL LETTER U WITH DOUBLE ACUTE -> 'U' */ + { 0x71, 0x75 }, /* LATIN SMALL LETTER U WITH DOUBLE ACUTE -> 'u' */ + { 0x72, 0x55 }, /* LATIN CAPITAL LETTER U WITH OGONEK -> 'U' */ + { 0x73, 0x75 }, /* LATIN SMALL LETTER U WITH OGONEK -> 'u' */ + { 0x74, 0x57 }, /* LATIN CAPITAL LETTER W WITH CIRCUMFLEX -> 'W' */ + { 0x75, 0x77 }, /* LATIN SMALL LETTER W WITH CIRCUMFLEX -> 'w' */ + { 0x76, 0x59 }, /* LATIN CAPITAL LETTER Y WITH CIRCUMFLEX -> 'Y' */ + { 0x77, 0x79 }, /* LATIN SMALL LETTER Y WITH CIRCUMFLEX -> 'y' */ + { 0x78, 0x59 }, /* LATIN CAPITAL LETTER Y WITH DIAERESIS -> 'Y' */ + { 0x79, 0x5A }, /* LATIN CAPITAL LETTER Z WITH ACUTE -> 'Z' */ + { 0x7A, 0x7A }, /* LATIN SMALL LETTER Z WITH ACUTE -> 'z' */ + { 0x7B, 0x5A }, /* LATIN CAPITAL LETTER Z WITH DOT ABOVE -> 'Z' */ + { 0x7C, 0x7A }, /* LATIN SMALL LETTER Z WITH DOT ABOVE -> 'z' */ + { 0x7D, 0x5A }, /* LATIN CAPITAL LETTER Z WITH CARON -> 'Z' */ + { 0x7E, 0x7A }, /* LATIN SMALL LETTER Z WITH CARON -> 'z' */ + { 0x7F, 0x73 }, /* LATIN SMALL LETTER LONG S -> 's' */ + { 0x80, 0x62 }, /* LATIN SMALL LETTER B WITH STROKE -> 'b' */ + { 0x81, 0x42 }, /* LATIN CAPITAL LETTER B WITH HOOK -> 'B' */ + { 0x82, 0x42 }, /* LATIN CAPITAL LETTER B WITH TOPBAR -> 'B' */ + { 0x83, 0x62 }, /* LATIN SMALL LETTER B WITH TOPBAR -> 'b' */ + { 0x84, 0x36 }, /* LATIN CAPITAL LETTER TONE SIX -> '6' */ + { 0x85, 0x36 }, /* LATIN SMALL LETTER TONE SIX -> '6' */ + { 0x86, 0x4F }, /* LATIN CAPITAL LETTER OPEN O -> 'O' */ + { 0x87, 0x43 }, /* LATIN CAPITAL LETTER C WITH HOOK -> 'C' */ + { 0x88, 0x63 }, /* LATIN SMALL LETTER C WITH HOOK -> 'c' */ + { 0x89, 0x00 }, /* LATIN CAPITAL LETTER AFRICAN D -> ... */ + { 0x8B, 0x44 }, /* LATIN CAPITAL LETTER D WITH TOPBAR -> 'D' */ + { 0x8C, 0x64 }, /* LATIN SMALL LETTER D WITH TOPBAR -> 'd' */ + { 0x8D, 0x64 }, /* LATIN SMALL LETTER TURNED DELTA -> 'd' */ + { 0x8E, 0x33 }, /* LATIN CAPITAL LETTER REVERSED E -> '3' */ + { 0x8F, 0x40 }, /* LATIN CAPITAL LETTER SCHWA -> '@' */ + { 0x90, 0x45 }, /* LATIN CAPITAL LETTER OPEN E -> 'E' */ + { 0x91, 0x46 }, /* LATIN CAPITAL LETTER F WITH HOOK -> 'F' */ + { 0x92, 0x66 }, /* LATIN SMALL LETTER F WITH HOOK -> 'f' */ + { 0x93, 0x47 }, /* LATIN CAPITAL LETTER G WITH HOOK -> 'G' */ + { 0x94, 0x47 }, /* LATIN CAPITAL LETTER GAMMA -> 'G' */ + { 0x96, 0x49 }, /* LATIN CAPITAL LETTER IOTA -> 'I' */ + { 0x97, 0x49 }, /* LATIN CAPITAL LETTER I WITH STROKE -> 'I' */ + { 0x98, 0x4B }, /* LATIN CAPITAL LETTER K WITH HOOK -> 'K' */ + { 0x99, 0x6B }, /* LATIN SMALL LETTER K WITH HOOK -> 'k' */ + { 0x9A, 0x6C }, /* LATIN SMALL LETTER L WITH BAR -> 'l' */ + { 0x9B, 0x6C }, /* LATIN SMALL LETTER LAMBDA WITH STROKE -> 'l' */ + { 0x9C, 0x57 }, /* LATIN CAPITAL LETTER TURNED M -> 'W' */ + { 0x9D, 0x4E }, /* LATIN CAPITAL LETTER N WITH LEFT HOOK -> 'N' */ + { 0x9E, 0x6E }, /* LATIN SMALL LETTER N WITH LONG RIGHT LEG -> 'n' */ + { 0x9F, 0x4F }, /* LATIN CAPITAL LETTER O WITH MIDDLE TILDE -> 'O' */ + { 0xA0, 0x4F }, /* LATIN CAPITAL LETTER O WITH HORN -> 'O' */ + { 0xA1, 0x6F }, /* LATIN SMALL LETTER O WITH HORN -> 'o' */ + { 0xA4, 0x50 }, /* LATIN CAPITAL LETTER P WITH HOOK -> 'P' */ + { 0xA5, 0x70 }, /* LATIN SMALL LETTER P WITH HOOK -> 'p' */ + { 0xA7, 0x32 }, /* LATIN CAPITAL LETTER TONE TWO -> '2' */ + { 0xA8, 0x32 }, /* LATIN SMALL LETTER TONE TWO -> '2' */ + { 0xAB, 0x74 }, /* LATIN SMALL LETTER T WITH PALATAL HOOK -> 't' */ + { 0xAC, 0x54 }, /* LATIN CAPITAL LETTER T WITH HOOK -> 'T' */ + { 0xAD, 0x74 }, /* LATIN SMALL LETTER T WITH HOOK -> 't' */ + { 0xAE, 0x54 }, /* LATIN CAPITAL LETTER T WITH RETROFLEX HOOK -> 'T' */ + { 0xAF, 0x55 }, /* LATIN CAPITAL LETTER U WITH HORN -> 'U' */ + { 0xB0, 0x75 }, /* LATIN SMALL LETTER U WITH HORN -> 'u' */ + { 0xB1, 0x59 }, /* LATIN CAPITAL LETTER UPSILON -> 'Y' */ + { 0xB2, 0x56 }, /* LATIN CAPITAL LETTER V WITH HOOK -> 'V' */ + { 0xB3, 0x59 }, /* LATIN CAPITAL LETTER Y WITH HOOK -> 'Y' */ + { 0xB4, 0x79 }, /* LATIN SMALL LETTER Y WITH HOOK -> 'y' */ + { 0xB5, 0x5A }, /* LATIN CAPITAL LETTER Z WITH STROKE -> 'Z' */ + { 0xB6, 0x7A }, /* LATIN SMALL LETTER Z WITH STROKE -> 'z' */ + { 0xBB, 0x32 }, /* LATIN LETTER TWO WITH STROKE -> '2' */ + { 0xBC, 0x35 }, /* LATIN CAPITAL LETTER TONE FIVE -> '5' */ + { 0xBD, 0x35 }, /* LATIN SMALL LETTER TONE FIVE -> '5' */ + { 0xBF, 0x77 }, /* LATIN LETTER WYNN -> 'w' */ + { 0xC0, 0x7C }, /* LATIN LETTER DENTAL CLICK -> '|' */ + { 0xC3, 0x21 }, /* LATIN LETTER RETROFLEX CLICK -> '!' */ + { 0xCD, 0x41 }, /* LATIN CAPITAL LETTER A WITH CARON -> 'A' */ + { 0xCE, 0x61 }, /* LATIN SMALL LETTER A WITH CARON -> 'a' */ + { 0xCF, 0x49 }, /* LATIN CAPITAL LETTER I WITH CARON -> 'I' */ + { 0xD0, 0x69 }, /* LATIN SMALL LETTER I WITH CARON -> 'i' */ + { 0xD1, 0x4F }, /* LATIN CAPITAL LETTER O WITH CARON -> 'O' */ + { 0xD2, 0x6F }, /* LATIN SMALL LETTER O WITH CARON -> 'o' */ + { 0xD3, 0x55 }, /* LATIN CAPITAL LETTER U WITH CARON -> 'U' */ + { 0xD4, 0x75 }, /* LATIN SMALL LETTER U WITH CARON -> 'u' */ + { 0xD5, 0x55 }, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON -> 'U' */ + { 0xD6, 0x75 }, /* LATIN SMALL LETTER U WITH DIAERESIS AND MACRON -> 'u' */ + { 0xD7, 0x55 }, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE -> 'U' */ + { 0xD8, 0x75 }, /* LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE -> 'u' */ + { 0xD9, 0x55 }, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON -> 'U' */ + { 0xDA, 0x75 }, /* LATIN SMALL LETTER U WITH DIAERESIS AND CARON -> 'u' */ + { 0xDB, 0x55 }, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE -> 'U' */ + { 0xDC, 0x75 }, /* LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE -> 'u' */ + { 0xDD, 0x40 }, /* LATIN SMALL LETTER TURNED E -> '@' */ + { 0xDE, 0x41 }, /* LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON -> 'A' */ + { 0xDF, 0x61 }, /* LATIN SMALL LETTER A WITH DIAERESIS AND MACRON -> 'a' */ + { 0xE0, 0x41 }, /* LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON -> 'A' */ + { 0xE1, 0x61 }, /* LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON -> 'a' */ + { 0xE4, 0x47 }, /* LATIN CAPITAL LETTER G WITH STROKE -> 'G' */ + { 0xE5, 0x67 }, /* LATIN SMALL LETTER G WITH STROKE -> 'g' */ + { 0xE6, 0x47 }, /* LATIN CAPITAL LETTER G WITH CARON -> 'G' */ + { 0xE7, 0x67 }, /* LATIN SMALL LETTER G WITH CARON -> 'g' */ + { 0xE8, 0x4B }, /* LATIN CAPITAL LETTER K WITH CARON -> 'K' */ + { 0xE9, 0x6B }, /* LATIN SMALL LETTER K WITH CARON -> 'k' */ + { 0xEA, 0x4F }, /* LATIN CAPITAL LETTER O WITH OGONEK -> 'O' */ + { 0xEB, 0x6F }, /* LATIN SMALL LETTER O WITH OGONEK -> 'o' */ + { 0xEC, 0x4F }, /* LATIN CAPITAL LETTER O WITH OGONEK AND MACRON -> 'O' */ + { 0xED, 0x6F }, /* LATIN SMALL LETTER O WITH OGONEK AND MACRON -> 'o' */ + { 0xF0, 0x6A }, /* LATIN SMALL LETTER J WITH CARON -> 'j' */ + { 0xF4, 0x47 }, /* LATIN CAPITAL LETTER G WITH ACUTE -> 'G' */ + { 0xF5, 0x67 }, /* LATIN SMALL LETTER G WITH ACUTE -> 'g' */ + { 0xF7, 0x57 }, /* LATIN CAPITAL LETTER WYNN -> 'W' */ + { 0xF8, 0x4E }, /* LATIN CAPITAL LETTER N WITH GRAVE -> 'N' */ + { 0xF9, 0x6E }, /* LATIN SMALL LETTER N WITH GRAVE -> 'n' */ + { 0xFA, 0x41 }, /* LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE -> 'A' */ + { 0xFB, 0x61 }, /* LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE -> 'a' */ + { 0xFE, 0x4F }, /* LATIN CAPITAL LETTER O WITH STROKE AND ACUTE -> 'O' */ + { 0xFF, 0x6F }, /* LATIN SMALL LETTER O WITH STROKE AND ACUTE -> 'o' */ + /* Entries for page 0x02 */ + { 0x00, 0x41 }, /* LATIN CAPITAL LETTER A WITH DOUBLE GRAVE -> 'A' */ + { 0x01, 0x61 }, /* LATIN SMALL LETTER A WITH DOUBLE GRAVE -> 'a' */ + { 0x02, 0x41 }, /* LATIN CAPITAL LETTER A WITH INVERTED BREVE -> 'A' */ + { 0x03, 0x61 }, /* LATIN SMALL LETTER A WITH INVERTED BREVE -> 'a' */ + { 0x04, 0x45 }, /* LATIN CAPITAL LETTER E WITH DOUBLE GRAVE -> 'E' */ + { 0x05, 0x65 }, /* LATIN SMALL LETTER E WITH DOUBLE GRAVE -> 'e' */ + { 0x06, 0x45 }, /* LATIN CAPITAL LETTER E WITH INVERTED BREVE -> 'E' */ + { 0x07, 0x65 }, /* LATIN SMALL LETTER E WITH INVERTED BREVE -> 'e' */ + { 0x08, 0x49 }, /* LATIN CAPITAL LETTER I WITH DOUBLE GRAVE -> 'I' */ + { 0x09, 0x69 }, /* LATIN SMALL LETTER I WITH DOUBLE GRAVE -> 'i' */ + { 0x0A, 0x49 }, /* LATIN CAPITAL LETTER I WITH INVERTED BREVE -> 'I' */ + { 0x0B, 0x69 }, /* LATIN SMALL LETTER I WITH INVERTED BREVE -> 'i' */ + { 0x0C, 0x4F }, /* LATIN CAPITAL LETTER O WITH DOUBLE GRAVE -> 'O' */ + { 0x0D, 0x6F }, /* LATIN SMALL LETTER O WITH DOUBLE GRAVE -> 'o' */ + { 0x0E, 0x4F }, /* LATIN CAPITAL LETTER O WITH INVERTED BREVE -> 'O' */ + { 0x0F, 0x6F }, /* LATIN SMALL LETTER O WITH INVERTED BREVE -> 'o' */ + { 0x10, 0x52 }, /* LATIN CAPITAL LETTER R WITH DOUBLE GRAVE -> 'R' */ + { 0x11, 0x72 }, /* LATIN SMALL LETTER R WITH DOUBLE GRAVE -> 'r' */ + { 0x12, 0x52 }, /* LATIN CAPITAL LETTER R WITH INVERTED BREVE -> 'R' */ + { 0x13, 0x72 }, /* LATIN SMALL LETTER R WITH INVERTED BREVE -> 'r' */ + { 0x14, 0x55 }, /* LATIN CAPITAL LETTER U WITH DOUBLE GRAVE -> 'U' */ + { 0x15, 0x75 }, /* LATIN SMALL LETTER U WITH DOUBLE GRAVE -> 'u' */ + { 0x16, 0x55 }, /* LATIN CAPITAL LETTER U WITH INVERTED BREVE -> 'U' */ + { 0x17, 0x75 }, /* LATIN SMALL LETTER U WITH INVERTED BREVE -> 'u' */ + { 0x18, 0x53 }, /* LATIN CAPITAL LETTER S WITH COMMA BELOW -> 'S' */ + { 0x19, 0x73 }, /* LATIN SMALL LETTER S WITH COMMA BELOW -> 's' */ + { 0x1A, 0x54 }, /* LATIN CAPITAL LETTER T WITH COMMA BELOW -> 'T' */ + { 0x1B, 0x74 }, /* LATIN SMALL LETTER T WITH COMMA BELOW -> 't' */ + { 0x1C, 0x59 }, /* LATIN CAPITAL LETTER YOGH -> 'Y' */ + { 0x1D, 0x79 }, /* LATIN SMALL LETTER YOGH -> 'y' */ + { 0x1E, 0x48 }, /* LATIN CAPITAL LETTER H WITH CARON -> 'H' */ + { 0x1F, 0x68 }, /* LATIN SMALL LETTER H WITH CARON -> 'h' */ + { 0x20, 0x4E }, /* LATIN CAPITAL LETTER N WITH LONG RIGHT LEG -> 'N' */ + { 0x21, 0x64 }, /* LATIN SMALL LETTER D WITH CURL -> 'd' */ + { 0x24, 0x5A }, /* LATIN CAPITAL LETTER Z WITH HOOK -> 'Z' */ + { 0x25, 0x7A }, /* LATIN SMALL LETTER Z WITH HOOK -> 'z' */ + { 0x26, 0x41 }, /* LATIN CAPITAL LETTER A WITH DOT ABOVE -> 'A' */ + { 0x27, 0x61 }, /* LATIN SMALL LETTER A WITH DOT ABOVE -> 'a' */ + { 0x28, 0x45 }, /* LATIN CAPITAL LETTER E WITH CEDILLA -> 'E' */ + { 0x29, 0x65 }, /* LATIN SMALL LETTER E WITH CEDILLA -> 'e' */ + { 0x2A, 0x4F }, /* LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON -> 'O' */ + { 0x2B, 0x6F }, /* LATIN SMALL LETTER O WITH DIAERESIS AND MACRON -> 'o' */ + { 0x2C, 0x4F }, /* LATIN CAPITAL LETTER O WITH TILDE AND MACRON -> 'O' */ + { 0x2D, 0x6F }, /* LATIN SMALL LETTER O WITH TILDE AND MACRON -> 'o' */ + { 0x2E, 0x4F }, /* LATIN CAPITAL LETTER O WITH DOT ABOVE -> 'O' */ + { 0x2F, 0x6F }, /* LATIN SMALL LETTER O WITH DOT ABOVE -> 'o' */ + { 0x30, 0x4F }, /* LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON -> 'O' */ + { 0x31, 0x6F }, /* LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON -> 'o' */ + { 0x32, 0x59 }, /* LATIN CAPITAL LETTER Y WITH MACRON -> 'Y' */ + { 0x33, 0x79 }, /* LATIN SMALL LETTER Y WITH MACRON -> 'y' */ + { 0x34, 0x6C }, /* LATIN SMALL LETTER L WITH CURL -> 'l' */ + { 0x35, 0x6E }, /* LATIN SMALL LETTER N WITH CURL -> 'n' */ + { 0x36, 0x74 }, /* LATIN SMALL LETTER T WITH CURL -> 't' */ + { 0x37, 0x6A }, /* LATIN SMALL LETTER DOTLESS J -> 'j' */ + { 0x3A, 0x41 }, /* LATIN CAPITAL LETTER A WITH STROKE -> 'A' */ + { 0x3B, 0x43 }, /* LATIN CAPITAL LETTER C WITH STROKE -> 'C' */ + { 0x3C, 0x63 }, /* LATIN SMALL LETTER C WITH STROKE -> 'c' */ + { 0x3D, 0x4C }, /* LATIN CAPITAL LETTER L WITH BAR -> 'L' */ + { 0x3E, 0x54 }, /* LATIN CAPITAL LETTER T WITH DIAGONAL STROKE -> 'T' */ + { 0x3F, 0x73 }, /* LATIN SMALL LETTER S WITH SWASH TAIL -> 's' */ + { 0x40, 0x7A }, /* LATIN SMALL LETTER Z WITH SWASH TAIL -> 'z' */ + { 0x43, 0x42 }, /* LATIN CAPITAL LETTER B WITH STROKE -> 'B' */ + { 0x44, 0x55 }, /* LATIN CAPITAL LETTER U BAR -> 'U' */ + { 0x45, 0x5E }, /* LATIN CAPITAL LETTER TURNED V -> '^' */ + { 0x46, 0x45 }, /* LATIN CAPITAL LETTER E WITH STROKE -> 'E' */ + { 0x47, 0x65 }, /* LATIN SMALL LETTER E WITH STROKE -> 'e' */ + { 0x48, 0x4A }, /* LATIN CAPITAL LETTER J WITH STROKE -> 'J' */ + { 0x49, 0x6A }, /* LATIN SMALL LETTER J WITH STROKE -> 'j' */ + { 0x4A, 0x71 }, /* LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL -> 'q' */ + { 0x4B, 0x71 }, /* LATIN SMALL LETTER Q WITH HOOK TAIL -> 'q' */ + { 0x4C, 0x52 }, /* LATIN CAPITAL LETTER R WITH STROKE -> 'R' */ + { 0x4D, 0x72 }, /* LATIN SMALL LETTER R WITH STROKE -> 'r' */ + { 0x4E, 0x59 }, /* LATIN CAPITAL LETTER Y WITH STROKE -> 'Y' */ + { 0x4F, 0x79 }, /* LATIN SMALL LETTER Y WITH STROKE -> 'y' */ + { 0x50, 0x00 }, /* LATIN SMALL LETTER TURNED A -> ... */ + { 0x52, 0x61 }, /* LATIN SMALL LETTER TURNED ALPHA -> 'a' */ + { 0x53, 0x62 }, /* LATIN SMALL LETTER B WITH HOOK -> 'b' */ + { 0x54, 0x6F }, /* LATIN SMALL LETTER OPEN O -> 'o' */ + { 0x55, 0x63 }, /* LATIN SMALL LETTER C WITH CURL -> 'c' */ + { 0x56, 0x64 }, /* LATIN SMALL LETTER D WITH TAIL -> 'd' */ + { 0x57, 0x64 }, /* LATIN SMALL LETTER D WITH HOOK -> 'd' */ + { 0x58, 0x65 }, /* LATIN SMALL LETTER REVERSED E -> 'e' */ + { 0x59, 0x40 }, /* LATIN SMALL LETTER SCHWA -> '@' */ + { 0x5A, 0x40 }, /* LATIN SMALL LETTER SCHWA WITH HOOK -> '@' */ + { 0x5B, 0x00 }, /* LATIN SMALL LETTER OPEN E -> ... */ + { 0x5E, 0x65 }, /* LATIN SMALL LETTER CLOSED REVERSED OPEN E -> 'e' */ + { 0x5F, 0x6A }, /* LATIN SMALL LETTER DOTLESS J WITH STROKE -> 'j' */ + { 0x60, 0x00 }, /* LATIN SMALL LETTER G WITH HOOK -> ... */ + { 0x63, 0x67 }, /* LATIN SMALL LETTER GAMMA -> 'g' */ + { 0x64, 0x75 }, /* LATIN SMALL LETTER RAMS HORN -> 'u' */ + { 0x65, 0x59 }, /* LATIN SMALL LETTER TURNED H -> 'Y' */ + { 0x66, 0x68 }, /* LATIN SMALL LETTER H WITH HOOK -> 'h' */ + { 0x67, 0x68 }, /* LATIN SMALL LETTER HENG WITH HOOK -> 'h' */ + { 0x68, 0x69 }, /* LATIN SMALL LETTER I WITH STROKE -> 'i' */ + { 0x69, 0x69 }, /* LATIN SMALL LETTER IOTA -> 'i' */ + { 0x6A, 0x49 }, /* LATIN LETTER SMALL CAPITAL I -> 'I' */ + { 0x6B, 0x00 }, /* LATIN SMALL LETTER L WITH MIDDLE TILDE -> ... */ + { 0x6D, 0x6C }, /* LATIN SMALL LETTER L WITH RETROFLEX HOOK -> 'l' */ + { 0x6F, 0x57 }, /* LATIN SMALL LETTER TURNED M -> 'W' */ + { 0x70, 0x57 }, /* LATIN SMALL LETTER TURNED M WITH LONG LEG -> 'W' */ + { 0x71, 0x6D }, /* LATIN SMALL LETTER M WITH HOOK -> 'm' */ + { 0x72, 0x00 }, /* LATIN SMALL LETTER N WITH LEFT HOOK -> ... */ + { 0x74, 0x6E }, /* LATIN LETTER SMALL CAPITAL N -> 'n' */ + { 0x75, 0x6F }, /* LATIN SMALL LETTER BARRED O -> 'o' */ + { 0x77, 0x4F }, /* LATIN SMALL LETTER CLOSED OMEGA -> 'O' */ + { 0x78, 0x46 }, /* LATIN SMALL LETTER PHI -> 'F' */ + { 0x79, 0x00 }, /* LATIN SMALL LETTER TURNED R -> ... */ + { 0x7F, 0x72 }, /* LATIN SMALL LETTER REVERSED R WITH FISHHOOK -> 'r' */ + { 0x80, 0x52 }, /* LATIN LETTER SMALL CAPITAL R -> 'R' */ + { 0x81, 0x52 }, /* LATIN LETTER SMALL CAPITAL INVERTED R -> 'R' */ + { 0x82, 0x73 }, /* LATIN SMALL LETTER S WITH HOOK -> 's' */ + { 0x83, 0x53 }, /* LATIN SMALL LETTER ESH -> 'S' */ + { 0x84, 0x6A }, /* LATIN SMALL LETTER DOTLESS J WITH STROKE AND HOOK -> 'j' */ + { 0x85, 0x53 }, /* LATIN SMALL LETTER SQUAT REVERSED ESH -> 'S' */ + { 0x86, 0x53 }, /* LATIN SMALL LETTER ESH WITH CURL -> 'S' */ + { 0x87, 0x74 }, /* LATIN SMALL LETTER TURNED T -> 't' */ + { 0x88, 0x74 }, /* LATIN SMALL LETTER T WITH RETROFLEX HOOK -> 't' */ + { 0x89, 0x75 }, /* LATIN SMALL LETTER U BAR -> 'u' */ + { 0x8A, 0x55 }, /* LATIN SMALL LETTER UPSILON -> 'U' */ + { 0x8B, 0x76 }, /* LATIN SMALL LETTER V WITH HOOK -> 'v' */ + { 0x8C, 0x5E }, /* LATIN SMALL LETTER TURNED V -> '^' */ + { 0x8D, 0x77 }, /* LATIN SMALL LETTER TURNED W -> 'w' */ + { 0x8E, 0x79 }, /* LATIN SMALL LETTER TURNED Y -> 'y' */ + { 0x8F, 0x59 }, /* LATIN LETTER SMALL CAPITAL Y -> 'Y' */ + { 0x90, 0x7A }, /* LATIN SMALL LETTER Z WITH RETROFLEX HOOK -> 'z' */ + { 0x91, 0x7A }, /* LATIN SMALL LETTER Z WITH CURL -> 'z' */ + { 0x92, 0x5A }, /* LATIN SMALL LETTER EZH -> 'Z' */ + { 0x93, 0x5A }, /* LATIN SMALL LETTER EZH WITH CURL -> 'Z' */ + { 0x94, 0x00 }, /* LATIN LETTER GLOTTAL STOP -> ... */ + { 0x96, 0x3F }, /* LATIN LETTER INVERTED GLOTTAL STOP -> '?' */ + { 0x97, 0x43 }, /* LATIN LETTER STRETCHED C -> 'C' */ + { 0x98, 0x40 }, /* LATIN LETTER BILABIAL CLICK -> '@' */ + { 0x99, 0x42 }, /* LATIN LETTER SMALL CAPITAL B -> 'B' */ + { 0x9A, 0x45 }, /* LATIN SMALL LETTER CLOSED OPEN E -> 'E' */ + { 0x9B, 0x47 }, /* LATIN LETTER SMALL CAPITAL G WITH HOOK -> 'G' */ + { 0x9C, 0x48 }, /* LATIN LETTER SMALL CAPITAL H -> 'H' */ + { 0x9D, 0x6A }, /* LATIN SMALL LETTER J WITH CROSSED-TAIL -> 'j' */ + { 0x9E, 0x6B }, /* LATIN SMALL LETTER TURNED K -> 'k' */ + { 0x9F, 0x4C }, /* LATIN LETTER SMALL CAPITAL L -> 'L' */ + { 0xA0, 0x71 }, /* LATIN SMALL LETTER Q WITH HOOK -> 'q' */ + { 0xA1, 0x3F }, /* LATIN LETTER GLOTTAL STOP WITH STROKE -> '?' */ + { 0xA2, 0x3F }, /* LATIN LETTER REVERSED GLOTTAL STOP WITH STROKE -> '?' */ + { 0xAE, 0x00 }, /* LATIN SMALL LETTER TURNED H WITH FISHHOOK -> ... */ + { 0xB1, 0x68 }, /* MODIFIER LETTER SMALL H WITH HOOK -> 'h' */ + { 0xB2, 0x6A }, /* MODIFIER LETTER SMALL J -> 'j' */ + { 0xB3, 0x00 }, /* MODIFIER LETTER SMALL R -> ... */ + { 0xB6, 0x72 }, /* MODIFIER LETTER SMALL CAPITAL INVERTED R -> 'r' */ + { 0xB7, 0x77 }, /* MODIFIER LETTER SMALL W -> 'w' */ + { 0xB8, 0x79 }, /* MODIFIER LETTER SMALL Y -> 'y' */ + { 0xB9, 0x27 }, /* MODIFIER LETTER PRIME -> ''' */ + { 0xBA, 0x22 }, /* MODIFIER LETTER DOUBLE PRIME -> '"' */ + { 0xBB, 0x60 }, /* MODIFIER LETTER TURNED COMMA -> '`' */ + { 0xBC, 0x27 }, /* MODIFIER LETTER APOSTROPHE -> ''' */ + { 0xBD, 0x60 }, /* MODIFIER LETTER REVERSED COMMA -> '`' */ + { 0xBE, 0x60 }, /* MODIFIER LETTER RIGHT HALF RING -> '`' */ + { 0xBF, 0x27 }, /* MODIFIER LETTER LEFT HALF RING -> ''' */ + { 0xC0, 0x3F }, /* MODIFIER LETTER GLOTTAL STOP -> '?' */ + { 0xC1, 0x3F }, /* MODIFIER LETTER REVERSED GLOTTAL STOP -> '?' */ + { 0xC2, 0x3C }, /* MODIFIER LETTER LEFT ARROWHEAD -> '<' */ + { 0xC3, 0x3E }, /* MODIFIER LETTER RIGHT ARROWHEAD -> '>' */ + { 0xC4, 0x5E }, /* MODIFIER LETTER UP ARROWHEAD -> '^' */ + { 0xC5, 0x56 }, /* MODIFIER LETTER DOWN ARROWHEAD -> 'V' */ + { 0xC6, 0x5E }, /* MODIFIER LETTER CIRCUMFLEX ACCENT -> '^' */ + { 0xC7, 0x56 }, /* CARON -> 'V' */ + { 0xC8, 0x27 }, /* MODIFIER LETTER VERTICAL LINE -> ''' */ + { 0xC9, 0x2D }, /* MODIFIER LETTER MACRON -> '-' */ + { 0xCA, 0x2F }, /* MODIFIER LETTER ACUTE ACCENT -> '/' */ + { 0xCB, 0x5C }, /* MODIFIER LETTER GRAVE ACCENT -> '\' */ + { 0xCC, 0x2C }, /* MODIFIER LETTER LOW VERTICAL LINE -> ',' */ + { 0xCD, 0x5F }, /* MODIFIER LETTER LOW MACRON -> '_' */ + { 0xCE, 0x5C }, /* MODIFIER LETTER LOW GRAVE ACCENT -> '\' */ + { 0xCF, 0x2F }, /* MODIFIER LETTER LOW ACUTE ACCENT -> '/' */ + { 0xD0, 0x3A }, /* MODIFIER LETTER TRIANGULAR COLON -> ':' */ + { 0xD1, 0x2E }, /* MODIFIER LETTER HALF TRIANGULAR COLON -> '.' */ + { 0xD2, 0x60 }, /* MODIFIER LETTER CENTRED RIGHT HALF RING -> '`' */ + { 0xD3, 0x27 }, /* MODIFIER LETTER CENTRED LEFT HALF RING -> ''' */ + { 0xD4, 0x5E }, /* MODIFIER LETTER UP TACK -> '^' */ + { 0xD5, 0x56 }, /* MODIFIER LETTER DOWN TACK -> 'V' */ + { 0xD6, 0x2B }, /* MODIFIER LETTER PLUS SIGN -> '+' */ + { 0xD7, 0x2D }, /* MODIFIER LETTER MINUS SIGN -> '-' */ + { 0xD8, 0x56 }, /* BREVE -> 'V' */ + { 0xD9, 0x2E }, /* DOT ABOVE -> '.' */ + { 0xDA, 0x40 }, /* RING ABOVE -> '@' */ + { 0xDB, 0x2C }, /* OGONEK -> ',' */ + { 0xDC, 0x7E }, /* SMALL TILDE -> '~' */ + { 0xDD, 0x22 }, /* DOUBLE ACUTE ACCENT -> '"' */ + { 0xDE, 0x52 }, /* MODIFIER LETTER RHOTIC HOOK -> 'R' */ + { 0xDF, 0x58 }, /* MODIFIER LETTER CROSS ACCENT -> 'X' */ + { 0xE0, 0x47 }, /* MODIFIER LETTER SMALL GAMMA -> 'G' */ + { 0xE1, 0x6C }, /* MODIFIER LETTER SMALL L -> 'l' */ + { 0xE2, 0x73 }, /* MODIFIER LETTER SMALL S -> 's' */ + { 0xE3, 0x78 }, /* MODIFIER LETTER SMALL X -> 'x' */ + { 0xE4, 0x3F }, /* MODIFIER LETTER SMALL REVERSED GLOTTAL STOP -> '?' */ + { 0xEC, 0x56 }, /* MODIFIER LETTER VOICING -> 'V' */ + { 0xED, 0x3D }, /* MODIFIER LETTER UNASPIRATED -> '=' */ + { 0xEE, 0x22 }, /* MODIFIER LETTER DOUBLE APOSTROPHE -> '"' */ + /* Entries for page 0x03 */ + { 0x63, 0x61 }, /* COMBINING LATIN SMALL LETTER A -> 'a' */ + { 0x64, 0x65 }, /* COMBINING LATIN SMALL LETTER E -> 'e' */ + { 0x65, 0x69 }, /* COMBINING LATIN SMALL LETTER I -> 'i' */ + { 0x66, 0x6F }, /* COMBINING LATIN SMALL LETTER O -> 'o' */ + { 0x67, 0x75 }, /* COMBINING LATIN SMALL LETTER U -> 'u' */ + { 0x68, 0x63 }, /* COMBINING LATIN SMALL LETTER C -> 'c' */ + { 0x69, 0x64 }, /* COMBINING LATIN SMALL LETTER D -> 'd' */ + { 0x6A, 0x68 }, /* COMBINING LATIN SMALL LETTER H -> 'h' */ + { 0x6B, 0x6D }, /* COMBINING LATIN SMALL LETTER M -> 'm' */ + { 0x6C, 0x72 }, /* COMBINING LATIN SMALL LETTER R -> 'r' */ + { 0x6D, 0x74 }, /* COMBINING LATIN SMALL LETTER T -> 't' */ + { 0x6E, 0x76 }, /* COMBINING LATIN SMALL LETTER V -> 'v' */ + { 0x6F, 0x78 }, /* COMBINING LATIN SMALL LETTER X -> 'x' */ + { 0x74, 0x27 }, /* GREEK NUMERAL SIGN -> ''' */ + { 0x75, 0x2C }, /* GREEK LOWER NUMERAL SIGN -> ',' */ + { 0x7E, 0x3F }, /* GREEK QUESTION MARK -> '?' */ + { 0x86, 0x41 }, /* GREEK CAPITAL LETTER ALPHA WITH TONOS -> 'A' */ + { 0x87, 0x3B }, /* GREEK ANO TELEIA -> ';' */ + { 0x88, 0x45 }, /* GREEK CAPITAL LETTER EPSILON WITH TONOS -> 'E' */ + { 0x89, 0x45 }, /* GREEK CAPITAL LETTER ETA WITH TONOS -> 'E' */ + { 0x8A, 0x49 }, /* GREEK CAPITAL LETTER IOTA WITH TONOS -> 'I' */ + { 0x8C, 0x4F }, /* GREEK CAPITAL LETTER OMICRON WITH TONOS -> 'O' */ + { 0x8E, 0x55 }, /* GREEK CAPITAL LETTER UPSILON WITH TONOS -> 'U' */ + { 0x8F, 0x4F }, /* GREEK CAPITAL LETTER OMEGA WITH TONOS -> 'O' */ + { 0x90, 0x49 }, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS -> 'I' */ + { 0x91, 0x41 }, /* GREEK CAPITAL LETTER ALPHA -> 'A' */ + { 0x92, 0x42 }, /* GREEK CAPITAL LETTER BETA -> 'B' */ + { 0x93, 0x47 }, /* GREEK CAPITAL LETTER GAMMA -> 'G' */ + { 0x94, 0x44 }, /* GREEK CAPITAL LETTER DELTA -> 'D' */ + { 0x95, 0x45 }, /* GREEK CAPITAL LETTER EPSILON -> 'E' */ + { 0x96, 0x5A }, /* GREEK CAPITAL LETTER ZETA -> 'Z' */ + { 0x97, 0x45 }, /* GREEK CAPITAL LETTER ETA -> 'E' */ + { 0x99, 0x49 }, /* GREEK CAPITAL LETTER IOTA -> 'I' */ + { 0x9A, 0x4B }, /* GREEK CAPITAL LETTER KAPPA -> 'K' */ + { 0x9B, 0x4C }, /* GREEK CAPITAL LETTER LAMDA -> 'L' */ + { 0x9C, 0x4D }, /* GREEK CAPITAL LETTER MU -> 'M' */ + { 0x9D, 0x4E }, /* GREEK CAPITAL LETTER NU -> 'N' */ + { 0x9F, 0x4F }, /* GREEK CAPITAL LETTER OMICRON -> 'O' */ + { 0xA0, 0x50 }, /* GREEK CAPITAL LETTER PI -> 'P' */ + { 0xA1, 0x52 }, /* GREEK CAPITAL LETTER RHO -> 'R' */ + { 0xA3, 0x53 }, /* GREEK CAPITAL LETTER SIGMA -> 'S' */ + { 0xA4, 0x54 }, /* GREEK CAPITAL LETTER TAU -> 'T' */ + { 0xA5, 0x55 }, /* GREEK CAPITAL LETTER UPSILON -> 'U' */ + { 0xA9, 0x4F }, /* GREEK CAPITAL LETTER OMEGA -> 'O' */ + { 0xAA, 0x49 }, /* GREEK CAPITAL LETTER IOTA WITH DIALYTIKA -> 'I' */ + { 0xAB, 0x55 }, /* GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA -> 'U' */ + { 0xAC, 0x61 }, /* GREEK SMALL LETTER ALPHA WITH TONOS -> 'a' */ + { 0xAD, 0x65 }, /* GREEK SMALL LETTER EPSILON WITH TONOS -> 'e' */ + { 0xAE, 0x65 }, /* GREEK SMALL LETTER ETA WITH TONOS -> 'e' */ + { 0xAF, 0x69 }, /* GREEK SMALL LETTER IOTA WITH TONOS -> 'i' */ + { 0xB0, 0x75 }, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS -> 'u' */ + { 0xB1, 0x61 }, /* GREEK SMALL LETTER ALPHA -> 'a' */ + { 0xB2, 0x62 }, /* GREEK SMALL LETTER BETA -> 'b' */ + { 0xB3, 0x67 }, /* GREEK SMALL LETTER GAMMA -> 'g' */ + { 0xB4, 0x64 }, /* GREEK SMALL LETTER DELTA -> 'd' */ + { 0xB5, 0x65 }, /* GREEK SMALL LETTER EPSILON -> 'e' */ + { 0xB6, 0x7A }, /* GREEK SMALL LETTER ZETA -> 'z' */ + { 0xB7, 0x65 }, /* GREEK SMALL LETTER ETA -> 'e' */ + { 0xB9, 0x69 }, /* GREEK SMALL LETTER IOTA -> 'i' */ + { 0xBA, 0x6B }, /* GREEK SMALL LETTER KAPPA -> 'k' */ + { 0xBB, 0x6C }, /* GREEK SMALL LETTER LAMDA -> 'l' */ + { 0xBC, 0x6D }, /* GREEK SMALL LETTER MU -> 'm' */ + { 0xBD, 0x6E }, /* GREEK SMALL LETTER NU -> 'n' */ + { 0xBE, 0x78 }, /* GREEK SMALL LETTER XI -> 'x' */ + { 0xBF, 0x6F }, /* GREEK SMALL LETTER OMICRON -> 'o' */ + { 0xC0, 0x70 }, /* GREEK SMALL LETTER PI -> 'p' */ + { 0xC1, 0x72 }, /* GREEK SMALL LETTER RHO -> 'r' */ + { 0xC2, 0x73 }, /* GREEK SMALL LETTER FINAL SIGMA -> 's' */ + { 0xC3, 0x73 }, /* GREEK SMALL LETTER SIGMA -> 's' */ + { 0xC4, 0x74 }, /* GREEK SMALL LETTER TAU -> 't' */ + { 0xC5, 0x75 }, /* GREEK SMALL LETTER UPSILON -> 'u' */ + { 0xC9, 0x6F }, /* GREEK SMALL LETTER OMEGA -> 'o' */ + { 0xCA, 0x69 }, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA -> 'i' */ + { 0xCB, 0x75 }, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA -> 'u' */ + { 0xCC, 0x6F }, /* GREEK SMALL LETTER OMICRON WITH TONOS -> 'o' */ + { 0xCD, 0x75 }, /* GREEK SMALL LETTER UPSILON WITH TONOS -> 'u' */ + { 0xCE, 0x6F }, /* GREEK SMALL LETTER OMEGA WITH TONOS -> 'o' */ + { 0xD0, 0x62 }, /* GREEK BETA SYMBOL -> 'b' */ + { 0xD2, 0x00 }, /* GREEK UPSILON WITH HOOK SYMBOL -> ... */ + { 0xD4, 0x55 }, /* GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL -> 'U' */ + { 0xD6, 0x70 }, /* GREEK PI SYMBOL -> 'p' */ + { 0xD7, 0x26 }, /* GREEK KAI SYMBOL -> '&' */ + { 0xDC, 0x57 }, /* GREEK LETTER DIGAMMA -> 'W' */ + { 0xDD, 0x77 }, /* GREEK SMALL LETTER DIGAMMA -> 'w' */ + { 0xDE, 0x51 }, /* GREEK LETTER KOPPA -> 'Q' */ + { 0xDF, 0x71 }, /* GREEK SMALL LETTER KOPPA -> 'q' */ + { 0xE4, 0x46 }, /* COPTIC CAPITAL LETTER FEI -> 'F' */ + { 0xE5, 0x66 }, /* COPTIC SMALL LETTER FEI -> 'f' */ + { 0xE8, 0x48 }, /* COPTIC CAPITAL LETTER HORI -> 'H' */ + { 0xE9, 0x68 }, /* COPTIC SMALL LETTER HORI -> 'h' */ + { 0xEA, 0x47 }, /* COPTIC CAPITAL LETTER GANGIA -> 'G' */ + { 0xEB, 0x67 }, /* COPTIC SMALL LETTER GANGIA -> 'g' */ + { 0xF0, 0x6B }, /* GREEK KAPPA SYMBOL -> 'k' */ + { 0xF1, 0x72 }, /* GREEK RHO SYMBOL -> 'r' */ + { 0xF2, 0x63 }, /* GREEK LUNATE SIGMA SYMBOL -> 'c' */ + { 0xF3, 0x6A }, /* GREEK LETTER YOT -> 'j' */ + /* Entries for page 0x04 */ + { 0x06, 0x49 }, /* CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I -> 'I' */ + { 0x08, 0x4A }, /* CYRILLIC CAPITAL LETTER JE -> 'J' */ + { 0x0D, 0x49 }, /* CYRILLIC CAPITAL LETTER I WITH GRAVE -> 'I' */ + { 0x0E, 0x55 }, /* CYRILLIC CAPITAL LETTER SHORT U -> 'U' */ + { 0x10, 0x41 }, /* CYRILLIC CAPITAL LETTER A -> 'A' */ + { 0x11, 0x42 }, /* CYRILLIC CAPITAL LETTER BE -> 'B' */ + { 0x12, 0x56 }, /* CYRILLIC CAPITAL LETTER VE -> 'V' */ + { 0x13, 0x47 }, /* CYRILLIC CAPITAL LETTER GHE -> 'G' */ + { 0x14, 0x44 }, /* CYRILLIC CAPITAL LETTER DE -> 'D' */ + { 0x15, 0x45 }, /* CYRILLIC CAPITAL LETTER IE -> 'E' */ + { 0x17, 0x5A }, /* CYRILLIC CAPITAL LETTER ZE -> 'Z' */ + { 0x18, 0x49 }, /* CYRILLIC CAPITAL LETTER I -> 'I' */ + { 0x19, 0x49 }, /* CYRILLIC CAPITAL LETTER SHORT I -> 'I' */ + { 0x1A, 0x4B }, /* CYRILLIC CAPITAL LETTER KA -> 'K' */ + { 0x1B, 0x4C }, /* CYRILLIC CAPITAL LETTER EL -> 'L' */ + { 0x1C, 0x4D }, /* CYRILLIC CAPITAL LETTER EM -> 'M' */ + { 0x1D, 0x4E }, /* CYRILLIC CAPITAL LETTER EN -> 'N' */ + { 0x1E, 0x4F }, /* CYRILLIC CAPITAL LETTER O -> 'O' */ + { 0x1F, 0x50 }, /* CYRILLIC CAPITAL LETTER PE -> 'P' */ + { 0x20, 0x52 }, /* CYRILLIC CAPITAL LETTER ER -> 'R' */ + { 0x21, 0x53 }, /* CYRILLIC CAPITAL LETTER ES -> 'S' */ + { 0x22, 0x54 }, /* CYRILLIC CAPITAL LETTER TE -> 'T' */ + { 0x23, 0x55 }, /* CYRILLIC CAPITAL LETTER U -> 'U' */ + { 0x24, 0x46 }, /* CYRILLIC CAPITAL LETTER EF -> 'F' */ + { 0x2A, 0x27 }, /* CYRILLIC CAPITAL LETTER HARD SIGN -> ''' */ + { 0x2B, 0x59 }, /* CYRILLIC CAPITAL LETTER YERU -> 'Y' */ + { 0x2C, 0x27 }, /* CYRILLIC CAPITAL LETTER SOFT SIGN -> ''' */ + { 0x2D, 0x45 }, /* CYRILLIC CAPITAL LETTER E -> 'E' */ + { 0x30, 0x61 }, /* CYRILLIC SMALL LETTER A -> 'a' */ + { 0x31, 0x62 }, /* CYRILLIC SMALL LETTER BE -> 'b' */ + { 0x32, 0x76 }, /* CYRILLIC SMALL LETTER VE -> 'v' */ + { 0x33, 0x67 }, /* CYRILLIC SMALL LETTER GHE -> 'g' */ + { 0x34, 0x64 }, /* CYRILLIC SMALL LETTER DE -> 'd' */ + { 0x35, 0x65 }, /* CYRILLIC SMALL LETTER IE -> 'e' */ + { 0x37, 0x7A }, /* CYRILLIC SMALL LETTER ZE -> 'z' */ + { 0x38, 0x69 }, /* CYRILLIC SMALL LETTER I -> 'i' */ + { 0x39, 0x69 }, /* CYRILLIC SMALL LETTER SHORT I -> 'i' */ + { 0x3A, 0x6B }, /* CYRILLIC SMALL LETTER KA -> 'k' */ + { 0x3B, 0x6C }, /* CYRILLIC SMALL LETTER EL -> 'l' */ + { 0x3C, 0x6D }, /* CYRILLIC SMALL LETTER EM -> 'm' */ + { 0x3D, 0x6E }, /* CYRILLIC SMALL LETTER EN -> 'n' */ + { 0x3E, 0x6F }, /* CYRILLIC SMALL LETTER O -> 'o' */ + { 0x3F, 0x70 }, /* CYRILLIC SMALL LETTER PE -> 'p' */ + { 0x40, 0x72 }, /* CYRILLIC SMALL LETTER ER -> 'r' */ + { 0x41, 0x73 }, /* CYRILLIC SMALL LETTER ES -> 's' */ + { 0x42, 0x74 }, /* CYRILLIC SMALL LETTER TE -> 't' */ + { 0x43, 0x75 }, /* CYRILLIC SMALL LETTER U -> 'u' */ + { 0x44, 0x66 }, /* CYRILLIC SMALL LETTER EF -> 'f' */ + { 0x4A, 0x27 }, /* CYRILLIC SMALL LETTER HARD SIGN -> ''' */ + { 0x4B, 0x79 }, /* CYRILLIC SMALL LETTER YERU -> 'y' */ + { 0x4C, 0x27 }, /* CYRILLIC SMALL LETTER SOFT SIGN -> ''' */ + { 0x4D, 0x65 }, /* CYRILLIC SMALL LETTER E -> 'e' */ + { 0x56, 0x69 }, /* CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I -> 'i' */ + { 0x58, 0x6A }, /* CYRILLIC SMALL LETTER JE -> 'j' */ + { 0x5D, 0x69 }, /* CYRILLIC SMALL LETTER I WITH GRAVE -> 'i' */ + { 0x5E, 0x75 }, /* CYRILLIC SMALL LETTER SHORT U -> 'u' */ + { 0x60, 0x4F }, /* CYRILLIC CAPITAL LETTER OMEGA -> 'O' */ + { 0x61, 0x6F }, /* CYRILLIC SMALL LETTER OMEGA -> 'o' */ + { 0x62, 0x45 }, /* CYRILLIC CAPITAL LETTER YAT -> 'E' */ + { 0x63, 0x65 }, /* CYRILLIC SMALL LETTER YAT -> 'e' */ + { 0x66, 0x45 }, /* CYRILLIC CAPITAL LETTER LITTLE YUS -> 'E' */ + { 0x67, 0x65 }, /* CYRILLIC SMALL LETTER LITTLE YUS -> 'e' */ + { 0x6A, 0x4F }, /* CYRILLIC CAPITAL LETTER BIG YUS -> 'O' */ + { 0x6B, 0x6F }, /* CYRILLIC SMALL LETTER BIG YUS -> 'o' */ + { 0x72, 0x46 }, /* CYRILLIC CAPITAL LETTER FITA -> 'F' */ + { 0x73, 0x66 }, /* CYRILLIC SMALL LETTER FITA -> 'f' */ + { 0x74, 0x59 }, /* CYRILLIC CAPITAL LETTER IZHITSA -> 'Y' */ + { 0x75, 0x79 }, /* CYRILLIC SMALL LETTER IZHITSA -> 'y' */ + { 0x76, 0x59 }, /* CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT -> 'Y' */ + { 0x77, 0x79 }, /* CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT -> 'y' */ + { 0x78, 0x75 }, /* CYRILLIC CAPITAL LETTER UK -> 'u' */ + { 0x79, 0x75 }, /* CYRILLIC SMALL LETTER UK -> 'u' */ + { 0x7A, 0x4F }, /* CYRILLIC CAPITAL LETTER ROUND OMEGA -> 'O' */ + { 0x7B, 0x6F }, /* CYRILLIC SMALL LETTER ROUND OMEGA -> 'o' */ + { 0x7C, 0x4F }, /* CYRILLIC CAPITAL LETTER OMEGA WITH TITLO -> 'O' */ + { 0x7D, 0x6F }, /* CYRILLIC SMALL LETTER OMEGA WITH TITLO -> 'o' */ + { 0x80, 0x51 }, /* CYRILLIC CAPITAL LETTER KOPPA -> 'Q' */ + { 0x81, 0x71 }, /* CYRILLIC SMALL LETTER KOPPA -> 'q' */ + { 0x8C, 0x22 }, /* CYRILLIC CAPITAL LETTER SEMISOFT SIGN -> '"' */ + { 0x8D, 0x22 }, /* CYRILLIC SMALL LETTER SEMISOFT SIGN -> '"' */ + { 0xAE, 0x55 }, /* CYRILLIC CAPITAL LETTER STRAIGHT U -> 'U' */ + { 0xAF, 0x75 }, /* CYRILLIC SMALL LETTER STRAIGHT U -> 'u' */ + { 0xBA, 0x48 }, /* CYRILLIC CAPITAL LETTER SHHA -> 'H' */ + { 0xBB, 0x68 }, /* CYRILLIC SMALL LETTER SHHA -> 'h' */ + { 0xC0, 0x60 }, /* CYRILLIC LETTER PALOCHKA -> '`' */ + { 0xD0, 0x61 }, /* CYRILLIC CAPITAL LETTER A WITH BREVE -> 'a' */ + { 0xD1, 0x61 }, /* CYRILLIC SMALL LETTER A WITH BREVE -> 'a' */ + { 0xD2, 0x41 }, /* CYRILLIC CAPITAL LETTER A WITH DIAERESIS -> 'A' */ + { 0xD3, 0x61 }, /* CYRILLIC SMALL LETTER A WITH DIAERESIS -> 'a' */ + { 0xD8, 0x00 }, /* CYRILLIC CAPITAL LETTER SCHWA -> ... */ + { 0xDB, 0x40 }, /* CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS -> '@' */ + { 0xDE, 0x5A }, /* CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS -> 'Z' */ + { 0xDF, 0x7A }, /* CYRILLIC SMALL LETTER ZE WITH DIAERESIS -> 'z' */ + { 0xE2, 0x49 }, /* CYRILLIC CAPITAL LETTER I WITH MACRON -> 'I' */ + { 0xE3, 0x69 }, /* CYRILLIC SMALL LETTER I WITH MACRON -> 'i' */ + { 0xE4, 0x49 }, /* CYRILLIC CAPITAL LETTER I WITH DIAERESIS -> 'I' */ + { 0xE5, 0x69 }, /* CYRILLIC SMALL LETTER I WITH DIAERESIS -> 'i' */ + { 0xE6, 0x4F }, /* CYRILLIC CAPITAL LETTER O WITH DIAERESIS -> 'O' */ + { 0xE7, 0x6F }, /* CYRILLIC SMALL LETTER O WITH DIAERESIS -> 'o' */ + { 0xE8, 0x4F }, /* CYRILLIC CAPITAL LETTER BARRED O -> 'O' */ + { 0xE9, 0x6F }, /* CYRILLIC SMALL LETTER BARRED O -> 'o' */ + { 0xEA, 0x4F }, /* CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS -> 'O' */ + { 0xEB, 0x6F }, /* CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS -> 'o' */ + { 0xEC, 0x45 }, /* CYRILLIC CAPITAL LETTER E WITH DIAERESIS -> 'E' */ + { 0xED, 0x65 }, /* CYRILLIC SMALL LETTER E WITH DIAERESIS -> 'e' */ + { 0xEE, 0x55 }, /* CYRILLIC CAPITAL LETTER U WITH MACRON -> 'U' */ + { 0xEF, 0x75 }, /* CYRILLIC SMALL LETTER U WITH MACRON -> 'u' */ + { 0xF0, 0x55 }, /* CYRILLIC CAPITAL LETTER U WITH DIAERESIS -> 'U' */ + { 0xF1, 0x75 }, /* CYRILLIC SMALL LETTER U WITH DIAERESIS -> 'u' */ + { 0xF2, 0x55 }, /* CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE -> 'U' */ + { 0xF3, 0x75 }, /* CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE -> 'u' */ + { 0xF8, 0x59 }, /* CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS -> 'Y' */ + { 0xF9, 0x79 }, /* CYRILLIC SMALL LETTER YERU WITH DIAERESIS -> 'y' */ + /* Entries for page 0x05 */ + { 0x31, 0x41 }, /* ARMENIAN CAPITAL LETTER AYB -> 'A' */ + { 0x32, 0x42 }, /* ARMENIAN CAPITAL LETTER BEN -> 'B' */ + { 0x33, 0x47 }, /* ARMENIAN CAPITAL LETTER GIM -> 'G' */ + { 0x34, 0x44 }, /* ARMENIAN CAPITAL LETTER DA -> 'D' */ + { 0x35, 0x45 }, /* ARMENIAN CAPITAL LETTER ECH -> 'E' */ + { 0x36, 0x5A }, /* ARMENIAN CAPITAL LETTER ZA -> 'Z' */ + { 0x37, 0x45 }, /* ARMENIAN CAPITAL LETTER EH -> 'E' */ + { 0x38, 0x45 }, /* ARMENIAN CAPITAL LETTER ET -> 'E' */ + { 0x3B, 0x49 }, /* ARMENIAN CAPITAL LETTER INI -> 'I' */ + { 0x3C, 0x4C }, /* ARMENIAN CAPITAL LETTER LIWN -> 'L' */ + { 0x3F, 0x4B }, /* ARMENIAN CAPITAL LETTER KEN -> 'K' */ + { 0x40, 0x48 }, /* ARMENIAN CAPITAL LETTER HO -> 'H' */ + { 0x44, 0x4D }, /* ARMENIAN CAPITAL LETTER MEN -> 'M' */ + { 0x45, 0x59 }, /* ARMENIAN CAPITAL LETTER YI -> 'Y' */ + { 0x46, 0x4E }, /* ARMENIAN CAPITAL LETTER NOW -> 'N' */ + { 0x48, 0x4F }, /* ARMENIAN CAPITAL LETTER VO -> 'O' */ + { 0x4A, 0x50 }, /* ARMENIAN CAPITAL LETTER PEH -> 'P' */ + { 0x4B, 0x4A }, /* ARMENIAN CAPITAL LETTER JHEH -> 'J' */ + { 0x4D, 0x53 }, /* ARMENIAN CAPITAL LETTER SEH -> 'S' */ + { 0x4E, 0x56 }, /* ARMENIAN CAPITAL LETTER VEW -> 'V' */ + { 0x4F, 0x54 }, /* ARMENIAN CAPITAL LETTER TIWN -> 'T' */ + { 0x50, 0x52 }, /* ARMENIAN CAPITAL LETTER REH -> 'R' */ + { 0x52, 0x57 }, /* ARMENIAN CAPITAL LETTER YIWN -> 'W' */ + { 0x55, 0x4F }, /* ARMENIAN CAPITAL LETTER OH -> 'O' */ + { 0x56, 0x46 }, /* ARMENIAN CAPITAL LETTER FEH -> 'F' */ + { 0x59, 0x3C }, /* ARMENIAN MODIFIER LETTER LEFT HALF RING -> '<' */ + { 0x5A, 0x27 }, /* ARMENIAN APOSTROPHE -> ''' */ + { 0x5B, 0x2F }, /* ARMENIAN EMPHASIS MARK -> '/' */ + { 0x5C, 0x21 }, /* ARMENIAN EXCLAMATION MARK -> '!' */ + { 0x5D, 0x2C }, /* ARMENIAN COMMA -> ',' */ + { 0x5E, 0x3F }, /* ARMENIAN QUESTION MARK -> '?' */ + { 0x5F, 0x2E }, /* ARMENIAN ABBREVIATION MARK -> '.' */ + { 0x61, 0x61 }, /* ARMENIAN SMALL LETTER AYB -> 'a' */ + { 0x62, 0x62 }, /* ARMENIAN SMALL LETTER BEN -> 'b' */ + { 0x63, 0x67 }, /* ARMENIAN SMALL LETTER GIM -> 'g' */ + { 0x64, 0x64 }, /* ARMENIAN SMALL LETTER DA -> 'd' */ + { 0x65, 0x65 }, /* ARMENIAN SMALL LETTER ECH -> 'e' */ + { 0x66, 0x7A }, /* ARMENIAN SMALL LETTER ZA -> 'z' */ + { 0x67, 0x65 }, /* ARMENIAN SMALL LETTER EH -> 'e' */ + { 0x68, 0x65 }, /* ARMENIAN SMALL LETTER ET -> 'e' */ + { 0x6B, 0x69 }, /* ARMENIAN SMALL LETTER INI -> 'i' */ + { 0x6C, 0x6C }, /* ARMENIAN SMALL LETTER LIWN -> 'l' */ + { 0x6F, 0x6B }, /* ARMENIAN SMALL LETTER KEN -> 'k' */ + { 0x70, 0x68 }, /* ARMENIAN SMALL LETTER HO -> 'h' */ + { 0x74, 0x6D }, /* ARMENIAN SMALL LETTER MEN -> 'm' */ + { 0x75, 0x79 }, /* ARMENIAN SMALL LETTER YI -> 'y' */ + { 0x76, 0x6E }, /* ARMENIAN SMALL LETTER NOW -> 'n' */ + { 0x78, 0x6F }, /* ARMENIAN SMALL LETTER VO -> 'o' */ + { 0x7A, 0x70 }, /* ARMENIAN SMALL LETTER PEH -> 'p' */ + { 0x7B, 0x6A }, /* ARMENIAN SMALL LETTER JHEH -> 'j' */ + { 0x7D, 0x73 }, /* ARMENIAN SMALL LETTER SEH -> 's' */ + { 0x7E, 0x76 }, /* ARMENIAN SMALL LETTER VEW -> 'v' */ + { 0x7F, 0x74 }, /* ARMENIAN SMALL LETTER TIWN -> 't' */ + { 0x80, 0x72 }, /* ARMENIAN SMALL LETTER REH -> 'r' */ + { 0x82, 0x77 }, /* ARMENIAN SMALL LETTER YIWN -> 'w' */ + { 0x85, 0x6F }, /* ARMENIAN SMALL LETTER OH -> 'o' */ + { 0x86, 0x66 }, /* ARMENIAN SMALL LETTER FEH -> 'f' */ + { 0x89, 0x3A }, /* ARMENIAN FULL STOP -> ':' */ + { 0x8A, 0x2D }, /* ARMENIAN HYPHEN -> '-' */ + { 0xB1, 0x65 }, /* HEBREW POINT HATAF SEGOL -> 'e' */ + { 0xB2, 0x61 }, /* HEBREW POINT HATAF PATAH -> 'a' */ + { 0xB3, 0x6F }, /* HEBREW POINT HATAF QAMATS -> 'o' */ + { 0xB4, 0x69 }, /* HEBREW POINT HIRIQ -> 'i' */ + { 0xB5, 0x65 }, /* HEBREW POINT TSERE -> 'e' */ + { 0xB6, 0x65 }, /* HEBREW POINT SEGOL -> 'e' */ + { 0xB7, 0x61 }, /* HEBREW POINT PATAH -> 'a' */ + { 0xB8, 0x61 }, /* HEBREW POINT QAMATS -> 'a' */ + { 0xB9, 0x6F }, /* HEBREW POINT HOLAM -> 'o' */ + { 0xBA, 0x6F }, /* HEBREW POINT HOLAM HASER FOR VAV -> 'o' */ + { 0xBB, 0x75 }, /* HEBREW POINT QUBUTS -> 'u' */ + { 0xBE, 0x2D }, /* HEBREW PUNCTUATION MAQAF -> '-' */ + { 0xC0, 0x7C }, /* HEBREW PUNCTUATION PASEQ -> '|' */ + { 0xC3, 0x2E }, /* HEBREW PUNCTUATION SOF PASUQ -> '.' */ + { 0xC6, 0x6E }, /* HEBREW PUNCTUATION NUN HAFUKHA -> 'n' */ + { 0xC7, 0x6F }, /* HEBREW POINT QAMATS QATAN -> 'o' */ + { 0xD0, 0x41 }, /* HEBREW LETTER ALEF -> 'A' */ + { 0xD1, 0x62 }, /* HEBREW LETTER BET -> 'b' */ + { 0xD2, 0x67 }, /* HEBREW LETTER GIMEL -> 'g' */ + { 0xD3, 0x64 }, /* HEBREW LETTER DALET -> 'd' */ + { 0xD4, 0x68 }, /* HEBREW LETTER HE -> 'h' */ + { 0xD5, 0x76 }, /* HEBREW LETTER VAV -> 'v' */ + { 0xD6, 0x7A }, /* HEBREW LETTER ZAYIN -> 'z' */ + { 0xD7, 0x48 }, /* HEBREW LETTER HET -> 'H' */ + { 0xD8, 0x54 }, /* HEBREW LETTER TET -> 'T' */ + { 0xD9, 0x79 }, /* HEBREW LETTER YOD -> 'y' */ + { 0xDC, 0x6C }, /* HEBREW LETTER LAMED -> 'l' */ + { 0xDD, 0x6D }, /* HEBREW LETTER FINAL MEM -> 'm' */ + { 0xDE, 0x6D }, /* HEBREW LETTER MEM -> 'm' */ + { 0xDF, 0x6E }, /* HEBREW LETTER FINAL NUN -> 'n' */ + { 0xE0, 0x6E }, /* HEBREW LETTER NUN -> 'n' */ + { 0xE1, 0x73 }, /* HEBREW LETTER SAMEKH -> 's' */ + { 0xE2, 0x60 }, /* HEBREW LETTER AYIN -> '`' */ + { 0xE3, 0x70 }, /* HEBREW LETTER FINAL PE -> 'p' */ + { 0xE4, 0x70 }, /* HEBREW LETTER PE -> 'p' */ + { 0xE7, 0x6B }, /* HEBREW LETTER QOF -> 'k' */ + { 0xE8, 0x72 }, /* HEBREW LETTER RESH -> 'r' */ + { 0xEA, 0x74 }, /* HEBREW LETTER TAV -> 't' */ + { 0xF0, 0x56 }, /* HEBREW LIGATURE YIDDISH DOUBLE VAV -> 'V' */ + { 0xF3, 0x27 }, /* HEBREW PUNCTUATION GERESH -> ''' */ + { 0xF4, 0x22 }, /* HEBREW PUNCTUATION GERSHAYIM -> '"' */ + /* Entries for page 0x06 */ + { 0x0C, 0x2C }, /* ARABIC COMMA -> ',' */ + { 0x1B, 0x3B }, /* ARABIC SEMICOLON -> ';' */ + { 0x1F, 0x3F }, /* ARABIC QUESTION MARK -> '?' */ + { 0x22, 0x61 }, /* ARABIC LETTER ALEF WITH MADDA ABOVE -> 'a' */ + { 0x23, 0x27 }, /* ARABIC LETTER ALEF WITH HAMZA ABOVE -> ''' */ + { 0x28, 0x62 }, /* ARABIC LETTER BEH -> 'b' */ + { 0x29, 0x40 }, /* ARABIC LETTER TEH MARBUTA -> '@' */ + { 0x2A, 0x74 }, /* ARABIC LETTER TEH -> 't' */ + { 0x2C, 0x6A }, /* ARABIC LETTER JEEM -> 'j' */ + { 0x2D, 0x48 }, /* ARABIC LETTER HAH -> 'H' */ + { 0x2F, 0x64 }, /* ARABIC LETTER DAL -> 'd' */ + { 0x31, 0x72 }, /* ARABIC LETTER REH -> 'r' */ + { 0x32, 0x7A }, /* ARABIC LETTER ZAIN -> 'z' */ + { 0x33, 0x73 }, /* ARABIC LETTER SEEN -> 's' */ + { 0x35, 0x53 }, /* ARABIC LETTER SAD -> 'S' */ + { 0x36, 0x44 }, /* ARABIC LETTER DAD -> 'D' */ + { 0x37, 0x54 }, /* ARABIC LETTER TAH -> 'T' */ + { 0x38, 0x5A }, /* ARABIC LETTER ZAH -> 'Z' */ + { 0x39, 0x60 }, /* ARABIC LETTER AIN -> '`' */ + { 0x3A, 0x47 }, /* ARABIC LETTER GHAIN -> 'G' */ + { 0x41, 0x66 }, /* ARABIC LETTER FEH -> 'f' */ + { 0x42, 0x71 }, /* ARABIC LETTER QAF -> 'q' */ + { 0x43, 0x6B }, /* ARABIC LETTER KAF -> 'k' */ + { 0x44, 0x6C }, /* ARABIC LETTER LAM -> 'l' */ + { 0x45, 0x6D }, /* ARABIC LETTER MEEM -> 'm' */ + { 0x46, 0x6E }, /* ARABIC LETTER NOON -> 'n' */ + { 0x47, 0x68 }, /* ARABIC LETTER HEH -> 'h' */ + { 0x48, 0x77 }, /* ARABIC LETTER WAW -> 'w' */ + { 0x49, 0x7E }, /* ARABIC LETTER ALEF MAKSURA -> '~' */ + { 0x4A, 0x79 }, /* ARABIC LETTER YEH -> 'y' */ + { 0x4E, 0x61 }, /* ARABIC FATHA -> 'a' */ + { 0x4F, 0x75 }, /* ARABIC DAMMA -> 'u' */ + { 0x50, 0x69 }, /* ARABIC KASRA -> 'i' */ + { 0x51, 0x57 }, /* ARABIC SHADDA -> 'W' */ + { 0x54, 0x27 }, /* ARABIC HAMZA ABOVE -> ''' */ + { 0x55, 0x27 }, /* ARABIC HAMZA BELOW -> ''' */ + { 0x60, 0x30 }, /* ARABIC-INDIC DIGIT ZERO -> '0' */ + { 0x61, 0x31 }, /* ARABIC-INDIC DIGIT ONE -> '1' */ + { 0x62, 0x32 }, /* ARABIC-INDIC DIGIT TWO -> '2' */ + { 0x63, 0x33 }, /* ARABIC-INDIC DIGIT THREE -> '3' */ + { 0x64, 0x34 }, /* ARABIC-INDIC DIGIT FOUR -> '4' */ + { 0x65, 0x35 }, /* ARABIC-INDIC DIGIT FIVE -> '5' */ + { 0x66, 0x36 }, /* ARABIC-INDIC DIGIT SIX -> '6' */ + { 0x67, 0x37 }, /* ARABIC-INDIC DIGIT SEVEN -> '7' */ + { 0x68, 0x38 }, /* ARABIC-INDIC DIGIT EIGHT -> '8' */ + { 0x69, 0x39 }, /* ARABIC-INDIC DIGIT NINE -> '9' */ + { 0x6A, 0x25 }, /* ARABIC PERCENT SIGN -> '%' */ + { 0x6B, 0x2E }, /* ARABIC DECIMAL SEPARATOR -> '.' */ + { 0x6C, 0x2C }, /* ARABIC THOUSANDS SEPARATOR -> ',' */ + { 0x6D, 0x2A }, /* ARABIC FIVE POINTED STAR -> '*' */ + { 0x71, 0x00 }, /* ARABIC LETTER ALEF WASLA -> ... */ + { 0x73, 0x27 }, /* ARABIC LETTER ALEF WITH WAVY HAMZA BELOW -> ''' */ + { 0x75, 0x27 }, /* ARABIC LETTER HIGH HAMZA ALEF -> ''' */ + { 0x7B, 0x62 }, /* ARABIC LETTER BEEH -> 'b' */ + { 0x7C, 0x74 }, /* ARABIC LETTER TEH WITH RING -> 't' */ + { 0x7D, 0x54 }, /* ARABIC LETTER TEH WITH THREE DOTS ABOVE DOWNWARDS -> 'T' */ + { 0x7E, 0x70 }, /* ARABIC LETTER PEH -> 'p' */ + { 0x82, 0x48 }, /* ARABIC LETTER HAH WITH TWO DOTS VERTICAL ABOVE -> 'H' */ + { 0x85, 0x48 }, /* ARABIC LETTER HAH WITH THREE DOTS ABOVE -> 'H' */ + { 0x89, 0x44 }, /* ARABIC LETTER DAL WITH RING -> 'D' */ + { 0x8A, 0x44 }, /* ARABIC LETTER DAL WITH DOT BELOW -> 'D' */ + { 0x8E, 0x64 }, /* ARABIC LETTER DUL -> 'd' */ + { 0x8F, 0x44 }, /* ARABIC LETTER DAL WITH THREE DOTS ABOVE DOWNWARDS -> 'D' */ + { 0x90, 0x44 }, /* ARABIC LETTER DAL WITH FOUR DOTS ABOVE -> 'D' */ + { 0x92, 0x00 }, /* ARABIC LETTER REH WITH SMALL V -> ... */ + { 0x97, 0x52 }, /* ARABIC LETTER REH WITH TWO DOTS ABOVE -> 'R' */ + { 0x98, 0x6A }, /* ARABIC LETTER JEH -> 'j' */ + { 0x99, 0x52 }, /* ARABIC LETTER REH WITH FOUR DOTS ABOVE -> 'R' */ + { 0x9A, 0x00 }, /* ARABIC LETTER SEEN WITH DOT BELOW AND DOT ABOVE -> ... */ + { 0x9E, 0x53 }, /* ARABIC LETTER SAD WITH THREE DOTS ABOVE -> 'S' */ + { 0x9F, 0x54 }, /* ARABIC LETTER TAH WITH THREE DOTS ABOVE -> 'T' */ + { 0xA1, 0x00 }, /* ARABIC LETTER DOTLESS FEH -> ... */ + { 0xA3, 0x46 }, /* ARABIC LETTER FEH WITH DOT BELOW -> 'F' */ + { 0xA4, 0x76 }, /* ARABIC LETTER VEH -> 'v' */ + { 0xA5, 0x66 }, /* ARABIC LETTER FEH WITH THREE DOTS BELOW -> 'f' */ + { 0xA7, 0x51 }, /* ARABIC LETTER QAF WITH DOT ABOVE -> 'Q' */ + { 0xA8, 0x51 }, /* ARABIC LETTER QAF WITH THREE DOTS ABOVE -> 'Q' */ + { 0xAA, 0x6B }, /* ARABIC LETTER SWASH KAF -> 'k' */ + { 0xAB, 0x4B }, /* ARABIC LETTER KAF WITH RING -> 'K' */ + { 0xAC, 0x4B }, /* ARABIC LETTER KAF WITH DOT ABOVE -> 'K' */ + { 0xAE, 0x4B }, /* ARABIC LETTER KAF WITH THREE DOTS BELOW -> 'K' */ + { 0xAF, 0x67 }, /* ARABIC LETTER GAF -> 'g' */ + { 0xB0, 0x47 }, /* ARABIC LETTER GAF WITH RING -> 'G' */ + { 0xB1, 0x4E }, /* ARABIC LETTER NGOEH -> 'N' */ + { 0xB2, 0x00 }, /* ARABIC LETTER GAF WITH TWO DOTS BELOW -> ... */ + { 0xB4, 0x47 }, /* ARABIC LETTER GAF WITH THREE DOTS ABOVE -> 'G' */ + { 0xB5, 0x00 }, /* ARABIC LETTER LAM WITH SMALL V -> ... */ + { 0xB8, 0x4C }, /* ARABIC LETTER LAM WITH THREE DOTS BELOW -> 'L' */ + { 0xB9, 0x00 }, /* ARABIC LETTER NOON WITH DOT BELOW -> ... */ + { 0xBD, 0x4E }, /* ARABIC LETTER NOON WITH THREE DOTS ABOVE -> 'N' */ + { 0xBE, 0x68 }, /* ARABIC LETTER HEH DOACHASHMEE -> 'h' */ + { 0xC1, 0x68 }, /* ARABIC LETTER HEH GOAL -> 'h' */ + { 0xC2, 0x48 }, /* ARABIC LETTER HEH GOAL WITH HAMZA ABOVE -> 'H' */ + { 0xC3, 0x40 }, /* ARABIC LETTER TEH MARBUTA GOAL -> '@' */ + { 0xC4, 0x57 }, /* ARABIC LETTER WAW WITH RING -> 'W' */ + { 0xC7, 0x75 }, /* ARABIC LETTER U -> 'u' */ + { 0xCA, 0x57 }, /* ARABIC LETTER WAW WITH TWO DOTS ABOVE -> 'W' */ + { 0xCB, 0x76 }, /* ARABIC LETTER VE -> 'v' */ + { 0xCC, 0x79 }, /* ARABIC LETTER FARSI YEH -> 'y' */ + { 0xCD, 0x59 }, /* ARABIC LETTER YEH WITH TAIL -> 'Y' */ + { 0xCE, 0x59 }, /* ARABIC LETTER YEH WITH SMALL V -> 'Y' */ + { 0xCF, 0x57 }, /* ARABIC LETTER WAW WITH DOT ABOVE -> 'W' */ + { 0xD2, 0x79 }, /* ARABIC LETTER YEH BARREE -> 'y' */ + { 0xD4, 0x2E }, /* ARABIC FULL STOP -> '.' */ + { 0xDD, 0x40 }, /* ARABIC END OF AYAH -> '@' */ + { 0xDE, 0x23 }, /* ARABIC START OF RUB EL HIZB -> '#' */ + { 0xE9, 0x5E }, /* ARABIC PLACE OF SAJDAH -> '^' */ + { 0xF0, 0x30 }, /* EXTENDED ARABIC-INDIC DIGIT ZERO -> '0' */ + { 0xF1, 0x31 }, /* EXTENDED ARABIC-INDIC DIGIT ONE -> '1' */ + { 0xF2, 0x32 }, /* EXTENDED ARABIC-INDIC DIGIT TWO -> '2' */ + { 0xF3, 0x33 }, /* EXTENDED ARABIC-INDIC DIGIT THREE -> '3' */ + { 0xF4, 0x34 }, /* EXTENDED ARABIC-INDIC DIGIT FOUR -> '4' */ + { 0xF5, 0x35 }, /* EXTENDED ARABIC-INDIC DIGIT FIVE -> '5' */ + { 0xF6, 0x36 }, /* EXTENDED ARABIC-INDIC DIGIT SIX -> '6' */ + { 0xF7, 0x37 }, /* EXTENDED ARABIC-INDIC DIGIT SEVEN -> '7' */ + { 0xF8, 0x38 }, /* EXTENDED ARABIC-INDIC DIGIT EIGHT -> '8' */ + { 0xF9, 0x39 }, /* EXTENDED ARABIC-INDIC DIGIT NINE -> '9' */ + { 0xFB, 0x44 }, /* ARABIC LETTER DAD WITH DOT BELOW -> 'D' */ + { 0xFD, 0x26 }, /* ARABIC SIGN SINDHI AMPERSAND -> '&' */ + /* Entries for page 0x07 */ + { 0x01, 0x2F }, /* SYRIAC SUPRALINEAR FULL STOP -> '/' */ + { 0x02, 0x2C }, /* SYRIAC SUBLINEAR FULL STOP -> ',' */ + { 0x03, 0x21 }, /* SYRIAC SUPRALINEAR COLON -> '!' */ + { 0x04, 0x21 }, /* SYRIAC SUBLINEAR COLON -> '!' */ + { 0x05, 0x2D }, /* SYRIAC HORIZONTAL COLON -> '-' */ + { 0x06, 0x2C }, /* SYRIAC COLON SKEWED LEFT -> ',' */ + { 0x07, 0x2C }, /* SYRIAC COLON SKEWED RIGHT -> ',' */ + { 0x08, 0x3B }, /* SYRIAC SUPRALINEAR COLON SKEWED LEFT -> ';' */ + { 0x09, 0x3F }, /* SYRIAC SUBLINEAR COLON SKEWED RIGHT -> '?' */ + { 0x0A, 0x7E }, /* SYRIAC CONTRACTION -> '~' */ + { 0x0B, 0x7B }, /* SYRIAC HARKLEAN OBELUS -> '{' */ + { 0x0C, 0x7D }, /* SYRIAC HARKLEAN METOBELUS -> '}' */ + { 0x0D, 0x2A }, /* SYRIAC HARKLEAN ASTERISCUS -> '*' */ + { 0x10, 0x27 }, /* SYRIAC LETTER ALAPH -> ''' */ + { 0x12, 0x62 }, /* SYRIAC LETTER BETH -> 'b' */ + { 0x13, 0x67 }, /* SYRIAC LETTER GAMAL -> 'g' */ + { 0x14, 0x67 }, /* SYRIAC LETTER GAMAL GARSHUNI -> 'g' */ + { 0x15, 0x64 }, /* SYRIAC LETTER DALATH -> 'd' */ + { 0x16, 0x64 }, /* SYRIAC LETTER DOTLESS DALATH RISH -> 'd' */ + { 0x17, 0x68 }, /* SYRIAC LETTER HE -> 'h' */ + { 0x18, 0x77 }, /* SYRIAC LETTER WAW -> 'w' */ + { 0x19, 0x7A }, /* SYRIAC LETTER ZAIN -> 'z' */ + { 0x1A, 0x48 }, /* SYRIAC LETTER HETH -> 'H' */ + { 0x1B, 0x74 }, /* SYRIAC LETTER TETH -> 't' */ + { 0x1C, 0x74 }, /* SYRIAC LETTER TETH GARSHUNI -> 't' */ + { 0x1D, 0x79 }, /* SYRIAC LETTER YUDH -> 'y' */ + { 0x1F, 0x6B }, /* SYRIAC LETTER KAPH -> 'k' */ + { 0x20, 0x6C }, /* SYRIAC LETTER LAMADH -> 'l' */ + { 0x21, 0x6D }, /* SYRIAC LETTER MIM -> 'm' */ + { 0x22, 0x6E }, /* SYRIAC LETTER NUN -> 'n' */ + { 0x23, 0x73 }, /* SYRIAC LETTER SEMKATH -> 's' */ + { 0x24, 0x73 }, /* SYRIAC LETTER FINAL SEMKATH -> 's' */ + { 0x25, 0x60 }, /* SYRIAC LETTER E -> '`' */ + { 0x26, 0x70 }, /* SYRIAC LETTER PE -> 'p' */ + { 0x27, 0x70 }, /* SYRIAC LETTER REVERSED PE -> 'p' */ + { 0x28, 0x53 }, /* SYRIAC LETTER SADHE -> 'S' */ + { 0x29, 0x71 }, /* SYRIAC LETTER QAPH -> 'q' */ + { 0x2A, 0x72 }, /* SYRIAC LETTER RISH -> 'r' */ + { 0x2C, 0x74 }, /* SYRIAC LETTER TAW -> 't' */ + { 0x30, 0x00 }, /* SYRIAC PTHAHA ABOVE -> ... */ + { 0x32, 0x61 }, /* SYRIAC PTHAHA DOTTED -> 'a' */ + { 0x33, 0x00 }, /* SYRIAC ZQAPHA ABOVE -> ... */ + { 0x35, 0x41 }, /* SYRIAC ZQAPHA DOTTED -> 'A' */ + { 0x36, 0x00 }, /* SYRIAC RBASA ABOVE -> ... */ + { 0x38, 0x65 }, /* SYRIAC DOTTED ZLAMA HORIZONTAL -> 'e' */ + { 0x39, 0x45 }, /* SYRIAC DOTTED ZLAMA ANGULAR -> 'E' */ + { 0x3A, 0x69 }, /* SYRIAC HBASA ABOVE -> 'i' */ + { 0x3B, 0x69 }, /* SYRIAC HBASA BELOW -> 'i' */ + { 0x3C, 0x00 }, /* SYRIAC HBASA-ESASA DOTTED -> ... */ + { 0x3E, 0x75 }, /* SYRIAC ESASA BELOW -> 'u' */ + { 0x3F, 0x6F }, /* SYRIAC RWAHA -> 'o' */ + { 0x41, 0x60 }, /* SYRIAC QUSHSHAYA -> '`' */ + { 0x42, 0x27 }, /* SYRIAC RUKKAKHA -> ''' */ + { 0x45, 0x58 }, /* SYRIAC THREE DOTS ABOVE -> 'X' */ + { 0x46, 0x51 }, /* SYRIAC THREE DOTS BELOW -> 'Q' */ + { 0x47, 0x40 }, /* SYRIAC OBLIQUE LINE ABOVE -> '@' */ + { 0x48, 0x40 }, /* SYRIAC OBLIQUE LINE BELOW -> '@' */ + { 0x49, 0x7C }, /* SYRIAC MUSIC -> '|' */ + { 0x4A, 0x2B }, /* SYRIAC BARREKH -> '+' */ + { 0x80, 0x68 }, /* THAANA LETTER HAA -> 'h' */ + { 0x82, 0x6E }, /* THAANA LETTER NOONU -> 'n' */ + { 0x83, 0x72 }, /* THAANA LETTER RAA -> 'r' */ + { 0x84, 0x62 }, /* THAANA LETTER BAA -> 'b' */ + { 0x85, 0x4C }, /* THAANA LETTER LHAVIYANI -> 'L' */ + { 0x86, 0x6B }, /* THAANA LETTER KAAFU -> 'k' */ + { 0x87, 0x27 }, /* THAANA LETTER ALIFU -> ''' */ + { 0x88, 0x76 }, /* THAANA LETTER VAAVU -> 'v' */ + { 0x89, 0x6D }, /* THAANA LETTER MEEMU -> 'm' */ + { 0x8A, 0x66 }, /* THAANA LETTER FAAFU -> 'f' */ + { 0x8D, 0x6C }, /* THAANA LETTER LAAMU -> 'l' */ + { 0x8E, 0x67 }, /* THAANA LETTER GAAFU -> 'g' */ + { 0x90, 0x73 }, /* THAANA LETTER SEENU -> 's' */ + { 0x91, 0x64 }, /* THAANA LETTER DAVIYANI -> 'd' */ + { 0x92, 0x7A }, /* THAANA LETTER ZAVIYANI -> 'z' */ + { 0x93, 0x74 }, /* THAANA LETTER TAVIYANI -> 't' */ + { 0x94, 0x79 }, /* THAANA LETTER YAA -> 'y' */ + { 0x95, 0x70 }, /* THAANA LETTER PAVIYANI -> 'p' */ + { 0x96, 0x6A }, /* THAANA LETTER JAVIYANI -> 'j' */ + { 0x9C, 0x7A }, /* THAANA LETTER ZAA -> 'z' */ + { 0x9E, 0x73 }, /* THAANA LETTER SAADHU -> 's' */ + { 0x9F, 0x64 }, /* THAANA LETTER DAADHU -> 'd' */ + { 0xA0, 0x74 }, /* THAANA LETTER TO -> 't' */ + { 0xA1, 0x7A }, /* THAANA LETTER ZO -> 'z' */ + { 0xA2, 0x60 }, /* THAANA LETTER AINU -> '`' */ + { 0xA4, 0x71 }, /* THAANA LETTER QAAFU -> 'q' */ + { 0xA5, 0x77 }, /* THAANA LETTER WAAVU -> 'w' */ + { 0xA6, 0x61 }, /* THAANA ABAFILI -> 'a' */ + { 0xA8, 0x69 }, /* THAANA IBIFILI -> 'i' */ + { 0xAA, 0x75 }, /* THAANA UBUFILI -> 'u' */ + { 0xAC, 0x65 }, /* THAANA EBEFILI -> 'e' */ + { 0xAE, 0x6F }, /* THAANA OBOFILI -> 'o' */ + /* Entries for page 0x09 */ + { 0x01, 0x4E }, /* DEVANAGARI SIGN CANDRABINDU -> 'N' */ + { 0x02, 0x4E }, /* DEVANAGARI SIGN ANUSVARA -> 'N' */ + { 0x03, 0x48 }, /* DEVANAGARI SIGN VISARGA -> 'H' */ + { 0x05, 0x61 }, /* DEVANAGARI LETTER A -> 'a' */ + { 0x07, 0x69 }, /* DEVANAGARI LETTER I -> 'i' */ + { 0x09, 0x75 }, /* DEVANAGARI LETTER U -> 'u' */ + { 0x0B, 0x52 }, /* DEVANAGARI LETTER VOCALIC R -> 'R' */ + { 0x0C, 0x4C }, /* DEVANAGARI LETTER VOCALIC L -> 'L' */ + { 0x0E, 0x65 }, /* DEVANAGARI LETTER SHORT E -> 'e' */ + { 0x0F, 0x65 }, /* DEVANAGARI LETTER E -> 'e' */ + { 0x12, 0x6F }, /* DEVANAGARI LETTER SHORT O -> 'o' */ + { 0x13, 0x6F }, /* DEVANAGARI LETTER O -> 'o' */ + { 0x15, 0x6B }, /* DEVANAGARI LETTER KA -> 'k' */ + { 0x17, 0x67 }, /* DEVANAGARI LETTER GA -> 'g' */ + { 0x1A, 0x63 }, /* DEVANAGARI LETTER CA -> 'c' */ + { 0x1C, 0x6A }, /* DEVANAGARI LETTER JA -> 'j' */ + { 0x24, 0x74 }, /* DEVANAGARI LETTER TA -> 't' */ + { 0x26, 0x64 }, /* DEVANAGARI LETTER DA -> 'd' */ + { 0x28, 0x6E }, /* DEVANAGARI LETTER NA -> 'n' */ + { 0x2A, 0x70 }, /* DEVANAGARI LETTER PA -> 'p' */ + { 0x2C, 0x62 }, /* DEVANAGARI LETTER BA -> 'b' */ + { 0x2E, 0x6D }, /* DEVANAGARI LETTER MA -> 'm' */ + { 0x2F, 0x79 }, /* DEVANAGARI LETTER YA -> 'y' */ + { 0x30, 0x72 }, /* DEVANAGARI LETTER RA -> 'r' */ + { 0x32, 0x6C }, /* DEVANAGARI LETTER LA -> 'l' */ + { 0x33, 0x6C }, /* DEVANAGARI LETTER LLA -> 'l' */ + { 0x35, 0x76 }, /* DEVANAGARI LETTER VA -> 'v' */ + { 0x38, 0x73 }, /* DEVANAGARI LETTER SA -> 's' */ + { 0x39, 0x68 }, /* DEVANAGARI LETTER HA -> 'h' */ + { 0x3C, 0x27 }, /* DEVANAGARI SIGN NUKTA -> ''' */ + { 0x3D, 0x27 }, /* DEVANAGARI SIGN AVAGRAHA -> ''' */ + { 0x3F, 0x69 }, /* DEVANAGARI VOWEL SIGN I -> 'i' */ + { 0x41, 0x75 }, /* DEVANAGARI VOWEL SIGN U -> 'u' */ + { 0x43, 0x52 }, /* DEVANAGARI VOWEL SIGN VOCALIC R -> 'R' */ + { 0x46, 0x65 }, /* DEVANAGARI VOWEL SIGN SHORT E -> 'e' */ + { 0x47, 0x65 }, /* DEVANAGARI VOWEL SIGN E -> 'e' */ + { 0x4A, 0x6F }, /* DEVANAGARI VOWEL SIGN SHORT O -> 'o' */ + { 0x4B, 0x6F }, /* DEVANAGARI VOWEL SIGN O -> 'o' */ + { 0x51, 0x27 }, /* DEVANAGARI STRESS SIGN UDATTA -> ''' */ + { 0x52, 0x27 }, /* DEVANAGARI STRESS SIGN ANUDATTA -> ''' */ + { 0x53, 0x60 }, /* DEVANAGARI GRAVE ACCENT -> '`' */ + { 0x54, 0x27 }, /* DEVANAGARI ACUTE ACCENT -> ''' */ + { 0x58, 0x71 }, /* DEVANAGARI LETTER QA -> 'q' */ + { 0x5B, 0x7A }, /* DEVANAGARI LETTER ZA -> 'z' */ + { 0x5E, 0x66 }, /* DEVANAGARI LETTER FA -> 'f' */ + { 0x62, 0x4C }, /* DEVANAGARI VOWEL SIGN VOCALIC L -> 'L' */ + { 0x66, 0x30 }, /* DEVANAGARI DIGIT ZERO -> '0' */ + { 0x67, 0x31 }, /* DEVANAGARI DIGIT ONE -> '1' */ + { 0x68, 0x32 }, /* DEVANAGARI DIGIT TWO -> '2' */ + { 0x69, 0x33 }, /* DEVANAGARI DIGIT THREE -> '3' */ + { 0x6A, 0x34 }, /* DEVANAGARI DIGIT FOUR -> '4' */ + { 0x6B, 0x35 }, /* DEVANAGARI DIGIT FIVE -> '5' */ + { 0x6C, 0x36 }, /* DEVANAGARI DIGIT SIX -> '6' */ + { 0x6D, 0x37 }, /* DEVANAGARI DIGIT SEVEN -> '7' */ + { 0x6E, 0x38 }, /* DEVANAGARI DIGIT EIGHT -> '8' */ + { 0x6F, 0x39 }, /* DEVANAGARI DIGIT NINE -> '9' */ + { 0x70, 0x2E }, /* DEVANAGARI ABBREVIATION SIGN -> '.' */ + { 0x81, 0x4E }, /* BENGALI SIGN CANDRABINDU -> 'N' */ + { 0x82, 0x4E }, /* BENGALI SIGN ANUSVARA -> 'N' */ + { 0x83, 0x48 }, /* BENGALI SIGN VISARGA -> 'H' */ + { 0x85, 0x61 }, /* BENGALI LETTER A -> 'a' */ + { 0x87, 0x69 }, /* BENGALI LETTER I -> 'i' */ + { 0x89, 0x75 }, /* BENGALI LETTER U -> 'u' */ + { 0x8B, 0x52 }, /* BENGALI LETTER VOCALIC R -> 'R' */ + { 0x8F, 0x65 }, /* BENGALI LETTER E -> 'e' */ + { 0x93, 0x6F }, /* BENGALI LETTER O -> 'o' */ + { 0x95, 0x6B }, /* BENGALI LETTER KA -> 'k' */ + { 0x97, 0x67 }, /* BENGALI LETTER GA -> 'g' */ + { 0x9A, 0x63 }, /* BENGALI LETTER CA -> 'c' */ + { 0x9C, 0x6A }, /* BENGALI LETTER JA -> 'j' */ + { 0xA4, 0x74 }, /* BENGALI LETTER TA -> 't' */ + { 0xA6, 0x64 }, /* BENGALI LETTER DA -> 'd' */ + { 0xA8, 0x6E }, /* BENGALI LETTER NA -> 'n' */ + { 0xAA, 0x70 }, /* BENGALI LETTER PA -> 'p' */ + { 0xAC, 0x62 }, /* BENGALI LETTER BA -> 'b' */ + { 0xAE, 0x6D }, /* BENGALI LETTER MA -> 'm' */ + { 0xAF, 0x79 }, /* BENGALI LETTER YA -> 'y' */ + { 0xB0, 0x72 }, /* BENGALI LETTER RA -> 'r' */ + { 0xB2, 0x6C }, /* BENGALI LETTER LA -> 'l' */ + { 0xB8, 0x73 }, /* BENGALI LETTER SA -> 's' */ + { 0xB9, 0x68 }, /* BENGALI LETTER HA -> 'h' */ + { 0xBC, 0x27 }, /* BENGALI SIGN NUKTA -> ''' */ + { 0xBF, 0x69 }, /* BENGALI VOWEL SIGN I -> 'i' */ + { 0xC1, 0x75 }, /* BENGALI VOWEL SIGN U -> 'u' */ + { 0xC3, 0x52 }, /* BENGALI VOWEL SIGN VOCALIC R -> 'R' */ + { 0xC7, 0x65 }, /* BENGALI VOWEL SIGN E -> 'e' */ + { 0xCB, 0x6F }, /* BENGALI VOWEL SIGN O -> 'o' */ + { 0xD7, 0x2B }, /* BENGALI AU LENGTH MARK -> '+' */ + { 0xE2, 0x4C }, /* BENGALI VOWEL SIGN VOCALIC L -> 'L' */ + { 0xE6, 0x30 }, /* BENGALI DIGIT ZERO -> '0' */ + { 0xE7, 0x31 }, /* BENGALI DIGIT ONE -> '1' */ + { 0xE8, 0x32 }, /* BENGALI DIGIT TWO -> '2' */ + { 0xE9, 0x33 }, /* BENGALI DIGIT THREE -> '3' */ + { 0xEA, 0x34 }, /* BENGALI DIGIT FOUR -> '4' */ + { 0xEB, 0x35 }, /* BENGALI DIGIT FIVE -> '5' */ + { 0xEC, 0x36 }, /* BENGALI DIGIT SIX -> '6' */ + { 0xED, 0x37 }, /* BENGALI DIGIT SEVEN -> '7' */ + { 0xEE, 0x38 }, /* BENGALI DIGIT EIGHT -> '8' */ + { 0xEF, 0x39 }, /* BENGALI DIGIT NINE -> '9' */ + /* Entries for page 0x0A */ + { 0x02, 0x4E }, /* GURMUKHI SIGN BINDI -> 'N' */ + { 0x05, 0x61 }, /* GURMUKHI LETTER A -> 'a' */ + { 0x07, 0x69 }, /* GURMUKHI LETTER I -> 'i' */ + { 0x09, 0x75 }, /* GURMUKHI LETTER U -> 'u' */ + { 0x15, 0x6B }, /* GURMUKHI LETTER KA -> 'k' */ + { 0x17, 0x67 }, /* GURMUKHI LETTER GA -> 'g' */ + { 0x1A, 0x63 }, /* GURMUKHI LETTER CA -> 'c' */ + { 0x1C, 0x6A }, /* GURMUKHI LETTER JA -> 'j' */ + { 0x24, 0x74 }, /* GURMUKHI LETTER TA -> 't' */ + { 0x26, 0x64 }, /* GURMUKHI LETTER DA -> 'd' */ + { 0x28, 0x6E }, /* GURMUKHI LETTER NA -> 'n' */ + { 0x2A, 0x70 }, /* GURMUKHI LETTER PA -> 'p' */ + { 0x2C, 0x62 }, /* GURMUKHI LETTER BA -> 'b' */ + { 0x2E, 0x6D }, /* GURMUKHI LETTER MA -> 'm' */ + { 0x2F, 0x79 }, /* GURMUKHI LETTER YA -> 'y' */ + { 0x30, 0x72 }, /* GURMUKHI LETTER RA -> 'r' */ + { 0x32, 0x6C }, /* GURMUKHI LETTER LA -> 'l' */ + { 0x35, 0x76 }, /* GURMUKHI LETTER VA -> 'v' */ + { 0x38, 0x73 }, /* GURMUKHI LETTER SA -> 's' */ + { 0x39, 0x68 }, /* GURMUKHI LETTER HA -> 'h' */ + { 0x3C, 0x27 }, /* GURMUKHI SIGN NUKTA -> ''' */ + { 0x3F, 0x69 }, /* GURMUKHI VOWEL SIGN I -> 'i' */ + { 0x41, 0x75 }, /* GURMUKHI VOWEL SIGN U -> 'u' */ + { 0x5B, 0x7A }, /* GURMUKHI LETTER ZA -> 'z' */ + { 0x5E, 0x66 }, /* GURMUKHI LETTER FA -> 'f' */ + { 0x66, 0x30 }, /* GURMUKHI DIGIT ZERO -> '0' */ + { 0x67, 0x31 }, /* GURMUKHI DIGIT ONE -> '1' */ + { 0x68, 0x32 }, /* GURMUKHI DIGIT TWO -> '2' */ + { 0x69, 0x33 }, /* GURMUKHI DIGIT THREE -> '3' */ + { 0x6A, 0x34 }, /* GURMUKHI DIGIT FOUR -> '4' */ + { 0x6B, 0x35 }, /* GURMUKHI DIGIT FIVE -> '5' */ + { 0x6C, 0x36 }, /* GURMUKHI DIGIT SIX -> '6' */ + { 0x6D, 0x37 }, /* GURMUKHI DIGIT SEVEN -> '7' */ + { 0x6E, 0x38 }, /* GURMUKHI DIGIT EIGHT -> '8' */ + { 0x6F, 0x39 }, /* GURMUKHI DIGIT NINE -> '9' */ + { 0x70, 0x4E }, /* GURMUKHI TIPPI -> 'N' */ + { 0x71, 0x48 }, /* GURMUKHI ADDAK -> 'H' */ + { 0x81, 0x4E }, /* GUJARATI SIGN CANDRABINDU -> 'N' */ + { 0x82, 0x4E }, /* GUJARATI SIGN ANUSVARA -> 'N' */ + { 0x83, 0x48 }, /* GUJARATI SIGN VISARGA -> 'H' */ + { 0x85, 0x61 }, /* GUJARATI LETTER A -> 'a' */ + { 0x87, 0x69 }, /* GUJARATI LETTER I -> 'i' */ + { 0x89, 0x75 }, /* GUJARATI LETTER U -> 'u' */ + { 0x8B, 0x52 }, /* GUJARATI LETTER VOCALIC R -> 'R' */ + { 0x8F, 0x65 }, /* GUJARATI LETTER E -> 'e' */ + { 0x93, 0x6F }, /* GUJARATI LETTER O -> 'o' */ + { 0x95, 0x6B }, /* GUJARATI LETTER KA -> 'k' */ + { 0x97, 0x67 }, /* GUJARATI LETTER GA -> 'g' */ + { 0x9A, 0x63 }, /* GUJARATI LETTER CA -> 'c' */ + { 0x9C, 0x6A }, /* GUJARATI LETTER JA -> 'j' */ + { 0xA4, 0x74 }, /* GUJARATI LETTER TA -> 't' */ + { 0xA6, 0x64 }, /* GUJARATI LETTER DA -> 'd' */ + { 0xA8, 0x6E }, /* GUJARATI LETTER NA -> 'n' */ + { 0xAA, 0x70 }, /* GUJARATI LETTER PA -> 'p' */ + { 0xAC, 0x62 }, /* GUJARATI LETTER BA -> 'b' */ + { 0xAE, 0x6D }, /* GUJARATI LETTER MA -> 'm' */ + { 0xB0, 0x72 }, /* GUJARATI LETTER RA -> 'r' */ + { 0xB2, 0x6C }, /* GUJARATI LETTER LA -> 'l' */ + { 0xB5, 0x76 }, /* GUJARATI LETTER VA -> 'v' */ + { 0xB8, 0x73 }, /* GUJARATI LETTER SA -> 's' */ + { 0xB9, 0x68 }, /* GUJARATI LETTER HA -> 'h' */ + { 0xBC, 0x27 }, /* GUJARATI SIGN NUKTA -> ''' */ + { 0xBD, 0x27 }, /* GUJARATI SIGN AVAGRAHA -> ''' */ + { 0xBF, 0x69 }, /* GUJARATI VOWEL SIGN I -> 'i' */ + { 0xC1, 0x75 }, /* GUJARATI VOWEL SIGN U -> 'u' */ + { 0xC3, 0x52 }, /* GUJARATI VOWEL SIGN VOCALIC R -> 'R' */ + { 0xC7, 0x65 }, /* GUJARATI VOWEL SIGN E -> 'e' */ + { 0xCB, 0x6F }, /* GUJARATI VOWEL SIGN O -> 'o' */ + { 0xE6, 0x30 }, /* GUJARATI DIGIT ZERO -> '0' */ + { 0xE7, 0x31 }, /* GUJARATI DIGIT ONE -> '1' */ + { 0xE8, 0x32 }, /* GUJARATI DIGIT TWO -> '2' */ + { 0xE9, 0x33 }, /* GUJARATI DIGIT THREE -> '3' */ + { 0xEA, 0x34 }, /* GUJARATI DIGIT FOUR -> '4' */ + { 0xEB, 0x35 }, /* GUJARATI DIGIT FIVE -> '5' */ + { 0xEC, 0x36 }, /* GUJARATI DIGIT SIX -> '6' */ + { 0xED, 0x37 }, /* GUJARATI DIGIT SEVEN -> '7' */ + { 0xEE, 0x38 }, /* GUJARATI DIGIT EIGHT -> '8' */ + { 0xEF, 0x39 }, /* GUJARATI DIGIT NINE -> '9' */ + /* Entries for page 0x0B */ + { 0x01, 0x4E }, /* ORIYA SIGN CANDRABINDU -> 'N' */ + { 0x02, 0x4E }, /* ORIYA SIGN ANUSVARA -> 'N' */ + { 0x03, 0x48 }, /* ORIYA SIGN VISARGA -> 'H' */ + { 0x05, 0x61 }, /* ORIYA LETTER A -> 'a' */ + { 0x07, 0x69 }, /* ORIYA LETTER I -> 'i' */ + { 0x09, 0x75 }, /* ORIYA LETTER U -> 'u' */ + { 0x0B, 0x52 }, /* ORIYA LETTER VOCALIC R -> 'R' */ + { 0x0C, 0x4C }, /* ORIYA LETTER VOCALIC L -> 'L' */ + { 0x0F, 0x65 }, /* ORIYA LETTER E -> 'e' */ + { 0x13, 0x6F }, /* ORIYA LETTER O -> 'o' */ + { 0x15, 0x6B }, /* ORIYA LETTER KA -> 'k' */ + { 0x17, 0x67 }, /* ORIYA LETTER GA -> 'g' */ + { 0x1A, 0x63 }, /* ORIYA LETTER CA -> 'c' */ + { 0x1C, 0x6A }, /* ORIYA LETTER JA -> 'j' */ + { 0x24, 0x74 }, /* ORIYA LETTER TA -> 't' */ + { 0x26, 0x64 }, /* ORIYA LETTER DA -> 'd' */ + { 0x28, 0x6E }, /* ORIYA LETTER NA -> 'n' */ + { 0x2A, 0x70 }, /* ORIYA LETTER PA -> 'p' */ + { 0x2C, 0x62 }, /* ORIYA LETTER BA -> 'b' */ + { 0x2E, 0x6D }, /* ORIYA LETTER MA -> 'm' */ + { 0x2F, 0x79 }, /* ORIYA LETTER YA -> 'y' */ + { 0x30, 0x72 }, /* ORIYA LETTER RA -> 'r' */ + { 0x32, 0x6C }, /* ORIYA LETTER LA -> 'l' */ + { 0x38, 0x73 }, /* ORIYA LETTER SA -> 's' */ + { 0x39, 0x68 }, /* ORIYA LETTER HA -> 'h' */ + { 0x3C, 0x27 }, /* ORIYA SIGN NUKTA -> ''' */ + { 0x3D, 0x27 }, /* ORIYA SIGN AVAGRAHA -> ''' */ + { 0x3F, 0x69 }, /* ORIYA VOWEL SIGN I -> 'i' */ + { 0x41, 0x75 }, /* ORIYA VOWEL SIGN U -> 'u' */ + { 0x43, 0x52 }, /* ORIYA VOWEL SIGN VOCALIC R -> 'R' */ + { 0x47, 0x65 }, /* ORIYA VOWEL SIGN E -> 'e' */ + { 0x4B, 0x6F }, /* ORIYA VOWEL SIGN O -> 'o' */ + { 0x56, 0x2B }, /* ORIYA AI LENGTH MARK -> '+' */ + { 0x57, 0x2B }, /* ORIYA AU LENGTH MARK -> '+' */ + { 0x66, 0x30 }, /* ORIYA DIGIT ZERO -> '0' */ + { 0x67, 0x31 }, /* ORIYA DIGIT ONE -> '1' */ + { 0x68, 0x32 }, /* ORIYA DIGIT TWO -> '2' */ + { 0x69, 0x33 }, /* ORIYA DIGIT THREE -> '3' */ + { 0x6A, 0x34 }, /* ORIYA DIGIT FOUR -> '4' */ + { 0x6B, 0x35 }, /* ORIYA DIGIT FIVE -> '5' */ + { 0x6C, 0x36 }, /* ORIYA DIGIT SIX -> '6' */ + { 0x6D, 0x37 }, /* ORIYA DIGIT SEVEN -> '7' */ + { 0x6E, 0x38 }, /* ORIYA DIGIT EIGHT -> '8' */ + { 0x6F, 0x39 }, /* ORIYA DIGIT NINE -> '9' */ + { 0x82, 0x4E }, /* TAMIL SIGN ANUSVARA -> 'N' */ + { 0x83, 0x48 }, /* TAMIL SIGN VISARGA -> 'H' */ + { 0x85, 0x61 }, /* TAMIL LETTER A -> 'a' */ + { 0x87, 0x69 }, /* TAMIL LETTER I -> 'i' */ + { 0x89, 0x75 }, /* TAMIL LETTER U -> 'u' */ + { 0x8E, 0x65 }, /* TAMIL LETTER E -> 'e' */ + { 0x92, 0x6F }, /* TAMIL LETTER O -> 'o' */ + { 0x95, 0x6B }, /* TAMIL LETTER KA -> 'k' */ + { 0x9A, 0x63 }, /* TAMIL LETTER CA -> 'c' */ + { 0x9C, 0x6A }, /* TAMIL LETTER JA -> 'j' */ + { 0xA4, 0x74 }, /* TAMIL LETTER TA -> 't' */ + { 0xA8, 0x6E }, /* TAMIL LETTER NA -> 'n' */ + { 0xAA, 0x70 }, /* TAMIL LETTER PA -> 'p' */ + { 0xAE, 0x6D }, /* TAMIL LETTER MA -> 'm' */ + { 0xAF, 0x79 }, /* TAMIL LETTER YA -> 'y' */ + { 0xB0, 0x72 }, /* TAMIL LETTER RA -> 'r' */ + { 0xB2, 0x6C }, /* TAMIL LETTER LA -> 'l' */ + { 0xB5, 0x76 }, /* TAMIL LETTER VA -> 'v' */ + { 0xB8, 0x73 }, /* TAMIL LETTER SA -> 's' */ + { 0xB9, 0x68 }, /* TAMIL LETTER HA -> 'h' */ + { 0xBF, 0x69 }, /* TAMIL VOWEL SIGN I -> 'i' */ + { 0xC1, 0x75 }, /* TAMIL VOWEL SIGN U -> 'u' */ + { 0xC6, 0x65 }, /* TAMIL VOWEL SIGN E -> 'e' */ + { 0xCA, 0x6F }, /* TAMIL VOWEL SIGN O -> 'o' */ + { 0xD7, 0x2B }, /* TAMIL AU LENGTH MARK -> '+' */ + { 0xE6, 0x30 }, /* TAMIL DIGIT ZERO -> '0' */ + { 0xE7, 0x31 }, /* TAMIL DIGIT ONE -> '1' */ + { 0xE8, 0x32 }, /* TAMIL DIGIT TWO -> '2' */ + { 0xE9, 0x33 }, /* TAMIL DIGIT THREE -> '3' */ + { 0xEA, 0x34 }, /* TAMIL DIGIT FOUR -> '4' */ + { 0xEB, 0x35 }, /* TAMIL DIGIT FIVE -> '5' */ + { 0xEC, 0x36 }, /* TAMIL DIGIT SIX -> '6' */ + { 0xED, 0x37 }, /* TAMIL DIGIT SEVEN -> '7' */ + { 0xEE, 0x38 }, /* TAMIL DIGIT EIGHT -> '8' */ + { 0xEF, 0x39 }, /* TAMIL DIGIT NINE -> '9' */ + /* Entries for page 0x0C */ + { 0x01, 0x4E }, /* TELUGU SIGN CANDRABINDU -> 'N' */ + { 0x02, 0x4E }, /* TELUGU SIGN ANUSVARA -> 'N' */ + { 0x03, 0x48 }, /* TELUGU SIGN VISARGA -> 'H' */ + { 0x05, 0x61 }, /* TELUGU LETTER A -> 'a' */ + { 0x07, 0x69 }, /* TELUGU LETTER I -> 'i' */ + { 0x09, 0x75 }, /* TELUGU LETTER U -> 'u' */ + { 0x0B, 0x52 }, /* TELUGU LETTER VOCALIC R -> 'R' */ + { 0x0C, 0x4C }, /* TELUGU LETTER VOCALIC L -> 'L' */ + { 0x0E, 0x65 }, /* TELUGU LETTER E -> 'e' */ + { 0x12, 0x6F }, /* TELUGU LETTER O -> 'o' */ + { 0x15, 0x6B }, /* TELUGU LETTER KA -> 'k' */ + { 0x17, 0x67 }, /* TELUGU LETTER GA -> 'g' */ + { 0x1A, 0x63 }, /* TELUGU LETTER CA -> 'c' */ + { 0x1C, 0x6A }, /* TELUGU LETTER JA -> 'j' */ + { 0x24, 0x74 }, /* TELUGU LETTER TA -> 't' */ + { 0x26, 0x64 }, /* TELUGU LETTER DA -> 'd' */ + { 0x28, 0x6E }, /* TELUGU LETTER NA -> 'n' */ + { 0x2A, 0x70 }, /* TELUGU LETTER PA -> 'p' */ + { 0x2C, 0x62 }, /* TELUGU LETTER BA -> 'b' */ + { 0x2E, 0x6D }, /* TELUGU LETTER MA -> 'm' */ + { 0x2F, 0x79 }, /* TELUGU LETTER YA -> 'y' */ + { 0x30, 0x72 }, /* TELUGU LETTER RA -> 'r' */ + { 0x32, 0x6C }, /* TELUGU LETTER LA -> 'l' */ + { 0x35, 0x76 }, /* TELUGU LETTER VA -> 'v' */ + { 0x38, 0x73 }, /* TELUGU LETTER SA -> 's' */ + { 0x39, 0x68 }, /* TELUGU LETTER HA -> 'h' */ + { 0x3F, 0x69 }, /* TELUGU VOWEL SIGN I -> 'i' */ + { 0x41, 0x75 }, /* TELUGU VOWEL SIGN U -> 'u' */ + { 0x43, 0x52 }, /* TELUGU VOWEL SIGN VOCALIC R -> 'R' */ + { 0x46, 0x65 }, /* TELUGU VOWEL SIGN E -> 'e' */ + { 0x4A, 0x6F }, /* TELUGU VOWEL SIGN O -> 'o' */ + { 0x55, 0x2B }, /* TELUGU LENGTH MARK -> '+' */ + { 0x56, 0x2B }, /* TELUGU AI LENGTH MARK -> '+' */ + { 0x66, 0x30 }, /* TELUGU DIGIT ZERO -> '0' */ + { 0x67, 0x31 }, /* TELUGU DIGIT ONE -> '1' */ + { 0x68, 0x32 }, /* TELUGU DIGIT TWO -> '2' */ + { 0x69, 0x33 }, /* TELUGU DIGIT THREE -> '3' */ + { 0x6A, 0x34 }, /* TELUGU DIGIT FOUR -> '4' */ + { 0x6B, 0x35 }, /* TELUGU DIGIT FIVE -> '5' */ + { 0x6C, 0x36 }, /* TELUGU DIGIT SIX -> '6' */ + { 0x6D, 0x37 }, /* TELUGU DIGIT SEVEN -> '7' */ + { 0x6E, 0x38 }, /* TELUGU DIGIT EIGHT -> '8' */ + { 0x6F, 0x39 }, /* TELUGU DIGIT NINE -> '9' */ + { 0x82, 0x4E }, /* KANNADA SIGN ANUSVARA -> 'N' */ + { 0x83, 0x48 }, /* KANNADA SIGN VISARGA -> 'H' */ + { 0x85, 0x61 }, /* KANNADA LETTER A -> 'a' */ + { 0x87, 0x69 }, /* KANNADA LETTER I -> 'i' */ + { 0x89, 0x75 }, /* KANNADA LETTER U -> 'u' */ + { 0x8B, 0x52 }, /* KANNADA LETTER VOCALIC R -> 'R' */ + { 0x8C, 0x4C }, /* KANNADA LETTER VOCALIC L -> 'L' */ + { 0x8E, 0x65 }, /* KANNADA LETTER E -> 'e' */ + { 0x92, 0x6F }, /* KANNADA LETTER O -> 'o' */ + { 0x95, 0x6B }, /* KANNADA LETTER KA -> 'k' */ + { 0x97, 0x67 }, /* KANNADA LETTER GA -> 'g' */ + { 0x9A, 0x63 }, /* KANNADA LETTER CA -> 'c' */ + { 0x9C, 0x6A }, /* KANNADA LETTER JA -> 'j' */ + { 0xA4, 0x74 }, /* KANNADA LETTER TA -> 't' */ + { 0xA6, 0x64 }, /* KANNADA LETTER DA -> 'd' */ + { 0xA8, 0x6E }, /* KANNADA LETTER NA -> 'n' */ + { 0xAA, 0x70 }, /* KANNADA LETTER PA -> 'p' */ + { 0xAC, 0x62 }, /* KANNADA LETTER BA -> 'b' */ + { 0xAE, 0x6D }, /* KANNADA LETTER MA -> 'm' */ + { 0xAF, 0x79 }, /* KANNADA LETTER YA -> 'y' */ + { 0xB0, 0x72 }, /* KANNADA LETTER RA -> 'r' */ + { 0xB2, 0x6C }, /* KANNADA LETTER LA -> 'l' */ + { 0xB5, 0x76 }, /* KANNADA LETTER VA -> 'v' */ + { 0xB8, 0x73 }, /* KANNADA LETTER SA -> 's' */ + { 0xB9, 0x68 }, /* KANNADA LETTER HA -> 'h' */ + { 0xBF, 0x69 }, /* KANNADA VOWEL SIGN I -> 'i' */ + { 0xC1, 0x75 }, /* KANNADA VOWEL SIGN U -> 'u' */ + { 0xC3, 0x52 }, /* KANNADA VOWEL SIGN VOCALIC R -> 'R' */ + { 0xC6, 0x65 }, /* KANNADA VOWEL SIGN E -> 'e' */ + { 0xCA, 0x6F }, /* KANNADA VOWEL SIGN O -> 'o' */ + { 0xD5, 0x2B }, /* KANNADA LENGTH MARK -> '+' */ + { 0xD6, 0x2B }, /* KANNADA AI LENGTH MARK -> '+' */ + { 0xE6, 0x30 }, /* KANNADA DIGIT ZERO -> '0' */ + { 0xE7, 0x31 }, /* KANNADA DIGIT ONE -> '1' */ + { 0xE8, 0x32 }, /* KANNADA DIGIT TWO -> '2' */ + { 0xE9, 0x33 }, /* KANNADA DIGIT THREE -> '3' */ + { 0xEA, 0x34 }, /* KANNADA DIGIT FOUR -> '4' */ + { 0xEB, 0x35 }, /* KANNADA DIGIT FIVE -> '5' */ + { 0xEC, 0x36 }, /* KANNADA DIGIT SIX -> '6' */ + { 0xED, 0x37 }, /* KANNADA DIGIT SEVEN -> '7' */ + { 0xEE, 0x38 }, /* KANNADA DIGIT EIGHT -> '8' */ + { 0xEF, 0x39 }, /* KANNADA DIGIT NINE -> '9' */ + /* Entries for page 0x0D */ + { 0x02, 0x4E }, /* MALAYALAM SIGN ANUSVARA -> 'N' */ + { 0x03, 0x48 }, /* MALAYALAM SIGN VISARGA -> 'H' */ + { 0x05, 0x61 }, /* MALAYALAM LETTER A -> 'a' */ + { 0x07, 0x69 }, /* MALAYALAM LETTER I -> 'i' */ + { 0x09, 0x75 }, /* MALAYALAM LETTER U -> 'u' */ + { 0x0B, 0x52 }, /* MALAYALAM LETTER VOCALIC R -> 'R' */ + { 0x0C, 0x4C }, /* MALAYALAM LETTER VOCALIC L -> 'L' */ + { 0x0E, 0x65 }, /* MALAYALAM LETTER E -> 'e' */ + { 0x12, 0x6F }, /* MALAYALAM LETTER O -> 'o' */ + { 0x15, 0x6B }, /* MALAYALAM LETTER KA -> 'k' */ + { 0x17, 0x67 }, /* MALAYALAM LETTER GA -> 'g' */ + { 0x1A, 0x63 }, /* MALAYALAM LETTER CA -> 'c' */ + { 0x1C, 0x6A }, /* MALAYALAM LETTER JA -> 'j' */ + { 0x24, 0x74 }, /* MALAYALAM LETTER TA -> 't' */ + { 0x26, 0x64 }, /* MALAYALAM LETTER DA -> 'd' */ + { 0x28, 0x6E }, /* MALAYALAM LETTER NA -> 'n' */ + { 0x2A, 0x70 }, /* MALAYALAM LETTER PA -> 'p' */ + { 0x2C, 0x62 }, /* MALAYALAM LETTER BA -> 'b' */ + { 0x2E, 0x6D }, /* MALAYALAM LETTER MA -> 'm' */ + { 0x2F, 0x79 }, /* MALAYALAM LETTER YA -> 'y' */ + { 0x30, 0x72 }, /* MALAYALAM LETTER RA -> 'r' */ + { 0x32, 0x6C }, /* MALAYALAM LETTER LA -> 'l' */ + { 0x35, 0x76 }, /* MALAYALAM LETTER VA -> 'v' */ + { 0x38, 0x73 }, /* MALAYALAM LETTER SA -> 's' */ + { 0x39, 0x68 }, /* MALAYALAM LETTER HA -> 'h' */ + { 0x3F, 0x69 }, /* MALAYALAM VOWEL SIGN I -> 'i' */ + { 0x41, 0x75 }, /* MALAYALAM VOWEL SIGN U -> 'u' */ + { 0x43, 0x52 }, /* MALAYALAM VOWEL SIGN VOCALIC R -> 'R' */ + { 0x46, 0x65 }, /* MALAYALAM VOWEL SIGN E -> 'e' */ + { 0x4A, 0x6F }, /* MALAYALAM VOWEL SIGN O -> 'o' */ + { 0x57, 0x2B }, /* MALAYALAM AU LENGTH MARK -> '+' */ + { 0x66, 0x30 }, /* MALAYALAM DIGIT ZERO -> '0' */ + { 0x67, 0x31 }, /* MALAYALAM DIGIT ONE -> '1' */ + { 0x68, 0x32 }, /* MALAYALAM DIGIT TWO -> '2' */ + { 0x69, 0x33 }, /* MALAYALAM DIGIT THREE -> '3' */ + { 0x6A, 0x34 }, /* MALAYALAM DIGIT FOUR -> '4' */ + { 0x6B, 0x35 }, /* MALAYALAM DIGIT FIVE -> '5' */ + { 0x6C, 0x36 }, /* MALAYALAM DIGIT SIX -> '6' */ + { 0x6D, 0x37 }, /* MALAYALAM DIGIT SEVEN -> '7' */ + { 0x6E, 0x38 }, /* MALAYALAM DIGIT EIGHT -> '8' */ + { 0x6F, 0x39 }, /* MALAYALAM DIGIT NINE -> '9' */ + { 0x82, 0x4E }, /* SINHALA SIGN ANUSVARAYA -> 'N' */ + { 0x83, 0x48 }, /* SINHALA SIGN VISARGAYA -> 'H' */ + { 0x85, 0x61 }, /* SINHALA LETTER AYANNA -> 'a' */ + { 0x89, 0x69 }, /* SINHALA LETTER IYANNA -> 'i' */ + { 0x8B, 0x75 }, /* SINHALA LETTER UYANNA -> 'u' */ + { 0x8D, 0x52 }, /* SINHALA LETTER IRUYANNA -> 'R' */ + { 0x8F, 0x4C }, /* SINHALA LETTER ILUYANNA -> 'L' */ + { 0x91, 0x65 }, /* SINHALA LETTER EYANNA -> 'e' */ + { 0x94, 0x6F }, /* SINHALA LETTER OYANNA -> 'o' */ + { 0x9A, 0x6B }, /* SINHALA LETTER ALPAPRAANA KAYANNA -> 'k' */ + { 0x9C, 0x67 }, /* SINHALA LETTER ALPAPRAANA GAYANNA -> 'g' */ + { 0xA0, 0x63 }, /* SINHALA LETTER ALPAPRAANA CAYANNA -> 'c' */ + { 0xA2, 0x6A }, /* SINHALA LETTER ALPAPRAANA JAYANNA -> 'j' */ + { 0xAD, 0x74 }, /* SINHALA LETTER ALPAPRAANA TAYANNA -> 't' */ + { 0xAF, 0x64 }, /* SINHALA LETTER ALPAPRAANA DAYANNA -> 'd' */ + { 0xB1, 0x6E }, /* SINHALA LETTER DANTAJA NAYANNA -> 'n' */ + { 0xB4, 0x70 }, /* SINHALA LETTER ALPAPRAANA PAYANNA -> 'p' */ + { 0xB6, 0x62 }, /* SINHALA LETTER ALPAPRAANA BAYANNA -> 'b' */ + { 0xB8, 0x6D }, /* SINHALA LETTER MAYANNA -> 'm' */ + { 0xBA, 0x79 }, /* SINHALA LETTER YAYANNA -> 'y' */ + { 0xBB, 0x72 }, /* SINHALA LETTER RAYANNA -> 'r' */ + { 0xBD, 0x6C }, /* SINHALA LETTER DANTAJA LAYANNA -> 'l' */ + { 0xC0, 0x76 }, /* SINHALA LETTER VAYANNA -> 'v' */ + { 0xC3, 0x73 }, /* SINHALA LETTER DANTAJA SAYANNA -> 's' */ + { 0xC4, 0x68 }, /* SINHALA LETTER HAYANNA -> 'h' */ + { 0xC6, 0x66 }, /* SINHALA LETTER FAYANNA -> 'f' */ + { 0xD2, 0x69 }, /* SINHALA VOWEL SIGN KETTI IS-PILLA -> 'i' */ + { 0xD4, 0x75 }, /* SINHALA VOWEL SIGN KETTI PAA-PILLA -> 'u' */ + { 0xD8, 0x52 }, /* SINHALA VOWEL SIGN GAETTA-PILLA -> 'R' */ + { 0xD9, 0x65 }, /* SINHALA VOWEL SIGN KOMBUVA -> 'e' */ + { 0xDC, 0x6F }, /* SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA -> 'o' */ + { 0xDF, 0x4C }, /* SINHALA VOWEL SIGN GAYANUKITTA -> 'L' */ + /* Entries for page 0x0E */ + { 0x01, 0x6B }, /* THAI CHARACTER KO KAI -> 'k' */ + { 0x0D, 0x79 }, /* THAI CHARACTER YO YING -> 'y' */ + { 0x0E, 0x64 }, /* THAI CHARACTER DO CHADA -> 'd' */ + { 0x0F, 0x74 }, /* THAI CHARACTER TO PATAK -> 't' */ + { 0x13, 0x6E }, /* THAI CHARACTER NO NEN -> 'n' */ + { 0x14, 0x64 }, /* THAI CHARACTER DO DEK -> 'd' */ + { 0x15, 0x74 }, /* THAI CHARACTER TO TAO -> 't' */ + { 0x19, 0x6E }, /* THAI CHARACTER NO NU -> 'n' */ + { 0x1A, 0x62 }, /* THAI CHARACTER BO BAIMAI -> 'b' */ + { 0x1B, 0x70 }, /* THAI CHARACTER PO PLA -> 'p' */ + { 0x1D, 0x66 }, /* THAI CHARACTER FO FA -> 'f' */ + { 0x1F, 0x66 }, /* THAI CHARACTER FO FAN -> 'f' */ + { 0x21, 0x6D }, /* THAI CHARACTER MO MA -> 'm' */ + { 0x22, 0x79 }, /* THAI CHARACTER YO YAK -> 'y' */ + { 0x23, 0x72 }, /* THAI CHARACTER RO RUA -> 'r' */ + { 0x24, 0x52 }, /* THAI CHARACTER RU -> 'R' */ + { 0x25, 0x6C }, /* THAI CHARACTER LO LING -> 'l' */ + { 0x26, 0x4C }, /* THAI CHARACTER LU -> 'L' */ + { 0x27, 0x77 }, /* THAI CHARACTER WO WAEN -> 'w' */ + { 0x28, 0x00 }, /* THAI CHARACTER SO SALA -> ... */ + { 0x2A, 0x73 }, /* THAI CHARACTER SO SUA -> 's' */ + { 0x2B, 0x68 }, /* THAI CHARACTER HO HIP -> 'h' */ + { 0x2C, 0x6C }, /* THAI CHARACTER LO CHULA -> 'l' */ + { 0x2D, 0x60 }, /* THAI CHARACTER O ANG -> '`' */ + { 0x2E, 0x68 }, /* THAI CHARACTER HO NOKHUK -> 'h' */ + { 0x2F, 0x7E }, /* THAI CHARACTER PAIYANNOI -> '~' */ + { 0x30, 0x61 }, /* THAI CHARACTER SARA A -> 'a' */ + { 0x31, 0x61 }, /* THAI CHARACTER MAI HAN-AKAT -> 'a' */ + { 0x34, 0x69 }, /* THAI CHARACTER SARA I -> 'i' */ + { 0x38, 0x75 }, /* THAI CHARACTER SARA U -> 'u' */ + { 0x3A, 0x27 }, /* THAI CHARACTER PHINTHU -> ''' */ + { 0x40, 0x65 }, /* THAI CHARACTER SARA E -> 'e' */ + { 0x42, 0x6F }, /* THAI CHARACTER SARA O -> 'o' */ + { 0x46, 0x2B }, /* THAI CHARACTER MAIYAMOK -> '+' */ + { 0x4D, 0x4D }, /* THAI CHARACTER NIKHAHIT -> 'M' */ + { 0x50, 0x30 }, /* THAI DIGIT ZERO -> '0' */ + { 0x51, 0x31 }, /* THAI DIGIT ONE -> '1' */ + { 0x52, 0x32 }, /* THAI DIGIT TWO -> '2' */ + { 0x53, 0x33 }, /* THAI DIGIT THREE -> '3' */ + { 0x54, 0x34 }, /* THAI DIGIT FOUR -> '4' */ + { 0x55, 0x35 }, /* THAI DIGIT FIVE -> '5' */ + { 0x56, 0x36 }, /* THAI DIGIT SIX -> '6' */ + { 0x57, 0x37 }, /* THAI DIGIT SEVEN -> '7' */ + { 0x58, 0x38 }, /* THAI DIGIT EIGHT -> '8' */ + { 0x59, 0x39 }, /* THAI DIGIT NINE -> '9' */ + { 0x81, 0x6B }, /* LAO LETTER KO -> 'k' */ + { 0x8A, 0x73 }, /* LAO LETTER SO TAM -> 's' */ + { 0x94, 0x64 }, /* LAO LETTER DO -> 'd' */ + { 0x95, 0x68 }, /* LAO LETTER TO -> 'h' */ + { 0x99, 0x6E }, /* LAO LETTER NO -> 'n' */ + { 0x9A, 0x62 }, /* LAO LETTER BO -> 'b' */ + { 0x9B, 0x70 }, /* LAO LETTER PO -> 'p' */ + { 0x9D, 0x66 }, /* LAO LETTER FO TAM -> 'f' */ + { 0x9F, 0x66 }, /* LAO LETTER FO SUNG -> 'f' */ + { 0xA1, 0x6D }, /* LAO LETTER MO -> 'm' */ + { 0xA2, 0x79 }, /* LAO LETTER YO -> 'y' */ + { 0xA3, 0x72 }, /* LAO LETTER LO LING -> 'r' */ + { 0xA5, 0x6C }, /* LAO LETTER LO LOOT -> 'l' */ + { 0xA7, 0x77 }, /* LAO LETTER WO -> 'w' */ + { 0xAA, 0x73 }, /* LAO LETTER SO SUNG -> 's' */ + { 0xAB, 0x68 }, /* LAO LETTER HO SUNG -> 'h' */ + { 0xAD, 0x60 }, /* LAO LETTER O -> '`' */ + { 0xAF, 0x7E }, /* LAO ELLIPSIS -> '~' */ + { 0xB0, 0x61 }, /* LAO VOWEL SIGN A -> 'a' */ + { 0xB4, 0x69 }, /* LAO VOWEL SIGN I -> 'i' */ + { 0xB6, 0x79 }, /* LAO VOWEL SIGN Y -> 'y' */ + { 0xB8, 0x75 }, /* LAO VOWEL SIGN U -> 'u' */ + { 0xBB, 0x6F }, /* LAO VOWEL SIGN MAI KON -> 'o' */ + { 0xBC, 0x6C }, /* LAO SEMIVOWEL SIGN LO -> 'l' */ + { 0xC0, 0x65 }, /* LAO VOWEL SIGN E -> 'e' */ + { 0xC2, 0x6F }, /* LAO VOWEL SIGN O -> 'o' */ + { 0xC6, 0x2B }, /* LAO KO LA -> '+' */ + { 0xCD, 0x4D }, /* LAO NIGGAHITA -> 'M' */ + { 0xD0, 0x30 }, /* LAO DIGIT ZERO -> '0' */ + { 0xD1, 0x31 }, /* LAO DIGIT ONE -> '1' */ + { 0xD2, 0x32 }, /* LAO DIGIT TWO -> '2' */ + { 0xD3, 0x33 }, /* LAO DIGIT THREE -> '3' */ + { 0xD4, 0x34 }, /* LAO DIGIT FOUR -> '4' */ + { 0xD5, 0x35 }, /* LAO DIGIT FIVE -> '5' */ + { 0xD6, 0x36 }, /* LAO DIGIT SIX -> '6' */ + { 0xD7, 0x37 }, /* LAO DIGIT SEVEN -> '7' */ + { 0xD8, 0x38 }, /* LAO DIGIT EIGHT -> '8' */ + { 0xD9, 0x39 }, /* LAO DIGIT NINE -> '9' */ + /* Entries for page 0x0F */ + { 0x0B, 0x2D }, /* TIBETAN MARK INTERSYLLABIC TSHEG -> '-' */ + { 0x20, 0x30 }, /* TIBETAN DIGIT ZERO -> '0' */ + { 0x21, 0x31 }, /* TIBETAN DIGIT ONE -> '1' */ + { 0x22, 0x32 }, /* TIBETAN DIGIT TWO -> '2' */ + { 0x23, 0x33 }, /* TIBETAN DIGIT THREE -> '3' */ + { 0x24, 0x34 }, /* TIBETAN DIGIT FOUR -> '4' */ + { 0x25, 0x35 }, /* TIBETAN DIGIT FIVE -> '5' */ + { 0x26, 0x36 }, /* TIBETAN DIGIT SIX -> '6' */ + { 0x27, 0x37 }, /* TIBETAN DIGIT SEVEN -> '7' */ + { 0x28, 0x38 }, /* TIBETAN DIGIT EIGHT -> '8' */ + { 0x29, 0x39 }, /* TIBETAN DIGIT NINE -> '9' */ + { 0x34, 0x2B }, /* TIBETAN MARK BSDUS RTAGS -> '+' */ + { 0x35, 0x2A }, /* TIBETAN MARK NGAS BZUNG NYI ZLA -> '*' */ + { 0x36, 0x5E }, /* TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN -> '^' */ + { 0x37, 0x5F }, /* TIBETAN MARK NGAS BZUNG SGOR RTAGS -> '_' */ + { 0x39, 0x7E }, /* TIBETAN MARK TSA -PHRU -> '~' */ + { 0x3B, 0x5D }, /* TIBETAN MARK GUG RTAGS GYAS -> ']' */ + { 0x40, 0x6B }, /* TIBETAN LETTER KA -> 'k' */ + { 0x42, 0x67 }, /* TIBETAN LETTER GA -> 'g' */ + { 0x45, 0x63 }, /* TIBETAN LETTER CA -> 'c' */ + { 0x47, 0x6A }, /* TIBETAN LETTER JA -> 'j' */ + { 0x4F, 0x74 }, /* TIBETAN LETTER TA -> 't' */ + { 0x51, 0x64 }, /* TIBETAN LETTER DA -> 'd' */ + { 0x53, 0x6E }, /* TIBETAN LETTER NA -> 'n' */ + { 0x54, 0x70 }, /* TIBETAN LETTER PA -> 'p' */ + { 0x56, 0x62 }, /* TIBETAN LETTER BA -> 'b' */ + { 0x58, 0x6D }, /* TIBETAN LETTER MA -> 'm' */ + { 0x5D, 0x77 }, /* TIBETAN LETTER WA -> 'w' */ + { 0x5F, 0x7A }, /* TIBETAN LETTER ZA -> 'z' */ + { 0x60, 0x27 }, /* TIBETAN LETTER -A -> ''' */ + { 0x61, 0x79 }, /* TIBETAN LETTER YA -> 'y' */ + { 0x62, 0x72 }, /* TIBETAN LETTER RA -> 'r' */ + { 0x63, 0x6C }, /* TIBETAN LETTER LA -> 'l' */ + { 0x66, 0x73 }, /* TIBETAN LETTER SA -> 's' */ + { 0x67, 0x68 }, /* TIBETAN LETTER HA -> 'h' */ + { 0x68, 0x61 }, /* TIBETAN LETTER A -> 'a' */ + { 0x6A, 0x72 }, /* TIBETAN LETTER FIXED-FORM RA -> 'r' */ + { 0x72, 0x69 }, /* TIBETAN VOWEL SIGN I -> 'i' */ + { 0x74, 0x75 }, /* TIBETAN VOWEL SIGN U -> 'u' */ + { 0x76, 0x52 }, /* TIBETAN VOWEL SIGN VOCALIC R -> 'R' */ + { 0x78, 0x4C }, /* TIBETAN VOWEL SIGN VOCALIC L -> 'L' */ + { 0x7A, 0x65 }, /* TIBETAN VOWEL SIGN E -> 'e' */ + { 0x7C, 0x6F }, /* TIBETAN VOWEL SIGN O -> 'o' */ + { 0x7E, 0x4D }, /* TIBETAN SIGN RJES SU NGA RO -> 'M' */ + { 0x7F, 0x48 }, /* TIBETAN SIGN RNAM BCAD -> 'H' */ + { 0x80, 0x69 }, /* TIBETAN VOWEL SIGN REVERSED I -> 'i' */ + { 0x90, 0x6B }, /* TIBETAN SUBJOINED LETTER KA -> 'k' */ + { 0x92, 0x67 }, /* TIBETAN SUBJOINED LETTER GA -> 'g' */ + { 0x95, 0x63 }, /* TIBETAN SUBJOINED LETTER CA -> 'c' */ + { 0x97, 0x6A }, /* TIBETAN SUBJOINED LETTER JA -> 'j' */ + { 0x9F, 0x74 }, /* TIBETAN SUBJOINED LETTER TA -> 't' */ + { 0xA1, 0x64 }, /* TIBETAN SUBJOINED LETTER DA -> 'd' */ + { 0xA3, 0x6E }, /* TIBETAN SUBJOINED LETTER NA -> 'n' */ + { 0xA4, 0x70 }, /* TIBETAN SUBJOINED LETTER PA -> 'p' */ + { 0xA6, 0x62 }, /* TIBETAN SUBJOINED LETTER BA -> 'b' */ + { 0xA8, 0x6D }, /* TIBETAN SUBJOINED LETTER MA -> 'm' */ + { 0xAD, 0x77 }, /* TIBETAN SUBJOINED LETTER WA -> 'w' */ + { 0xAF, 0x7A }, /* TIBETAN SUBJOINED LETTER ZA -> 'z' */ + { 0xB0, 0x27 }, /* TIBETAN SUBJOINED LETTER -A -> ''' */ + { 0xB1, 0x79 }, /* TIBETAN SUBJOINED LETTER YA -> 'y' */ + { 0xB2, 0x72 }, /* TIBETAN SUBJOINED LETTER RA -> 'r' */ + { 0xB3, 0x6C }, /* TIBETAN SUBJOINED LETTER LA -> 'l' */ + { 0xB6, 0x73 }, /* TIBETAN SUBJOINED LETTER SA -> 's' */ + { 0xB7, 0x68 }, /* TIBETAN SUBJOINED LETTER HA -> 'h' */ + { 0xB8, 0x61 }, /* TIBETAN SUBJOINED LETTER A -> 'a' */ + { 0xBA, 0x77 }, /* TIBETAN SUBJOINED LETTER FIXED-FORM WA -> 'w' */ + { 0xBB, 0x79 }, /* TIBETAN SUBJOINED LETTER FIXED-FORM YA -> 'y' */ + { 0xBC, 0x72 }, /* TIBETAN SUBJOINED LETTER FIXED-FORM RA -> 'r' */ + { 0xBE, 0x58 }, /* TIBETAN KU RU KHA -> 'X' */ + /* Entries for page 0x10 */ + { 0x00, 0x6B }, /* MYANMAR LETTER KA -> 'k' */ + { 0x02, 0x67 }, /* MYANMAR LETTER GA -> 'g' */ + { 0x05, 0x63 }, /* MYANMAR LETTER CA -> 'c' */ + { 0x07, 0x6A }, /* MYANMAR LETTER JA -> 'j' */ + { 0x12, 0x64 }, /* MYANMAR LETTER DA -> 'd' */ + { 0x14, 0x6E }, /* MYANMAR LETTER NA -> 'n' */ + { 0x15, 0x70 }, /* MYANMAR LETTER PA -> 'p' */ + { 0x17, 0x62 }, /* MYANMAR LETTER BA -> 'b' */ + { 0x19, 0x6D }, /* MYANMAR LETTER MA -> 'm' */ + { 0x1A, 0x79 }, /* MYANMAR LETTER YA -> 'y' */ + { 0x1B, 0x72 }, /* MYANMAR LETTER RA -> 'r' */ + { 0x1C, 0x6C }, /* MYANMAR LETTER LA -> 'l' */ + { 0x1D, 0x77 }, /* MYANMAR LETTER WA -> 'w' */ + { 0x1E, 0x73 }, /* MYANMAR LETTER SA -> 's' */ + { 0x1F, 0x68 }, /* MYANMAR LETTER HA -> 'h' */ + { 0x21, 0x61 }, /* MYANMAR LETTER A -> 'a' */ + { 0x23, 0x69 }, /* MYANMAR LETTER I -> 'i' */ + { 0x25, 0x75 }, /* MYANMAR LETTER U -> 'u' */ + { 0x27, 0x65 }, /* MYANMAR LETTER E -> 'e' */ + { 0x29, 0x6F }, /* MYANMAR LETTER O -> 'o' */ + { 0x2D, 0x69 }, /* MYANMAR VOWEL SIGN I -> 'i' */ + { 0x2F, 0x75 }, /* MYANMAR VOWEL SIGN U -> 'u' */ + { 0x31, 0x65 }, /* MYANMAR VOWEL SIGN E -> 'e' */ + { 0x36, 0x4E }, /* MYANMAR SIGN ANUSVARA -> 'N' */ + { 0x37, 0x27 }, /* MYANMAR SIGN DOT BELOW -> ''' */ + { 0x38, 0x3A }, /* MYANMAR SIGN VISARGA -> ':' */ + { 0x40, 0x30 }, /* MYANMAR DIGIT ZERO -> '0' */ + { 0x41, 0x31 }, /* MYANMAR DIGIT ONE -> '1' */ + { 0x42, 0x32 }, /* MYANMAR DIGIT TWO -> '2' */ + { 0x43, 0x33 }, /* MYANMAR DIGIT THREE -> '3' */ + { 0x44, 0x34 }, /* MYANMAR DIGIT FOUR -> '4' */ + { 0x45, 0x35 }, /* MYANMAR DIGIT FIVE -> '5' */ + { 0x46, 0x36 }, /* MYANMAR DIGIT SIX -> '6' */ + { 0x47, 0x37 }, /* MYANMAR DIGIT SEVEN -> '7' */ + { 0x48, 0x38 }, /* MYANMAR DIGIT EIGHT -> '8' */ + { 0x49, 0x39 }, /* MYANMAR DIGIT NINE -> '9' */ + { 0x52, 0x52 }, /* MYANMAR LETTER VOCALIC R -> 'R' */ + { 0x54, 0x4C }, /* MYANMAR LETTER VOCALIC L -> 'L' */ + { 0x56, 0x52 }, /* MYANMAR VOWEL SIGN VOCALIC R -> 'R' */ + { 0x58, 0x4C }, /* MYANMAR VOWEL SIGN VOCALIC L -> 'L' */ + { 0xA0, 0x41 }, /* GEORGIAN CAPITAL LETTER AN -> 'A' */ + { 0xA1, 0x42 }, /* GEORGIAN CAPITAL LETTER BAN -> 'B' */ + { 0xA2, 0x47 }, /* GEORGIAN CAPITAL LETTER GAN -> 'G' */ + { 0xA3, 0x44 }, /* GEORGIAN CAPITAL LETTER DON -> 'D' */ + { 0xA4, 0x45 }, /* GEORGIAN CAPITAL LETTER EN -> 'E' */ + { 0xA5, 0x56 }, /* GEORGIAN CAPITAL LETTER VIN -> 'V' */ + { 0xA6, 0x5A }, /* GEORGIAN CAPITAL LETTER ZEN -> 'Z' */ + { 0xA8, 0x49 }, /* GEORGIAN CAPITAL LETTER IN -> 'I' */ + { 0xA9, 0x4B }, /* GEORGIAN CAPITAL LETTER KAN -> 'K' */ + { 0xAA, 0x4C }, /* GEORGIAN CAPITAL LETTER LAS -> 'L' */ + { 0xAB, 0x4D }, /* GEORGIAN CAPITAL LETTER MAN -> 'M' */ + { 0xAC, 0x4E }, /* GEORGIAN CAPITAL LETTER NAR -> 'N' */ + { 0xAD, 0x4F }, /* GEORGIAN CAPITAL LETTER ON -> 'O' */ + { 0xAE, 0x50 }, /* GEORGIAN CAPITAL LETTER PAR -> 'P' */ + { 0xB0, 0x52 }, /* GEORGIAN CAPITAL LETTER RAE -> 'R' */ + { 0xB1, 0x53 }, /* GEORGIAN CAPITAL LETTER SAN -> 'S' */ + { 0xB2, 0x54 }, /* GEORGIAN CAPITAL LETTER TAR -> 'T' */ + { 0xB3, 0x55 }, /* GEORGIAN CAPITAL LETTER UN -> 'U' */ + { 0xB7, 0x51 }, /* GEORGIAN CAPITAL LETTER QAR -> 'Q' */ + { 0xBC, 0x43 }, /* GEORGIAN CAPITAL LETTER CIL -> 'C' */ + { 0xBE, 0x58 }, /* GEORGIAN CAPITAL LETTER XAN -> 'X' */ + { 0xBF, 0x4A }, /* GEORGIAN CAPITAL LETTER JHAN -> 'J' */ + { 0xC0, 0x48 }, /* GEORGIAN CAPITAL LETTER HAE -> 'H' */ + { 0xC1, 0x45 }, /* GEORGIAN CAPITAL LETTER HE -> 'E' */ + { 0xC2, 0x59 }, /* GEORGIAN CAPITAL LETTER HIE -> 'Y' */ + { 0xC3, 0x57 }, /* GEORGIAN CAPITAL LETTER WE -> 'W' */ + { 0xD0, 0x61 }, /* GEORGIAN LETTER AN -> 'a' */ + { 0xD1, 0x62 }, /* GEORGIAN LETTER BAN -> 'b' */ + { 0xD2, 0x67 }, /* GEORGIAN LETTER GAN -> 'g' */ + { 0xD3, 0x64 }, /* GEORGIAN LETTER DON -> 'd' */ + { 0xD4, 0x65 }, /* GEORGIAN LETTER EN -> 'e' */ + { 0xD5, 0x76 }, /* GEORGIAN LETTER VIN -> 'v' */ + { 0xD6, 0x7A }, /* GEORGIAN LETTER ZEN -> 'z' */ + { 0xD8, 0x69 }, /* GEORGIAN LETTER IN -> 'i' */ + { 0xD9, 0x6B }, /* GEORGIAN LETTER KAN -> 'k' */ + { 0xDA, 0x6C }, /* GEORGIAN LETTER LAS -> 'l' */ + { 0xDB, 0x6D }, /* GEORGIAN LETTER MAN -> 'm' */ + { 0xDC, 0x6E }, /* GEORGIAN LETTER NAR -> 'n' */ + { 0xDD, 0x6F }, /* GEORGIAN LETTER ON -> 'o' */ + { 0xDE, 0x70 }, /* GEORGIAN LETTER PAR -> 'p' */ + { 0xE0, 0x72 }, /* GEORGIAN LETTER RAE -> 'r' */ + { 0xE1, 0x73 }, /* GEORGIAN LETTER SAN -> 's' */ + { 0xE2, 0x74 }, /* GEORGIAN LETTER TAR -> 't' */ + { 0xE3, 0x75 }, /* GEORGIAN LETTER UN -> 'u' */ + { 0xE7, 0x71 }, /* GEORGIAN LETTER QAR -> 'q' */ + { 0xEC, 0x63 }, /* GEORGIAN LETTER CIL -> 'c' */ + { 0xEE, 0x78 }, /* GEORGIAN LETTER XAN -> 'x' */ + { 0xEF, 0x6A }, /* GEORGIAN LETTER JHAN -> 'j' */ + { 0xF0, 0x68 }, /* GEORGIAN LETTER HAE -> 'h' */ + { 0xF1, 0x65 }, /* GEORGIAN LETTER HE -> 'e' */ + { 0xF2, 0x79 }, /* GEORGIAN LETTER HIE -> 'y' */ + { 0xF3, 0x77 }, /* GEORGIAN LETTER WE -> 'w' */ + { 0xF6, 0x66 }, /* GEORGIAN LETTER FI -> 'f' */ + /* Entries for page 0x11 */ + { 0x00, 0x67 }, /* HANGUL CHOSEONG KIYEOK -> 'g' */ + { 0x02, 0x6E }, /* HANGUL CHOSEONG NIEUN -> 'n' */ + { 0x03, 0x64 }, /* HANGUL CHOSEONG TIKEUT -> 'd' */ + { 0x05, 0x72 }, /* HANGUL CHOSEONG RIEUL -> 'r' */ + { 0x06, 0x6D }, /* HANGUL CHOSEONG MIEUM -> 'm' */ + { 0x07, 0x62 }, /* HANGUL CHOSEONG PIEUP -> 'b' */ + { 0x09, 0x73 }, /* HANGUL CHOSEONG SIOS -> 's' */ + { 0x0C, 0x6A }, /* HANGUL CHOSEONG CIEUC -> 'j' */ + { 0x0E, 0x63 }, /* HANGUL CHOSEONG CHIEUCH -> 'c' */ + { 0x0F, 0x6B }, /* HANGUL CHOSEONG KHIEUKH -> 'k' */ + { 0x10, 0x74 }, /* HANGUL CHOSEONG THIEUTH -> 't' */ + { 0x11, 0x70 }, /* HANGUL CHOSEONG PHIEUPH -> 'p' */ + { 0x12, 0x68 }, /* HANGUL CHOSEONG HIEUH -> 'h' */ + { 0x35, 0x73 }, /* HANGUL CHOSEONG SIOS-IEUNG -> 's' */ + { 0x40, 0x5A }, /* HANGUL CHOSEONG PANSIOS -> 'Z' */ + { 0x41, 0x67 }, /* HANGUL CHOSEONG IEUNG-KIYEOK -> 'g' */ + { 0x42, 0x64 }, /* HANGUL CHOSEONG IEUNG-TIKEUT -> 'd' */ + { 0x43, 0x6D }, /* HANGUL CHOSEONG IEUNG-MIEUM -> 'm' */ + { 0x44, 0x62 }, /* HANGUL CHOSEONG IEUNG-PIEUP -> 'b' */ + { 0x45, 0x73 }, /* HANGUL CHOSEONG IEUNG-SIOS -> 's' */ + { 0x46, 0x5A }, /* HANGUL CHOSEONG IEUNG-PANSIOS -> 'Z' */ + { 0x48, 0x6A }, /* HANGUL CHOSEONG IEUNG-CIEUC -> 'j' */ + { 0x49, 0x63 }, /* HANGUL CHOSEONG IEUNG-CHIEUCH -> 'c' */ + { 0x4A, 0x74 }, /* HANGUL CHOSEONG IEUNG-THIEUTH -> 't' */ + { 0x4B, 0x70 }, /* HANGUL CHOSEONG IEUNG-PHIEUPH -> 'p' */ + { 0x4C, 0x4E }, /* HANGUL CHOSEONG YESIEUNG -> 'N' */ + { 0x4D, 0x6A }, /* HANGUL CHOSEONG CIEUC-IEUNG -> 'j' */ + { 0x59, 0x51 }, /* HANGUL CHOSEONG YEORINHIEUH -> 'Q' */ + { 0x61, 0x61 }, /* HANGUL JUNGSEONG A -> 'a' */ + { 0x66, 0x65 }, /* HANGUL JUNGSEONG E -> 'e' */ + { 0x69, 0x6F }, /* HANGUL JUNGSEONG O -> 'o' */ + { 0x6E, 0x75 }, /* HANGUL JUNGSEONG U -> 'u' */ + { 0x75, 0x69 }, /* HANGUL JUNGSEONG I -> 'i' */ + { 0x9E, 0x55 }, /* HANGUL JUNGSEONG ARAEA -> 'U' */ + { 0xA8, 0x67 }, /* HANGUL JONGSEONG KIYEOK -> 'g' */ + { 0xAB, 0x6E }, /* HANGUL JONGSEONG NIEUN -> 'n' */ + { 0xAE, 0x64 }, /* HANGUL JONGSEONG TIKEUT -> 'd' */ + { 0xAF, 0x6C }, /* HANGUL JONGSEONG RIEUL -> 'l' */ + { 0xB7, 0x6D }, /* HANGUL JONGSEONG MIEUM -> 'm' */ + { 0xB8, 0x62 }, /* HANGUL JONGSEONG PIEUP -> 'b' */ + { 0xBA, 0x73 }, /* HANGUL JONGSEONG SIOS -> 's' */ + { 0xBD, 0x6A }, /* HANGUL JONGSEONG CIEUC -> 'j' */ + { 0xBE, 0x63 }, /* HANGUL JONGSEONG CHIEUCH -> 'c' */ + { 0xBF, 0x6B }, /* HANGUL JONGSEONG KHIEUKH -> 'k' */ + { 0xC0, 0x74 }, /* HANGUL JONGSEONG THIEUTH -> 't' */ + { 0xC1, 0x70 }, /* HANGUL JONGSEONG PHIEUPH -> 'p' */ + { 0xC2, 0x68 }, /* HANGUL JONGSEONG HIEUH -> 'h' */ + { 0xEB, 0x5A }, /* HANGUL JONGSEONG PANSIOS -> 'Z' */ + { 0xEC, 0x67 }, /* HANGUL JONGSEONG IEUNG-KIYEOK -> 'g' */ + { 0xF0, 0x4E }, /* HANGUL JONGSEONG YESIEUNG -> 'N' */ + { 0xF9, 0x51 }, /* HANGUL JONGSEONG YEORINHIEUH -> 'Q' */ + /* Entries for page 0x13 */ + { 0x61, 0x20 }, /* ETHIOPIC WORDSPACE -> ' ' */ + { 0x62, 0x2E }, /* ETHIOPIC FULL STOP -> '.' */ + { 0x63, 0x2C }, /* ETHIOPIC COMMA -> ',' */ + { 0x64, 0x3B }, /* ETHIOPIC SEMICOLON -> ';' */ + { 0x65, 0x3A }, /* ETHIOPIC COLON -> ':' */ + { 0x67, 0x3F }, /* ETHIOPIC QUESTION MARK -> '?' */ + { 0x69, 0x31 }, /* ETHIOPIC DIGIT ONE -> '1' */ + { 0x6A, 0x32 }, /* ETHIOPIC DIGIT TWO -> '2' */ + { 0x6B, 0x33 }, /* ETHIOPIC DIGIT THREE -> '3' */ + { 0x6C, 0x34 }, /* ETHIOPIC DIGIT FOUR -> '4' */ + { 0x6D, 0x35 }, /* ETHIOPIC DIGIT FIVE -> '5' */ + { 0x6E, 0x36 }, /* ETHIOPIC DIGIT SIX -> '6' */ + { 0x6F, 0x37 }, /* ETHIOPIC DIGIT SEVEN -> '7' */ + { 0x70, 0x38 }, /* ETHIOPIC DIGIT EIGHT -> '8' */ + { 0x71, 0x39 }, /* ETHIOPIC DIGIT NINE -> '9' */ + { 0xA0, 0x61 }, /* CHEROKEE LETTER A -> 'a' */ + { 0xA1, 0x65 }, /* CHEROKEE LETTER E -> 'e' */ + { 0xA2, 0x69 }, /* CHEROKEE LETTER I -> 'i' */ + { 0xA3, 0x6F }, /* CHEROKEE LETTER O -> 'o' */ + { 0xA4, 0x75 }, /* CHEROKEE LETTER U -> 'u' */ + { 0xA5, 0x76 }, /* CHEROKEE LETTER V -> 'v' */ + { 0xCD, 0x73 }, /* CHEROKEE LETTER S -> 's' */ + /* Entries for page 0x14 */ + { 0x01, 0x65 }, /* CANADIAN SYLLABICS E -> 'e' */ + { 0x03, 0x69 }, /* CANADIAN SYLLABICS I -> 'i' */ + { 0x05, 0x6F }, /* CANADIAN SYLLABICS O -> 'o' */ + { 0x09, 0x69 }, /* CANADIAN SYLLABICS CARRIER I -> 'i' */ + { 0x0A, 0x61 }, /* CANADIAN SYLLABICS A -> 'a' */ + { 0x1D, 0x77 }, /* CANADIAN SYLLABICS Y-CREE W -> 'w' */ + { 0x1E, 0x27 }, /* CANADIAN SYLLABICS GLOTTAL STOP -> ''' */ + { 0x1F, 0x74 }, /* CANADIAN SYLLABICS FINAL ACUTE -> 't' */ + { 0x20, 0x6B }, /* CANADIAN SYLLABICS FINAL GRAVE -> 'k' */ + { 0x22, 0x73 }, /* CANADIAN SYLLABICS FINAL TOP HALF RING -> 's' */ + { 0x23, 0x6E }, /* CANADIAN SYLLABICS FINAL RIGHT HALF RING -> 'n' */ + { 0x24, 0x77 }, /* CANADIAN SYLLABICS FINAL RING -> 'w' */ + { 0x25, 0x6E }, /* CANADIAN SYLLABICS FINAL DOUBLE ACUTE -> 'n' */ + { 0x27, 0x77 }, /* CANADIAN SYLLABICS FINAL MIDDLE DOT -> 'w' */ + { 0x28, 0x63 }, /* CANADIAN SYLLABICS FINAL SHORT HORIZONTAL STROKE -> 'c' */ + { 0x29, 0x3F }, /* CANADIAN SYLLABICS FINAL PLUS -> '?' */ + { 0x2A, 0x6C }, /* CANADIAN SYLLABICS FINAL DOWN TACK -> 'l' */ + { 0x49, 0x70 }, /* CANADIAN SYLLABICS P -> 'p' */ + { 0x4A, 0x70 }, /* CANADIAN SYLLABICS WEST-CREE P -> 'p' */ + { 0x4B, 0x68 }, /* CANADIAN SYLLABICS CARRIER H -> 'h' */ + { 0x66, 0x74 }, /* CANADIAN SYLLABICS T -> 't' */ + { 0x83, 0x6B }, /* CANADIAN SYLLABICS K -> 'k' */ + { 0xA1, 0x63 }, /* CANADIAN SYLLABICS C -> 'c' */ + { 0xBB, 0x6D }, /* CANADIAN SYLLABICS M -> 'm' */ + { 0xBC, 0x6D }, /* CANADIAN SYLLABICS WEST-CREE M -> 'm' */ + { 0xBE, 0x6D }, /* CANADIAN SYLLABICS ATHAPASCAN M -> 'm' */ + { 0xBF, 0x6D }, /* CANADIAN SYLLABICS SAYISI M -> 'm' */ + { 0xD0, 0x6E }, /* CANADIAN SYLLABICS N -> 'n' */ + { 0xEA, 0x00 }, /* CANADIAN SYLLABICS L -> ... */ + { 0xEC, 0x6C }, /* CANADIAN SYLLABICS MEDIAL L -> 'l' */ + /* Entries for page 0x15 */ + { 0x05, 0x73 }, /* CANADIAN SYLLABICS S -> 's' */ + { 0x06, 0x73 }, /* CANADIAN SYLLABICS ATHAPASCAN S -> 's' */ + { 0x08, 0x73 }, /* CANADIAN SYLLABICS BLACKFOOT S -> 's' */ + { 0x3E, 0x00 }, /* CANADIAN SYLLABICS Y -> ... */ + { 0x40, 0x79 }, /* CANADIAN SYLLABICS WEST-CREE Y -> 'y' */ + { 0x50, 0x00 }, /* CANADIAN SYLLABICS R -> ... */ + { 0x52, 0x72 }, /* CANADIAN SYLLABICS MEDIAL R -> 'r' */ + { 0x5D, 0x66 }, /* CANADIAN SYLLABICS F -> 'f' */ + { 0x7B, 0x68 }, /* CANADIAN SYLLABICS NUNAVIK H -> 'h' */ + { 0x7C, 0x68 }, /* CANADIAN SYLLABICS NUNAVUT H -> 'h' */ + { 0x85, 0x71 }, /* CANADIAN SYLLABICS Q -> 'q' */ + { 0xAF, 0x62 }, /* CANADIAN SYLLABICS AIVILIK B -> 'b' */ + { 0xB0, 0x65 }, /* CANADIAN SYLLABICS BLACKFOOT E -> 'e' */ + { 0xB1, 0x69 }, /* CANADIAN SYLLABICS BLACKFOOT I -> 'i' */ + { 0xB2, 0x6F }, /* CANADIAN SYLLABICS BLACKFOOT O -> 'o' */ + { 0xB3, 0x61 }, /* CANADIAN SYLLABICS BLACKFOOT A -> 'a' */ + { 0xEE, 0x70 }, /* CANADIAN SYLLABICS CARRIER P -> 'p' */ + /* Entries for page 0x16 */ + { 0x46, 0x7A }, /* CANADIAN SYLLABICS CARRIER Z -> 'z' */ + { 0x47, 0x7A }, /* CANADIAN SYLLABICS CARRIER INITIAL Z -> 'z' */ + { 0x6D, 0x58 }, /* CANADIAN SYLLABICS CHI SIGN -> 'X' */ + { 0x6E, 0x2E }, /* CANADIAN SYLLABICS FULL STOP -> '.' */ + { 0x80, 0x20 }, /* OGHAM SPACE MARK -> ' ' */ + { 0x81, 0x62 }, /* OGHAM LETTER BEITH -> 'b' */ + { 0x82, 0x6C }, /* OGHAM LETTER LUIS -> 'l' */ + { 0x83, 0x66 }, /* OGHAM LETTER FEARN -> 'f' */ + { 0x84, 0x73 }, /* OGHAM LETTER SAIL -> 's' */ + { 0x85, 0x6E }, /* OGHAM LETTER NION -> 'n' */ + { 0x86, 0x68 }, /* OGHAM LETTER UATH -> 'h' */ + { 0x87, 0x64 }, /* OGHAM LETTER DAIR -> 'd' */ + { 0x88, 0x74 }, /* OGHAM LETTER TINNE -> 't' */ + { 0x89, 0x63 }, /* OGHAM LETTER COLL -> 'c' */ + { 0x8A, 0x71 }, /* OGHAM LETTER CEIRT -> 'q' */ + { 0x8B, 0x6D }, /* OGHAM LETTER MUIN -> 'm' */ + { 0x8C, 0x67 }, /* OGHAM LETTER GORT -> 'g' */ + { 0x8E, 0x7A }, /* OGHAM LETTER STRAIF -> 'z' */ + { 0x8F, 0x72 }, /* OGHAM LETTER RUIS -> 'r' */ + { 0x90, 0x61 }, /* OGHAM LETTER AILM -> 'a' */ + { 0x91, 0x6F }, /* OGHAM LETTER ONN -> 'o' */ + { 0x92, 0x75 }, /* OGHAM LETTER UR -> 'u' */ + { 0x93, 0x65 }, /* OGHAM LETTER EADHADH -> 'e' */ + { 0x94, 0x69 }, /* OGHAM LETTER IODHADH -> 'i' */ + { 0x98, 0x70 }, /* OGHAM LETTER IFIN -> 'p' */ + { 0x99, 0x78 }, /* OGHAM LETTER EAMHANCHOLL -> 'x' */ + { 0x9A, 0x70 }, /* OGHAM LETTER PEITH -> 'p' */ + { 0x9B, 0x3C }, /* OGHAM FEATHER MARK -> '<' */ + { 0x9C, 0x3E }, /* OGHAM REVERSED FEATHER MARK -> '>' */ + { 0xA0, 0x66 }, /* RUNIC LETTER FEHU FEOH FE F -> 'f' */ + { 0xA1, 0x76 }, /* RUNIC LETTER V -> 'v' */ + { 0xA2, 0x75 }, /* RUNIC LETTER URUZ UR U -> 'u' */ + { 0xA4, 0x79 }, /* RUNIC LETTER Y -> 'y' */ + { 0xA5, 0x77 }, /* RUNIC LETTER W -> 'w' */ + { 0xA8, 0x61 }, /* RUNIC LETTER ANSUZ A -> 'a' */ + { 0xA9, 0x6F }, /* RUNIC LETTER OS O -> 'o' */ + { 0xAC, 0x00 }, /* RUNIC LETTER LONG-BRANCH-OSS O -> ... */ + { 0xAE, 0x6F }, /* RUNIC LETTER O -> 'o' */ + { 0xB1, 0x72 }, /* RUNIC LETTER RAIDO RAD REID R -> 'r' */ + { 0xB2, 0x6B }, /* RUNIC LETTER KAUNA -> 'k' */ + { 0xB3, 0x63 }, /* RUNIC LETTER CEN -> 'c' */ + { 0xB4, 0x6B }, /* RUNIC LETTER KAUN K -> 'k' */ + { 0xB5, 0x67 }, /* RUNIC LETTER G -> 'g' */ + { 0xB7, 0x67 }, /* RUNIC LETTER GEBO GYFU G -> 'g' */ + { 0xB8, 0x67 }, /* RUNIC LETTER GAR -> 'g' */ + { 0xB9, 0x77 }, /* RUNIC LETTER WUNJO WYNN W -> 'w' */ + { 0xBA, 0x00 }, /* RUNIC LETTER HAGLAZ H -> ... */ + { 0xBD, 0x68 }, /* RUNIC LETTER SHORT-TWIG-HAGALL H -> 'h' */ + { 0xBE, 0x00 }, /* RUNIC LETTER NAUDIZ NYD NAUD N -> ... */ + { 0xC0, 0x6E }, /* RUNIC LETTER DOTTED-N -> 'n' */ + { 0xC1, 0x69 }, /* RUNIC LETTER ISAZ IS ISS I -> 'i' */ + { 0xC2, 0x65 }, /* RUNIC LETTER E -> 'e' */ + { 0xC3, 0x6A }, /* RUNIC LETTER JERAN J -> 'j' */ + { 0xC4, 0x67 }, /* RUNIC LETTER GER -> 'g' */ + { 0xC6, 0x61 }, /* RUNIC LETTER SHORT-TWIG-AR A -> 'a' */ + { 0xC8, 0x70 }, /* RUNIC LETTER PERTHO PEORTH P -> 'p' */ + { 0xC9, 0x7A }, /* RUNIC LETTER ALGIZ EOLHX -> 'z' */ + { 0xCA, 0x00 }, /* RUNIC LETTER SOWILO S -> ... */ + { 0xCC, 0x73 }, /* RUNIC LETTER SHORT-TWIG-SOL S -> 's' */ + { 0xCD, 0x63 }, /* RUNIC LETTER C -> 'c' */ + { 0xCE, 0x7A }, /* RUNIC LETTER Z -> 'z' */ + { 0xCF, 0x74 }, /* RUNIC LETTER TIWAZ TIR TYR T -> 't' */ + { 0xD0, 0x74 }, /* RUNIC LETTER SHORT-TWIG-TYR T -> 't' */ + { 0xD1, 0x64 }, /* RUNIC LETTER D -> 'd' */ + { 0xD2, 0x62 }, /* RUNIC LETTER BERKANAN BEORC BJARKAN B -> 'b' */ + { 0xD3, 0x62 }, /* RUNIC LETTER SHORT-TWIG-BJARKAN B -> 'b' */ + { 0xD4, 0x70 }, /* RUNIC LETTER DOTTED-P -> 'p' */ + { 0xD5, 0x70 }, /* RUNIC LETTER OPEN-P -> 'p' */ + { 0xD6, 0x65 }, /* RUNIC LETTER EHWAZ EH E -> 'e' */ + { 0xD7, 0x00 }, /* RUNIC LETTER MANNAZ MAN M -> ... */ + { 0xD9, 0x6D }, /* RUNIC LETTER SHORT-TWIG-MADR M -> 'm' */ + { 0xDA, 0x6C }, /* RUNIC LETTER LAUKAZ LAGU LOGR L -> 'l' */ + { 0xDB, 0x6C }, /* RUNIC LETTER DOTTED-L -> 'l' */ + { 0xDE, 0x64 }, /* RUNIC LETTER DAGAZ DAEG D -> 'd' */ + { 0xDF, 0x6F }, /* RUNIC LETTER OTHALAN ETHEL O -> 'o' */ + { 0xE5, 0x73 }, /* RUNIC LETTER STAN -> 's' */ + { 0xE9, 0x71 }, /* RUNIC LETTER Q -> 'q' */ + { 0xEA, 0x78 }, /* RUNIC LETTER X -> 'x' */ + { 0xEB, 0x2E }, /* RUNIC SINGLE PUNCTUATION -> '.' */ + { 0xEC, 0x3A }, /* RUNIC MULTIPLE PUNCTUATION -> ':' */ + { 0xED, 0x2B }, /* RUNIC CROSS PUNCTUATION -> '+' */ + /* Entries for page 0x17 */ + { 0x80, 0x6B }, /* KHMER LETTER KA -> 'k' */ + { 0x82, 0x67 }, /* KHMER LETTER KO -> 'g' */ + { 0x85, 0x63 }, /* KHMER LETTER CA -> 'c' */ + { 0x87, 0x6A }, /* KHMER LETTER CO -> 'j' */ + { 0x8A, 0x74 }, /* KHMER LETTER DA -> 't' */ + { 0x8C, 0x64 }, /* KHMER LETTER DO -> 'd' */ + { 0x8F, 0x74 }, /* KHMER LETTER TA -> 't' */ + { 0x91, 0x64 }, /* KHMER LETTER TO -> 'd' */ + { 0x93, 0x6E }, /* KHMER LETTER NO -> 'n' */ + { 0x94, 0x70 }, /* KHMER LETTER BA -> 'p' */ + { 0x96, 0x62 }, /* KHMER LETTER PO -> 'b' */ + { 0x98, 0x6D }, /* KHMER LETTER MO -> 'm' */ + { 0x99, 0x79 }, /* KHMER LETTER YO -> 'y' */ + { 0x9A, 0x72 }, /* KHMER LETTER RO -> 'r' */ + { 0x9B, 0x6C }, /* KHMER LETTER LO -> 'l' */ + { 0x9C, 0x76 }, /* KHMER LETTER VO -> 'v' */ + { 0x9F, 0x73 }, /* KHMER LETTER SA -> 's' */ + { 0xA0, 0x68 }, /* KHMER LETTER HA -> 'h' */ + { 0xA1, 0x6C }, /* KHMER LETTER LA -> 'l' */ + { 0xA2, 0x71 }, /* KHMER LETTER QA -> 'q' */ + { 0xA3, 0x61 }, /* KHMER INDEPENDENT VOWEL QAQ -> 'a' */ + { 0xA5, 0x69 }, /* KHMER INDEPENDENT VOWEL QI -> 'i' */ + { 0xA7, 0x75 }, /* KHMER INDEPENDENT VOWEL QU -> 'u' */ + { 0xAF, 0x65 }, /* KHMER INDEPENDENT VOWEL QE -> 'e' */ + { 0xB4, 0x61 }, /* KHMER VOWEL INHERENT AQ -> 'a' */ + { 0xB7, 0x69 }, /* KHMER VOWEL SIGN I -> 'i' */ + { 0xB9, 0x79 }, /* KHMER VOWEL SIGN Y -> 'y' */ + { 0xBB, 0x75 }, /* KHMER VOWEL SIGN U -> 'u' */ + { 0xC1, 0x65 }, /* KHMER VOWEL SIGN E -> 'e' */ + { 0xC6, 0x4D }, /* KHMER SIGN NIKAHIT -> 'M' */ + { 0xC7, 0x48 }, /* KHMER SIGN REAHMUK -> 'H' */ + { 0xCC, 0x72 }, /* KHMER SIGN ROBAT -> 'r' */ + { 0xCE, 0x21 }, /* KHMER SIGN KAKABAT -> '!' */ + { 0xD4, 0x2E }, /* KHMER SIGN KHAN -> '.' */ + { 0xD6, 0x3A }, /* KHMER SIGN CAMNUC PII KUUH -> ':' */ + { 0xD7, 0x2B }, /* KHMER SIGN LEK TOO -> '+' */ + { 0xDC, 0x27 }, /* KHMER SIGN AVAKRAHASANYA -> ''' */ + { 0xE0, 0x30 }, /* KHMER DIGIT ZERO -> '0' */ + { 0xE1, 0x31 }, /* KHMER DIGIT ONE -> '1' */ + { 0xE2, 0x32 }, /* KHMER DIGIT TWO -> '2' */ + { 0xE3, 0x33 }, /* KHMER DIGIT THREE -> '3' */ + { 0xE4, 0x34 }, /* KHMER DIGIT FOUR -> '4' */ + { 0xE5, 0x35 }, /* KHMER DIGIT FIVE -> '5' */ + { 0xE6, 0x36 }, /* KHMER DIGIT SIX -> '6' */ + { 0xE7, 0x37 }, /* KHMER DIGIT SEVEN -> '7' */ + { 0xE8, 0x38 }, /* KHMER DIGIT EIGHT -> '8' */ + { 0xE9, 0x39 }, /* KHMER DIGIT NINE -> '9' */ + /* Entries for page 0x18 */ + { 0x07, 0x2D }, /* MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER -> '-' */ + { 0x10, 0x30 }, /* MONGOLIAN DIGIT ZERO -> '0' */ + { 0x11, 0x31 }, /* MONGOLIAN DIGIT ONE -> '1' */ + { 0x12, 0x32 }, /* MONGOLIAN DIGIT TWO -> '2' */ + { 0x13, 0x33 }, /* MONGOLIAN DIGIT THREE -> '3' */ + { 0x14, 0x34 }, /* MONGOLIAN DIGIT FOUR -> '4' */ + { 0x15, 0x35 }, /* MONGOLIAN DIGIT FIVE -> '5' */ + { 0x16, 0x36 }, /* MONGOLIAN DIGIT SIX -> '6' */ + { 0x17, 0x37 }, /* MONGOLIAN DIGIT SEVEN -> '7' */ + { 0x18, 0x38 }, /* MONGOLIAN DIGIT EIGHT -> '8' */ + { 0x19, 0x39 }, /* MONGOLIAN DIGIT NINE -> '9' */ + { 0x20, 0x61 }, /* MONGOLIAN LETTER A -> 'a' */ + { 0x21, 0x65 }, /* MONGOLIAN LETTER E -> 'e' */ + { 0x22, 0x69 }, /* MONGOLIAN LETTER I -> 'i' */ + { 0x23, 0x6F }, /* MONGOLIAN LETTER O -> 'o' */ + { 0x24, 0x75 }, /* MONGOLIAN LETTER U -> 'u' */ + { 0x25, 0x4F }, /* MONGOLIAN LETTER OE -> 'O' */ + { 0x26, 0x55 }, /* MONGOLIAN LETTER UE -> 'U' */ + { 0x28, 0x6E }, /* MONGOLIAN LETTER NA -> 'n' */ + { 0x2A, 0x62 }, /* MONGOLIAN LETTER BA -> 'b' */ + { 0x2B, 0x70 }, /* MONGOLIAN LETTER PA -> 'p' */ + { 0x2C, 0x71 }, /* MONGOLIAN LETTER QA -> 'q' */ + { 0x2D, 0x67 }, /* MONGOLIAN LETTER GA -> 'g' */ + { 0x2E, 0x6D }, /* MONGOLIAN LETTER MA -> 'm' */ + { 0x2F, 0x6C }, /* MONGOLIAN LETTER LA -> 'l' */ + { 0x30, 0x73 }, /* MONGOLIAN LETTER SA -> 's' */ + { 0x32, 0x74 }, /* MONGOLIAN LETTER TA -> 't' */ + { 0x33, 0x64 }, /* MONGOLIAN LETTER DA -> 'd' */ + { 0x35, 0x6A }, /* MONGOLIAN LETTER JA -> 'j' */ + { 0x36, 0x79 }, /* MONGOLIAN LETTER YA -> 'y' */ + { 0x37, 0x72 }, /* MONGOLIAN LETTER RA -> 'r' */ + { 0x38, 0x77 }, /* MONGOLIAN LETTER WA -> 'w' */ + { 0x39, 0x66 }, /* MONGOLIAN LETTER FA -> 'f' */ + { 0x3A, 0x6B }, /* MONGOLIAN LETTER KA -> 'k' */ + { 0x3D, 0x7A }, /* MONGOLIAN LETTER ZA -> 'z' */ + { 0x3E, 0x68 }, /* MONGOLIAN LETTER HAA -> 'h' */ + { 0x43, 0x2D }, /* MONGOLIAN LETTER TODO LONG VOWEL SIGN -> '-' */ + { 0x44, 0x65 }, /* MONGOLIAN LETTER TODO E -> 'e' */ + { 0x45, 0x69 }, /* MONGOLIAN LETTER TODO I -> 'i' */ + { 0x46, 0x6F }, /* MONGOLIAN LETTER TODO O -> 'o' */ + { 0x47, 0x75 }, /* MONGOLIAN LETTER TODO U -> 'u' */ + { 0x48, 0x4F }, /* MONGOLIAN LETTER TODO OE -> 'O' */ + { 0x49, 0x55 }, /* MONGOLIAN LETTER TODO UE -> 'U' */ + { 0x4B, 0x62 }, /* MONGOLIAN LETTER TODO BA -> 'b' */ + { 0x4C, 0x70 }, /* MONGOLIAN LETTER TODO PA -> 'p' */ + { 0x4D, 0x71 }, /* MONGOLIAN LETTER TODO QA -> 'q' */ + { 0x4E, 0x67 }, /* MONGOLIAN LETTER TODO GA -> 'g' */ + { 0x4F, 0x6D }, /* MONGOLIAN LETTER TODO MA -> 'm' */ + { 0x50, 0x74 }, /* MONGOLIAN LETTER TODO TA -> 't' */ + { 0x51, 0x64 }, /* MONGOLIAN LETTER TODO DA -> 'd' */ + { 0x53, 0x6A }, /* MONGOLIAN LETTER TODO JA -> 'j' */ + { 0x55, 0x79 }, /* MONGOLIAN LETTER TODO YA -> 'y' */ + { 0x56, 0x77 }, /* MONGOLIAN LETTER TODO WA -> 'w' */ + { 0x57, 0x6B }, /* MONGOLIAN LETTER TODO KA -> 'k' */ + { 0x58, 0x67 }, /* MONGOLIAN LETTER TODO GAA -> 'g' */ + { 0x59, 0x68 }, /* MONGOLIAN LETTER TODO HAA -> 'h' */ + { 0x5D, 0x65 }, /* MONGOLIAN LETTER SIBE E -> 'e' */ + { 0x5E, 0x69 }, /* MONGOLIAN LETTER SIBE I -> 'i' */ + { 0x60, 0x55 }, /* MONGOLIAN LETTER SIBE UE -> 'U' */ + { 0x61, 0x75 }, /* MONGOLIAN LETTER SIBE U -> 'u' */ + { 0x63, 0x6B }, /* MONGOLIAN LETTER SIBE KA -> 'k' */ + { 0x64, 0x67 }, /* MONGOLIAN LETTER SIBE GA -> 'g' */ + { 0x65, 0x68 }, /* MONGOLIAN LETTER SIBE HA -> 'h' */ + { 0x66, 0x70 }, /* MONGOLIAN LETTER SIBE PA -> 'p' */ + { 0x68, 0x74 }, /* MONGOLIAN LETTER SIBE TA -> 't' */ + { 0x69, 0x64 }, /* MONGOLIAN LETTER SIBE DA -> 'd' */ + { 0x6A, 0x6A }, /* MONGOLIAN LETTER SIBE JA -> 'j' */ + { 0x6B, 0x66 }, /* MONGOLIAN LETTER SIBE FA -> 'f' */ + { 0x6C, 0x67 }, /* MONGOLIAN LETTER SIBE GAA -> 'g' */ + { 0x6D, 0x68 }, /* MONGOLIAN LETTER SIBE HAA -> 'h' */ + { 0x6F, 0x7A }, /* MONGOLIAN LETTER SIBE ZA -> 'z' */ + { 0x70, 0x72 }, /* MONGOLIAN LETTER SIBE RAA -> 'r' */ + { 0x73, 0x69 }, /* MONGOLIAN LETTER MANCHU I -> 'i' */ + { 0x74, 0x6B }, /* MONGOLIAN LETTER MANCHU KA -> 'k' */ + { 0x75, 0x72 }, /* MONGOLIAN LETTER MANCHU RA -> 'r' */ + { 0x76, 0x66 }, /* MONGOLIAN LETTER MANCHU FA -> 'f' */ + { 0x81, 0x48 }, /* MONGOLIAN LETTER ALI GALI VISARGA ONE -> 'H' */ + { 0x82, 0x58 }, /* MONGOLIAN LETTER ALI GALI DAMARU -> 'X' */ + { 0x83, 0x57 }, /* MONGOLIAN LETTER ALI GALI UBADAMA -> 'W' */ + { 0x84, 0x4D }, /* MONGOLIAN LETTER ALI GALI INVERTED UBADAMA -> 'M' */ + { 0x87, 0x61 }, /* MONGOLIAN LETTER ALI GALI A -> 'a' */ + { 0x88, 0x69 }, /* MONGOLIAN LETTER ALI GALI I -> 'i' */ + { 0x89, 0x6B }, /* MONGOLIAN LETTER ALI GALI KA -> 'k' */ + { 0x8B, 0x63 }, /* MONGOLIAN LETTER ALI GALI CA -> 'c' */ + { 0x90, 0x74 }, /* MONGOLIAN LETTER ALI GALI TA -> 't' */ + { 0x91, 0x64 }, /* MONGOLIAN LETTER ALI GALI DA -> 'd' */ + { 0x92, 0x70 }, /* MONGOLIAN LETTER ALI GALI PA -> 'p' */ + { 0x96, 0x7A }, /* MONGOLIAN LETTER ALI GALI ZA -> 'z' */ + { 0x97, 0x61 }, /* MONGOLIAN LETTER ALI GALI AH -> 'a' */ + { 0x98, 0x74 }, /* MONGOLIAN LETTER TODO ALI GALI TA -> 't' */ + { 0x9C, 0x63 }, /* MONGOLIAN LETTER MANCHU ALI GALI CA -> 'c' */ + { 0xA0, 0x74 }, /* MONGOLIAN LETTER MANCHU ALI GALI TA -> 't' */ + { 0xA5, 0x7A }, /* MONGOLIAN LETTER MANCHU ALI GALI ZA -> 'z' */ + { 0xA6, 0x75 }, /* MONGOLIAN LETTER ALI GALI HALF U -> 'u' */ + { 0xA7, 0x79 }, /* MONGOLIAN LETTER ALI GALI HALF YA -> 'y' */ + { 0xA9, 0x27 }, /* MONGOLIAN LETTER ALI GALI DAGALGA -> ''' */ + /* Entries for page 0x1D */ + { 0x00, 0x41 }, /* LATIN LETTER SMALL CAPITAL A -> 'A' */ + { 0x03, 0x42 }, /* LATIN LETTER SMALL CAPITAL BARRED B -> 'B' */ + { 0x04, 0x43 }, /* LATIN LETTER SMALL CAPITAL C -> 'C' */ + { 0x05, 0x44 }, /* LATIN LETTER SMALL CAPITAL D -> 'D' */ + { 0x06, 0x44 }, /* LATIN LETTER SMALL CAPITAL ETH -> 'D' */ + { 0x07, 0x45 }, /* LATIN LETTER SMALL CAPITAL E -> 'E' */ + { 0x08, 0x65 }, /* LATIN SMALL LETTER TURNED OPEN E -> 'e' */ + { 0x09, 0x69 }, /* LATIN SMALL LETTER TURNED I -> 'i' */ + { 0x0A, 0x4A }, /* LATIN LETTER SMALL CAPITAL J -> 'J' */ + { 0x0B, 0x4B }, /* LATIN LETTER SMALL CAPITAL K -> 'K' */ + { 0x0C, 0x4C }, /* LATIN LETTER SMALL CAPITAL L WITH STROKE -> 'L' */ + { 0x0D, 0x4D }, /* LATIN LETTER SMALL CAPITAL M -> 'M' */ + { 0x0E, 0x4E }, /* LATIN LETTER SMALL CAPITAL REVERSED N -> 'N' */ + { 0x0F, 0x4F }, /* LATIN LETTER SMALL CAPITAL O -> 'O' */ + { 0x11, 0x4F }, /* LATIN SMALL LETTER SIDEWAYS O -> 'O' */ + { 0x13, 0x4F }, /* LATIN SMALL LETTER SIDEWAYS O WITH STROKE -> 'O' */ + { 0x18, 0x50 }, /* LATIN LETTER SMALL CAPITAL P -> 'P' */ + { 0x19, 0x52 }, /* LATIN LETTER SMALL CAPITAL REVERSED R -> 'R' */ + { 0x1A, 0x52 }, /* LATIN LETTER SMALL CAPITAL TURNED R -> 'R' */ + { 0x1B, 0x54 }, /* LATIN LETTER SMALL CAPITAL T -> 'T' */ + { 0x1C, 0x55 }, /* LATIN LETTER SMALL CAPITAL U -> 'U' */ + { 0x1D, 0x75 }, /* LATIN SMALL LETTER SIDEWAYS U -> 'u' */ + { 0x1E, 0x75 }, /* LATIN SMALL LETTER SIDEWAYS DIAERESIZED U -> 'u' */ + { 0x1F, 0x6D }, /* LATIN SMALL LETTER SIDEWAYS TURNED M -> 'm' */ + { 0x20, 0x56 }, /* LATIN LETTER SMALL CAPITAL V -> 'V' */ + { 0x21, 0x57 }, /* LATIN LETTER SMALL CAPITAL W -> 'W' */ + { 0x22, 0x5A }, /* LATIN LETTER SMALL CAPITAL Z -> 'Z' */ + { 0x2C, 0x41 }, /* MODIFIER LETTER CAPITAL A -> 'A' */ + { 0x2E, 0x42 }, /* MODIFIER LETTER CAPITAL B -> 'B' */ + { 0x2F, 0x42 }, /* MODIFIER LETTER CAPITAL BARRED B -> 'B' */ + { 0x30, 0x44 }, /* MODIFIER LETTER CAPITAL D -> 'D' */ + { 0x31, 0x45 }, /* MODIFIER LETTER CAPITAL E -> 'E' */ + { 0x32, 0x45 }, /* MODIFIER LETTER CAPITAL REVERSED E -> 'E' */ + { 0x33, 0x47 }, /* MODIFIER LETTER CAPITAL G -> 'G' */ + { 0x34, 0x48 }, /* MODIFIER LETTER CAPITAL H -> 'H' */ + { 0x35, 0x49 }, /* MODIFIER LETTER CAPITAL I -> 'I' */ + { 0x36, 0x4A }, /* MODIFIER LETTER CAPITAL J -> 'J' */ + { 0x37, 0x4B }, /* MODIFIER LETTER CAPITAL K -> 'K' */ + { 0x38, 0x4C }, /* MODIFIER LETTER CAPITAL L -> 'L' */ + { 0x39, 0x4D }, /* MODIFIER LETTER CAPITAL M -> 'M' */ + { 0x3A, 0x4E }, /* MODIFIER LETTER CAPITAL N -> 'N' */ + { 0x3B, 0x4E }, /* MODIFIER LETTER CAPITAL REVERSED N -> 'N' */ + { 0x3C, 0x4F }, /* MODIFIER LETTER CAPITAL O -> 'O' */ + { 0x3E, 0x50 }, /* MODIFIER LETTER CAPITAL P -> 'P' */ + { 0x3F, 0x52 }, /* MODIFIER LETTER CAPITAL R -> 'R' */ + { 0x40, 0x54 }, /* MODIFIER LETTER CAPITAL T -> 'T' */ + { 0x41, 0x55 }, /* MODIFIER LETTER CAPITAL U -> 'U' */ + { 0x42, 0x57 }, /* MODIFIER LETTER CAPITAL W -> 'W' */ + { 0x43, 0x00 }, /* MODIFIER LETTER SMALL A -> ... */ + { 0x45, 0x61 }, /* MODIFIER LETTER SMALL ALPHA -> 'a' */ + { 0x47, 0x62 }, /* MODIFIER LETTER SMALL B -> 'b' */ + { 0x48, 0x64 }, /* MODIFIER LETTER SMALL D -> 'd' */ + { 0x49, 0x65 }, /* MODIFIER LETTER SMALL E -> 'e' */ + { 0x4B, 0x65 }, /* MODIFIER LETTER SMALL OPEN E -> 'e' */ + { 0x4C, 0x65 }, /* MODIFIER LETTER SMALL TURNED OPEN E -> 'e' */ + { 0x4D, 0x67 }, /* MODIFIER LETTER SMALL G -> 'g' */ + { 0x4E, 0x69 }, /* MODIFIER LETTER SMALL TURNED I -> 'i' */ + { 0x4F, 0x6B }, /* MODIFIER LETTER SMALL K -> 'k' */ + { 0x50, 0x6D }, /* MODIFIER LETTER SMALL M -> 'm' */ + { 0x52, 0x6F }, /* MODIFIER LETTER SMALL O -> 'o' */ + { 0x56, 0x70 }, /* MODIFIER LETTER SMALL P -> 'p' */ + { 0x57, 0x74 }, /* MODIFIER LETTER SMALL T -> 't' */ + { 0x58, 0x75 }, /* MODIFIER LETTER SMALL U -> 'u' */ + { 0x59, 0x75 }, /* MODIFIER LETTER SMALL SIDEWAYS U -> 'u' */ + { 0x5A, 0x6D }, /* MODIFIER LETTER SMALL TURNED M -> 'm' */ + { 0x5B, 0x76 }, /* MODIFIER LETTER SMALL V -> 'v' */ + { 0x5D, 0x62 }, /* MODIFIER LETTER SMALL BETA -> 'b' */ + { 0x5E, 0x67 }, /* MODIFIER LETTER SMALL GREEK GAMMA -> 'g' */ + { 0x5F, 0x64 }, /* MODIFIER LETTER SMALL DELTA -> 'd' */ + { 0x60, 0x66 }, /* MODIFIER LETTER SMALL GREEK PHI -> 'f' */ + { 0x62, 0x69 }, /* LATIN SUBSCRIPT SMALL LETTER I -> 'i' */ + { 0x63, 0x72 }, /* LATIN SUBSCRIPT SMALL LETTER R -> 'r' */ + { 0x64, 0x75 }, /* LATIN SUBSCRIPT SMALL LETTER U -> 'u' */ + { 0x65, 0x76 }, /* LATIN SUBSCRIPT SMALL LETTER V -> 'v' */ + { 0x66, 0x62 }, /* GREEK SUBSCRIPT SMALL LETTER BETA -> 'b' */ + { 0x67, 0x67 }, /* GREEK SUBSCRIPT SMALL LETTER GAMMA -> 'g' */ + { 0x68, 0x72 }, /* GREEK SUBSCRIPT SMALL LETTER RHO -> 'r' */ + { 0x69, 0x66 }, /* GREEK SUBSCRIPT SMALL LETTER PHI -> 'f' */ + { 0x6C, 0x62 }, /* LATIN SMALL LETTER B WITH MIDDLE TILDE -> 'b' */ + { 0x6D, 0x64 }, /* LATIN SMALL LETTER D WITH MIDDLE TILDE -> 'd' */ + { 0x6E, 0x66 }, /* LATIN SMALL LETTER F WITH MIDDLE TILDE -> 'f' */ + { 0x6F, 0x6D }, /* LATIN SMALL LETTER M WITH MIDDLE TILDE -> 'm' */ + { 0x70, 0x6E }, /* LATIN SMALL LETTER N WITH MIDDLE TILDE -> 'n' */ + { 0x71, 0x70 }, /* LATIN SMALL LETTER P WITH MIDDLE TILDE -> 'p' */ + { 0x72, 0x72 }, /* LATIN SMALL LETTER R WITH MIDDLE TILDE -> 'r' */ + { 0x73, 0x72 }, /* LATIN SMALL LETTER R WITH FISHHOOK AND MIDDLE TILDE -> 'r' */ + { 0x74, 0x73 }, /* LATIN SMALL LETTER S WITH MIDDLE TILDE -> 's' */ + { 0x75, 0x74 }, /* LATIN SMALL LETTER T WITH MIDDLE TILDE -> 't' */ + { 0x76, 0x7A }, /* LATIN SMALL LETTER Z WITH MIDDLE TILDE -> 'z' */ + { 0x77, 0x67 }, /* LATIN SMALL LETTER TURNED G -> 'g' */ + { 0x7D, 0x70 }, /* LATIN SMALL LETTER P WITH STROKE -> 'p' */ + { 0x80, 0x62 }, /* LATIN SMALL LETTER B WITH PALATAL HOOK -> 'b' */ + { 0x81, 0x64 }, /* LATIN SMALL LETTER D WITH PALATAL HOOK -> 'd' */ + { 0x82, 0x66 }, /* LATIN SMALL LETTER F WITH PALATAL HOOK -> 'f' */ + { 0x83, 0x67 }, /* LATIN SMALL LETTER G WITH PALATAL HOOK -> 'g' */ + { 0x84, 0x6B }, /* LATIN SMALL LETTER K WITH PALATAL HOOK -> 'k' */ + { 0x85, 0x6C }, /* LATIN SMALL LETTER L WITH PALATAL HOOK -> 'l' */ + { 0x86, 0x6D }, /* LATIN SMALL LETTER M WITH PALATAL HOOK -> 'm' */ + { 0x87, 0x6E }, /* LATIN SMALL LETTER N WITH PALATAL HOOK -> 'n' */ + { 0x88, 0x70 }, /* LATIN SMALL LETTER P WITH PALATAL HOOK -> 'p' */ + { 0x89, 0x72 }, /* LATIN SMALL LETTER R WITH PALATAL HOOK -> 'r' */ + { 0x8A, 0x73 }, /* LATIN SMALL LETTER S WITH PALATAL HOOK -> 's' */ + { 0x8C, 0x76 }, /* LATIN SMALL LETTER V WITH PALATAL HOOK -> 'v' */ + { 0x8D, 0x78 }, /* LATIN SMALL LETTER X WITH PALATAL HOOK -> 'x' */ + { 0x8E, 0x7A }, /* LATIN SMALL LETTER Z WITH PALATAL HOOK -> 'z' */ + /* Entries for page 0x1E */ + { 0x00, 0x41 }, /* LATIN CAPITAL LETTER A WITH RING BELOW -> 'A' */ + { 0x01, 0x61 }, /* LATIN SMALL LETTER A WITH RING BELOW -> 'a' */ + { 0x02, 0x42 }, /* LATIN CAPITAL LETTER B WITH DOT ABOVE -> 'B' */ + { 0x03, 0x62 }, /* LATIN SMALL LETTER B WITH DOT ABOVE -> 'b' */ + { 0x04, 0x42 }, /* LATIN CAPITAL LETTER B WITH DOT BELOW -> 'B' */ + { 0x05, 0x62 }, /* LATIN SMALL LETTER B WITH DOT BELOW -> 'b' */ + { 0x06, 0x42 }, /* LATIN CAPITAL LETTER B WITH LINE BELOW -> 'B' */ + { 0x07, 0x62 }, /* LATIN SMALL LETTER B WITH LINE BELOW -> 'b' */ + { 0x08, 0x43 }, /* LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE -> 'C' */ + { 0x09, 0x63 }, /* LATIN SMALL LETTER C WITH CEDILLA AND ACUTE -> 'c' */ + { 0x0A, 0x44 }, /* LATIN CAPITAL LETTER D WITH DOT ABOVE -> 'D' */ + { 0x0B, 0x64 }, /* LATIN SMALL LETTER D WITH DOT ABOVE -> 'd' */ + { 0x0C, 0x44 }, /* LATIN CAPITAL LETTER D WITH DOT BELOW -> 'D' */ + { 0x0D, 0x64 }, /* LATIN SMALL LETTER D WITH DOT BELOW -> 'd' */ + { 0x0E, 0x44 }, /* LATIN CAPITAL LETTER D WITH LINE BELOW -> 'D' */ + { 0x0F, 0x64 }, /* LATIN SMALL LETTER D WITH LINE BELOW -> 'd' */ + { 0x10, 0x44 }, /* LATIN CAPITAL LETTER D WITH CEDILLA -> 'D' */ + { 0x11, 0x64 }, /* LATIN SMALL LETTER D WITH CEDILLA -> 'd' */ + { 0x12, 0x44 }, /* LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW -> 'D' */ + { 0x13, 0x64 }, /* LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW -> 'd' */ + { 0x14, 0x45 }, /* LATIN CAPITAL LETTER E WITH MACRON AND GRAVE -> 'E' */ + { 0x15, 0x65 }, /* LATIN SMALL LETTER E WITH MACRON AND GRAVE -> 'e' */ + { 0x16, 0x45 }, /* LATIN CAPITAL LETTER E WITH MACRON AND ACUTE -> 'E' */ + { 0x17, 0x65 }, /* LATIN SMALL LETTER E WITH MACRON AND ACUTE -> 'e' */ + { 0x18, 0x45 }, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW -> 'E' */ + { 0x19, 0x65 }, /* LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW -> 'e' */ + { 0x1A, 0x45 }, /* LATIN CAPITAL LETTER E WITH TILDE BELOW -> 'E' */ + { 0x1B, 0x65 }, /* LATIN SMALL LETTER E WITH TILDE BELOW -> 'e' */ + { 0x1C, 0x45 }, /* LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE -> 'E' */ + { 0x1D, 0x65 }, /* LATIN SMALL LETTER E WITH CEDILLA AND BREVE -> 'e' */ + { 0x1E, 0x46 }, /* LATIN CAPITAL LETTER F WITH DOT ABOVE -> 'F' */ + { 0x1F, 0x66 }, /* LATIN SMALL LETTER F WITH DOT ABOVE -> 'f' */ + { 0x20, 0x47 }, /* LATIN CAPITAL LETTER G WITH MACRON -> 'G' */ + { 0x21, 0x67 }, /* LATIN SMALL LETTER G WITH MACRON -> 'g' */ + { 0x22, 0x48 }, /* LATIN CAPITAL LETTER H WITH DOT ABOVE -> 'H' */ + { 0x23, 0x68 }, /* LATIN SMALL LETTER H WITH DOT ABOVE -> 'h' */ + { 0x24, 0x48 }, /* LATIN CAPITAL LETTER H WITH DOT BELOW -> 'H' */ + { 0x25, 0x68 }, /* LATIN SMALL LETTER H WITH DOT BELOW -> 'h' */ + { 0x26, 0x48 }, /* LATIN CAPITAL LETTER H WITH DIAERESIS -> 'H' */ + { 0x27, 0x68 }, /* LATIN SMALL LETTER H WITH DIAERESIS -> 'h' */ + { 0x28, 0x48 }, /* LATIN CAPITAL LETTER H WITH CEDILLA -> 'H' */ + { 0x29, 0x68 }, /* LATIN SMALL LETTER H WITH CEDILLA -> 'h' */ + { 0x2A, 0x48 }, /* LATIN CAPITAL LETTER H WITH BREVE BELOW -> 'H' */ + { 0x2B, 0x68 }, /* LATIN SMALL LETTER H WITH BREVE BELOW -> 'h' */ + { 0x2C, 0x49 }, /* LATIN CAPITAL LETTER I WITH TILDE BELOW -> 'I' */ + { 0x2D, 0x69 }, /* LATIN SMALL LETTER I WITH TILDE BELOW -> 'i' */ + { 0x2E, 0x49 }, /* LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE -> 'I' */ + { 0x2F, 0x69 }, /* LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE -> 'i' */ + { 0x30, 0x4B }, /* LATIN CAPITAL LETTER K WITH ACUTE -> 'K' */ + { 0x31, 0x6B }, /* LATIN SMALL LETTER K WITH ACUTE -> 'k' */ + { 0x32, 0x4B }, /* LATIN CAPITAL LETTER K WITH DOT BELOW -> 'K' */ + { 0x33, 0x6B }, /* LATIN SMALL LETTER K WITH DOT BELOW -> 'k' */ + { 0x34, 0x4B }, /* LATIN CAPITAL LETTER K WITH LINE BELOW -> 'K' */ + { 0x35, 0x6B }, /* LATIN SMALL LETTER K WITH LINE BELOW -> 'k' */ + { 0x36, 0x4C }, /* LATIN CAPITAL LETTER L WITH DOT BELOW -> 'L' */ + { 0x37, 0x6C }, /* LATIN SMALL LETTER L WITH DOT BELOW -> 'l' */ + { 0x38, 0x4C }, /* LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON -> 'L' */ + { 0x39, 0x6C }, /* LATIN SMALL LETTER L WITH DOT BELOW AND MACRON -> 'l' */ + { 0x3A, 0x4C }, /* LATIN CAPITAL LETTER L WITH LINE BELOW -> 'L' */ + { 0x3B, 0x6C }, /* LATIN SMALL LETTER L WITH LINE BELOW -> 'l' */ + { 0x3C, 0x4C }, /* LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW -> 'L' */ + { 0x3D, 0x6C }, /* LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW -> 'l' */ + { 0x3E, 0x4D }, /* LATIN CAPITAL LETTER M WITH ACUTE -> 'M' */ + { 0x3F, 0x6D }, /* LATIN SMALL LETTER M WITH ACUTE -> 'm' */ + { 0x40, 0x4D }, /* LATIN CAPITAL LETTER M WITH DOT ABOVE -> 'M' */ + { 0x41, 0x6D }, /* LATIN SMALL LETTER M WITH DOT ABOVE -> 'm' */ + { 0x42, 0x4D }, /* LATIN CAPITAL LETTER M WITH DOT BELOW -> 'M' */ + { 0x43, 0x6D }, /* LATIN SMALL LETTER M WITH DOT BELOW -> 'm' */ + { 0x44, 0x4E }, /* LATIN CAPITAL LETTER N WITH DOT ABOVE -> 'N' */ + { 0x45, 0x6E }, /* LATIN SMALL LETTER N WITH DOT ABOVE -> 'n' */ + { 0x46, 0x4E }, /* LATIN CAPITAL LETTER N WITH DOT BELOW -> 'N' */ + { 0x47, 0x6E }, /* LATIN SMALL LETTER N WITH DOT BELOW -> 'n' */ + { 0x48, 0x4E }, /* LATIN CAPITAL LETTER N WITH LINE BELOW -> 'N' */ + { 0x49, 0x6E }, /* LATIN SMALL LETTER N WITH LINE BELOW -> 'n' */ + { 0x4A, 0x4E }, /* LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW -> 'N' */ + { 0x4B, 0x6E }, /* LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW -> 'n' */ + { 0x4C, 0x4F }, /* LATIN CAPITAL LETTER O WITH TILDE AND ACUTE -> 'O' */ + { 0x4D, 0x6F }, /* LATIN SMALL LETTER O WITH TILDE AND ACUTE -> 'o' */ + { 0x4E, 0x4F }, /* LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS -> 'O' */ + { 0x4F, 0x6F }, /* LATIN SMALL LETTER O WITH TILDE AND DIAERESIS -> 'o' */ + { 0x50, 0x4F }, /* LATIN CAPITAL LETTER O WITH MACRON AND GRAVE -> 'O' */ + { 0x51, 0x6F }, /* LATIN SMALL LETTER O WITH MACRON AND GRAVE -> 'o' */ + { 0x52, 0x4F }, /* LATIN CAPITAL LETTER O WITH MACRON AND ACUTE -> 'O' */ + { 0x53, 0x6F }, /* LATIN SMALL LETTER O WITH MACRON AND ACUTE -> 'o' */ + { 0x54, 0x50 }, /* LATIN CAPITAL LETTER P WITH ACUTE -> 'P' */ + { 0x55, 0x70 }, /* LATIN SMALL LETTER P WITH ACUTE -> 'p' */ + { 0x56, 0x50 }, /* LATIN CAPITAL LETTER P WITH DOT ABOVE -> 'P' */ + { 0x57, 0x70 }, /* LATIN SMALL LETTER P WITH DOT ABOVE -> 'p' */ + { 0x58, 0x52 }, /* LATIN CAPITAL LETTER R WITH DOT ABOVE -> 'R' */ + { 0x59, 0x72 }, /* LATIN SMALL LETTER R WITH DOT ABOVE -> 'r' */ + { 0x5A, 0x52 }, /* LATIN CAPITAL LETTER R WITH DOT BELOW -> 'R' */ + { 0x5B, 0x72 }, /* LATIN SMALL LETTER R WITH DOT BELOW -> 'r' */ + { 0x5C, 0x52 }, /* LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON -> 'R' */ + { 0x5D, 0x72 }, /* LATIN SMALL LETTER R WITH DOT BELOW AND MACRON -> 'r' */ + { 0x5E, 0x52 }, /* LATIN CAPITAL LETTER R WITH LINE BELOW -> 'R' */ + { 0x5F, 0x72 }, /* LATIN SMALL LETTER R WITH LINE BELOW -> 'r' */ + { 0x60, 0x53 }, /* LATIN CAPITAL LETTER S WITH DOT ABOVE -> 'S' */ + { 0x61, 0x73 }, /* LATIN SMALL LETTER S WITH DOT ABOVE -> 's' */ + { 0x62, 0x53 }, /* LATIN CAPITAL LETTER S WITH DOT BELOW -> 'S' */ + { 0x63, 0x73 }, /* LATIN SMALL LETTER S WITH DOT BELOW -> 's' */ + { 0x64, 0x53 }, /* LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE -> 'S' */ + { 0x65, 0x73 }, /* LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE -> 's' */ + { 0x66, 0x53 }, /* LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE -> 'S' */ + { 0x67, 0x73 }, /* LATIN SMALL LETTER S WITH CARON AND DOT ABOVE -> 's' */ + { 0x68, 0x53 }, /* LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE -> 'S' */ + { 0x69, 0x73 }, /* LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE -> 's' */ + { 0x6A, 0x54 }, /* LATIN CAPITAL LETTER T WITH DOT ABOVE -> 'T' */ + { 0x6B, 0x74 }, /* LATIN SMALL LETTER T WITH DOT ABOVE -> 't' */ + { 0x6C, 0x54 }, /* LATIN CAPITAL LETTER T WITH DOT BELOW -> 'T' */ + { 0x6D, 0x74 }, /* LATIN SMALL LETTER T WITH DOT BELOW -> 't' */ + { 0x6E, 0x54 }, /* LATIN CAPITAL LETTER T WITH LINE BELOW -> 'T' */ + { 0x6F, 0x74 }, /* LATIN SMALL LETTER T WITH LINE BELOW -> 't' */ + { 0x70, 0x54 }, /* LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW -> 'T' */ + { 0x71, 0x74 }, /* LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW -> 't' */ + { 0x72, 0x55 }, /* LATIN CAPITAL LETTER U WITH DIAERESIS BELOW -> 'U' */ + { 0x73, 0x75 }, /* LATIN SMALL LETTER U WITH DIAERESIS BELOW -> 'u' */ + { 0x74, 0x55 }, /* LATIN CAPITAL LETTER U WITH TILDE BELOW -> 'U' */ + { 0x75, 0x75 }, /* LATIN SMALL LETTER U WITH TILDE BELOW -> 'u' */ + { 0x76, 0x55 }, /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW -> 'U' */ + { 0x77, 0x75 }, /* LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW -> 'u' */ + { 0x78, 0x55 }, /* LATIN CAPITAL LETTER U WITH TILDE AND ACUTE -> 'U' */ + { 0x79, 0x75 }, /* LATIN SMALL LETTER U WITH TILDE AND ACUTE -> 'u' */ + { 0x7A, 0x55 }, /* LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS -> 'U' */ + { 0x7B, 0x75 }, /* LATIN SMALL LETTER U WITH MACRON AND DIAERESIS -> 'u' */ + { 0x7C, 0x56 }, /* LATIN CAPITAL LETTER V WITH TILDE -> 'V' */ + { 0x7D, 0x76 }, /* LATIN SMALL LETTER V WITH TILDE -> 'v' */ + { 0x7E, 0x56 }, /* LATIN CAPITAL LETTER V WITH DOT BELOW -> 'V' */ + { 0x7F, 0x76 }, /* LATIN SMALL LETTER V WITH DOT BELOW -> 'v' */ + { 0x80, 0x57 }, /* LATIN CAPITAL LETTER W WITH GRAVE -> 'W' */ + { 0x81, 0x77 }, /* LATIN SMALL LETTER W WITH GRAVE -> 'w' */ + { 0x82, 0x57 }, /* LATIN CAPITAL LETTER W WITH ACUTE -> 'W' */ + { 0x83, 0x77 }, /* LATIN SMALL LETTER W WITH ACUTE -> 'w' */ + { 0x84, 0x57 }, /* LATIN CAPITAL LETTER W WITH DIAERESIS -> 'W' */ + { 0x85, 0x77 }, /* LATIN SMALL LETTER W WITH DIAERESIS -> 'w' */ + { 0x86, 0x57 }, /* LATIN CAPITAL LETTER W WITH DOT ABOVE -> 'W' */ + { 0x87, 0x77 }, /* LATIN SMALL LETTER W WITH DOT ABOVE -> 'w' */ + { 0x88, 0x57 }, /* LATIN CAPITAL LETTER W WITH DOT BELOW -> 'W' */ + { 0x89, 0x77 }, /* LATIN SMALL LETTER W WITH DOT BELOW -> 'w' */ + { 0x8A, 0x58 }, /* LATIN CAPITAL LETTER X WITH DOT ABOVE -> 'X' */ + { 0x8B, 0x78 }, /* LATIN SMALL LETTER X WITH DOT ABOVE -> 'x' */ + { 0x8C, 0x58 }, /* LATIN CAPITAL LETTER X WITH DIAERESIS -> 'X' */ + { 0x8D, 0x78 }, /* LATIN SMALL LETTER X WITH DIAERESIS -> 'x' */ + { 0x8E, 0x59 }, /* LATIN CAPITAL LETTER Y WITH DOT ABOVE -> 'Y' */ + { 0x8F, 0x79 }, /* LATIN SMALL LETTER Y WITH DOT ABOVE -> 'y' */ + { 0x90, 0x5A }, /* LATIN CAPITAL LETTER Z WITH CIRCUMFLEX -> 'Z' */ + { 0x91, 0x7A }, /* LATIN SMALL LETTER Z WITH CIRCUMFLEX -> 'z' */ + { 0x92, 0x5A }, /* LATIN CAPITAL LETTER Z WITH DOT BELOW -> 'Z' */ + { 0x93, 0x7A }, /* LATIN SMALL LETTER Z WITH DOT BELOW -> 'z' */ + { 0x94, 0x5A }, /* LATIN CAPITAL LETTER Z WITH LINE BELOW -> 'Z' */ + { 0x95, 0x7A }, /* LATIN SMALL LETTER Z WITH LINE BELOW -> 'z' */ + { 0x96, 0x68 }, /* LATIN SMALL LETTER H WITH LINE BELOW -> 'h' */ + { 0x97, 0x74 }, /* LATIN SMALL LETTER T WITH DIAERESIS -> 't' */ + { 0x98, 0x77 }, /* LATIN SMALL LETTER W WITH RING ABOVE -> 'w' */ + { 0x99, 0x79 }, /* LATIN SMALL LETTER Y WITH RING ABOVE -> 'y' */ + { 0x9A, 0x61 }, /* LATIN SMALL LETTER A WITH RIGHT HALF RING -> 'a' */ + { 0x9B, 0x53 }, /* LATIN SMALL LETTER LONG S WITH DOT ABOVE -> 'S' */ + { 0xA0, 0x41 }, /* LATIN CAPITAL LETTER A WITH DOT BELOW -> 'A' */ + { 0xA1, 0x61 }, /* LATIN SMALL LETTER A WITH DOT BELOW -> 'a' */ + { 0xA2, 0x41 }, /* LATIN CAPITAL LETTER A WITH HOOK ABOVE -> 'A' */ + { 0xA3, 0x61 }, /* LATIN SMALL LETTER A WITH HOOK ABOVE -> 'a' */ + { 0xA4, 0x41 }, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE -> 'A' */ + { 0xA5, 0x61 }, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE -> 'a' */ + { 0xA6, 0x41 }, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE -> 'A' */ + { 0xA7, 0x61 }, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE -> 'a' */ + { 0xA8, 0x41 }, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE -> 'A' */ + { 0xA9, 0x61 }, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE -> 'a' */ + { 0xAA, 0x41 }, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE -> 'A' */ + { 0xAB, 0x61 }, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE -> 'a' */ + { 0xAC, 0x41 }, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW -> 'A' */ + { 0xAD, 0x61 }, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW -> 'a' */ + { 0xAE, 0x41 }, /* LATIN CAPITAL LETTER A WITH BREVE AND ACUTE -> 'A' */ + { 0xAF, 0x61 }, /* LATIN SMALL LETTER A WITH BREVE AND ACUTE -> 'a' */ + { 0xB0, 0x41 }, /* LATIN CAPITAL LETTER A WITH BREVE AND GRAVE -> 'A' */ + { 0xB1, 0x61 }, /* LATIN SMALL LETTER A WITH BREVE AND GRAVE -> 'a' */ + { 0xB2, 0x41 }, /* LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE -> 'A' */ + { 0xB3, 0x61 }, /* LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE -> 'a' */ + { 0xB4, 0x41 }, /* LATIN CAPITAL LETTER A WITH BREVE AND TILDE -> 'A' */ + { 0xB5, 0x61 }, /* LATIN SMALL LETTER A WITH BREVE AND TILDE -> 'a' */ + { 0xB6, 0x41 }, /* LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW -> 'A' */ + { 0xB7, 0x61 }, /* LATIN SMALL LETTER A WITH BREVE AND DOT BELOW -> 'a' */ + { 0xB8, 0x45 }, /* LATIN CAPITAL LETTER E WITH DOT BELOW -> 'E' */ + { 0xB9, 0x65 }, /* LATIN SMALL LETTER E WITH DOT BELOW -> 'e' */ + { 0xBA, 0x45 }, /* LATIN CAPITAL LETTER E WITH HOOK ABOVE -> 'E' */ + { 0xBB, 0x65 }, /* LATIN SMALL LETTER E WITH HOOK ABOVE -> 'e' */ + { 0xBC, 0x45 }, /* LATIN CAPITAL LETTER E WITH TILDE -> 'E' */ + { 0xBD, 0x65 }, /* LATIN SMALL LETTER E WITH TILDE -> 'e' */ + { 0xBE, 0x45 }, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE -> 'E' */ + { 0xBF, 0x65 }, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE -> 'e' */ + { 0xC0, 0x45 }, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE -> 'E' */ + { 0xC1, 0x65 }, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE -> 'e' */ + { 0xC2, 0x45 }, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE -> 'E' */ + { 0xC3, 0x65 }, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE -> 'e' */ + { 0xC4, 0x45 }, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE -> 'E' */ + { 0xC5, 0x65 }, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE -> 'e' */ + { 0xC6, 0x45 }, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW -> 'E' */ + { 0xC7, 0x65 }, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW -> 'e' */ + { 0xC8, 0x49 }, /* LATIN CAPITAL LETTER I WITH HOOK ABOVE -> 'I' */ + { 0xC9, 0x69 }, /* LATIN SMALL LETTER I WITH HOOK ABOVE -> 'i' */ + { 0xCA, 0x49 }, /* LATIN CAPITAL LETTER I WITH DOT BELOW -> 'I' */ + { 0xCB, 0x69 }, /* LATIN SMALL LETTER I WITH DOT BELOW -> 'i' */ + { 0xCC, 0x4F }, /* LATIN CAPITAL LETTER O WITH DOT BELOW -> 'O' */ + { 0xCD, 0x6F }, /* LATIN SMALL LETTER O WITH DOT BELOW -> 'o' */ + { 0xCE, 0x4F }, /* LATIN CAPITAL LETTER O WITH HOOK ABOVE -> 'O' */ + { 0xCF, 0x6F }, /* LATIN SMALL LETTER O WITH HOOK ABOVE -> 'o' */ + { 0xD0, 0x4F }, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE -> 'O' */ + { 0xD1, 0x6F }, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE -> 'o' */ + { 0xD2, 0x4F }, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE -> 'O' */ + { 0xD3, 0x6F }, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE -> 'o' */ + { 0xD4, 0x4F }, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE -> 'O' */ + { 0xD5, 0x6F }, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE -> 'o' */ + { 0xD6, 0x4F }, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE -> 'O' */ + { 0xD7, 0x6F }, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE -> 'o' */ + { 0xD8, 0x4F }, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW -> 'O' */ + { 0xD9, 0x6F }, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW -> 'o' */ + { 0xDA, 0x4F }, /* LATIN CAPITAL LETTER O WITH HORN AND ACUTE -> 'O' */ + { 0xDB, 0x6F }, /* LATIN SMALL LETTER O WITH HORN AND ACUTE -> 'o' */ + { 0xDC, 0x4F }, /* LATIN CAPITAL LETTER O WITH HORN AND GRAVE -> 'O' */ + { 0xDD, 0x6F }, /* LATIN SMALL LETTER O WITH HORN AND GRAVE -> 'o' */ + { 0xDE, 0x4F }, /* LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE -> 'O' */ + { 0xDF, 0x6F }, /* LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE -> 'o' */ + { 0xE0, 0x4F }, /* LATIN CAPITAL LETTER O WITH HORN AND TILDE -> 'O' */ + { 0xE1, 0x6F }, /* LATIN SMALL LETTER O WITH HORN AND TILDE -> 'o' */ + { 0xE2, 0x4F }, /* LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW -> 'O' */ + { 0xE3, 0x6F }, /* LATIN SMALL LETTER O WITH HORN AND DOT BELOW -> 'o' */ + { 0xE4, 0x55 }, /* LATIN CAPITAL LETTER U WITH DOT BELOW -> 'U' */ + { 0xE5, 0x75 }, /* LATIN SMALL LETTER U WITH DOT BELOW -> 'u' */ + { 0xE6, 0x55 }, /* LATIN CAPITAL LETTER U WITH HOOK ABOVE -> 'U' */ + { 0xE7, 0x75 }, /* LATIN SMALL LETTER U WITH HOOK ABOVE -> 'u' */ + { 0xE8, 0x55 }, /* LATIN CAPITAL LETTER U WITH HORN AND ACUTE -> 'U' */ + { 0xE9, 0x75 }, /* LATIN SMALL LETTER U WITH HORN AND ACUTE -> 'u' */ + { 0xEA, 0x55 }, /* LATIN CAPITAL LETTER U WITH HORN AND GRAVE -> 'U' */ + { 0xEB, 0x75 }, /* LATIN SMALL LETTER U WITH HORN AND GRAVE -> 'u' */ + { 0xEC, 0x55 }, /* LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE -> 'U' */ + { 0xED, 0x75 }, /* LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE -> 'u' */ + { 0xEE, 0x55 }, /* LATIN CAPITAL LETTER U WITH HORN AND TILDE -> 'U' */ + { 0xEF, 0x75 }, /* LATIN SMALL LETTER U WITH HORN AND TILDE -> 'u' */ + { 0xF0, 0x55 }, /* LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW -> 'U' */ + { 0xF1, 0x75 }, /* LATIN SMALL LETTER U WITH HORN AND DOT BELOW -> 'u' */ + { 0xF2, 0x59 }, /* LATIN CAPITAL LETTER Y WITH GRAVE -> 'Y' */ + { 0xF3, 0x79 }, /* LATIN SMALL LETTER Y WITH GRAVE -> 'y' */ + { 0xF4, 0x59 }, /* LATIN CAPITAL LETTER Y WITH DOT BELOW -> 'Y' */ + { 0xF5, 0x79 }, /* LATIN SMALL LETTER Y WITH DOT BELOW -> 'y' */ + { 0xF6, 0x59 }, /* LATIN CAPITAL LETTER Y WITH HOOK ABOVE -> 'Y' */ + { 0xF7, 0x79 }, /* LATIN SMALL LETTER Y WITH HOOK ABOVE -> 'y' */ + { 0xF8, 0x59 }, /* LATIN CAPITAL LETTER Y WITH TILDE -> 'Y' */ + { 0xF9, 0x79 }, /* LATIN SMALL LETTER Y WITH TILDE -> 'y' */ + /* Entries for page 0x1F */ + { 0x00, 0x00 }, /* GREEK SMALL LETTER ALPHA WITH PSILI -> ... */ + { 0x07, 0x61 }, /* GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI -> 'a' */ + { 0x08, 0x00 }, /* GREEK CAPITAL LETTER ALPHA WITH PSILI -> ... */ + { 0x0F, 0x41 }, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI -> 'A' */ + { 0x10, 0x00 }, /* GREEK SMALL LETTER EPSILON WITH PSILI -> ... */ + { 0x15, 0x65 }, /* GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA -> 'e' */ + { 0x18, 0x00 }, /* GREEK CAPITAL LETTER EPSILON WITH PSILI -> ... */ + { 0x1D, 0x45 }, /* GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA -> 'E' */ + { 0x20, 0x00 }, /* GREEK SMALL LETTER ETA WITH PSILI -> ... */ + { 0x27, 0x65 }, /* GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI -> 'e' */ + { 0x28, 0x00 }, /* GREEK CAPITAL LETTER ETA WITH PSILI -> ... */ + { 0x2F, 0x45 }, /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI -> 'E' */ + { 0x30, 0x00 }, /* GREEK SMALL LETTER IOTA WITH PSILI -> ... */ + { 0x37, 0x69 }, /* GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI -> 'i' */ + { 0x38, 0x00 }, /* GREEK CAPITAL LETTER IOTA WITH PSILI -> ... */ + { 0x3F, 0x49 }, /* GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI -> 'I' */ + { 0x40, 0x00 }, /* GREEK SMALL LETTER OMICRON WITH PSILI -> ... */ + { 0x45, 0x6F }, /* GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA -> 'o' */ + { 0x48, 0x00 }, /* GREEK CAPITAL LETTER OMICRON WITH PSILI -> ... */ + { 0x4D, 0x4F }, /* GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA -> 'O' */ + { 0x50, 0x00 }, /* GREEK SMALL LETTER UPSILON WITH PSILI -> ... */ + { 0x57, 0x75 }, /* GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI -> 'u' */ + { 0x59, 0x55 }, /* GREEK CAPITAL LETTER UPSILON WITH DASIA -> 'U' */ + { 0x5B, 0x55 }, /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA -> 'U' */ + { 0x5D, 0x55 }, /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA -> 'U' */ + { 0x5F, 0x55 }, /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI -> 'U' */ + { 0x60, 0x00 }, /* GREEK SMALL LETTER OMEGA WITH PSILI -> ... */ + { 0x67, 0x6F }, /* GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI -> 'o' */ + { 0x68, 0x00 }, /* GREEK CAPITAL LETTER OMEGA WITH PSILI -> ... */ + { 0x6F, 0x4F }, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI -> 'O' */ + { 0x70, 0x61 }, /* GREEK SMALL LETTER ALPHA WITH VARIA -> 'a' */ + { 0x71, 0x61 }, /* GREEK SMALL LETTER ALPHA WITH OXIA -> 'a' */ + { 0x72, 0x00 }, /* GREEK SMALL LETTER EPSILON WITH VARIA -> ... */ + { 0x75, 0x65 }, /* GREEK SMALL LETTER ETA WITH OXIA -> 'e' */ + { 0x76, 0x69 }, /* GREEK SMALL LETTER IOTA WITH VARIA -> 'i' */ + { 0x77, 0x69 }, /* GREEK SMALL LETTER IOTA WITH OXIA -> 'i' */ + { 0x78, 0x6F }, /* GREEK SMALL LETTER OMICRON WITH VARIA -> 'o' */ + { 0x79, 0x6F }, /* GREEK SMALL LETTER OMICRON WITH OXIA -> 'o' */ + { 0x7A, 0x75 }, /* GREEK SMALL LETTER UPSILON WITH VARIA -> 'u' */ + { 0x7B, 0x75 }, /* GREEK SMALL LETTER UPSILON WITH OXIA -> 'u' */ + { 0x7C, 0x6F }, /* GREEK SMALL LETTER OMEGA WITH VARIA -> 'o' */ + { 0x7D, 0x6F }, /* GREEK SMALL LETTER OMEGA WITH OXIA -> 'o' */ + { 0x80, 0x00 }, /* GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI -> ... */ + { 0x87, 0x61 }, /* GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI -> 'a' */ + { 0x88, 0x00 }, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI -> ... */ + { 0x8F, 0x41 }, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI -> 'A' */ + { 0x90, 0x00 }, /* GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI -> ... */ + { 0x97, 0x65 }, /* GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI -> 'e' */ + { 0x98, 0x00 }, /* GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI -> ... */ + { 0x9F, 0x45 }, /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI -> 'E' */ + { 0xA0, 0x00 }, /* GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI -> ... */ + { 0xA7, 0x6F }, /* GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI -> 'o' */ + { 0xA8, 0x00 }, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI -> ... */ + { 0xAF, 0x4F }, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI -> 'O' */ + { 0xB0, 0x00 }, /* GREEK SMALL LETTER ALPHA WITH VRACHY -> ... */ + { 0xB4, 0x61 }, /* GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI -> 'a' */ + { 0xB6, 0x61 }, /* GREEK SMALL LETTER ALPHA WITH PERISPOMENI -> 'a' */ + { 0xB7, 0x61 }, /* GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI -> 'a' */ + { 0xB8, 0x00 }, /* GREEK CAPITAL LETTER ALPHA WITH VRACHY -> ... */ + { 0xBC, 0x41 }, /* GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI -> 'A' */ + { 0xBD, 0x27 }, /* GREEK KORONIS -> ''' */ + { 0xBE, 0x69 }, /* GREEK PROSGEGRAMMENI -> 'i' */ + { 0xBF, 0x27 }, /* GREEK PSILI -> ''' */ + { 0xC0, 0x7E }, /* GREEK PERISPOMENI -> '~' */ + { 0xC2, 0x00 }, /* GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI -> ... */ + { 0xC4, 0x65 }, /* GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI -> 'e' */ + { 0xC6, 0x65 }, /* GREEK SMALL LETTER ETA WITH PERISPOMENI -> 'e' */ + { 0xC7, 0x65 }, /* GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI -> 'e' */ + { 0xC8, 0x00 }, /* GREEK CAPITAL LETTER EPSILON WITH VARIA -> ... */ + { 0xCC, 0x45 }, /* GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI -> 'E' */ + { 0xD0, 0x00 }, /* GREEK SMALL LETTER IOTA WITH VRACHY -> ... */ + { 0xD3, 0x69 }, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA -> 'i' */ + { 0xD6, 0x69 }, /* GREEK SMALL LETTER IOTA WITH PERISPOMENI -> 'i' */ + { 0xD7, 0x69 }, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI -> 'i' */ + { 0xD8, 0x00 }, /* GREEK CAPITAL LETTER IOTA WITH VRACHY -> ... */ + { 0xDB, 0x49 }, /* GREEK CAPITAL LETTER IOTA WITH OXIA -> 'I' */ + { 0xE0, 0x00 }, /* GREEK SMALL LETTER UPSILON WITH VRACHY -> ... */ + { 0xE3, 0x75 }, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA -> 'u' */ + { 0xE4, 0x52 }, /* GREEK SMALL LETTER RHO WITH PSILI -> 'R' */ + { 0xE5, 0x52 }, /* GREEK SMALL LETTER RHO WITH DASIA -> 'R' */ + { 0xE6, 0x75 }, /* GREEK SMALL LETTER UPSILON WITH PERISPOMENI -> 'u' */ + { 0xE7, 0x75 }, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI -> 'u' */ + { 0xE8, 0x00 }, /* GREEK CAPITAL LETTER UPSILON WITH VRACHY -> ... */ + { 0xEB, 0x55 }, /* GREEK CAPITAL LETTER UPSILON WITH OXIA -> 'U' */ + { 0xEC, 0x52 }, /* GREEK CAPITAL LETTER RHO WITH DASIA -> 'R' */ + { 0xEF, 0x60 }, /* GREEK VARIA -> '`' */ + { 0xF2, 0x00 }, /* GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI -> ... */ + { 0xF4, 0x6F }, /* GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI -> 'o' */ + { 0xF6, 0x6F }, /* GREEK SMALL LETTER OMEGA WITH PERISPOMENI -> 'o' */ + { 0xF7, 0x6F }, /* GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI -> 'o' */ + { 0xF8, 0x00 }, /* GREEK CAPITAL LETTER OMICRON WITH VARIA -> ... */ + { 0xFC, 0x4F }, /* GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI -> 'O' */ + { 0xFD, 0x27 }, /* GREEK OXIA -> ''' */ + { 0xFE, 0x60 }, /* GREEK DASIA -> '`' */ + /* Entries for page 0x20 */ + { 0x00, 0x00 }, /* EN QUAD -> ... */ + { 0x0B, 0x20 }, /* ZERO WIDTH SPACE -> ' ' */ + { 0x10, 0x00 }, /* HYPHEN -> ... */ + { 0x15, 0x2D }, /* HORIZONTAL BAR -> '-' */ + { 0x17, 0x5F }, /* DOUBLE LOW LINE -> '_' */ + { 0x18, 0x27 }, /* LEFT SINGLE QUOTATION MARK -> ''' */ + { 0x19, 0x27 }, /* RIGHT SINGLE QUOTATION MARK -> ''' */ + { 0x1A, 0x2C }, /* SINGLE LOW-9 QUOTATION MARK -> ',' */ + { 0x1B, 0x27 }, /* SINGLE HIGH-REVERSED-9 QUOTATION MARK -> ''' */ + { 0x1C, 0x00 }, /* LEFT DOUBLE QUOTATION MARK -> ... */ + { 0x1F, 0x22 }, /* DOUBLE HIGH-REVERSED-9 QUOTATION MARK -> '"' */ + { 0x20, 0x2B }, /* DAGGER -> '+' */ + { 0x22, 0x2A }, /* BULLET -> '*' */ + { 0x23, 0x3E }, /* TRIANGULAR BULLET -> '>' */ + { 0x24, 0x2E }, /* ONE DOT LEADER -> '.' */ + { 0x26, 0x2E }, /* HORIZONTAL ELLIPSIS -> '.' */ + { 0x27, 0x2E }, /* HYPHENATION POINT -> '.' */ + { 0x2F, 0x20 }, /* NARROW NO-BREAK SPACE -> ' ' */ + { 0x32, 0x27 }, /* PRIME -> ''' */ + { 0x33, 0x22 }, /* DOUBLE PRIME -> '"' */ + { 0x35, 0x60 }, /* REVERSED PRIME -> '`' */ + { 0x38, 0x5E }, /* CARET -> '^' */ + { 0x39, 0x3C }, /* SINGLE LEFT-POINTING ANGLE QUOTATION MARK -> '<' */ + { 0x3A, 0x3E }, /* SINGLE RIGHT-POINTING ANGLE QUOTATION MARK -> '>' */ + { 0x3B, 0x2A }, /* REFERENCE MARK -> '*' */ + { 0x3C, 0x21 }, /* DOUBLE EXCLAMATION MARK -> '!' */ + { 0x3D, 0x3F }, /* INTERROBANG -> '?' */ + { 0x3E, 0x2D }, /* OVERLINE -> '-' */ + { 0x3F, 0x5F }, /* UNDERTIE -> '_' */ + { 0x40, 0x2D }, /* CHARACTER TIE -> '-' */ + { 0x41, 0x5E }, /* CARET INSERTION POINT -> '^' */ + { 0x42, 0x2A }, /* ASTERISM -> '*' */ + { 0x43, 0x2D }, /* HYPHEN BULLET -> '-' */ + { 0x44, 0x2F }, /* FRACTION SLASH -> '/' */ + { 0x47, 0x3F }, /* DOUBLE QUESTION MARK -> '?' */ + { 0x48, 0x3F }, /* QUESTION EXCLAMATION MARK -> '?' */ + { 0x49, 0x21 }, /* EXCLAMATION QUESTION MARK -> '!' */ + { 0x4A, 0x26 }, /* TIRONIAN SIGN ET -> '&' */ + { 0x4B, 0x50 }, /* REVERSED PILCROW SIGN -> 'P' */ + { 0x4C, 0x3C }, /* BLACK LEFTWARDS BULLET -> '<' */ + { 0x4D, 0x3E }, /* BLACK RIGHTWARDS BULLET -> '>' */ + { 0x4E, 0x2A }, /* LOW ASTERISK -> '*' */ + { 0x4F, 0x3B }, /* REVERSED SEMICOLON -> ';' */ + { 0x51, 0x2A }, /* TWO ASTERISKS ALIGNED VERTICALLY -> '*' */ + { 0x52, 0x2D }, /* COMMERCIAL MINUS SIGN -> '-' */ + { 0x53, 0x7E }, /* SWUNG DASH -> '~' */ + { 0x55, 0x2A }, /* FLOWER PUNCTUATION MARK -> '*' */ + { 0x5B, 0x3A }, /* FOUR DOT MARK -> ':' */ + { 0x5F, 0x20 }, /* MEDIUM MATHEMATICAL SPACE -> ' ' */ + { 0x70, 0x30 }, /* SUPERSCRIPT ZERO -> '0' */ + { 0x71, 0x69 }, /* SUPERSCRIPT LATIN SMALL LETTER I -> 'i' */ + { 0x74, 0x34 }, /* SUPERSCRIPT FOUR -> '4' */ + { 0x75, 0x35 }, /* SUPERSCRIPT FIVE -> '5' */ + { 0x76, 0x36 }, /* SUPERSCRIPT SIX -> '6' */ + { 0x77, 0x37 }, /* SUPERSCRIPT SEVEN -> '7' */ + { 0x78, 0x38 }, /* SUPERSCRIPT EIGHT -> '8' */ + { 0x79, 0x39 }, /* SUPERSCRIPT NINE -> '9' */ + { 0x7A, 0x2B }, /* SUPERSCRIPT PLUS SIGN -> '+' */ + { 0x7B, 0x2D }, /* SUPERSCRIPT MINUS -> '-' */ + { 0x7C, 0x3D }, /* SUPERSCRIPT EQUALS SIGN -> '=' */ + { 0x7D, 0x28 }, /* SUPERSCRIPT LEFT PARENTHESIS -> '(' */ + { 0x7E, 0x29 }, /* SUPERSCRIPT RIGHT PARENTHESIS -> ')' */ + { 0x7F, 0x6E }, /* SUPERSCRIPT LATIN SMALL LETTER N -> 'n' */ + { 0x80, 0x30 }, /* SUBSCRIPT ZERO -> '0' */ + { 0x81, 0x31 }, /* SUBSCRIPT ONE -> '1' */ + { 0x82, 0x32 }, /* SUBSCRIPT TWO -> '2' */ + { 0x83, 0x33 }, /* SUBSCRIPT THREE -> '3' */ + { 0x84, 0x34 }, /* SUBSCRIPT FOUR -> '4' */ + { 0x85, 0x35 }, /* SUBSCRIPT FIVE -> '5' */ + { 0x86, 0x36 }, /* SUBSCRIPT SIX -> '6' */ + { 0x87, 0x37 }, /* SUBSCRIPT SEVEN -> '7' */ + { 0x88, 0x38 }, /* SUBSCRIPT EIGHT -> '8' */ + { 0x89, 0x39 }, /* SUBSCRIPT NINE -> '9' */ + { 0x8A, 0x2B }, /* SUBSCRIPT PLUS SIGN -> '+' */ + { 0x8B, 0x2D }, /* SUBSCRIPT MINUS -> '-' */ + { 0x8C, 0x3D }, /* SUBSCRIPT EQUALS SIGN -> '=' */ + { 0x8D, 0x28 }, /* SUBSCRIPT LEFT PARENTHESIS -> '(' */ + { 0x8E, 0x29 }, /* SUBSCRIPT RIGHT PARENTHESIS -> ')' */ + { 0x90, 0x61 }, /* LATIN SUBSCRIPT SMALL LETTER A -> 'a' */ + { 0x91, 0x65 }, /* LATIN SUBSCRIPT SMALL LETTER E -> 'e' */ + { 0x92, 0x6F }, /* LATIN SUBSCRIPT SMALL LETTER O -> 'o' */ + { 0x93, 0x78 }, /* LATIN SUBSCRIPT SMALL LETTER X -> 'x' */ + { 0x95, 0x68 }, /* LATIN SUBSCRIPT SMALL LETTER H -> 'h' */ + { 0x96, 0x6B }, /* LATIN SUBSCRIPT SMALL LETTER K -> 'k' */ + { 0x97, 0x6C }, /* LATIN SUBSCRIPT SMALL LETTER L -> 'l' */ + { 0x98, 0x6D }, /* LATIN SUBSCRIPT SMALL LETTER M -> 'm' */ + { 0x99, 0x6E }, /* LATIN SUBSCRIPT SMALL LETTER N -> 'n' */ + { 0x9A, 0x70 }, /* LATIN SUBSCRIPT SMALL LETTER P -> 'p' */ + { 0x9B, 0x73 }, /* LATIN SUBSCRIPT SMALL LETTER S -> 's' */ + { 0x9C, 0x74 }, /* LATIN SUBSCRIPT SMALL LETTER T -> 't' */ + { 0xA4, 0x4C }, /* LIRA SIGN -> 'L' */ + { 0xA6, 0x4E }, /* NAIRA SIGN -> 'N' */ + { 0xA9, 0x57 }, /* WON SIGN -> 'W' */ + { 0xAB, 0x44 }, /* DONG SIGN -> 'D' */ + { 0xAC, 0x45 }, /* EURO SIGN -> 'E' */ + { 0xAD, 0x4B }, /* KIP SIGN -> 'K' */ + { 0xAE, 0x54 }, /* TUGRIK SIGN -> 'T' */ + { 0xB1, 0x50 }, /* PESO SIGN -> 'P' */ + { 0xB2, 0x47 }, /* GUARANI SIGN -> 'G' */ + { 0xB3, 0x41 }, /* AUSTRAL SIGN -> 'A' */ + { 0xB6, 0x4C }, /* LIVRE TOURNOIS SIGN -> 'L' */ + { 0xB8, 0x54 }, /* TENGE SIGN -> 'T' */ + { 0xBA, 0x4C }, /* TURKISH LIRA SIGN -> 'L' */ + { 0xBB, 0x4D }, /* NORDIC MARK SIGN -> 'M' */ + { 0xBC, 0x6D }, /* MANAT SIGN -> 'm' */ + { 0xBD, 0x52 }, /* RUBLE SIGN -> 'R' */ + { 0xBE, 0x6C }, /* LARI SIGN -> 'l' */ + /* Entries for page 0x21 */ + { 0x02, 0x43 }, /* DOUBLE-STRUCK CAPITAL C -> 'C' */ + { 0x03, 0x43 }, /* DEGREE CELSIUS -> 'C' */ + { 0x09, 0x46 }, /* DEGREE FAHRENHEIT -> 'F' */ + { 0x0A, 0x67 }, /* SCRIPT SMALL G -> 'g' */ + { 0x0B, 0x00 }, /* SCRIPT CAPITAL H -> ... */ + { 0x0D, 0x48 }, /* DOUBLE-STRUCK CAPITAL H -> 'H' */ + { 0x0E, 0x68 }, /* PLANCK CONSTANT -> 'h' */ + { 0x10, 0x49 }, /* SCRIPT CAPITAL I -> 'I' */ + { 0x11, 0x49 }, /* BLACK-LETTER CAPITAL I -> 'I' */ + { 0x12, 0x4C }, /* SCRIPT CAPITAL L -> 'L' */ + { 0x13, 0x6C }, /* SCRIPT SMALL L -> 'l' */ + { 0x15, 0x4E }, /* DOUBLE-STRUCK CAPITAL N -> 'N' */ + { 0x19, 0x50 }, /* DOUBLE-STRUCK CAPITAL P -> 'P' */ + { 0x1A, 0x51 }, /* DOUBLE-STRUCK CAPITAL Q -> 'Q' */ + { 0x1B, 0x00 }, /* SCRIPT CAPITAL R -> ... */ + { 0x1D, 0x52 }, /* DOUBLE-STRUCK CAPITAL R -> 'R' */ + { 0x22, 0x54 }, /* TRADE MARK SIGN -> 'T' */ + { 0x24, 0x5A }, /* DOUBLE-STRUCK CAPITAL Z -> 'Z' */ + { 0x28, 0x5A }, /* BLACK-LETTER CAPITAL Z -> 'Z' */ + { 0x2A, 0x4B }, /* KELVIN SIGN -> 'K' */ + { 0x2B, 0x41 }, /* ANGSTROM SIGN -> 'A' */ + { 0x2C, 0x42 }, /* SCRIPT CAPITAL B -> 'B' */ + { 0x2D, 0x43 }, /* BLACK-LETTER CAPITAL C -> 'C' */ + { 0x2E, 0x65 }, /* ESTIMATED SYMBOL -> 'e' */ + { 0x2F, 0x65 }, /* SCRIPT SMALL E -> 'e' */ + { 0x30, 0x45 }, /* SCRIPT CAPITAL E -> 'E' */ + { 0x31, 0x46 }, /* SCRIPT CAPITAL F -> 'F' */ + { 0x32, 0x46 }, /* TURNED CAPITAL F -> 'F' */ + { 0x33, 0x4D }, /* SCRIPT CAPITAL M -> 'M' */ + { 0x34, 0x6F }, /* SCRIPT SMALL O -> 'o' */ + { 0x39, 0x69 }, /* INFORMATION SOURCE -> 'i' */ + { 0x45, 0x44 }, /* DOUBLE-STRUCK ITALIC CAPITAL D -> 'D' */ + { 0x46, 0x64 }, /* DOUBLE-STRUCK ITALIC SMALL D -> 'd' */ + { 0x47, 0x65 }, /* DOUBLE-STRUCK ITALIC SMALL E -> 'e' */ + { 0x48, 0x69 }, /* DOUBLE-STRUCK ITALIC SMALL I -> 'i' */ + { 0x49, 0x6A }, /* DOUBLE-STRUCK ITALIC SMALL J -> 'j' */ + { 0x4E, 0x46 }, /* TURNED SMALL F -> 'F' */ + { 0x60, 0x49 }, /* ROMAN NUMERAL ONE -> 'I' */ + { 0x64, 0x56 }, /* ROMAN NUMERAL FIVE -> 'V' */ + { 0x69, 0x58 }, /* ROMAN NUMERAL TEN -> 'X' */ + { 0x6C, 0x4C }, /* ROMAN NUMERAL FIFTY -> 'L' */ + { 0x6D, 0x43 }, /* ROMAN NUMERAL ONE HUNDRED -> 'C' */ + { 0x6E, 0x44 }, /* ROMAN NUMERAL FIVE HUNDRED -> 'D' */ + { 0x6F, 0x4D }, /* ROMAN NUMERAL ONE THOUSAND -> 'M' */ + { 0x70, 0x69 }, /* SMALL ROMAN NUMERAL ONE -> 'i' */ + { 0x74, 0x76 }, /* SMALL ROMAN NUMERAL FIVE -> 'v' */ + { 0x79, 0x78 }, /* SMALL ROMAN NUMERAL TEN -> 'x' */ + { 0x7C, 0x6C }, /* SMALL ROMAN NUMERAL FIFTY -> 'l' */ + { 0x7D, 0x63 }, /* SMALL ROMAN NUMERAL ONE HUNDRED -> 'c' */ + { 0x7E, 0x64 }, /* SMALL ROMAN NUMERAL FIVE HUNDRED -> 'd' */ + { 0x7F, 0x6D }, /* SMALL ROMAN NUMERAL ONE THOUSAND -> 'm' */ + { 0x83, 0x29 }, /* ROMAN NUMERAL REVERSED ONE HUNDRED -> ')' */ + { 0x90, 0x3C }, /* LEFTWARDS ARROW -> '<' */ + { 0x91, 0x5E }, /* UPWARDS ARROW -> '^' */ + { 0x92, 0x3E }, /* RIGHTWARDS ARROW -> '>' */ + { 0x93, 0x76 }, /* DOWNWARDS ARROW -> 'v' */ + { 0x94, 0x2D }, /* LEFT RIGHT ARROW -> '-' */ + { 0x95, 0x7C }, /* UP DOWN ARROW -> '|' */ + { 0x96, 0x5C }, /* NORTH WEST ARROW -> '\' */ + { 0x97, 0x2F }, /* NORTH EAST ARROW -> '/' */ + { 0x98, 0x5C }, /* SOUTH EAST ARROW -> '\' */ + { 0x99, 0x2F }, /* SOUTH WEST ARROW -> '/' */ + { 0x9A, 0x21 }, /* LEFTWARDS ARROW WITH STROKE -> '!' */ + { 0x9B, 0x21 }, /* RIGHTWARDS ARROW WITH STROKE -> '!' */ + { 0x9C, 0x7E }, /* LEFTWARDS WAVE ARROW -> '~' */ + { 0x9D, 0x7E }, /* RIGHTWARDS WAVE ARROW -> '~' */ + { 0x9E, 0x2D }, /* LEFTWARDS TWO HEADED ARROW -> '-' */ + { 0x9F, 0x7C }, /* UPWARDS TWO HEADED ARROW -> '|' */ + { 0xA0, 0x2D }, /* RIGHTWARDS TWO HEADED ARROW -> '-' */ + { 0xA1, 0x7C }, /* DOWNWARDS TWO HEADED ARROW -> '|' */ + { 0xA2, 0x00 }, /* LEFTWARDS ARROW WITH TAIL -> ... */ + { 0xA4, 0x2D }, /* LEFTWARDS ARROW FROM BAR -> '-' */ + { 0xA5, 0x7C }, /* UPWARDS ARROW FROM BAR -> '|' */ + { 0xA6, 0x2D }, /* RIGHTWARDS ARROW FROM BAR -> '-' */ + { 0xA7, 0x7C }, /* DOWNWARDS ARROW FROM BAR -> '|' */ + { 0xA8, 0x7C }, /* UP DOWN ARROW WITH BASE -> '|' */ + { 0xA9, 0x00 }, /* LEFTWARDS ARROW WITH HOOK -> ... */ + { 0xAD, 0x2D }, /* LEFT RIGHT WAVE ARROW -> '-' */ + { 0xAE, 0x21 }, /* LEFT RIGHT ARROW WITH STROKE -> '!' */ + { 0xAF, 0x00 }, /* DOWNWARDS ZIGZAG ARROW -> ... */ + { 0xB5, 0x7C }, /* DOWNWARDS ARROW WITH CORNER LEFTWARDS -> '|' */ + { 0xB6, 0x5E }, /* ANTICLOCKWISE TOP SEMICIRCLE ARROW -> '^' */ + { 0xB7, 0x56 }, /* CLOCKWISE TOP SEMICIRCLE ARROW -> 'V' */ + { 0xB8, 0x5C }, /* NORTH WEST ARROW TO LONG BAR -> '\' */ + { 0xB9, 0x3D }, /* LEFTWARDS ARROW TO BAR OVER RIGHTWARDS ARROW TO BAR -> '=' */ + { 0xBA, 0x56 }, /* ANTICLOCKWISE OPEN CIRCLE ARROW -> 'V' */ + { 0xBB, 0x5E }, /* CLOCKWISE OPEN CIRCLE ARROW -> '^' */ + { 0xBC, 0x2D }, /* LEFTWARDS HARPOON WITH BARB UPWARDS -> '-' */ + { 0xBD, 0x2D }, /* LEFTWARDS HARPOON WITH BARB DOWNWARDS -> '-' */ + { 0xBE, 0x7C }, /* UPWARDS HARPOON WITH BARB RIGHTWARDS -> '|' */ + { 0xBF, 0x7C }, /* UPWARDS HARPOON WITH BARB LEFTWARDS -> '|' */ + { 0xC0, 0x2D }, /* RIGHTWARDS HARPOON WITH BARB UPWARDS -> '-' */ + { 0xC1, 0x2D }, /* RIGHTWARDS HARPOON WITH BARB DOWNWARDS -> '-' */ + { 0xC2, 0x7C }, /* DOWNWARDS HARPOON WITH BARB RIGHTWARDS -> '|' */ + { 0xC3, 0x7C }, /* DOWNWARDS HARPOON WITH BARB LEFTWARDS -> '|' */ + { 0xC4, 0x3D }, /* RIGHTWARDS ARROW OVER LEFTWARDS ARROW -> '=' */ + { 0xC5, 0x7C }, /* UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW -> '|' */ + { 0xC6, 0x3D }, /* LEFTWARDS ARROW OVER RIGHTWARDS ARROW -> '=' */ + { 0xC7, 0x3D }, /* LEFTWARDS PAIRED ARROWS -> '=' */ + { 0xC8, 0x7C }, /* UPWARDS PAIRED ARROWS -> '|' */ + { 0xC9, 0x3D }, /* RIGHTWARDS PAIRED ARROWS -> '=' */ + { 0xCA, 0x7C }, /* DOWNWARDS PAIRED ARROWS -> '|' */ + { 0xCB, 0x3D }, /* LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON -> '=' */ + { 0xCC, 0x3D }, /* RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON -> '=' */ + { 0xCD, 0x00 }, /* LEFTWARDS DOUBLE ARROW WITH STROKE -> ... */ + { 0xCF, 0x21 }, /* RIGHTWARDS DOUBLE ARROW WITH STROKE -> '!' */ + { 0xD0, 0x3C }, /* LEFTWARDS DOUBLE ARROW -> '<' */ + { 0xD1, 0x5E }, /* UPWARDS DOUBLE ARROW -> '^' */ + { 0xD2, 0x3E }, /* RIGHTWARDS DOUBLE ARROW -> '>' */ + { 0xD3, 0x76 }, /* DOWNWARDS DOUBLE ARROW -> 'v' */ + { 0xD4, 0x3D }, /* LEFT RIGHT DOUBLE ARROW -> '=' */ + { 0xD5, 0x7C }, /* UP DOWN DOUBLE ARROW -> '|' */ + { 0xD6, 0x5C }, /* NORTH WEST DOUBLE ARROW -> '\' */ + { 0xD7, 0x2F }, /* NORTH EAST DOUBLE ARROW -> '/' */ + { 0xD8, 0x5C }, /* SOUTH EAST DOUBLE ARROW -> '\' */ + { 0xD9, 0x2F }, /* SOUTH WEST DOUBLE ARROW -> '/' */ + { 0xDA, 0x3D }, /* LEFTWARDS TRIPLE ARROW -> '=' */ + { 0xDB, 0x3D }, /* RIGHTWARDS TRIPLE ARROW -> '=' */ + { 0xDC, 0x7E }, /* LEFTWARDS SQUIGGLE ARROW -> '~' */ + { 0xDD, 0x7E }, /* RIGHTWARDS SQUIGGLE ARROW -> '~' */ + { 0xDE, 0x7C }, /* UPWARDS ARROW WITH DOUBLE STROKE -> '|' */ + { 0xDF, 0x7C }, /* DOWNWARDS ARROW WITH DOUBLE STROKE -> '|' */ + { 0xE0, 0x2D }, /* LEFTWARDS DASHED ARROW -> '-' */ + { 0xE1, 0x7C }, /* UPWARDS DASHED ARROW -> '|' */ + { 0xE2, 0x2D }, /* RIGHTWARDS DASHED ARROW -> '-' */ + { 0xE3, 0x7C }, /* DOWNWARDS DASHED ARROW -> '|' */ + { 0xE4, 0x00 }, /* LEFTWARDS ARROW TO BAR -> ... */ + { 0xE6, 0x2D }, /* LEFTWARDS WHITE ARROW -> '-' */ + { 0xE7, 0x7C }, /* UPWARDS WHITE ARROW -> '|' */ + { 0xE8, 0x2D }, /* RIGHTWARDS WHITE ARROW -> '-' */ + { 0xE9, 0x00 }, /* DOWNWARDS WHITE ARROW -> ... */ + { 0xEF, 0x7C }, /* UPWARDS WHITE DOUBLE ARROW ON PEDESTAL -> '|' */ + { 0xF0, 0x2D }, /* RIGHTWARDS WHITE ARROW FROM WALL -> '-' */ + { 0xF1, 0x5C }, /* NORTH WEST ARROW TO CORNER -> '\' */ + { 0xF2, 0x5C }, /* SOUTH EAST ARROW TO CORNER -> '\' */ + { 0xF3, 0x7C }, /* UP DOWN WHITE ARROW -> '|' */ + /* Entries for page 0x22 */ + { 0x04, 0x21 }, /* THERE DOES NOT EXIST -> '!' */ + { 0x09, 0x21 }, /* NOT AN ELEMENT OF -> '!' */ + { 0x0C, 0x21 }, /* DOES NOT CONTAIN AS MEMBER -> '!' */ + { 0x12, 0x2D }, /* MINUS SIGN -> '-' */ + { 0x15, 0x2F }, /* DIVISION SLASH -> '/' */ + { 0x16, 0x5C }, /* SET MINUS -> '\' */ + { 0x17, 0x2A }, /* ASTERISK OPERATOR -> '*' */ + { 0x18, 0x6F }, /* RING OPERATOR -> 'o' */ + { 0x19, 0x2E }, /* BULLET OPERATOR -> '.' */ + { 0x23, 0x7C }, /* DIVIDES -> '|' */ + { 0x24, 0x21 }, /* DOES NOT DIVIDE -> '!' */ + { 0x26, 0x21 }, /* NOT PARALLEL TO -> '!' */ + { 0x36, 0x3A }, /* RATIO -> ':' */ + { 0x3C, 0x7E }, /* TILDE OPERATOR -> '~' */ + { 0x41, 0x23 }, /* NOT TILDE -> '#' */ + { 0x44, 0x23 }, /* NOT ASYMPTOTICALLY EQUAL TO -> '#' */ + { 0x49, 0x23 }, /* NOT ALMOST EQUAL TO -> '#' */ + { 0x60, 0x23 }, /* NOT EQUAL TO -> '#' */ + { 0x62, 0x23 }, /* NOT IDENTICAL TO -> '#' */ + { 0x64, 0x3C }, /* LESS-THAN OR EQUAL TO -> '<' */ + { 0x65, 0x3E }, /* GREATER-THAN OR EQUAL TO -> '>' */ + { 0x68, 0x23 }, /* LESS-THAN BUT NOT EQUAL TO -> '#' */ + { 0x69, 0x23 }, /* GREATER-THAN BUT NOT EQUAL TO -> '#' */ + { 0x6D, 0x23 }, /* NOT EQUIVALENT TO -> '#' */ + { 0x6E, 0x21 }, /* NOT LESS-THAN -> '!' */ + { 0x6F, 0x21 }, /* NOT GREATER-THAN -> '!' */ + { 0x80, 0x21 }, /* DOES NOT PRECEDE -> '!' */ + { 0x81, 0x21 }, /* DOES NOT SUCCEED -> '!' */ + { 0x84, 0x21 }, /* NOT A SUBSET OF -> '!' */ + { 0x85, 0x21 }, /* NOT A SUPERSET OF -> '!' */ + { 0x8A, 0x23 }, /* SUBSET OF WITH NOT EQUAL TO -> '#' */ + { 0x8B, 0x23 }, /* SUPERSET OF WITH NOT EQUAL TO -> '#' */ + { 0x9B, 0x2A }, /* CIRCLED ASTERISK OPERATOR -> '*' */ + { 0xC6, 0x2A }, /* STAR OPERATOR -> '*' */ + /* Entries for page 0x23 */ + { 0x03, 0x5E }, /* UP ARROWHEAD -> '^' */ + { 0x29, 0x3C }, /* LEFT-POINTING ANGLE BRACKET -> '<' */ + { 0x5F, 0x2A }, /* APL FUNCTIONAL SYMBOL CIRCLE STAR -> '*' */ + { 0x63, 0x2A }, /* APL FUNCTIONAL SYMBOL STAR DIAERESIS -> '*' */ + /* Entries for page 0x24 */ + { 0x60, 0x31 }, /* CIRCLED DIGIT ONE -> '1' */ + { 0x61, 0x32 }, /* CIRCLED DIGIT TWO -> '2' */ + { 0x62, 0x33 }, /* CIRCLED DIGIT THREE -> '3' */ + { 0x63, 0x34 }, /* CIRCLED DIGIT FOUR -> '4' */ + { 0x64, 0x35 }, /* CIRCLED DIGIT FIVE -> '5' */ + { 0x65, 0x36 }, /* CIRCLED DIGIT SIX -> '6' */ + { 0x66, 0x37 }, /* CIRCLED DIGIT SEVEN -> '7' */ + { 0x67, 0x38 }, /* CIRCLED DIGIT EIGHT -> '8' */ + { 0x68, 0x39 }, /* CIRCLED DIGIT NINE -> '9' */ + { 0xB6, 0x41 }, /* CIRCLED LATIN CAPITAL LETTER A -> 'A' */ + { 0xB7, 0x42 }, /* CIRCLED LATIN CAPITAL LETTER B -> 'B' */ + { 0xB8, 0x43 }, /* CIRCLED LATIN CAPITAL LETTER C -> 'C' */ + { 0xB9, 0x44 }, /* CIRCLED LATIN CAPITAL LETTER D -> 'D' */ + { 0xBA, 0x45 }, /* CIRCLED LATIN CAPITAL LETTER E -> 'E' */ + { 0xBB, 0x46 }, /* CIRCLED LATIN CAPITAL LETTER F -> 'F' */ + { 0xBC, 0x47 }, /* CIRCLED LATIN CAPITAL LETTER G -> 'G' */ + { 0xBD, 0x48 }, /* CIRCLED LATIN CAPITAL LETTER H -> 'H' */ + { 0xBE, 0x49 }, /* CIRCLED LATIN CAPITAL LETTER I -> 'I' */ + { 0xBF, 0x4A }, /* CIRCLED LATIN CAPITAL LETTER J -> 'J' */ + { 0xC0, 0x4B }, /* CIRCLED LATIN CAPITAL LETTER K -> 'K' */ + { 0xC1, 0x4C }, /* CIRCLED LATIN CAPITAL LETTER L -> 'L' */ + { 0xC2, 0x4D }, /* CIRCLED LATIN CAPITAL LETTER M -> 'M' */ + { 0xC3, 0x4E }, /* CIRCLED LATIN CAPITAL LETTER N -> 'N' */ + { 0xC4, 0x4F }, /* CIRCLED LATIN CAPITAL LETTER O -> 'O' */ + { 0xC5, 0x50 }, /* CIRCLED LATIN CAPITAL LETTER P -> 'P' */ + { 0xC6, 0x51 }, /* CIRCLED LATIN CAPITAL LETTER Q -> 'Q' */ + { 0xC7, 0x52 }, /* CIRCLED LATIN CAPITAL LETTER R -> 'R' */ + { 0xC8, 0x53 }, /* CIRCLED LATIN CAPITAL LETTER S -> 'S' */ + { 0xC9, 0x54 }, /* CIRCLED LATIN CAPITAL LETTER T -> 'T' */ + { 0xCA, 0x55 }, /* CIRCLED LATIN CAPITAL LETTER U -> 'U' */ + { 0xCB, 0x56 }, /* CIRCLED LATIN CAPITAL LETTER V -> 'V' */ + { 0xCC, 0x57 }, /* CIRCLED LATIN CAPITAL LETTER W -> 'W' */ + { 0xCD, 0x58 }, /* CIRCLED LATIN CAPITAL LETTER X -> 'X' */ + { 0xCE, 0x59 }, /* CIRCLED LATIN CAPITAL LETTER Y -> 'Y' */ + { 0xCF, 0x5A }, /* CIRCLED LATIN CAPITAL LETTER Z -> 'Z' */ + { 0xD0, 0x61 }, /* CIRCLED LATIN SMALL LETTER A -> 'a' */ + { 0xD1, 0x62 }, /* CIRCLED LATIN SMALL LETTER B -> 'b' */ + { 0xD2, 0x63 }, /* CIRCLED LATIN SMALL LETTER C -> 'c' */ + { 0xD3, 0x64 }, /* CIRCLED LATIN SMALL LETTER D -> 'd' */ + { 0xD4, 0x65 }, /* CIRCLED LATIN SMALL LETTER E -> 'e' */ + { 0xD5, 0x66 }, /* CIRCLED LATIN SMALL LETTER F -> 'f' */ + { 0xD6, 0x67 }, /* CIRCLED LATIN SMALL LETTER G -> 'g' */ + { 0xD7, 0x68 }, /* CIRCLED LATIN SMALL LETTER H -> 'h' */ + { 0xD8, 0x69 }, /* CIRCLED LATIN SMALL LETTER I -> 'i' */ + { 0xD9, 0x6A }, /* CIRCLED LATIN SMALL LETTER J -> 'j' */ + { 0xDA, 0x6B }, /* CIRCLED LATIN SMALL LETTER K -> 'k' */ + { 0xDB, 0x6C }, /* CIRCLED LATIN SMALL LETTER L -> 'l' */ + { 0xDC, 0x6D }, /* CIRCLED LATIN SMALL LETTER M -> 'm' */ + { 0xDD, 0x6E }, /* CIRCLED LATIN SMALL LETTER N -> 'n' */ + { 0xDE, 0x6F }, /* CIRCLED LATIN SMALL LETTER O -> 'o' */ + { 0xDF, 0x70 }, /* CIRCLED LATIN SMALL LETTER P -> 'p' */ + { 0xE0, 0x71 }, /* CIRCLED LATIN SMALL LETTER Q -> 'q' */ + { 0xE1, 0x72 }, /* CIRCLED LATIN SMALL LETTER R -> 'r' */ + { 0xE2, 0x73 }, /* CIRCLED LATIN SMALL LETTER S -> 's' */ + { 0xE3, 0x74 }, /* CIRCLED LATIN SMALL LETTER T -> 't' */ + { 0xE4, 0x75 }, /* CIRCLED LATIN SMALL LETTER U -> 'u' */ + { 0xE5, 0x76 }, /* CIRCLED LATIN SMALL LETTER V -> 'v' */ + { 0xE6, 0x77 }, /* CIRCLED LATIN SMALL LETTER W -> 'w' */ + { 0xE7, 0x78 }, /* CIRCLED LATIN SMALL LETTER X -> 'x' */ + { 0xE8, 0x79 }, /* CIRCLED LATIN SMALL LETTER Y -> 'y' */ + { 0xE9, 0x7A }, /* CIRCLED LATIN SMALL LETTER Z -> 'z' */ + { 0xEA, 0x30 }, /* CIRCLED DIGIT ZERO -> '0' */ + { 0xF5, 0x31 }, /* DOUBLE CIRCLED DIGIT ONE -> '1' */ + { 0xF6, 0x32 }, /* DOUBLE CIRCLED DIGIT TWO -> '2' */ + { 0xF7, 0x33 }, /* DOUBLE CIRCLED DIGIT THREE -> '3' */ + { 0xF8, 0x34 }, /* DOUBLE CIRCLED DIGIT FOUR -> '4' */ + { 0xF9, 0x35 }, /* DOUBLE CIRCLED DIGIT FIVE -> '5' */ + { 0xFA, 0x36 }, /* DOUBLE CIRCLED DIGIT SIX -> '6' */ + { 0xFB, 0x37 }, /* DOUBLE CIRCLED DIGIT SEVEN -> '7' */ + { 0xFC, 0x38 }, /* DOUBLE CIRCLED DIGIT EIGHT -> '8' */ + { 0xFD, 0x39 }, /* DOUBLE CIRCLED DIGIT NINE -> '9' */ + { 0xFF, 0x30 }, /* NEGATIVE CIRCLED DIGIT ZERO -> '0' */ + /* Entries for page 0x25 */ + { 0x00, 0x2D }, /* BOX DRAWINGS LIGHT HORIZONTAL -> '-' */ + { 0x01, 0x2D }, /* BOX DRAWINGS HEAVY HORIZONTAL -> '-' */ + { 0x02, 0x7C }, /* BOX DRAWINGS LIGHT VERTICAL -> '|' */ + { 0x03, 0x7C }, /* BOX DRAWINGS HEAVY VERTICAL -> '|' */ + { 0x04, 0x2D }, /* BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL -> '-' */ + { 0x05, 0x2D }, /* BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL -> '-' */ + { 0x06, 0x7C }, /* BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL -> '|' */ + { 0x07, 0x7C }, /* BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL -> '|' */ + { 0x08, 0x2D }, /* BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL -> '-' */ + { 0x09, 0x2D }, /* BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL -> '-' */ + { 0x0A, 0x7C }, /* BOX DRAWINGS LIGHT QUADRUPLE DASH VERTICAL -> '|' */ + { 0x0B, 0x7C }, /* BOX DRAWINGS HEAVY QUADRUPLE DASH VERTICAL -> '|' */ + { 0x0C, 0x00 }, /* BOX DRAWINGS LIGHT DOWN AND RIGHT -> ... */ + { 0x4B, 0x2B }, /* BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL -> '+' */ + { 0x4C, 0x2D }, /* BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL -> '-' */ + { 0x4D, 0x2D }, /* BOX DRAWINGS HEAVY DOUBLE DASH HORIZONTAL -> '-' */ + { 0x4E, 0x7C }, /* BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL -> '|' */ + { 0x4F, 0x7C }, /* BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL -> '|' */ + { 0x50, 0x2D }, /* BOX DRAWINGS DOUBLE HORIZONTAL -> '-' */ + { 0x51, 0x7C }, /* BOX DRAWINGS DOUBLE VERTICAL -> '|' */ + { 0x52, 0x00 }, /* BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE -> ... */ + { 0x70, 0x2B }, /* BOX DRAWINGS LIGHT ARC UP AND RIGHT -> '+' */ + { 0x71, 0x2F }, /* BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT -> '/' */ + { 0x72, 0x5C }, /* BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT -> '\' */ + { 0x73, 0x58 }, /* BOX DRAWINGS LIGHT DIAGONAL CROSS -> 'X' */ + { 0x74, 0x2D }, /* BOX DRAWINGS LIGHT LEFT -> '-' */ + { 0x75, 0x7C }, /* BOX DRAWINGS LIGHT UP -> '|' */ + { 0x76, 0x2D }, /* BOX DRAWINGS LIGHT RIGHT -> '-' */ + { 0x77, 0x7C }, /* BOX DRAWINGS LIGHT DOWN -> '|' */ + { 0x78, 0x2D }, /* BOX DRAWINGS HEAVY LEFT -> '-' */ + { 0x79, 0x7C }, /* BOX DRAWINGS HEAVY UP -> '|' */ + { 0x7A, 0x2D }, /* BOX DRAWINGS HEAVY RIGHT -> '-' */ + { 0x7B, 0x7C }, /* BOX DRAWINGS HEAVY DOWN -> '|' */ + { 0x7C, 0x2D }, /* BOX DRAWINGS LIGHT LEFT AND HEAVY RIGHT -> '-' */ + { 0x7D, 0x7C }, /* BOX DRAWINGS LIGHT UP AND HEAVY DOWN -> '|' */ + { 0x7E, 0x2D }, /* BOX DRAWINGS HEAVY LEFT AND LIGHT RIGHT -> '-' */ + { 0x7F, 0x7C }, /* BOX DRAWINGS HEAVY UP AND LIGHT DOWN -> '|' */ + { 0x80, 0x00 }, /* UPPER HALF BLOCK -> ... */ + { 0x93, 0x23 }, /* DARK SHADE -> '#' */ + { 0x94, 0x2D }, /* UPPER ONE EIGHTH BLOCK -> '-' */ + { 0x95, 0x7C }, /* RIGHT ONE EIGHTH BLOCK -> '|' */ + { 0x96, 0x00 }, /* QUADRANT LOWER LEFT -> ... */ + { 0xB1, 0x23 }, /* WHITE PARALLELOGRAM -> '#' */ + { 0xB2, 0x00 }, /* BLACK UP-POINTING TRIANGLE -> ... */ + { 0xB5, 0x5E }, /* WHITE UP-POINTING SMALL TRIANGLE -> '^' */ + { 0xB6, 0x00 }, /* BLACK RIGHT-POINTING TRIANGLE -> ... */ + { 0xBB, 0x3E }, /* WHITE RIGHT-POINTING POINTER -> '>' */ + { 0xBC, 0x00 }, /* BLACK DOWN-POINTING TRIANGLE -> ... */ + { 0xBF, 0x56 }, /* WHITE DOWN-POINTING SMALL TRIANGLE -> 'V' */ + { 0xC0, 0x00 }, /* BLACK LEFT-POINTING TRIANGLE -> ... */ + { 0xC5, 0x3C }, /* WHITE LEFT-POINTING POINTER -> '<' */ + { 0xC6, 0x00 }, /* BLACK DIAMOND -> ... */ + { 0xE6, 0x2A }, /* WHITE BULLET -> '*' */ + { 0xE7, 0x00 }, /* SQUARE WITH LEFT HALF BLACK -> ... */ + { 0xEB, 0x23 }, /* WHITE SQUARE WITH VERTICAL BISECTING LINE -> '#' */ + { 0xEC, 0x00 }, /* WHITE UP-POINTING TRIANGLE WITH DOT -> ... */ + { 0xEE, 0x5E }, /* UP-POINTING TRIANGLE WITH RIGHT HALF BLACK -> '^' */ + { 0xEF, 0x4F }, /* LARGE CIRCLE -> 'O' */ + { 0xF0, 0x00 }, /* WHITE SQUARE WITH UPPER LEFT QUADRANT -> ... */ + { 0xF7, 0x23 }, /* WHITE CIRCLE WITH UPPER RIGHT QUADRANT -> '#' */ + /* Entries for page 0x26 */ + { 0x05, 0x2A }, /* BLACK STAR -> '*' */ + { 0x06, 0x2A }, /* WHITE STAR -> '*' */ + { 0x2A, 0x2A }, /* STAR AND CRESCENT -> '*' */ + { 0x6F, 0x23 }, /* MUSIC SHARP SIGN -> '#' */ + { 0x98, 0x2A }, /* FLOWER -> '*' */ + { 0x9D, 0x2A }, /* OUTLINED WHITE STAR -> '*' */ + /* Entries for page 0x27 */ + { 0x13, 0x76 }, /* CHECK MARK -> 'v' */ + { 0x14, 0x56 }, /* HEAVY CHECK MARK -> 'V' */ + { 0x15, 0x78 }, /* MULTIPLICATION X -> 'x' */ + { 0x16, 0x58 }, /* HEAVY MULTIPLICATION X -> 'X' */ + { 0x17, 0x78 }, /* BALLOT X -> 'x' */ + { 0x18, 0x58 }, /* HEAVY BALLOT X -> 'X' */ + { 0x21, 0x00 }, /* STAR OF DAVID -> ... */ + { 0x46, 0x2A }, /* HEAVY CHEVRON SNOWFLAKE -> '*' */ + { 0x49, 0x00 }, /* BALLOON-SPOKED ASTERISK -> ... */ + { 0x4B, 0x2A }, /* HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK -> '*' */ + { 0x58, 0x7C }, /* LIGHT VERTICAL BAR -> '|' */ + { 0x5C, 0x27 }, /* HEAVY SINGLE COMMA QUOTATION MARK ORNAMENT -> ''' */ + { 0x5D, 0x22 }, /* HEAVY DOUBLE TURNED COMMA QUOTATION MARK ORNAMENT -> '"' */ + { 0x5E, 0x22 }, /* HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT -> '"' */ + { 0x5F, 0x2C }, /* HEAVY LOW SINGLE COMMA QUOTATION MARK ORNAMENT -> ',' */ + { 0x62, 0x21 }, /* HEAVY EXCLAMATION MARK ORNAMENT -> '!' */ + { 0xE6, 0x5B }, /* MATHEMATICAL LEFT WHITE SQUARE BRACKET -> '[' */ + { 0xE8, 0x3C }, /* MATHEMATICAL LEFT ANGLE BRACKET -> '<' */ + /* Entries for page 0x28 */ + { 0x00, 0x20 }, /* BRAILLE PATTERN BLANK -> ' ' */ + { 0x01, 0x61 }, /* BRAILLE PATTERN DOTS-1 -> 'a' */ + { 0x02, 0x31 }, /* BRAILLE PATTERN DOTS-2 -> '1' */ + { 0x03, 0x62 }, /* BRAILLE PATTERN DOTS-12 -> 'b' */ + { 0x04, 0x27 }, /* BRAILLE PATTERN DOTS-3 -> ''' */ + { 0x05, 0x6B }, /* BRAILLE PATTERN DOTS-13 -> 'k' */ + { 0x06, 0x32 }, /* BRAILLE PATTERN DOTS-23 -> '2' */ + { 0x07, 0x6C }, /* BRAILLE PATTERN DOTS-123 -> 'l' */ + { 0x08, 0x40 }, /* BRAILLE PATTERN DOTS-4 -> '@' */ + { 0x09, 0x63 }, /* BRAILLE PATTERN DOTS-14 -> 'c' */ + { 0x0A, 0x69 }, /* BRAILLE PATTERN DOTS-24 -> 'i' */ + { 0x0B, 0x66 }, /* BRAILLE PATTERN DOTS-124 -> 'f' */ + { 0x0C, 0x2F }, /* BRAILLE PATTERN DOTS-34 -> '/' */ + { 0x0D, 0x6D }, /* BRAILLE PATTERN DOTS-134 -> 'm' */ + { 0x0E, 0x73 }, /* BRAILLE PATTERN DOTS-234 -> 's' */ + { 0x0F, 0x70 }, /* BRAILLE PATTERN DOTS-1234 -> 'p' */ + { 0x10, 0x22 }, /* BRAILLE PATTERN DOTS-5 -> '"' */ + { 0x11, 0x65 }, /* BRAILLE PATTERN DOTS-15 -> 'e' */ + { 0x12, 0x33 }, /* BRAILLE PATTERN DOTS-25 -> '3' */ + { 0x13, 0x68 }, /* BRAILLE PATTERN DOTS-125 -> 'h' */ + { 0x14, 0x39 }, /* BRAILLE PATTERN DOTS-35 -> '9' */ + { 0x15, 0x6F }, /* BRAILLE PATTERN DOTS-135 -> 'o' */ + { 0x16, 0x36 }, /* BRAILLE PATTERN DOTS-235 -> '6' */ + { 0x17, 0x72 }, /* BRAILLE PATTERN DOTS-1235 -> 'r' */ + { 0x18, 0x5E }, /* BRAILLE PATTERN DOTS-45 -> '^' */ + { 0x19, 0x64 }, /* BRAILLE PATTERN DOTS-145 -> 'd' */ + { 0x1A, 0x6A }, /* BRAILLE PATTERN DOTS-245 -> 'j' */ + { 0x1B, 0x67 }, /* BRAILLE PATTERN DOTS-1245 -> 'g' */ + { 0x1C, 0x3E }, /* BRAILLE PATTERN DOTS-345 -> '>' */ + { 0x1D, 0x6E }, /* BRAILLE PATTERN DOTS-1345 -> 'n' */ + { 0x1E, 0x74 }, /* BRAILLE PATTERN DOTS-2345 -> 't' */ + { 0x1F, 0x71 }, /* BRAILLE PATTERN DOTS-12345 -> 'q' */ + { 0x20, 0x2C }, /* BRAILLE PATTERN DOTS-6 -> ',' */ + { 0x21, 0x2A }, /* BRAILLE PATTERN DOTS-16 -> '*' */ + { 0x22, 0x35 }, /* BRAILLE PATTERN DOTS-26 -> '5' */ + { 0x23, 0x3C }, /* BRAILLE PATTERN DOTS-126 -> '<' */ + { 0x24, 0x2D }, /* BRAILLE PATTERN DOTS-36 -> '-' */ + { 0x25, 0x75 }, /* BRAILLE PATTERN DOTS-136 -> 'u' */ + { 0x26, 0x38 }, /* BRAILLE PATTERN DOTS-236 -> '8' */ + { 0x27, 0x76 }, /* BRAILLE PATTERN DOTS-1236 -> 'v' */ + { 0x28, 0x2E }, /* BRAILLE PATTERN DOTS-46 -> '.' */ + { 0x29, 0x25 }, /* BRAILLE PATTERN DOTS-146 -> '%' */ + { 0x2A, 0x5B }, /* BRAILLE PATTERN DOTS-246 -> '[' */ + { 0x2B, 0x24 }, /* BRAILLE PATTERN DOTS-1246 -> '$' */ + { 0x2C, 0x2B }, /* BRAILLE PATTERN DOTS-346 -> '+' */ + { 0x2D, 0x78 }, /* BRAILLE PATTERN DOTS-1346 -> 'x' */ + { 0x2E, 0x21 }, /* BRAILLE PATTERN DOTS-2346 -> '!' */ + { 0x2F, 0x26 }, /* BRAILLE PATTERN DOTS-12346 -> '&' */ + { 0x30, 0x3B }, /* BRAILLE PATTERN DOTS-56 -> ';' */ + { 0x31, 0x3A }, /* BRAILLE PATTERN DOTS-156 -> ':' */ + { 0x32, 0x34 }, /* BRAILLE PATTERN DOTS-256 -> '4' */ + { 0x33, 0x5C }, /* BRAILLE PATTERN DOTS-1256 -> '\' */ + { 0x34, 0x30 }, /* BRAILLE PATTERN DOTS-356 -> '0' */ + { 0x35, 0x7A }, /* BRAILLE PATTERN DOTS-1356 -> 'z' */ + { 0x36, 0x37 }, /* BRAILLE PATTERN DOTS-2356 -> '7' */ + { 0x37, 0x28 }, /* BRAILLE PATTERN DOTS-12356 -> '(' */ + { 0x38, 0x5F }, /* BRAILLE PATTERN DOTS-456 -> '_' */ + { 0x39, 0x3F }, /* BRAILLE PATTERN DOTS-1456 -> '?' */ + { 0x3A, 0x77 }, /* BRAILLE PATTERN DOTS-2456 -> 'w' */ + { 0x3B, 0x5D }, /* BRAILLE PATTERN DOTS-12456 -> ']' */ + { 0x3C, 0x23 }, /* BRAILLE PATTERN DOTS-3456 -> '#' */ + { 0x3D, 0x79 }, /* BRAILLE PATTERN DOTS-13456 -> 'y' */ + { 0x3E, 0x29 }, /* BRAILLE PATTERN DOTS-23456 -> ')' */ + { 0x3F, 0x3D }, /* BRAILLE PATTERN DOTS-123456 -> '=' */ + /* Entries for page 0x29 */ + { 0x83, 0x7B }, /* LEFT WHITE CURLY BRACKET -> '{' */ + /* Entries for page 0x2C */ + { 0x60, 0x4C }, /* LATIN CAPITAL LETTER L WITH DOUBLE BAR -> 'L' */ + { 0x61, 0x6C }, /* LATIN SMALL LETTER L WITH DOUBLE BAR -> 'l' */ + { 0x62, 0x4C }, /* LATIN CAPITAL LETTER L WITH MIDDLE TILDE -> 'L' */ + { 0x63, 0x50 }, /* LATIN CAPITAL LETTER P WITH STROKE -> 'P' */ + { 0x64, 0x52 }, /* LATIN CAPITAL LETTER R WITH TAIL -> 'R' */ + { 0x65, 0x61 }, /* LATIN SMALL LETTER A WITH STROKE -> 'a' */ + { 0x66, 0x74 }, /* LATIN SMALL LETTER T WITH DIAGONAL STROKE -> 't' */ + { 0x67, 0x48 }, /* LATIN CAPITAL LETTER H WITH DESCENDER -> 'H' */ + { 0x68, 0x68 }, /* LATIN SMALL LETTER H WITH DESCENDER -> 'h' */ + { 0x69, 0x4B }, /* LATIN CAPITAL LETTER K WITH DESCENDER -> 'K' */ + { 0x6A, 0x6B }, /* LATIN SMALL LETTER K WITH DESCENDER -> 'k' */ + { 0x6B, 0x5A }, /* LATIN CAPITAL LETTER Z WITH DESCENDER -> 'Z' */ + { 0x6C, 0x7A }, /* LATIN SMALL LETTER Z WITH DESCENDER -> 'z' */ + { 0x6E, 0x4D }, /* LATIN CAPITAL LETTER M WITH HOOK -> 'M' */ + { 0x6F, 0x41 }, /* LATIN CAPITAL LETTER TURNED A -> 'A' */ + /* Entries for page 0x2E */ + { 0x00, 0x72 }, /* RIGHT ANGLE SUBSTITUTION MARKER -> 'r' */ + { 0x06, 0x54 }, /* RAISED INTERPOLATION MARKER -> 'T' */ + { 0x09, 0x73 }, /* LEFT TRANSPOSITION BRACKET -> 's' */ + { 0x0C, 0x5C }, /* LEFT RAISED OMISSION BRACKET -> '\' */ + { 0x0D, 0x2F }, /* RIGHT RAISED OMISSION BRACKET -> '/' */ + { 0x12, 0x3E }, /* HYPODIASTOLE -> '>' */ + { 0x13, 0x25 }, /* DOTTED OBELOS -> '%' */ + { 0x16, 0x3E }, /* DOTTED RIGHT-POINTING ANGLE -> '>' */ + { 0x17, 0x3D }, /* DOUBLE OBLIQUE HYPHEN -> '=' */ + { 0x19, 0x2F }, /* PALM BRANCH -> '/' */ + { 0x1A, 0x2D }, /* HYPHEN WITH DIAERESIS -> '-' */ + { 0x1B, 0x7E }, /* TILDE WITH RING ABOVE -> '~' */ + { 0x1C, 0x5C }, /* LEFT LOW PARAPHRASE BRACKET -> '\' */ + { 0x1D, 0x2F }, /* RIGHT LOW PARAPHRASE BRACKET -> '/' */ + { 0x1E, 0x7E }, /* TILDE WITH DOT ABOVE -> '~' */ + { 0x1F, 0x7E }, /* TILDE WITH DOT BELOW -> '~' */ + { 0x2E, 0x3F }, /* REVERSED QUESTION MARK -> '?' */ + { 0x2F, 0x27 }, /* VERTICAL TILDE -> ''' */ + { 0x30, 0x6F }, /* RING POINT -> 'o' */ + { 0x31, 0x2E }, /* WORD SEPARATOR MIDDLE DOT -> '.' */ + { 0x32, 0x2C }, /* TURNED COMMA -> ',' */ + { 0x33, 0x2E }, /* RAISED DOT -> '.' */ + { 0x34, 0x2C }, /* RAISED COMMA -> ',' */ + { 0x35, 0x3B }, /* TURNED SEMICOLON -> ';' */ + { 0x3C, 0x78 }, /* STENOGRAPHIC FULL STOP -> 'x' */ + { 0x3D, 0x7C }, /* VERTICAL SIX DOTS -> '|' */ + { 0x40, 0x3D }, /* DOUBLE HYPHEN -> '=' */ + { 0x41, 0x2C }, /* REVERSED COMMA -> ',' */ + { 0x42, 0x22 }, /* DOUBLE LOW-REVERSED-9 QUOTATION MARK -> '"' */ + /* Entries for page 0x30 */ + { 0x00, 0x20 }, /* IDEOGRAPHIC SPACE -> ' ' */ + { 0x03, 0x22 }, /* DITTO MARK -> '"' */ + { 0x05, 0x22 }, /* IDEOGRAPHIC ITERATION MARK -> '"' */ + { 0x06, 0x2F }, /* IDEOGRAPHIC CLOSING MARK -> '/' */ + { 0x07, 0x30 }, /* IDEOGRAPHIC NUMBER ZERO -> '0' */ + { 0x08, 0x3C }, /* LEFT ANGLE BRACKET -> '<' */ + { 0x0C, 0x5B }, /* LEFT CORNER BRACKET -> '[' */ + { 0x0E, 0x7B }, /* LEFT WHITE CORNER BRACKET -> '{' */ + { 0x12, 0x40 }, /* POSTAL MARK -> '@' */ + { 0x14, 0x5B }, /* LEFT TORTOISE SHELL BRACKET -> '[' */ + { 0x20, 0x40 }, /* POSTAL MARK FACE -> '@' */ + { 0x21, 0x31 }, /* HANGZHOU NUMERAL ONE -> '1' */ + { 0x22, 0x32 }, /* HANGZHOU NUMERAL TWO -> '2' */ + { 0x23, 0x33 }, /* HANGZHOU NUMERAL THREE -> '3' */ + { 0x24, 0x34 }, /* HANGZHOU NUMERAL FOUR -> '4' */ + { 0x25, 0x35 }, /* HANGZHOU NUMERAL FIVE -> '5' */ + { 0x26, 0x36 }, /* HANGZHOU NUMERAL SIX -> '6' */ + { 0x27, 0x37 }, /* HANGZHOU NUMERAL SEVEN -> '7' */ + { 0x28, 0x38 }, /* HANGZHOU NUMERAL EIGHT -> '8' */ + { 0x29, 0x39 }, /* HANGZHOU NUMERAL NINE -> '9' */ + { 0x30, 0x7E }, /* WAVY DASH -> '~' */ + { 0x31, 0x00 }, /* VERTICAL KANA REPEAT MARK -> ... */ + { 0x34, 0x2B }, /* VERTICAL KANA REPEAT WITH VOICED SOUND MARK UPPER HALF -> '+' */ + { 0x36, 0x40 }, /* CIRCLED POSTAL MARK -> '@' */ + { 0x41, 0x61 }, /* HIRAGANA LETTER SMALL A -> 'a' */ + { 0x42, 0x61 }, /* HIRAGANA LETTER A -> 'a' */ + { 0x43, 0x69 }, /* HIRAGANA LETTER SMALL I -> 'i' */ + { 0x44, 0x69 }, /* HIRAGANA LETTER I -> 'i' */ + { 0x45, 0x75 }, /* HIRAGANA LETTER SMALL U -> 'u' */ + { 0x46, 0x75 }, /* HIRAGANA LETTER U -> 'u' */ + { 0x47, 0x65 }, /* HIRAGANA LETTER SMALL E -> 'e' */ + { 0x48, 0x65 }, /* HIRAGANA LETTER E -> 'e' */ + { 0x49, 0x6F }, /* HIRAGANA LETTER SMALL O -> 'o' */ + { 0x4A, 0x6F }, /* HIRAGANA LETTER O -> 'o' */ + { 0x93, 0x6E }, /* HIRAGANA LETTER N -> 'n' */ + { 0x9D, 0x22 }, /* HIRAGANA ITERATION MARK -> '"' */ + { 0x9E, 0x22 }, /* HIRAGANA VOICED ITERATION MARK -> '"' */ + { 0xA0, 0x3D }, /* KATAKANA-HIRAGANA DOUBLE HYPHEN -> '=' */ + { 0xA1, 0x61 }, /* KATAKANA LETTER SMALL A -> 'a' */ + { 0xA2, 0x61 }, /* KATAKANA LETTER A -> 'a' */ + { 0xA3, 0x69 }, /* KATAKANA LETTER SMALL I -> 'i' */ + { 0xA4, 0x69 }, /* KATAKANA LETTER I -> 'i' */ + { 0xA5, 0x75 }, /* KATAKANA LETTER SMALL U -> 'u' */ + { 0xA6, 0x75 }, /* KATAKANA LETTER U -> 'u' */ + { 0xA7, 0x65 }, /* KATAKANA LETTER SMALL E -> 'e' */ + { 0xA8, 0x65 }, /* KATAKANA LETTER E -> 'e' */ + { 0xA9, 0x6F }, /* KATAKANA LETTER SMALL O -> 'o' */ + { 0xAA, 0x6F }, /* KATAKANA LETTER O -> 'o' */ + { 0xF3, 0x6E }, /* KATAKANA LETTER N -> 'n' */ + { 0xFB, 0x2A }, /* KATAKANA MIDDLE DOT -> '*' */ + { 0xFC, 0x2D }, /* KATAKANA-HIRAGANA PROLONGED SOUND MARK -> '-' */ + { 0xFD, 0x22 }, /* KATAKANA ITERATION MARK -> '"' */ + { 0xFE, 0x22 }, /* KATAKANA VOICED ITERATION MARK -> '"' */ + /* Entries for page 0x31 */ + { 0x05, 0x42 }, /* BOPOMOFO LETTER B -> 'B' */ + { 0x06, 0x50 }, /* BOPOMOFO LETTER P -> 'P' */ + { 0x07, 0x4D }, /* BOPOMOFO LETTER M -> 'M' */ + { 0x08, 0x46 }, /* BOPOMOFO LETTER F -> 'F' */ + { 0x09, 0x44 }, /* BOPOMOFO LETTER D -> 'D' */ + { 0x0A, 0x54 }, /* BOPOMOFO LETTER T -> 'T' */ + { 0x0B, 0x4E }, /* BOPOMOFO LETTER N -> 'N' */ + { 0x0C, 0x4C }, /* BOPOMOFO LETTER L -> 'L' */ + { 0x0D, 0x47 }, /* BOPOMOFO LETTER G -> 'G' */ + { 0x0E, 0x4B }, /* BOPOMOFO LETTER K -> 'K' */ + { 0x0F, 0x48 }, /* BOPOMOFO LETTER H -> 'H' */ + { 0x10, 0x4A }, /* BOPOMOFO LETTER J -> 'J' */ + { 0x11, 0x51 }, /* BOPOMOFO LETTER Q -> 'Q' */ + { 0x12, 0x58 }, /* BOPOMOFO LETTER X -> 'X' */ + { 0x16, 0x52 }, /* BOPOMOFO LETTER R -> 'R' */ + { 0x17, 0x5A }, /* BOPOMOFO LETTER Z -> 'Z' */ + { 0x18, 0x43 }, /* BOPOMOFO LETTER C -> 'C' */ + { 0x19, 0x53 }, /* BOPOMOFO LETTER S -> 'S' */ + { 0x1A, 0x41 }, /* BOPOMOFO LETTER A -> 'A' */ + { 0x1B, 0x4F }, /* BOPOMOFO LETTER O -> 'O' */ + { 0x1C, 0x45 }, /* BOPOMOFO LETTER E -> 'E' */ + { 0x27, 0x49 }, /* BOPOMOFO LETTER I -> 'I' */ + { 0x28, 0x55 }, /* BOPOMOFO LETTER U -> 'U' */ + { 0x2A, 0x56 }, /* BOPOMOFO LETTER V -> 'V' */ + { 0x31, 0x67 }, /* HANGUL LETTER KIYEOK -> 'g' */ + { 0x34, 0x6E }, /* HANGUL LETTER NIEUN -> 'n' */ + { 0x37, 0x64 }, /* HANGUL LETTER TIKEUT -> 'd' */ + { 0x39, 0x72 }, /* HANGUL LETTER RIEUL -> 'r' */ + { 0x41, 0x6D }, /* HANGUL LETTER MIEUM -> 'm' */ + { 0x42, 0x62 }, /* HANGUL LETTER PIEUP -> 'b' */ + { 0x45, 0x73 }, /* HANGUL LETTER SIOS -> 's' */ + { 0x48, 0x6A }, /* HANGUL LETTER CIEUC -> 'j' */ + { 0x4A, 0x63 }, /* HANGUL LETTER CHIEUCH -> 'c' */ + { 0x4B, 0x6B }, /* HANGUL LETTER KHIEUKH -> 'k' */ + { 0x4C, 0x74 }, /* HANGUL LETTER THIEUTH -> 't' */ + { 0x4D, 0x70 }, /* HANGUL LETTER PHIEUPH -> 'p' */ + { 0x4E, 0x68 }, /* HANGUL LETTER HIEUH -> 'h' */ + { 0x4F, 0x61 }, /* HANGUL LETTER A -> 'a' */ + { 0x54, 0x65 }, /* HANGUL LETTER E -> 'e' */ + { 0x57, 0x6F }, /* HANGUL LETTER O -> 'o' */ + { 0x5C, 0x75 }, /* HANGUL LETTER U -> 'u' */ + { 0x63, 0x69 }, /* HANGUL LETTER I -> 'i' */ + { 0x7F, 0x5A }, /* HANGUL LETTER PANSIOS -> 'Z' */ + { 0x81, 0x4E }, /* HANGUL LETTER YESIEUNG -> 'N' */ + { 0x86, 0x51 }, /* HANGUL LETTER YEORINHIEUH -> 'Q' */ + { 0x8D, 0x55 }, /* HANGUL LETTER ARAEA -> 'U' */ + { 0xB4, 0x50 }, /* BOPOMOFO FINAL LETTER P -> 'P' */ + { 0xB5, 0x54 }, /* BOPOMOFO FINAL LETTER T -> 'T' */ + { 0xB6, 0x4B }, /* BOPOMOFO FINAL LETTER K -> 'K' */ + { 0xB7, 0x48 }, /* BOPOMOFO FINAL LETTER H -> 'H' */ + /* Entries for page 0x32 */ + { 0xD0, 0x61 }, /* CIRCLED KATAKANA A -> 'a' */ + { 0xD1, 0x69 }, /* CIRCLED KATAKANA I -> 'i' */ + { 0xD2, 0x75 }, /* CIRCLED KATAKANA U -> 'u' */ + { 0xD3, 0x75 }, /* CIRCLED KATAKANA E -> 'u' */ + { 0xD4, 0x6F }, /* CIRCLED KATAKANA O -> 'o' */ + /* Entries for page 0xA0 */ + { 0x02, 0x69 }, /* YI SYLLABLE I -> 'i' */ + { 0x0A, 0x61 }, /* YI SYLLABLE A -> 'a' */ + { 0x11, 0x6F }, /* YI SYLLABLE O -> 'o' */ + { 0x14, 0x65 }, /* YI SYLLABLE E -> 'e' */ + /* Entries for page 0xC5 */ + { 0x44, 0x61 }, /* HANGUL SYLLABLE A -> 'a' */ + { 0xD0, 0x65 }, /* HANGUL SYLLABLE E -> 'e' */ + /* Entries for page 0xC6 */ + { 0x24, 0x6F }, /* HANGUL SYLLABLE O -> 'o' */ + { 0xB0, 0x75 }, /* HANGUL SYLLABLE U -> 'u' */ + /* Entries for page 0xC7 */ + { 0x74, 0x69 }, /* HANGUL SYLLABLE I -> 'i' */ + /* Entries for page 0xFB */ + { 0x1D, 0x69 }, /* HEBREW LETTER YOD WITH HIRIQ -> 'i' */ + { 0x20, 0x60 }, /* HEBREW LETTER ALTERNATIVE AYIN -> '`' */ + { 0x21, 0x41 }, /* HEBREW LETTER WIDE ALEF -> 'A' */ + { 0x22, 0x64 }, /* HEBREW LETTER WIDE DALET -> 'd' */ + { 0x23, 0x68 }, /* HEBREW LETTER WIDE HE -> 'h' */ + { 0x25, 0x6C }, /* HEBREW LETTER WIDE LAMED -> 'l' */ + { 0x26, 0x6D }, /* HEBREW LETTER WIDE FINAL MEM -> 'm' */ + { 0x27, 0x72 }, /* HEBREW LETTER WIDE RESH -> 'r' */ + { 0x28, 0x74 }, /* HEBREW LETTER WIDE TAV -> 't' */ + { 0x29, 0x2B }, /* HEBREW LETTER ALTERNATIVE PLUS SIGN -> '+' */ + { 0x2B, 0x53 }, /* HEBREW LETTER SHIN WITH SIN DOT -> 'S' */ + { 0x2D, 0x53 }, /* HEBREW LETTER SHIN WITH DAGESH AND SIN DOT -> 'S' */ + { 0x2E, 0x61 }, /* HEBREW LETTER ALEF WITH PATAH -> 'a' */ + { 0x2F, 0x61 }, /* HEBREW LETTER ALEF WITH QAMATS -> 'a' */ + { 0x30, 0x41 }, /* HEBREW LETTER ALEF WITH MAPIQ -> 'A' */ + { 0x31, 0x62 }, /* HEBREW LETTER BET WITH DAGESH -> 'b' */ + { 0x32, 0x67 }, /* HEBREW LETTER GIMEL WITH DAGESH -> 'g' */ + { 0x33, 0x64 }, /* HEBREW LETTER DALET WITH DAGESH -> 'd' */ + { 0x34, 0x68 }, /* HEBREW LETTER HE WITH MAPIQ -> 'h' */ + { 0x35, 0x76 }, /* HEBREW LETTER VAV WITH DAGESH -> 'v' */ + { 0x36, 0x7A }, /* HEBREW LETTER ZAYIN WITH DAGESH -> 'z' */ + { 0x38, 0x74 }, /* HEBREW LETTER TET WITH DAGESH -> 't' */ + { 0x39, 0x79 }, /* HEBREW LETTER YOD WITH DAGESH -> 'y' */ + { 0x3C, 0x6C }, /* HEBREW LETTER LAMED WITH DAGESH -> 'l' */ + { 0x3E, 0x6D }, /* HEBREW LETTER MEM WITH DAGESH -> 'm' */ + { 0x40, 0x6E }, /* HEBREW LETTER NUN WITH DAGESH -> 'n' */ + { 0x41, 0x73 }, /* HEBREW LETTER SAMEKH WITH DAGESH -> 's' */ + { 0x43, 0x70 }, /* HEBREW LETTER FINAL PE WITH DAGESH -> 'p' */ + { 0x44, 0x70 }, /* HEBREW LETTER PE WITH DAGESH -> 'p' */ + { 0x47, 0x6B }, /* HEBREW LETTER QOF WITH DAGESH -> 'k' */ + { 0x48, 0x72 }, /* HEBREW LETTER RESH WITH DAGESH -> 'r' */ + { 0x4A, 0x74 }, /* HEBREW LETTER TAV WITH DAGESH -> 't' */ + { 0x4B, 0x6F }, /* HEBREW LETTER VAV WITH HOLAM -> 'o' */ + { 0x4C, 0x76 }, /* HEBREW LETTER BET WITH RAFE -> 'v' */ + { 0x4E, 0x66 }, /* HEBREW LETTER PE WITH RAFE -> 'f' */ + /* Entries for page 0xFE */ + { 0x23, 0x7E }, /* COMBINING DOUBLE TILDE RIGHT HALF -> '~' */ + { 0x32, 0x2D }, /* PRESENTATION FORM FOR VERTICAL EN DASH -> '-' */ + { 0x33, 0x5F }, /* PRESENTATION FORM FOR VERTICAL LOW LINE -> '_' */ + { 0x34, 0x5F }, /* PRESENTATION FORM FOR VERTICAL WAVY LOW LINE -> '_' */ + { 0x35, 0x28 }, /* PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS -> '(' */ + { 0x37, 0x7B }, /* PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET -> '{' */ + { 0x39, 0x5B }, /* PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET -> '[' */ + { 0x3F, 0x3C }, /* PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET -> '<' */ + { 0x41, 0x5B }, /* PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET -> '[' */ + { 0x43, 0x7B }, /* PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET -> '{' */ + { 0x44, 0x7D }, /* PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET -> '}' */ + { 0x50, 0x2C }, /* SMALL COMMA -> ',' */ + { 0x51, 0x2C }, /* SMALL IDEOGRAPHIC COMMA -> ',' */ + { 0x52, 0x2E }, /* SMALL FULL STOP -> '.' */ + { 0x54, 0x3B }, /* SMALL SEMICOLON -> ';' */ + { 0x55, 0x3A }, /* SMALL COLON -> ':' */ + { 0x56, 0x3F }, /* SMALL QUESTION MARK -> '?' */ + { 0x57, 0x21 }, /* SMALL EXCLAMATION MARK -> '!' */ + { 0x58, 0x2D }, /* SMALL EM DASH -> '-' */ + { 0x59, 0x28 }, /* SMALL LEFT PARENTHESIS -> '(' */ + { 0x5A, 0x29 }, /* SMALL RIGHT PARENTHESIS -> ')' */ + { 0x5B, 0x7B }, /* SMALL LEFT CURLY BRACKET -> '{' */ + { 0x5C, 0x7D }, /* SMALL RIGHT CURLY BRACKET -> '}' */ + { 0x5D, 0x7B }, /* SMALL LEFT TORTOISE SHELL BRACKET -> '{' */ + { 0x5E, 0x7D }, /* SMALL RIGHT TORTOISE SHELL BRACKET -> '}' */ + { 0x5F, 0x23 }, /* SMALL NUMBER SIGN -> '#' */ + { 0x60, 0x26 }, /* SMALL AMPERSAND -> '&' */ + { 0x61, 0x2A }, /* SMALL ASTERISK -> '*' */ + { 0x62, 0x2B }, /* SMALL PLUS SIGN -> '+' */ + { 0x63, 0x2D }, /* SMALL HYPHEN-MINUS -> '-' */ + { 0x64, 0x3C }, /* SMALL LESS-THAN SIGN -> '<' */ + { 0x65, 0x3E }, /* SMALL GREATER-THAN SIGN -> '>' */ + { 0x66, 0x3D }, /* SMALL EQUALS SIGN -> '=' */ + { 0x68, 0x5C }, /* SMALL REVERSE SOLIDUS -> '\' */ + { 0x69, 0x24 }, /* SMALL DOLLAR SIGN -> '$' */ + { 0x6A, 0x25 }, /* SMALL PERCENT SIGN -> '%' */ + { 0x6B, 0x40 }, /* SMALL COMMERCIAL AT -> '@' */ + /* Entries for page 0xFF */ + { 0x61, 0x2E }, /* HALFWIDTH IDEOGRAPHIC FULL STOP -> '.' */ + { 0x62, 0x5B }, /* HALFWIDTH LEFT CORNER BRACKET -> '[' */ + { 0x63, 0x5D }, /* HALFWIDTH RIGHT CORNER BRACKET -> ']' */ + { 0x64, 0x2C }, /* HALFWIDTH IDEOGRAPHIC COMMA -> ',' */ + { 0x65, 0x2A }, /* HALFWIDTH KATAKANA MIDDLE DOT -> '*' */ + { 0x67, 0x61 }, /* HALFWIDTH KATAKANA LETTER SMALL A -> 'a' */ + { 0x68, 0x69 }, /* HALFWIDTH KATAKANA LETTER SMALL I -> 'i' */ + { 0x69, 0x75 }, /* HALFWIDTH KATAKANA LETTER SMALL U -> 'u' */ + { 0x6A, 0x65 }, /* HALFWIDTH KATAKANA LETTER SMALL E -> 'e' */ + { 0x6B, 0x6F }, /* HALFWIDTH KATAKANA LETTER SMALL O -> 'o' */ + { 0x70, 0x2B }, /* HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK -> '+' */ + { 0x71, 0x61 }, /* HALFWIDTH KATAKANA LETTER A -> 'a' */ + { 0x72, 0x69 }, /* HALFWIDTH KATAKANA LETTER I -> 'i' */ + { 0x73, 0x75 }, /* HALFWIDTH KATAKANA LETTER U -> 'u' */ + { 0x74, 0x65 }, /* HALFWIDTH KATAKANA LETTER E -> 'e' */ + { 0x75, 0x6F }, /* HALFWIDTH KATAKANA LETTER O -> 'o' */ + { 0x9D, 0x6E }, /* HALFWIDTH KATAKANA LETTER N -> 'n' */ + { 0x9E, 0x3A }, /* HALFWIDTH KATAKANA VOICED SOUND MARK -> ':' */ + { 0x9F, 0x3B }, /* HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK -> ';' */ + { 0xA1, 0x67 }, /* HALFWIDTH HANGUL LETTER KIYEOK -> 'g' */ + { 0xA4, 0x6E }, /* HALFWIDTH HANGUL LETTER NIEUN -> 'n' */ + { 0xA7, 0x64 }, /* HALFWIDTH HANGUL LETTER TIKEUT -> 'd' */ + { 0xA9, 0x72 }, /* HALFWIDTH HANGUL LETTER RIEUL -> 'r' */ + { 0xB1, 0x6D }, /* HALFWIDTH HANGUL LETTER MIEUM -> 'm' */ + { 0xB2, 0x62 }, /* HALFWIDTH HANGUL LETTER PIEUP -> 'b' */ + { 0xB5, 0x73 }, /* HALFWIDTH HANGUL LETTER SIOS -> 's' */ + { 0xB8, 0x6A }, /* HALFWIDTH HANGUL LETTER CIEUC -> 'j' */ + { 0xBA, 0x63 }, /* HALFWIDTH HANGUL LETTER CHIEUCH -> 'c' */ + { 0xBB, 0x6B }, /* HALFWIDTH HANGUL LETTER KHIEUKH -> 'k' */ + { 0xBC, 0x74 }, /* HALFWIDTH HANGUL LETTER THIEUTH -> 't' */ + { 0xBD, 0x70 }, /* HALFWIDTH HANGUL LETTER PHIEUPH -> 'p' */ + { 0xBE, 0x68 }, /* HALFWIDTH HANGUL LETTER HIEUH -> 'h' */ + { 0xC2, 0x61 }, /* HALFWIDTH HANGUL LETTER A -> 'a' */ + { 0xC7, 0x65 }, /* HALFWIDTH HANGUL LETTER E -> 'e' */ + { 0xCC, 0x6F }, /* HALFWIDTH HANGUL LETTER O -> 'o' */ + { 0xD3, 0x75 }, /* HALFWIDTH HANGUL LETTER U -> 'u' */ + { 0xDC, 0x69 }, /* HALFWIDTH HANGUL LETTER I -> 'i' */ + { 0xE2, 0x21 }, /* FULLWIDTH NOT SIGN -> '!' */ + { 0xE3, 0x2D }, /* FULLWIDTH MACRON -> '-' */ + { 0xE4, 0x7C }, /* FULLWIDTH BROKEN BAR -> '|' */ + { 0xE8, 0x7C }, /* HALFWIDTH FORMS LIGHT VERTICAL -> '|' */ + { 0xE9, 0x3C }, /* HALFWIDTH LEFTWARDS ARROW -> '<' */ + { 0xEA, 0x5E }, /* HALFWIDTH UPWARDS ARROW -> '^' */ + { 0xEB, 0x3E }, /* HALFWIDTH RIGHTWARDS ARROW -> '>' */ + { 0xEC, 0x76 }, /* HALFWIDTH DOWNWARDS ARROW -> 'v' */ + { 0xED, 0x23 }, /* HALFWIDTH BLACK SQUARE -> '#' */ + { 0xEE, 0x4F }, /* HALFWIDTH WHITE CIRCLE -> 'O' */ + { 0xF9, 0x7B }, /* INTERLINEAR ANNOTATION ANCHOR -> '{' */ + { 0xFA, 0x7C }, /* INTERLINEAR ANNOTATION SEPARATOR -> '|' */ + { 0xFB, 0x7D }, /* INTERLINEAR ANNOTATION TERMINATOR -> '}' */ +}; + +#define UCS_PAGE_ENTRY_RANGE_MARKER 0 diff --git a/drivers/tty/vt/ucs_recompose_table.h_shipped b/drivers/tty/vt/ucs_recompose_table.h_shipped new file mode 100644 index 000000000000..bd91edde5d19 --- /dev/null +++ b/drivers/tty/vt/ucs_recompose_table.h_shipped @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * ucs_recompose_table.h - Unicode character recomposition + * + * Auto-generated by gen_ucs_recompose_table.py + * + * Unicode Version: 16.0.0 + * + * This file contains a table with most commonly used Latin, Greek, and + * Cyrillic recomposition pairs only (71 entries). To generate a table with + * all possible recomposition pairs from the Unicode BMP (1000 entries) + * instead, run: + * + * python gen_ucs_recompose_table.py --full + */ + +/* + * Table of most commonly used Latin, Greek, and Cyrillic recomposition pairs only + * Sorted by base character and then combining mark for binary search + */ +static const struct ucs_recomposition ucs_recomposition_table[] = { + { 0x0041, 0x0300, 0x00C0 }, /* LATIN CAPITAL LETTER A + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER A WITH GRAVE */ + { 0x0041, 0x0301, 0x00C1 }, /* LATIN CAPITAL LETTER A + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER A WITH ACUTE */ + { 0x0041, 0x0302, 0x00C2 }, /* LATIN CAPITAL LETTER A + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ + { 0x0041, 0x0303, 0x00C3 }, /* LATIN CAPITAL LETTER A + COMBINING TILDE = LATIN CAPITAL LETTER A WITH TILDE */ + { 0x0041, 0x0308, 0x00C4 }, /* LATIN CAPITAL LETTER A + COMBINING DIAERESIS = LATIN CAPITAL LETTER A WITH DIAERESIS */ + { 0x0041, 0x030A, 0x00C5 }, /* LATIN CAPITAL LETTER A + COMBINING RING ABOVE = LATIN CAPITAL LETTER A WITH RING ABOVE */ + { 0x0043, 0x0327, 0x00C7 }, /* LATIN CAPITAL LETTER C + COMBINING CEDILLA = LATIN CAPITAL LETTER C WITH CEDILLA */ + { 0x0045, 0x0300, 0x00C8 }, /* LATIN CAPITAL LETTER E + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER E WITH GRAVE */ + { 0x0045, 0x0301, 0x00C9 }, /* LATIN CAPITAL LETTER E + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER E WITH ACUTE */ + { 0x0045, 0x0302, 0x00CA }, /* LATIN CAPITAL LETTER E + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ + { 0x0045, 0x0308, 0x00CB }, /* LATIN CAPITAL LETTER E + COMBINING DIAERESIS = LATIN CAPITAL LETTER E WITH DIAERESIS */ + { 0x0049, 0x0300, 0x00CC }, /* LATIN CAPITAL LETTER I + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER I WITH GRAVE */ + { 0x0049, 0x0301, 0x00CD }, /* LATIN CAPITAL LETTER I + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER I WITH ACUTE */ + { 0x0049, 0x0302, 0x00CE }, /* LATIN CAPITAL LETTER I + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ + { 0x0049, 0x0308, 0x00CF }, /* LATIN CAPITAL LETTER I + COMBINING DIAERESIS = LATIN CAPITAL LETTER I WITH DIAERESIS */ + { 0x004E, 0x0303, 0x00D1 }, /* LATIN CAPITAL LETTER N + COMBINING TILDE = LATIN CAPITAL LETTER N WITH TILDE */ + { 0x004F, 0x0300, 0x00D2 }, /* LATIN CAPITAL LETTER O + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER O WITH GRAVE */ + { 0x004F, 0x0301, 0x00D3 }, /* LATIN CAPITAL LETTER O + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER O WITH ACUTE */ + { 0x004F, 0x0302, 0x00D4 }, /* LATIN CAPITAL LETTER O + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ + { 0x004F, 0x0303, 0x00D5 }, /* LATIN CAPITAL LETTER O + COMBINING TILDE = LATIN CAPITAL LETTER O WITH TILDE */ + { 0x004F, 0x0308, 0x00D6 }, /* LATIN CAPITAL LETTER O + COMBINING DIAERESIS = LATIN CAPITAL LETTER O WITH DIAERESIS */ + { 0x0055, 0x0300, 0x00D9 }, /* LATIN CAPITAL LETTER U + COMBINING GRAVE ACCENT = LATIN CAPITAL LETTER U WITH GRAVE */ + { 0x0055, 0x0301, 0x00DA }, /* LATIN CAPITAL LETTER U + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER U WITH ACUTE */ + { 0x0055, 0x0302, 0x00DB }, /* LATIN CAPITAL LETTER U + COMBINING CIRCUMFLEX ACCENT = LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ + { 0x0055, 0x0308, 0x00DC }, /* LATIN CAPITAL LETTER U + COMBINING DIAERESIS = LATIN CAPITAL LETTER U WITH DIAERESIS */ + { 0x0059, 0x0301, 0x00DD }, /* LATIN CAPITAL LETTER Y + COMBINING ACUTE ACCENT = LATIN CAPITAL LETTER Y WITH ACUTE */ + { 0x0061, 0x0300, 0x00E0 }, /* LATIN SMALL LETTER A + COMBINING GRAVE ACCENT = LATIN SMALL LETTER A WITH GRAVE */ + { 0x0061, 0x0301, 0x00E1 }, /* LATIN SMALL LETTER A + COMBINING ACUTE ACCENT = LATIN SMALL LETTER A WITH ACUTE */ + { 0x0061, 0x0302, 0x00E2 }, /* LATIN SMALL LETTER A + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER A WITH CIRCUMFLEX */ + { 0x0061, 0x0303, 0x00E3 }, /* LATIN SMALL LETTER A + COMBINING TILDE = LATIN SMALL LETTER A WITH TILDE */ + { 0x0061, 0x0308, 0x00E4 }, /* LATIN SMALL LETTER A + COMBINING DIAERESIS = LATIN SMALL LETTER A WITH DIAERESIS */ + { 0x0061, 0x030A, 0x00E5 }, /* LATIN SMALL LETTER A + COMBINING RING ABOVE = LATIN SMALL LETTER A WITH RING ABOVE */ + { 0x0063, 0x0327, 0x00E7 }, /* LATIN SMALL LETTER C + COMBINING CEDILLA = LATIN SMALL LETTER C WITH CEDILLA */ + { 0x0065, 0x0300, 0x00E8 }, /* LATIN SMALL LETTER E + COMBINING GRAVE ACCENT = LATIN SMALL LETTER E WITH GRAVE */ + { 0x0065, 0x0301, 0x00E9 }, /* LATIN SMALL LETTER E + COMBINING ACUTE ACCENT = LATIN SMALL LETTER E WITH ACUTE */ + { 0x0065, 0x0302, 0x00EA }, /* LATIN SMALL LETTER E + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER E WITH CIRCUMFLEX */ + { 0x0065, 0x0308, 0x00EB }, /* LATIN SMALL LETTER E + COMBINING DIAERESIS = LATIN SMALL LETTER E WITH DIAERESIS */ + { 0x0069, 0x0300, 0x00EC }, /* LATIN SMALL LETTER I + COMBINING GRAVE ACCENT = LATIN SMALL LETTER I WITH GRAVE */ + { 0x0069, 0x0301, 0x00ED }, /* LATIN SMALL LETTER I + COMBINING ACUTE ACCENT = LATIN SMALL LETTER I WITH ACUTE */ + { 0x0069, 0x0302, 0x00EE }, /* LATIN SMALL LETTER I + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER I WITH CIRCUMFLEX */ + { 0x0069, 0x0308, 0x00EF }, /* LATIN SMALL LETTER I + COMBINING DIAERESIS = LATIN SMALL LETTER I WITH DIAERESIS */ + { 0x006E, 0x0303, 0x00F1 }, /* LATIN SMALL LETTER N + COMBINING TILDE = LATIN SMALL LETTER N WITH TILDE */ + { 0x006F, 0x0300, 0x00F2 }, /* LATIN SMALL LETTER O + COMBINING GRAVE ACCENT = LATIN SMALL LETTER O WITH GRAVE */ + { 0x006F, 0x0301, 0x00F3 }, /* LATIN SMALL LETTER O + COMBINING ACUTE ACCENT = LATIN SMALL LETTER O WITH ACUTE */ + { 0x006F, 0x0302, 0x00F4 }, /* LATIN SMALL LETTER O + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER O WITH CIRCUMFLEX */ + { 0x006F, 0x0303, 0x00F5 }, /* LATIN SMALL LETTER O + COMBINING TILDE = LATIN SMALL LETTER O WITH TILDE */ + { 0x006F, 0x0308, 0x00F6 }, /* LATIN SMALL LETTER O + COMBINING DIAERESIS = LATIN SMALL LETTER O WITH DIAERESIS */ + { 0x0075, 0x0300, 0x00F9 }, /* LATIN SMALL LETTER U + COMBINING GRAVE ACCENT = LATIN SMALL LETTER U WITH GRAVE */ + { 0x0075, 0x0301, 0x00FA }, /* LATIN SMALL LETTER U + COMBINING ACUTE ACCENT = LATIN SMALL LETTER U WITH ACUTE */ + { 0x0075, 0x0302, 0x00FB }, /* LATIN SMALL LETTER U + COMBINING CIRCUMFLEX ACCENT = LATIN SMALL LETTER U WITH CIRCUMFLEX */ + { 0x0075, 0x0308, 0x00FC }, /* LATIN SMALL LETTER U + COMBINING DIAERESIS = LATIN SMALL LETTER U WITH DIAERESIS */ + { 0x0079, 0x0301, 0x00FD }, /* LATIN SMALL LETTER Y + COMBINING ACUTE ACCENT = LATIN SMALL LETTER Y WITH ACUTE */ + { 0x0079, 0x0308, 0x00FF }, /* LATIN SMALL LETTER Y + COMBINING DIAERESIS = LATIN SMALL LETTER Y WITH DIAERESIS */ + { 0x0391, 0x0301, 0x0386 }, /* GREEK CAPITAL LETTER ALPHA + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER ALPHA WITH TONOS */ + { 0x0395, 0x0301, 0x0388 }, /* GREEK CAPITAL LETTER EPSILON + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER EPSILON WITH TONOS */ + { 0x0397, 0x0301, 0x0389 }, /* GREEK CAPITAL LETTER ETA + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER ETA WITH TONOS */ + { 0x0399, 0x0301, 0x038A }, /* GREEK CAPITAL LETTER IOTA + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER IOTA WITH TONOS */ + { 0x039F, 0x0301, 0x038C }, /* GREEK CAPITAL LETTER OMICRON + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER OMICRON WITH TONOS */ + { 0x03A5, 0x0301, 0x038E }, /* GREEK CAPITAL LETTER UPSILON + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER UPSILON WITH TONOS */ + { 0x03A9, 0x0301, 0x038F }, /* GREEK CAPITAL LETTER OMEGA + COMBINING ACUTE ACCENT = GREEK CAPITAL LETTER OMEGA WITH TONOS */ + { 0x03B1, 0x0301, 0x03AC }, /* GREEK SMALL LETTER ALPHA + COMBINING ACUTE ACCENT = GREEK SMALL LETTER ALPHA WITH TONOS */ + { 0x03B5, 0x0301, 0x03AD }, /* GREEK SMALL LETTER EPSILON + COMBINING ACUTE ACCENT = GREEK SMALL LETTER EPSILON WITH TONOS */ + { 0x03B7, 0x0301, 0x03AE }, /* GREEK SMALL LETTER ETA + COMBINING ACUTE ACCENT = GREEK SMALL LETTER ETA WITH TONOS */ + { 0x03B9, 0x0301, 0x03AF }, /* GREEK SMALL LETTER IOTA + COMBINING ACUTE ACCENT = GREEK SMALL LETTER IOTA WITH TONOS */ + { 0x03BF, 0x0301, 0x03CC }, /* GREEK SMALL LETTER OMICRON + COMBINING ACUTE ACCENT = GREEK SMALL LETTER OMICRON WITH TONOS */ + { 0x03C5, 0x0301, 0x03CD }, /* GREEK SMALL LETTER UPSILON + COMBINING ACUTE ACCENT = GREEK SMALL LETTER UPSILON WITH TONOS */ + { 0x03C9, 0x0301, 0x03CE }, /* GREEK SMALL LETTER OMEGA + COMBINING ACUTE ACCENT = GREEK SMALL LETTER OMEGA WITH TONOS */ + { 0x0418, 0x0306, 0x0419 }, /* CYRILLIC CAPITAL LETTER I + COMBINING BREVE = CYRILLIC CAPITAL LETTER SHORT I */ + { 0x0423, 0x0306, 0x040E }, /* CYRILLIC CAPITAL LETTER U + COMBINING BREVE = CYRILLIC CAPITAL LETTER SHORT U */ + { 0x0438, 0x0306, 0x0439 }, /* CYRILLIC SMALL LETTER I + COMBINING BREVE = CYRILLIC SMALL LETTER SHORT I */ + { 0x0443, 0x0306, 0x045E }, /* CYRILLIC SMALL LETTER U + COMBINING BREVE = CYRILLIC SMALL LETTER SHORT U */ +}; + +/* + * Boundary values for quick rejection + * These are calculated by analyzing the table during generation + */ +#define UCS_RECOMPOSE_MIN_BASE 0x0041 +#define UCS_RECOMPOSE_MAX_BASE 0x0443 +#define UCS_RECOMPOSE_MIN_MARK 0x0300 +#define UCS_RECOMPOSE_MAX_MARK 0x0327 diff --git a/drivers/tty/vt/ucs_width_table.h_shipped b/drivers/tty/vt/ucs_width_table.h_shipped new file mode 100644 index 000000000000..6fcb8f1d577d --- /dev/null +++ b/drivers/tty/vt/ucs_width_table.h_shipped @@ -0,0 +1,453 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * ucs_width_table.h - Unicode character width + * + * Auto-generated by gen_ucs_width_table.py + * + * Unicode Version: 16.0.0 + */ + +/* Zero-width character ranges (BMP - Basic Multilingual Plane, U+0000 to U+FFFF) */ +static const struct ucs_interval16 ucs_zero_width_bmp_ranges[] = { + { 0x00AD, 0x00AD }, /* SOFT HYPHEN */ + { 0x0300, 0x036F }, /* COMBINING GRAVE ACCENT - COMBINING LATIN SMALL LETTER X */ + { 0x0483, 0x0489 }, /* COMBINING CYRILLIC TITLO - COMBINING CYRILLIC MILLIONS SIGN */ + { 0x0591, 0x05BD }, /* HEBREW ACCENT ETNAHTA - HEBREW POINT METEG */ + { 0x05BF, 0x05BF }, /* HEBREW POINT RAFE */ + { 0x05C1, 0x05C2 }, /* HEBREW POINT SHIN DOT - HEBREW POINT SIN DOT */ + { 0x05C4, 0x05C5 }, /* HEBREW MARK UPPER DOT - HEBREW MARK LOWER DOT */ + { 0x05C7, 0x05C7 }, /* HEBREW POINT QAMATS QATAN */ + { 0x0600, 0x0605 }, /* ARABIC NUMBER SIGN - ARABIC NUMBER MARK ABOVE */ + { 0x0610, 0x061A }, /* ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM - ARABIC SMALL KASRA */ + { 0x061C, 0x061C }, /* ARABIC LETTER MARK */ + { 0x064B, 0x065F }, /* ARABIC FATHATAN - ARABIC WAVY HAMZA BELOW */ + { 0x0670, 0x0670 }, /* ARABIC LETTER SUPERSCRIPT ALEF */ + { 0x06D6, 0x06DD }, /* ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA - ARABIC END OF AYAH */ + { 0x06DF, 0x06E4 }, /* ARABIC SMALL HIGH ROUNDED ZERO - ARABIC SMALL HIGH MADDA */ + { 0x06E7, 0x06E8 }, /* ARABIC SMALL HIGH YEH - ARABIC SMALL HIGH NOON */ + { 0x06EA, 0x06ED }, /* ARABIC EMPTY CENTRE LOW STOP - ARABIC SMALL LOW MEEM */ + { 0x070F, 0x070F }, /* SYRIAC ABBREVIATION MARK */ + { 0x0711, 0x0711 }, /* SYRIAC LETTER SUPERSCRIPT ALAPH */ + { 0x0730, 0x074A }, /* SYRIAC PTHAHA ABOVE - SYRIAC BARREKH */ + { 0x07A6, 0x07B0 }, /* THAANA ABAFILI - THAANA SUKUN */ + { 0x07EB, 0x07F3 }, /* NKO COMBINING SHORT HIGH TONE - NKO COMBINING DOUBLE DOT ABOVE */ + { 0x07FD, 0x07FD }, /* NKO DANTAYALAN */ + { 0x0816, 0x0819 }, /* SAMARITAN MARK IN - SAMARITAN MARK DAGESH */ + { 0x081B, 0x0823 }, /* SAMARITAN MARK EPENTHETIC YUT - SAMARITAN VOWEL SIGN A */ + { 0x0825, 0x0827 }, /* SAMARITAN VOWEL SIGN SHORT A - SAMARITAN VOWEL SIGN U */ + { 0x0829, 0x082D }, /* SAMARITAN VOWEL SIGN LONG I - SAMARITAN MARK NEQUDAA */ + { 0x0859, 0x085B }, /* MANDAIC AFFRICATION MARK - MANDAIC GEMINATION MARK */ + { 0x0890, 0x0891 }, /* ARABIC POUND MARK ABOVE - ARABIC PIASTRE MARK ABOVE */ + { 0x0897, 0x089F }, /* ARABIC PEPET - ARABIC HALF MADDA OVER MADDA */ + { 0x08CA, 0x0903 }, /* ARABIC SMALL HIGH FARSI YEH - DEVANAGARI SIGN VISARGA */ + { 0x093A, 0x093C }, /* DEVANAGARI VOWEL SIGN OE - DEVANAGARI SIGN NUKTA */ + { 0x093E, 0x094F }, /* DEVANAGARI VOWEL SIGN AA - DEVANAGARI VOWEL SIGN AW */ + { 0x0951, 0x0957 }, /* DEVANAGARI STRESS SIGN UDATTA - DEVANAGARI VOWEL SIGN UUE */ + { 0x0962, 0x0963 }, /* DEVANAGARI VOWEL SIGN VOCALIC L - DEVANAGARI VOWEL SIGN VOCALIC LL */ + { 0x0981, 0x0983 }, /* BENGALI SIGN CANDRABINDU - BENGALI SIGN VISARGA */ + { 0x09BC, 0x09BC }, /* BENGALI SIGN NUKTA */ + { 0x09BE, 0x09C4 }, /* BENGALI VOWEL SIGN AA - BENGALI VOWEL SIGN VOCALIC RR */ + { 0x09C7, 0x09C8 }, /* BENGALI VOWEL SIGN E - BENGALI VOWEL SIGN AI */ + { 0x09CB, 0x09CD }, /* BENGALI VOWEL SIGN O - BENGALI SIGN VIRAMA */ + { 0x09D7, 0x09D7 }, /* BENGALI AU LENGTH MARK */ + { 0x09E2, 0x09E3 }, /* BENGALI VOWEL SIGN VOCALIC L - BENGALI VOWEL SIGN VOCALIC LL */ + { 0x09FE, 0x09FE }, /* BENGALI SANDHI MARK */ + { 0x0A01, 0x0A03 }, /* GURMUKHI SIGN ADAK BINDI - GURMUKHI SIGN VISARGA */ + { 0x0A3C, 0x0A3C }, /* GURMUKHI SIGN NUKTA */ + { 0x0A3E, 0x0A42 }, /* GURMUKHI VOWEL SIGN AA - GURMUKHI VOWEL SIGN UU */ + { 0x0A47, 0x0A48 }, /* GURMUKHI VOWEL SIGN EE - GURMUKHI VOWEL SIGN AI */ + { 0x0A4B, 0x0A4D }, /* GURMUKHI VOWEL SIGN OO - GURMUKHI SIGN VIRAMA */ + { 0x0A51, 0x0A51 }, /* GURMUKHI SIGN UDAAT */ + { 0x0A70, 0x0A71 }, /* GURMUKHI TIPPI - GURMUKHI ADDAK */ + { 0x0A75, 0x0A75 }, /* GURMUKHI SIGN YAKASH */ + { 0x0A81, 0x0A83 }, /* GUJARATI SIGN CANDRABINDU - GUJARATI SIGN VISARGA */ + { 0x0ABC, 0x0ABC }, /* GUJARATI SIGN NUKTA */ + { 0x0ABE, 0x0AC5 }, /* GUJARATI VOWEL SIGN AA - GUJARATI VOWEL SIGN CANDRA E */ + { 0x0AC7, 0x0AC9 }, /* GUJARATI VOWEL SIGN E - GUJARATI VOWEL SIGN CANDRA O */ + { 0x0ACB, 0x0ACD }, /* GUJARATI VOWEL SIGN O - GUJARATI SIGN VIRAMA */ + { 0x0AE2, 0x0AE3 }, /* GUJARATI VOWEL SIGN VOCALIC L - GUJARATI VOWEL SIGN VOCALIC LL */ + { 0x0AFA, 0x0AFF }, /* GUJARATI SIGN SUKUN - GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE */ + { 0x0B01, 0x0B03 }, /* ORIYA SIGN CANDRABINDU - ORIYA SIGN VISARGA */ + { 0x0B3C, 0x0B3C }, /* ORIYA SIGN NUKTA */ + { 0x0B3E, 0x0B44 }, /* ORIYA VOWEL SIGN AA - ORIYA VOWEL SIGN VOCALIC RR */ + { 0x0B47, 0x0B48 }, /* ORIYA VOWEL SIGN E - ORIYA VOWEL SIGN AI */ + { 0x0B4B, 0x0B4D }, /* ORIYA VOWEL SIGN O - ORIYA SIGN VIRAMA */ + { 0x0B55, 0x0B57 }, /* ORIYA SIGN OVERLINE - ORIYA AU LENGTH MARK */ + { 0x0B62, 0x0B63 }, /* ORIYA VOWEL SIGN VOCALIC L - ORIYA VOWEL SIGN VOCALIC LL */ + { 0x0B82, 0x0B82 }, /* TAMIL SIGN ANUSVARA */ + { 0x0BBE, 0x0BC2 }, /* TAMIL VOWEL SIGN AA - TAMIL VOWEL SIGN UU */ + { 0x0BC6, 0x0BC8 }, /* TAMIL VOWEL SIGN E - TAMIL VOWEL SIGN AI */ + { 0x0BCA, 0x0BCD }, /* TAMIL VOWEL SIGN O - TAMIL SIGN VIRAMA */ + { 0x0BD7, 0x0BD7 }, /* TAMIL AU LENGTH MARK */ + { 0x0C00, 0x0C04 }, /* TELUGU SIGN COMBINING CANDRABINDU ABOVE - TELUGU SIGN COMBINING ANUSVARA ABOVE */ + { 0x0C3C, 0x0C3C }, /* TELUGU SIGN NUKTA */ + { 0x0C3E, 0x0C44 }, /* TELUGU VOWEL SIGN AA - TELUGU VOWEL SIGN VOCALIC RR */ + { 0x0C46, 0x0C48 }, /* TELUGU VOWEL SIGN E - TELUGU VOWEL SIGN AI */ + { 0x0C4A, 0x0C4D }, /* TELUGU VOWEL SIGN O - TELUGU SIGN VIRAMA */ + { 0x0C55, 0x0C56 }, /* TELUGU LENGTH MARK - TELUGU AI LENGTH MARK */ + { 0x0C62, 0x0C63 }, /* TELUGU VOWEL SIGN VOCALIC L - TELUGU VOWEL SIGN VOCALIC LL */ + { 0x0C81, 0x0C83 }, /* KANNADA SIGN CANDRABINDU - KANNADA SIGN VISARGA */ + { 0x0CBC, 0x0CBC }, /* KANNADA SIGN NUKTA */ + { 0x0CBE, 0x0CC4 }, /* KANNADA VOWEL SIGN AA - KANNADA VOWEL SIGN VOCALIC RR */ + { 0x0CC6, 0x0CC8 }, /* KANNADA VOWEL SIGN E - KANNADA VOWEL SIGN AI */ + { 0x0CCA, 0x0CCD }, /* KANNADA VOWEL SIGN O - KANNADA SIGN VIRAMA */ + { 0x0CD5, 0x0CD6 }, /* KANNADA LENGTH MARK - KANNADA AI LENGTH MARK */ + { 0x0CE2, 0x0CE3 }, /* KANNADA VOWEL SIGN VOCALIC L - KANNADA VOWEL SIGN VOCALIC LL */ + { 0x0CF3, 0x0CF3 }, /* KANNADA SIGN COMBINING ANUSVARA ABOVE RIGHT */ + { 0x0D00, 0x0D03 }, /* MALAYALAM SIGN COMBINING ANUSVARA ABOVE - MALAYALAM SIGN VISARGA */ + { 0x0D3B, 0x0D3C }, /* MALAYALAM SIGN VERTICAL BAR VIRAMA - MALAYALAM SIGN CIRCULAR VIRAMA */ + { 0x0D3E, 0x0D44 }, /* MALAYALAM VOWEL SIGN AA - MALAYALAM VOWEL SIGN VOCALIC RR */ + { 0x0D46, 0x0D48 }, /* MALAYALAM VOWEL SIGN E - MALAYALAM VOWEL SIGN AI */ + { 0x0D4A, 0x0D4D }, /* MALAYALAM VOWEL SIGN O - MALAYALAM SIGN VIRAMA */ + { 0x0D57, 0x0D57 }, /* MALAYALAM AU LENGTH MARK */ + { 0x0D62, 0x0D63 }, /* MALAYALAM VOWEL SIGN VOCALIC L - MALAYALAM VOWEL SIGN VOCALIC LL */ + { 0x0D81, 0x0D83 }, /* SINHALA SIGN CANDRABINDU - SINHALA SIGN VISARGAYA */ + { 0x0DCA, 0x0DCA }, /* SINHALA SIGN AL-LAKUNA */ + { 0x0DCF, 0x0DD4 }, /* SINHALA VOWEL SIGN AELA-PILLA - SINHALA VOWEL SIGN KETTI PAA-PILLA */ + { 0x0DD6, 0x0DD6 }, /* SINHALA VOWEL SIGN DIGA PAA-PILLA */ + { 0x0DD8, 0x0DDF }, /* SINHALA VOWEL SIGN GAETTA-PILLA - SINHALA VOWEL SIGN GAYANUKITTA */ + { 0x0DF2, 0x0DF3 }, /* SINHALA VOWEL SIGN DIGA GAETTA-PILLA - SINHALA VOWEL SIGN DIGA GAYANUKITTA */ + { 0x0E31, 0x0E31 }, /* THAI CHARACTER MAI HAN-AKAT */ + { 0x0E34, 0x0E3A }, /* THAI CHARACTER SARA I - THAI CHARACTER PHINTHU */ + { 0x0E47, 0x0E4E }, /* THAI CHARACTER MAITAIKHU - THAI CHARACTER YAMAKKAN */ + { 0x0EB1, 0x0EB1 }, /* LAO VOWEL SIGN MAI KAN */ + { 0x0EB4, 0x0EBC }, /* LAO VOWEL SIGN I - LAO SEMIVOWEL SIGN LO */ + { 0x0EC8, 0x0ECE }, /* LAO TONE MAI EK - LAO YAMAKKAN */ + { 0x0F18, 0x0F19 }, /* TIBETAN ASTROLOGICAL SIGN -KHYUD PA - TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS */ + { 0x0F35, 0x0F35 }, /* TIBETAN MARK NGAS BZUNG NYI ZLA */ + { 0x0F37, 0x0F37 }, /* TIBETAN MARK NGAS BZUNG SGOR RTAGS */ + { 0x0F39, 0x0F39 }, /* TIBETAN MARK TSA -PHRU */ + { 0x0F3E, 0x0F3F }, /* TIBETAN SIGN YAR TSHES - TIBETAN SIGN MAR TSHES */ + { 0x0F71, 0x0F84 }, /* TIBETAN VOWEL SIGN AA - TIBETAN MARK HALANTA */ + { 0x0F86, 0x0F87 }, /* TIBETAN SIGN LCI RTAGS - TIBETAN SIGN YANG RTAGS */ + { 0x0F8D, 0x0F97 }, /* TIBETAN SUBJOINED SIGN LCE TSA CAN - TIBETAN SUBJOINED LETTER JA */ + { 0x0F99, 0x0FBC }, /* TIBETAN SUBJOINED LETTER NYA - TIBETAN SUBJOINED LETTER FIXED-FORM RA */ + { 0x0FC6, 0x0FC6 }, /* TIBETAN SYMBOL PADMA GDAN */ + { 0x102B, 0x103E }, /* MYANMAR VOWEL SIGN TALL AA - MYANMAR CONSONANT SIGN MEDIAL HA */ + { 0x1056, 0x1059 }, /* MYANMAR VOWEL SIGN VOCALIC R - MYANMAR VOWEL SIGN VOCALIC LL */ + { 0x105E, 0x1060 }, /* MYANMAR CONSONANT SIGN MON MEDIAL NA - MYANMAR CONSONANT SIGN MON MEDIAL LA */ + { 0x1062, 0x1064 }, /* MYANMAR VOWEL SIGN SGAW KAREN EU - MYANMAR TONE MARK SGAW KAREN KE PHO */ + { 0x1067, 0x106D }, /* MYANMAR VOWEL SIGN WESTERN PWO KAREN EU - MYANMAR SIGN WESTERN PWO KAREN TONE-5 */ + { 0x1071, 0x1074 }, /* MYANMAR VOWEL SIGN GEBA KAREN I - MYANMAR VOWEL SIGN KAYAH EE */ + { 0x1082, 0x108D }, /* MYANMAR CONSONANT SIGN SHAN MEDIAL WA - MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE */ + { 0x108F, 0x108F }, /* MYANMAR SIGN RUMAI PALAUNG TONE-5 */ + { 0x109A, 0x109D }, /* MYANMAR SIGN KHAMTI TONE-1 - MYANMAR VOWEL SIGN AITON AI */ + { 0x135D, 0x135F }, /* ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK - ETHIOPIC COMBINING GEMINATION MARK */ + { 0x1712, 0x1715 }, /* TAGALOG VOWEL SIGN I - TAGALOG SIGN PAMUDPOD */ + { 0x1732, 0x1734 }, /* HANUNOO VOWEL SIGN I - HANUNOO SIGN PAMUDPOD */ + { 0x1752, 0x1753 }, /* BUHID VOWEL SIGN I - BUHID VOWEL SIGN U */ + { 0x1772, 0x1773 }, /* TAGBANWA VOWEL SIGN I - TAGBANWA VOWEL SIGN U */ + { 0x17B4, 0x17D3 }, /* KHMER VOWEL INHERENT AQ - KHMER SIGN BATHAMASAT */ + { 0x17DD, 0x17DD }, /* KHMER SIGN ATTHACAN */ + { 0x180B, 0x180F }, /* MONGOLIAN FREE VARIATION SELECTOR ONE - MONGOLIAN FREE VARIATION SELECTOR FOUR */ + { 0x1885, 0x1886 }, /* MONGOLIAN LETTER ALI GALI BALUDA - MONGOLIAN LETTER ALI GALI THREE BALUDA */ + { 0x18A9, 0x18A9 }, /* MONGOLIAN LETTER ALI GALI DAGALGA */ + { 0x1920, 0x192B }, /* LIMBU VOWEL SIGN A - LIMBU SUBJOINED LETTER WA */ + { 0x1930, 0x193B }, /* LIMBU SMALL LETTER KA - LIMBU SIGN SA-I */ + { 0x1A17, 0x1A1B }, /* BUGINESE VOWEL SIGN I - BUGINESE VOWEL SIGN AE */ + { 0x1A55, 0x1A5E }, /* TAI THAM CONSONANT SIGN MEDIAL RA - TAI THAM CONSONANT SIGN SA */ + { 0x1A60, 0x1A7C }, /* TAI THAM SIGN SAKOT - TAI THAM SIGN KHUEN-LUE KARAN */ + { 0x1A7F, 0x1A7F }, /* TAI THAM COMBINING CRYPTOGRAMMIC DOT */ + { 0x1AB0, 0x1ACE }, /* COMBINING DOUBLED CIRCUMFLEX ACCENT - COMBINING LATIN SMALL LETTER INSULAR T */ + { 0x1B00, 0x1B04 }, /* BALINESE SIGN ULU RICEM - BALINESE SIGN BISAH */ + { 0x1B34, 0x1B44 }, /* BALINESE SIGN REREKAN - BALINESE ADEG ADEG */ + { 0x1B6B, 0x1B73 }, /* BALINESE MUSICAL SYMBOL COMBINING TEGEH - BALINESE MUSICAL SYMBOL COMBINING GONG */ + { 0x1B80, 0x1B82 }, /* SUNDANESE SIGN PANYECEK - SUNDANESE SIGN PANGWISAD */ + { 0x1BA1, 0x1BAD }, /* SUNDANESE CONSONANT SIGN PAMINGKAL - SUNDANESE CONSONANT SIGN PASANGAN WA */ + { 0x1BE6, 0x1BF3 }, /* BATAK SIGN TOMPI - BATAK PANONGONAN */ + { 0x1C24, 0x1C37 }, /* LEPCHA SUBJOINED LETTER YA - LEPCHA SIGN NUKTA */ + { 0x1CD0, 0x1CD2 }, /* VEDIC TONE KARSHANA - VEDIC TONE PRENKHA */ + { 0x1CD4, 0x1CE8 }, /* VEDIC SIGN YAJURVEDIC MIDLINE SVARITA - VEDIC SIGN VISARGA ANUDATTA WITH TAIL */ + { 0x1CED, 0x1CED }, /* VEDIC SIGN TIRYAK */ + { 0x1CF4, 0x1CF4 }, /* VEDIC TONE CANDRA ABOVE */ + { 0x1CF7, 0x1CF9 }, /* VEDIC SIGN ATIKRAMA - VEDIC TONE DOUBLE RING ABOVE */ + { 0x1DC0, 0x1DFF }, /* COMBINING DOTTED GRAVE ACCENT - COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW */ + { 0x200B, 0x200F }, /* ZERO WIDTH SPACE - RIGHT-TO-LEFT MARK */ + { 0x202A, 0x202E }, /* LEFT-TO-RIGHT EMBEDDING - RIGHT-TO-LEFT OVERRIDE */ + { 0x2060, 0x2064 }, /* WORD JOINER - INVISIBLE PLUS */ + { 0x2066, 0x206F }, /* LEFT-TO-RIGHT ISOLATE - NOMINAL DIGIT SHAPES */ + { 0x20D0, 0x20F0 }, /* COMBINING LEFT HARPOON ABOVE - COMBINING ASTERISK ABOVE */ + { 0x2640, 0x2640 }, /* FEMALE SIGN */ + { 0x2642, 0x2642 }, /* MALE SIGN */ + { 0x26A7, 0x26A7 }, /* MALE WITH STROKE AND MALE AND FEMALE SIGN */ + { 0x2CEF, 0x2CF1 }, /* COPTIC COMBINING NI ABOVE - COPTIC COMBINING SPIRITUS LENIS */ + { 0x2D7F, 0x2D7F }, /* TIFINAGH CONSONANT JOINER */ + { 0x2DE0, 0x2DFF }, /* COMBINING CYRILLIC LETTER BE - COMBINING CYRILLIC LETTER IOTIFIED BIG YUS */ + { 0x302A, 0x302F }, /* IDEOGRAPHIC LEVEL TONE MARK - HANGUL DOUBLE DOT TONE MARK */ + { 0x3099, 0x309A }, /* COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK - COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ + { 0xA66F, 0xA672 }, /* COMBINING CYRILLIC VZMET - COMBINING CYRILLIC THOUSAND MILLIONS SIGN */ + { 0xA674, 0xA67D }, /* COMBINING CYRILLIC LETTER UKRAINIAN IE - COMBINING CYRILLIC PAYEROK */ + { 0xA69E, 0xA69F }, /* COMBINING CYRILLIC LETTER EF - COMBINING CYRILLIC LETTER IOTIFIED E */ + { 0xA6F0, 0xA6F1 }, /* BAMUM COMBINING MARK KOQNDON - BAMUM COMBINING MARK TUKWENTIS */ + { 0xA802, 0xA802 }, /* SYLOTI NAGRI SIGN DVISVARA */ + { 0xA806, 0xA806 }, /* SYLOTI NAGRI SIGN HASANTA */ + { 0xA80B, 0xA80B }, /* SYLOTI NAGRI SIGN ANUSVARA */ + { 0xA823, 0xA827 }, /* SYLOTI NAGRI VOWEL SIGN A - SYLOTI NAGRI VOWEL SIGN OO */ + { 0xA82C, 0xA82C }, /* SYLOTI NAGRI SIGN ALTERNATE HASANTA */ + { 0xA880, 0xA881 }, /* SAURASHTRA SIGN ANUSVARA - SAURASHTRA SIGN VISARGA */ + { 0xA8B4, 0xA8C5 }, /* SAURASHTRA CONSONANT SIGN HAARU - SAURASHTRA SIGN CANDRABINDU */ + { 0xA8E0, 0xA8F1 }, /* COMBINING DEVANAGARI DIGIT ZERO - COMBINING DEVANAGARI SIGN AVAGRAHA */ + { 0xA8FF, 0xA8FF }, /* DEVANAGARI VOWEL SIGN AY */ + { 0xA926, 0xA92D }, /* KAYAH LI VOWEL UE - KAYAH LI TONE CALYA PLOPHU */ + { 0xA947, 0xA953 }, /* REJANG VOWEL SIGN I - REJANG VIRAMA */ + { 0xA980, 0xA983 }, /* JAVANESE SIGN PANYANGGA - JAVANESE SIGN WIGNYAN */ + { 0xA9B3, 0xA9C0 }, /* JAVANESE SIGN CECAK TELU - JAVANESE PANGKON */ + { 0xA9E5, 0xA9E5 }, /* MYANMAR SIGN SHAN SAW */ + { 0xAA29, 0xAA36 }, /* CHAM VOWEL SIGN AA - CHAM CONSONANT SIGN WA */ + { 0xAA43, 0xAA43 }, /* CHAM CONSONANT SIGN FINAL NG */ + { 0xAA4C, 0xAA4D }, /* CHAM CONSONANT SIGN FINAL M - CHAM CONSONANT SIGN FINAL H */ + { 0xAA7B, 0xAA7D }, /* MYANMAR SIGN PAO KAREN TONE - MYANMAR SIGN TAI LAING TONE-5 */ + { 0xAAB0, 0xAAB0 }, /* TAI VIET MAI KANG */ + { 0xAAB2, 0xAAB4 }, /* TAI VIET VOWEL I - TAI VIET VOWEL U */ + { 0xAAB7, 0xAAB8 }, /* TAI VIET MAI KHIT - TAI VIET VOWEL IA */ + { 0xAABE, 0xAABF }, /* TAI VIET VOWEL AM - TAI VIET TONE MAI EK */ + { 0xAAC1, 0xAAC1 }, /* TAI VIET TONE MAI THO */ + { 0xAAEB, 0xAAEF }, /* MEETEI MAYEK VOWEL SIGN II - MEETEI MAYEK VOWEL SIGN AAU */ + { 0xAAF5, 0xAAF6 }, /* MEETEI MAYEK VOWEL SIGN VISARGA - MEETEI MAYEK VIRAMA */ + { 0xABE3, 0xABEA }, /* MEETEI MAYEK VOWEL SIGN ONAP - MEETEI MAYEK VOWEL SIGN NUNG */ + { 0xABEC, 0xABED }, /* MEETEI MAYEK LUM IYEK - MEETEI MAYEK APUN IYEK */ + { 0xFB1E, 0xFB1E }, /* HEBREW POINT JUDEO-SPANISH VARIKA */ + { 0xFE00, 0xFE0F }, /* VARIATION SELECTOR-1 - VARIATION SELECTOR-16 */ + { 0xFE20, 0xFE2F }, /* COMBINING LIGATURE LEFT HALF - COMBINING CYRILLIC TITLO RIGHT HALF */ + { 0xFEFF, 0xFEFF }, /* ZERO WIDTH NO-BREAK SPACE */ + { 0xFFF9, 0xFFFB }, /* INTERLINEAR ANNOTATION ANCHOR - INTERLINEAR ANNOTATION TERMINATOR */ +}; + +/* Zero-width character ranges (non-BMP, U+10000 and above) */ +static const struct ucs_interval32 ucs_zero_width_non_bmp_ranges[] = { + { 0x101FD, 0x101FD }, /* PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE */ + { 0x102E0, 0x102E0 }, /* COPTIC EPACT THOUSANDS MARK */ + { 0x10376, 0x1037A }, /* COMBINING OLD PERMIC LETTER AN - COMBINING OLD PERMIC LETTER SII */ + { 0x10A01, 0x10A03 }, /* KHAROSHTHI VOWEL SIGN I - KHAROSHTHI VOWEL SIGN VOCALIC R */ + { 0x10A05, 0x10A06 }, /* KHAROSHTHI VOWEL SIGN E - KHAROSHTHI VOWEL SIGN O */ + { 0x10A0C, 0x10A0F }, /* KHAROSHTHI VOWEL LENGTH MARK - KHAROSHTHI SIGN VISARGA */ + { 0x10A38, 0x10A3A }, /* KHAROSHTHI SIGN BAR ABOVE - KHAROSHTHI SIGN DOT BELOW */ + { 0x10A3F, 0x10A3F }, /* KHAROSHTHI VIRAMA */ + { 0x10AE5, 0x10AE6 }, /* MANICHAEAN ABBREVIATION MARK ABOVE - MANICHAEAN ABBREVIATION MARK BELOW */ + { 0x10D24, 0x10D27 }, /* HANIFI ROHINGYA SIGN HARBAHAY - HANIFI ROHINGYA SIGN TASSI */ + { 0x10D69, 0x10D6D }, /* GARAY VOWEL SIGN E - GARAY CONSONANT NASALIZATION MARK */ + { 0x10EAB, 0x10EAC }, /* YEZIDI COMBINING HAMZA MARK - YEZIDI COMBINING MADDA MARK */ + { 0x10EFC, 0x10EFF }, /* ARABIC COMBINING ALEF OVERLAY - ARABIC SMALL LOW WORD MADDA */ + { 0x10F46, 0x10F50 }, /* SOGDIAN COMBINING DOT BELOW - SOGDIAN COMBINING STROKE BELOW */ + { 0x10F82, 0x10F85 }, /* OLD UYGHUR COMBINING DOT ABOVE - OLD UYGHUR COMBINING TWO DOTS BELOW */ + { 0x11000, 0x11002 }, /* BRAHMI SIGN CANDRABINDU - BRAHMI SIGN VISARGA */ + { 0x11038, 0x11046 }, /* BRAHMI VOWEL SIGN AA - BRAHMI VIRAMA */ + { 0x11070, 0x11070 }, /* BRAHMI SIGN OLD TAMIL VIRAMA */ + { 0x11073, 0x11074 }, /* BRAHMI VOWEL SIGN OLD TAMIL SHORT E - BRAHMI VOWEL SIGN OLD TAMIL SHORT O */ + { 0x1107F, 0x11082 }, /* BRAHMI NUMBER JOINER - KAITHI SIGN VISARGA */ + { 0x110B0, 0x110BA }, /* KAITHI VOWEL SIGN AA - KAITHI SIGN NUKTA */ + { 0x110BD, 0x110BD }, /* KAITHI NUMBER SIGN */ + { 0x110C2, 0x110C2 }, /* KAITHI VOWEL SIGN VOCALIC R */ + { 0x110CD, 0x110CD }, /* KAITHI NUMBER SIGN ABOVE */ + { 0x11100, 0x11102 }, /* CHAKMA SIGN CANDRABINDU - CHAKMA SIGN VISARGA */ + { 0x11127, 0x11134 }, /* CHAKMA VOWEL SIGN A - CHAKMA MAAYYAA */ + { 0x11145, 0x11146 }, /* CHAKMA VOWEL SIGN AA - CHAKMA VOWEL SIGN EI */ + { 0x11173, 0x11173 }, /* MAHAJANI SIGN NUKTA */ + { 0x11180, 0x11182 }, /* SHARADA SIGN CANDRABINDU - SHARADA SIGN VISARGA */ + { 0x111B3, 0x111C0 }, /* SHARADA VOWEL SIGN AA - SHARADA SIGN VIRAMA */ + { 0x111C9, 0x111CC }, /* SHARADA SANDHI MARK - SHARADA EXTRA SHORT VOWEL MARK */ + { 0x111CE, 0x111CF }, /* SHARADA VOWEL SIGN PRISHTHAMATRA E - SHARADA SIGN INVERTED CANDRABINDU */ + { 0x1122C, 0x11237 }, /* KHOJKI VOWEL SIGN AA - KHOJKI SIGN SHADDA */ + { 0x1123E, 0x1123E }, /* KHOJKI SIGN SUKUN */ + { 0x11241, 0x11241 }, /* KHOJKI VOWEL SIGN VOCALIC R */ + { 0x112DF, 0x112EA }, /* KHUDAWADI SIGN ANUSVARA - KHUDAWADI SIGN VIRAMA */ + { 0x11300, 0x11303 }, /* GRANTHA SIGN COMBINING ANUSVARA ABOVE - GRANTHA SIGN VISARGA */ + { 0x1133B, 0x1133C }, /* COMBINING BINDU BELOW - GRANTHA SIGN NUKTA */ + { 0x1133E, 0x11344 }, /* GRANTHA VOWEL SIGN AA - GRANTHA VOWEL SIGN VOCALIC RR */ + { 0x11347, 0x11348 }, /* GRANTHA VOWEL SIGN EE - GRANTHA VOWEL SIGN AI */ + { 0x1134B, 0x1134D }, /* GRANTHA VOWEL SIGN OO - GRANTHA SIGN VIRAMA */ + { 0x11357, 0x11357 }, /* GRANTHA AU LENGTH MARK */ + { 0x11362, 0x11363 }, /* GRANTHA VOWEL SIGN VOCALIC L - GRANTHA VOWEL SIGN VOCALIC LL */ + { 0x11366, 0x1136C }, /* COMBINING GRANTHA DIGIT ZERO - COMBINING GRANTHA DIGIT SIX */ + { 0x11370, 0x11374 }, /* COMBINING GRANTHA LETTER A - COMBINING GRANTHA LETTER PA */ + { 0x113B8, 0x113C0 }, /* TULU-TIGALARI VOWEL SIGN AA - TULU-TIGALARI VOWEL SIGN VOCALIC LL */ + { 0x113C2, 0x113C2 }, /* TULU-TIGALARI VOWEL SIGN EE */ + { 0x113C5, 0x113C5 }, /* TULU-TIGALARI VOWEL SIGN AI */ + { 0x113C7, 0x113CA }, /* TULU-TIGALARI VOWEL SIGN OO - TULU-TIGALARI SIGN CANDRA ANUNASIKA */ + { 0x113CC, 0x113D0 }, /* TULU-TIGALARI SIGN ANUSVARA - TULU-TIGALARI CONJOINER */ + { 0x113D2, 0x113D2 }, /* TULU-TIGALARI GEMINATION MARK */ + { 0x113E1, 0x113E2 }, /* TULU-TIGALARI VEDIC TONE SVARITA - TULU-TIGALARI VEDIC TONE ANUDATTA */ + { 0x11435, 0x11446 }, /* NEWA VOWEL SIGN AA - NEWA SIGN NUKTA */ + { 0x1145E, 0x1145E }, /* NEWA SANDHI MARK */ + { 0x114B0, 0x114C3 }, /* TIRHUTA VOWEL SIGN AA - TIRHUTA SIGN NUKTA */ + { 0x115AF, 0x115B5 }, /* SIDDHAM VOWEL SIGN AA - SIDDHAM VOWEL SIGN VOCALIC RR */ + { 0x115B8, 0x115C0 }, /* SIDDHAM VOWEL SIGN E - SIDDHAM SIGN NUKTA */ + { 0x115DC, 0x115DD }, /* SIDDHAM VOWEL SIGN ALTERNATE U - SIDDHAM VOWEL SIGN ALTERNATE UU */ + { 0x11630, 0x11640 }, /* MODI VOWEL SIGN AA - MODI SIGN ARDHACANDRA */ + { 0x116AB, 0x116B7 }, /* TAKRI SIGN ANUSVARA - TAKRI SIGN NUKTA */ + { 0x1171D, 0x1172B }, /* AHOM CONSONANT SIGN MEDIAL LA - AHOM SIGN KILLER */ + { 0x1182C, 0x1183A }, /* DOGRA VOWEL SIGN AA - DOGRA SIGN NUKTA */ + { 0x11930, 0x11935 }, /* DIVES AKURU VOWEL SIGN AA - DIVES AKURU VOWEL SIGN E */ + { 0x11937, 0x11938 }, /* DIVES AKURU VOWEL SIGN AI - DIVES AKURU VOWEL SIGN O */ + { 0x1193B, 0x1193E }, /* DIVES AKURU SIGN ANUSVARA - DIVES AKURU VIRAMA */ + { 0x11940, 0x11940 }, /* DIVES AKURU MEDIAL YA */ + { 0x11942, 0x11943 }, /* DIVES AKURU MEDIAL RA - DIVES AKURU SIGN NUKTA */ + { 0x119D1, 0x119D7 }, /* NANDINAGARI VOWEL SIGN AA - NANDINAGARI VOWEL SIGN VOCALIC RR */ + { 0x119DA, 0x119E0 }, /* NANDINAGARI VOWEL SIGN E - NANDINAGARI SIGN VIRAMA */ + { 0x119E4, 0x119E4 }, /* NANDINAGARI VOWEL SIGN PRISHTHAMATRA E */ + { 0x11A01, 0x11A0A }, /* ZANABAZAR SQUARE VOWEL SIGN I - ZANABAZAR SQUARE VOWEL LENGTH MARK */ + { 0x11A33, 0x11A39 }, /* ZANABAZAR SQUARE FINAL CONSONANT MARK - ZANABAZAR SQUARE SIGN VISARGA */ + { 0x11A3B, 0x11A3E }, /* ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA - ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA */ + { 0x11A47, 0x11A47 }, /* ZANABAZAR SQUARE SUBJOINER */ + { 0x11A51, 0x11A5B }, /* SOYOMBO VOWEL SIGN I - SOYOMBO VOWEL LENGTH MARK */ + { 0x11A8A, 0x11A99 }, /* SOYOMBO FINAL CONSONANT SIGN G - SOYOMBO SUBJOINER */ + { 0x11C2F, 0x11C36 }, /* BHAIKSUKI VOWEL SIGN AA - BHAIKSUKI VOWEL SIGN VOCALIC L */ + { 0x11C38, 0x11C3F }, /* BHAIKSUKI VOWEL SIGN E - BHAIKSUKI SIGN VIRAMA */ + { 0x11C92, 0x11CA7 }, /* MARCHEN SUBJOINED LETTER KA - MARCHEN SUBJOINED LETTER ZA */ + { 0x11CA9, 0x11CB6 }, /* MARCHEN SUBJOINED LETTER YA - MARCHEN SIGN CANDRABINDU */ + { 0x11D31, 0x11D36 }, /* MASARAM GONDI VOWEL SIGN AA - MASARAM GONDI VOWEL SIGN VOCALIC R */ + { 0x11D3A, 0x11D3A }, /* MASARAM GONDI VOWEL SIGN E */ + { 0x11D3C, 0x11D3D }, /* MASARAM GONDI VOWEL SIGN AI - MASARAM GONDI VOWEL SIGN O */ + { 0x11D3F, 0x11D45 }, /* MASARAM GONDI VOWEL SIGN AU - MASARAM GONDI VIRAMA */ + { 0x11D47, 0x11D47 }, /* MASARAM GONDI RA-KARA */ + { 0x11D8A, 0x11D8E }, /* GUNJALA GONDI VOWEL SIGN AA - GUNJALA GONDI VOWEL SIGN UU */ + { 0x11D90, 0x11D91 }, /* GUNJALA GONDI VOWEL SIGN EE - GUNJALA GONDI VOWEL SIGN AI */ + { 0x11D93, 0x11D97 }, /* GUNJALA GONDI VOWEL SIGN OO - GUNJALA GONDI VIRAMA */ + { 0x11EF3, 0x11EF6 }, /* MAKASAR VOWEL SIGN I - MAKASAR VOWEL SIGN O */ + { 0x11F00, 0x11F01 }, /* KAWI SIGN CANDRABINDU - KAWI SIGN ANUSVARA */ + { 0x11F03, 0x11F03 }, /* KAWI SIGN VISARGA */ + { 0x11F34, 0x11F3A }, /* KAWI VOWEL SIGN AA - KAWI VOWEL SIGN VOCALIC R */ + { 0x11F3E, 0x11F42 }, /* KAWI VOWEL SIGN E - KAWI CONJOINER */ + { 0x11F5A, 0x11F5A }, /* KAWI SIGN NUKTA */ + { 0x13430, 0x13440 }, /* EGYPTIAN HIEROGLYPH VERTICAL JOINER - EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY */ + { 0x13447, 0x13455 }, /* EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START - EGYPTIAN HIEROGLYPH MODIFIER DAMAGED */ + { 0x1611E, 0x1612F }, /* GURUNG KHEMA VOWEL SIGN AA - GURUNG KHEMA SIGN THOLHOMA */ + { 0x16AF0, 0x16AF4 }, /* BASSA VAH COMBINING HIGH TONE - BASSA VAH COMBINING HIGH-LOW TONE */ + { 0x16B30, 0x16B36 }, /* PAHAWH HMONG MARK CIM TUB - PAHAWH HMONG MARK CIM TAUM */ + { 0x16F4F, 0x16F4F }, /* MIAO SIGN CONSONANT MODIFIER BAR */ + { 0x16F51, 0x16F87 }, /* MIAO SIGN ASPIRATION - MIAO VOWEL SIGN UI */ + { 0x16F8F, 0x16F92 }, /* MIAO TONE RIGHT - MIAO TONE BELOW */ + { 0x16FE4, 0x16FE4 }, /* KHITAN SMALL SCRIPT FILLER */ + { 0x16FF0, 0x16FF1 }, /* VIETNAMESE ALTERNATE READING MARK CA - VIETNAMESE ALTERNATE READING MARK NHAY */ + { 0x1BC9D, 0x1BC9E }, /* DUPLOYAN THICK LETTER SELECTOR - DUPLOYAN DOUBLE MARK */ + { 0x1BCA0, 0x1BCA3 }, /* SHORTHAND FORMAT LETTER OVERLAP - SHORTHAND FORMAT UP STEP */ + { 0x1CF00, 0x1CF2D }, /* ZNAMENNY COMBINING MARK GORAZDO NIZKO S KRYZHEM ON LEFT - ZNAMENNY COMBINING MARK KRYZH ON LEFT */ + { 0x1CF30, 0x1CF46 }, /* ZNAMENNY COMBINING TONAL RANGE MARK MRACHNO - ZNAMENNY PRIZNAK MODIFIER ROG */ + { 0x1D165, 0x1D169 }, /* MUSICAL SYMBOL COMBINING STEM - MUSICAL SYMBOL COMBINING TREMOLO-3 */ + { 0x1D16D, 0x1D182 }, /* MUSICAL SYMBOL COMBINING AUGMENTATION DOT - MUSICAL SYMBOL COMBINING LOURE */ + { 0x1D185, 0x1D18B }, /* MUSICAL SYMBOL COMBINING DOIT - MUSICAL SYMBOL COMBINING TRIPLE TONGUE */ + { 0x1D1AA, 0x1D1AD }, /* MUSICAL SYMBOL COMBINING DOWN BOW - MUSICAL SYMBOL COMBINING SNAP PIZZICATO */ + { 0x1D242, 0x1D244 }, /* COMBINING GREEK MUSICAL TRISEME - COMBINING GREEK MUSICAL PENTASEME */ + { 0x1DA00, 0x1DA36 }, /* SIGNWRITING HEAD RIM - SIGNWRITING AIR SUCKING IN */ + { 0x1DA3B, 0x1DA6C }, /* SIGNWRITING MOUTH CLOSED NEUTRAL - SIGNWRITING EXCITEMENT */ + { 0x1DA75, 0x1DA75 }, /* SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS */ + { 0x1DA84, 0x1DA84 }, /* SIGNWRITING LOCATION HEAD NECK */ + { 0x1DA9B, 0x1DA9F }, /* SIGNWRITING FILL MODIFIER-2 - SIGNWRITING FILL MODIFIER-6 */ + { 0x1DAA1, 0x1DAAF }, /* SIGNWRITING ROTATION MODIFIER-2 - SIGNWRITING ROTATION MODIFIER-16 */ + { 0x1E000, 0x1E006 }, /* COMBINING GLAGOLITIC LETTER AZU - COMBINING GLAGOLITIC LETTER ZHIVETE */ + { 0x1E008, 0x1E018 }, /* COMBINING GLAGOLITIC LETTER ZEMLJA - COMBINING GLAGOLITIC LETTER HERU */ + { 0x1E01B, 0x1E021 }, /* COMBINING GLAGOLITIC LETTER SHTA - COMBINING GLAGOLITIC LETTER YATI */ + { 0x1E023, 0x1E024 }, /* COMBINING GLAGOLITIC LETTER YU - COMBINING GLAGOLITIC LETTER SMALL YUS */ + { 0x1E026, 0x1E02A }, /* COMBINING GLAGOLITIC LETTER YO - COMBINING GLAGOLITIC LETTER FITA */ + { 0x1E08F, 0x1E08F }, /* COMBINING CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ + { 0x1E130, 0x1E136 }, /* NYIAKENG PUACHUE HMONG TONE-B - NYIAKENG PUACHUE HMONG TONE-D */ + { 0x1E2AE, 0x1E2AE }, /* TOTO SIGN RISING TONE */ + { 0x1E2EC, 0x1E2EF }, /* WANCHO TONE TUP - WANCHO TONE KOINI */ + { 0x1E4EC, 0x1E4EF }, /* NAG MUNDARI SIGN MUHOR - NAG MUNDARI SIGN SUTUH */ + { 0x1E5EE, 0x1E5EF }, /* OL ONAL SIGN MU - OL ONAL SIGN IKIR */ + { 0x1E8D0, 0x1E8D6 }, /* MENDE KIKAKUI COMBINING NUMBER TEENS - MENDE KIKAKUI COMBINING NUMBER MILLIONS */ + { 0x1E944, 0x1E94A }, /* ADLAM ALIF LENGTHENER - ADLAM NUKTA */ + { 0x1F3FB, 0x1F3FF }, /* EMOJI MODIFIER FITZPATRICK TYPE-1-2 - EMOJI MODIFIER FITZPATRICK TYPE-6 */ + { 0x1F9B0, 0x1F9B3 }, /* EMOJI COMPONENT RED HAIR - EMOJI COMPONENT WHITE HAIR */ + { 0xE0001, 0xE0001 }, /* LANGUAGE TAG */ + { 0xE0020, 0xE007F }, /* TAG SPACE - CANCEL TAG */ + { 0xE0100, 0xE01EF }, /* VARIATION SELECTOR-17 - VARIATION SELECTOR-256 */ +}; + +/* Double-width character ranges (BMP - Basic Multilingual Plane, U+0000 to U+FFFF) */ +static const struct ucs_interval16 ucs_double_width_bmp_ranges[] = { + { 0x1100, 0x115F }, /* HANGUL CHOSEONG KIYEOK - HANGUL CHOSEONG FILLER */ + { 0x231A, 0x231B }, /* WATCH - HOURGLASS */ + { 0x2329, 0x232A }, /* LEFT-POINTING ANGLE BRACKET - RIGHT-POINTING ANGLE BRACKET */ + { 0x23E9, 0x23EC }, /* BLACK RIGHT-POINTING DOUBLE TRIANGLE - BLACK DOWN-POINTING DOUBLE TRIANGLE */ + { 0x23F0, 0x23F0 }, /* ALARM CLOCK */ + { 0x23F3, 0x23F3 }, /* HOURGLASS WITH FLOWING SAND */ + { 0x25FD, 0x25FE }, /* WHITE MEDIUM SMALL SQUARE - BLACK MEDIUM SMALL SQUARE */ + { 0x2614, 0x2615 }, /* UMBRELLA WITH RAIN DROPS - HOT BEVERAGE */ + { 0x2630, 0x2637 }, /* TRIGRAM FOR HEAVEN - TRIGRAM FOR EARTH */ + { 0x2648, 0x2653 }, /* ARIES - PISCES */ + { 0x267F, 0x267F }, /* WHEELCHAIR SYMBOL */ + { 0x268A, 0x268F }, /* MONOGRAM FOR YANG - DIGRAM FOR GREATER YIN */ + { 0x2693, 0x2693 }, /* ANCHOR */ + { 0x26A1, 0x26A1 }, /* HIGH VOLTAGE SIGN */ + { 0x26AA, 0x26AB }, /* MEDIUM WHITE CIRCLE - MEDIUM BLACK CIRCLE */ + { 0x26BD, 0x26BE }, /* SOCCER BALL - BASEBALL */ + { 0x26C4, 0x26C5 }, /* SNOWMAN WITHOUT SNOW - SUN BEHIND CLOUD */ + { 0x26CE, 0x26CE }, /* OPHIUCHUS */ + { 0x26D4, 0x26D4 }, /* NO ENTRY */ + { 0x26EA, 0x26EA }, /* CHURCH */ + { 0x26F2, 0x26F3 }, /* FOUNTAIN - FLAG IN HOLE */ + { 0x26F5, 0x26F5 }, /* SAILBOAT */ + { 0x26FA, 0x26FA }, /* TENT */ + { 0x26FD, 0x26FD }, /* FUEL PUMP */ + { 0x2705, 0x2705 }, /* WHITE HEAVY CHECK MARK */ + { 0x270A, 0x270B }, /* RAISED FIST - RAISED HAND */ + { 0x2728, 0x2728 }, /* SPARKLES */ + { 0x274C, 0x274C }, /* CROSS MARK */ + { 0x274E, 0x274E }, /* NEGATIVE SQUARED CROSS MARK */ + { 0x2753, 0x2755 }, /* BLACK QUESTION MARK ORNAMENT - WHITE EXCLAMATION MARK ORNAMENT */ + { 0x2757, 0x2757 }, /* HEAVY EXCLAMATION MARK SYMBOL */ + { 0x2795, 0x2797 }, /* HEAVY PLUS SIGN - HEAVY DIVISION SIGN */ + { 0x27B0, 0x27B0 }, /* CURLY LOOP */ + { 0x27BF, 0x27BF }, /* DOUBLE CURLY LOOP */ + { 0x2B1B, 0x2B1C }, /* BLACK LARGE SQUARE - WHITE LARGE SQUARE */ + { 0x2B50, 0x2B50 }, /* WHITE MEDIUM STAR */ + { 0x2B55, 0x2B55 }, /* HEAVY LARGE CIRCLE */ + { 0x2E80, 0x2E99 }, /* CJK RADICAL REPEAT - CJK RADICAL RAP */ + { 0x2E9B, 0x2EF3 }, /* CJK RADICAL CHOKE - CJK RADICAL C-SIMPLIFIED TURTLE */ + { 0x2F00, 0x2FD5 }, /* KANGXI RADICAL ONE - KANGXI RADICAL FLUTE */ + { 0x2FF0, 0x3029 }, /* IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT - HANGZHOU NUMERAL NINE */ + { 0x3030, 0x303E }, /* WAVY DASH - IDEOGRAPHIC VARIATION INDICATOR */ + { 0x3041, 0x3096 }, /* HIRAGANA LETTER SMALL A - HIRAGANA LETTER SMALL KE */ + { 0x309B, 0x30FF }, /* KATAKANA-HIRAGANA VOICED SOUND MARK - KATAKANA DIGRAPH KOTO */ + { 0x3105, 0x312F }, /* BOPOMOFO LETTER B - BOPOMOFO LETTER NN */ + { 0x3131, 0x318E }, /* HANGUL LETTER KIYEOK - HANGUL LETTER ARAEAE */ + { 0x3190, 0x31E5 }, /* IDEOGRAPHIC ANNOTATION LINKING MARK - CJK STROKE SZP */ + { 0x31EF, 0x321E }, /* IDEOGRAPHIC DESCRIPTION CHARACTER SUBTRACTION - PARENTHESIZED KOREAN CHARACTER O HU */ + { 0x3220, 0x3247 }, /* PARENTHESIZED IDEOGRAPH ONE - CIRCLED IDEOGRAPH KOTO */ + { 0x3250, 0xA48C }, /* PARTNERSHIP SIGN - YI SYLLABLE YYR */ + { 0xA490, 0xA4C6 }, /* YI RADICAL QOT - YI RADICAL KE */ + { 0xA960, 0xA97C }, /* HANGUL CHOSEONG TIKEUT-MIEUM - HANGUL CHOSEONG SSANGYEORINHIEUH */ + { 0xAC00, 0xD7A3 }, /* HANGUL SYLLABLE GA - HANGUL SYLLABLE HIH */ + { 0xF900, 0xFAFF }, /* U+F900 - U+FAFF */ + { 0xFE10, 0xFE19 }, /* PRESENTATION FORM FOR VERTICAL COMMA - PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS */ + { 0xFE30, 0xFE52 }, /* PRESENTATION FORM FOR VERTICAL TWO DOT LEADER - SMALL FULL STOP */ + { 0xFE54, 0xFE66 }, /* SMALL SEMICOLON - SMALL EQUALS SIGN */ + { 0xFE68, 0xFE6B }, /* SMALL REVERSE SOLIDUS - SMALL COMMERCIAL AT */ + { 0xFF01, 0xFF60 }, /* FULLWIDTH EXCLAMATION MARK - FULLWIDTH RIGHT WHITE PARENTHESIS */ + { 0xFFE0, 0xFFE6 }, /* FULLWIDTH CENT SIGN - FULLWIDTH WON SIGN */ +}; + +/* Double-width character ranges (non-BMP, U+10000 and above) */ +static const struct ucs_interval32 ucs_double_width_non_bmp_ranges[] = { + { 0x16FE0, 0x16FE3 }, /* TANGUT ITERATION MARK - OLD CHINESE ITERATION MARK */ + { 0x17000, 0x187F7 }, /* U+17000 - U+187F7 */ + { 0x18800, 0x18CD5 }, /* TANGUT COMPONENT-001 - KHITAN SMALL SCRIPT CHARACTER-18CD5 */ + { 0x18CFF, 0x18D08 }, /* U+18CFF - U+18D08 */ + { 0x1AFF0, 0x1AFF3 }, /* KATAKANA LETTER MINNAN TONE-2 - KATAKANA LETTER MINNAN TONE-5 */ + { 0x1AFF5, 0x1AFFB }, /* KATAKANA LETTER MINNAN TONE-7 - KATAKANA LETTER MINNAN NASALIZED TONE-5 */ + { 0x1AFFD, 0x1AFFE }, /* KATAKANA LETTER MINNAN NASALIZED TONE-7 - KATAKANA LETTER MINNAN NASALIZED TONE-8 */ + { 0x1B000, 0x1B122 }, /* KATAKANA LETTER ARCHAIC E - KATAKANA LETTER ARCHAIC WU */ + { 0x1B132, 0x1B132 }, /* HIRAGANA LETTER SMALL KO */ + { 0x1B150, 0x1B152 }, /* HIRAGANA LETTER SMALL WI - HIRAGANA LETTER SMALL WO */ + { 0x1B155, 0x1B155 }, /* KATAKANA LETTER SMALL KO */ + { 0x1B164, 0x1B167 }, /* KATAKANA LETTER SMALL WI - KATAKANA LETTER SMALL N */ + { 0x1B170, 0x1B2FB }, /* NUSHU CHARACTER-1B170 - NUSHU CHARACTER-1B2FB */ + { 0x1D300, 0x1D356 }, /* MONOGRAM FOR EARTH - TETRAGRAM FOR FOSTERING */ + { 0x1D360, 0x1D376 }, /* COUNTING ROD UNIT DIGIT ONE - IDEOGRAPHIC TALLY MARK FIVE */ + { 0x1F000, 0x1F02F }, /* U+1F000 - U+1F02F */ + { 0x1F0A0, 0x1F0FF }, /* U+1F0A0 - U+1F0FF */ + { 0x1F18E, 0x1F18E }, /* NEGATIVE SQUARED AB */ + { 0x1F191, 0x1F19A }, /* SQUARED CL - SQUARED VS */ + { 0x1F200, 0x1F202 }, /* SQUARE HIRAGANA HOKA - SQUARED KATAKANA SA */ + { 0x1F210, 0x1F23B }, /* SQUARED CJK UNIFIED IDEOGRAPH-624B - SQUARED CJK UNIFIED IDEOGRAPH-914D */ + { 0x1F240, 0x1F248 }, /* TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-672C - TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6557 */ + { 0x1F250, 0x1F251 }, /* CIRCLED IDEOGRAPH ADVANTAGE - CIRCLED IDEOGRAPH ACCEPT */ + { 0x1F260, 0x1F265 }, /* ROUNDED SYMBOL FOR FU - ROUNDED SYMBOL FOR CAI */ + { 0x1F300, 0x1F3FA }, /* CYCLONE - AMPHORA */ + { 0x1F400, 0x1F64F }, /* RAT - PERSON WITH FOLDED HANDS */ + { 0x1F680, 0x1F9AF }, /* ROCKET - PROBING CANE */ + { 0x1F9B4, 0x1FAFF }, /* U+1F9B4 - U+1FAFF */ + { 0x20000, 0x2FFFD }, /* U+20000 - U+2FFFD */ + { 0x30000, 0x3FFFD }, /* U+30000 - U+3FFFD */ +}; diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index 67e2cb7c96ee..79b33d998d43 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -48,10 +48,10 @@ #include <linux/uaccess.h> #include <asm/byteorder.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #define HEADER_SIZE 4u -#define CON_BUF_SIZE (CONFIG_BASE_SMALL ? 256 : PAGE_SIZE) +#define CON_BUF_SIZE (IS_ENABLED(CONFIG_BASE_SMALL) ? 256 : PAGE_SIZE) /* * Our minor space: diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 9b5b98dfc8b4..62049ceb34de 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -104,7 +104,6 @@ #include <linux/uaccess.h> #include <linux/kdb.h> #include <linux/ctype.h> -#include <linux/bsearch.h> #include <linux/gcd.h> #define MAX_NR_CON_DRIVER 16 @@ -444,6 +443,15 @@ static void vc_uniscr_scroll(struct vc_data *vc, unsigned int top, } } +static u32 vc_uniscr_getc(struct vc_data *vc, int relative_pos) +{ + int pos = vc->state.x + vc->vc_need_wrap + relative_pos; + + if (vc->vc_uni_lines && in_range(pos, 0, vc->vc_cols)) + return vc->vc_uni_lines[vc->state.y][pos]; + return 0; +} + static void vc_uniscr_copy_area(u32 **dst_lines, unsigned int dst_cols, unsigned int dst_rows, @@ -1862,6 +1870,14 @@ int mouse_reporting(void) return vc_cons[fg_console].d->vc_report_mouse; } +/* invoked via ioctl(TIOCLINUX) */ +static int get_bracketed_paste(struct tty_struct *tty) +{ + struct vc_data *vc = tty->driver_data; + + return vc->vc_bracketed_paste; +} + enum { CSI_DEC_hl_CURSOR_KEYS = 1, /* CKM: cursor keys send ^[Ox/^[[x */ CSI_DEC_hl_132_COLUMNS = 3, /* COLM: 80/132 mode switch */ @@ -1872,6 +1888,7 @@ enum { CSI_DEC_hl_MOUSE_X10 = 9, CSI_DEC_hl_SHOW_CURSOR = 25, /* TCEM */ CSI_DEC_hl_MOUSE_VT200 = 1000, + CSI_DEC_hl_BRACKETED_PASTE = 2004, }; /* console_lock is held */ @@ -1924,6 +1941,9 @@ static void csi_DEC_hl(struct vc_data *vc, bool on_off) case CSI_DEC_hl_MOUSE_VT200: vc->vc_report_mouse = on_off ? 2 : 0; break; + case CSI_DEC_hl_BRACKETED_PASTE: + vc->vc_bracketed_paste = on_off; + break; } } @@ -2149,6 +2169,7 @@ static void reset_terminal(struct vc_data *vc, int do_clear) vc->state.charset = 0; vc->vc_need_wrap = 0; vc->vc_report_mouse = 0; + vc->vc_bracketed_paste = 0; vc->vc_utf = default_utf8; vc->vc_utf_count = 0; @@ -2712,43 +2733,6 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c) } } -/* is_double_width() is based on the wcwidth() implementation by - * Markus Kuhn -- 2007-05-26 (Unicode 5.0) - * Latest version: https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c - */ -struct interval { - uint32_t first; - uint32_t last; -}; - -static int ucs_cmp(const void *key, const void *elt) -{ - uint32_t ucs = *(uint32_t *)key; - struct interval e = *(struct interval *) elt; - - if (ucs > e.last) - return 1; - else if (ucs < e.first) - return -1; - return 0; -} - -static int is_double_width(uint32_t ucs) -{ - static const struct interval double_width[] = { - { 0x1100, 0x115F }, { 0x2329, 0x232A }, { 0x2E80, 0x303E }, - { 0x3040, 0xA4CF }, { 0xAC00, 0xD7A3 }, { 0xF900, 0xFAFF }, - { 0xFE10, 0xFE19 }, { 0xFE30, 0xFE6F }, { 0xFF00, 0xFF60 }, - { 0xFFE0, 0xFFE6 }, { 0x20000, 0x2FFFD }, { 0x30000, 0x3FFFD } - }; - if (ucs < double_width[0].first || - ucs > double_width[ARRAY_SIZE(double_width) - 1].last) - return 0; - - return bsearch(&ucs, double_width, ARRAY_SIZE(double_width), - sizeof(struct interval), ucs_cmp) != NULL; -} - struct vc_draw_region { unsigned long from, to; int x; @@ -2817,7 +2801,7 @@ static int vc_translate_unicode(struct vc_data *vc, int c, bool *rescan) if ((c & 0xc0) == 0x80) { /* Unexpected continuation byte? */ if (!vc->vc_utf_count) - return 0xfffd; + goto bad_sequence; vc->vc_utf_char = (vc->vc_utf_char << 6) | (c & 0x3f); vc->vc_npar++; @@ -2829,17 +2813,17 @@ static int vc_translate_unicode(struct vc_data *vc, int c, bool *rescan) /* Reject overlong sequences */ if (c <= utf8_length_changes[vc->vc_npar - 1] || c > utf8_length_changes[vc->vc_npar]) - return 0xfffd; + goto bad_sequence; return vc_sanitize_unicode(c); } /* Single ASCII byte or first byte of a sequence received */ if (vc->vc_utf_count) { - /* Continuation byte expected */ + /* A continuation byte was expected */ *rescan = true; vc->vc_utf_count = 0; - return 0xfffd; + goto bad_sequence; } /* Nothing to do if an ASCII byte was received */ @@ -2858,11 +2842,14 @@ static int vc_translate_unicode(struct vc_data *vc, int c, bool *rescan) vc->vc_utf_count = 3; vc->vc_utf_char = (c & 0x07); } else { - return 0xfffd; + goto bad_sequence; } need_more_bytes: return -1; + +bad_sequence: + return 0xfffd; } static int vc_translate(struct vc_data *vc, int *c, bool *rescan) @@ -2940,54 +2927,143 @@ static bool vc_is_control(struct vc_data *vc, int tc, int c) return false; } +static void vc_con_rewind(struct vc_data *vc) +{ + if (vc->state.x && !vc->vc_need_wrap) { + vc->vc_pos -= 2; + vc->state.x--; + } + vc->vc_need_wrap = 0; +} + +#define UCS_ZWS 0x200b /* Zero Width Space */ +#define UCS_VS16 0xfe0f /* Variation Selector 16 */ +#define UCS_REPLACEMENT 0xfffd /* Replacement Character */ + +static int vc_process_ucs(struct vc_data *vc, int *c, int *tc) +{ + u32 prev_c, curr_c = *c; + + if (ucs_is_double_width(curr_c)) { + /* + * The Unicode screen memory is allocated only when + * required. This is one such case as we need to remember + * which displayed characters are double-width. + */ + vc_uniscr_check(vc); + return 2; + } + + if (!ucs_is_zero_width(curr_c)) + return 1; + + /* From here curr_c is known to be zero-width. */ + + if (ucs_is_double_width(vc_uniscr_getc(vc, -2))) { + /* + * Let's merge this zero-width code point with the preceding + * double-width code point by replacing the existing + * zero-width space padding. To do so we rewind one column + * and pretend this has a width of 1. + * We give the legacy display the same initial space padding. + */ + vc_con_rewind(vc); + *tc = ' '; + return 1; + } + + /* From here the preceding character, if any, must be single-width. */ + prev_c = vc_uniscr_getc(vc, -1); + + if (curr_c == UCS_VS16 && prev_c != 0) { + /* + * VS16 (U+FE0F) is special. It typically turns the preceding + * single-width character into a double-width one. Let it + * have a width of 1 effectively making the combination with + * the preceding character double-width. + */ + *tc = ' '; + return 1; + } + + /* try recomposition */ + prev_c = ucs_recompose(prev_c, curr_c); + if (prev_c != 0) { + vc_con_rewind(vc); + *tc = *c = prev_c; + return 1; + } + + /* Otherwise zero-width code points are ignored. */ + return 0; +} + +static int vc_get_glyph(struct vc_data *vc, int tc) +{ + int glyph = conv_uni_to_pc(vc, tc); + u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; + + if (!(glyph & ~charmask)) + return glyph; + + if (glyph == -1) + return -1; /* nothing to display */ + + /* Glyph not found */ + if ((!vc->vc_utf || vc->vc_disp_ctrl || tc < 128) && !(tc & ~charmask)) { + /* + * In legacy mode use the glyph we get by a 1:1 mapping. + * This would make absolutely no sense with Unicode in mind, but do this for + * ASCII characters since a font may lack Unicode mapping info and we don't + * want to end up with having question marks only. + */ + return tc; + } + + /* + * The Unicode screen memory is allocated only when required. + * This is one such case: we're about to "cheat" with the displayed + * character meaning the simple screen buffer won't hold the original + * information, whereas the Unicode screen buffer always does. + */ + vc_uniscr_check(vc); + + /* Try getting a simpler fallback character. */ + tc = ucs_get_fallback(tc); + if (tc) + return vc_get_glyph(vc, tc); + + /* Display U+FFFD (Unicode Replacement Character). */ + return conv_uni_to_pc(vc, UCS_REPLACEMENT); +} + static int vc_con_write_normal(struct vc_data *vc, int tc, int c, struct vc_draw_region *draw) { int next_c; unsigned char vc_attr = vc->vc_attr; - u16 himask = vc->vc_hi_font_mask, charmask = himask ? 0x1ff : 0xff; + u16 himask = vc->vc_hi_font_mask; u8 width = 1; bool inverse = false; if (vc->vc_utf && !vc->vc_disp_ctrl) { - if (is_double_width(c)) - width = 2; + width = vc_process_ucs(vc, &c, &tc); + if (!width) + goto out; } /* Now try to find out how to display it */ - tc = conv_uni_to_pc(vc, tc); - if (tc & ~charmask) { - if (tc == -1 || tc == -2) - return -1; /* nothing to display */ - - /* Glyph not found */ - if ((!vc->vc_utf || vc->vc_disp_ctrl || c < 128) && - !(c & ~charmask)) { - /* - * In legacy mode use the glyph we get by a 1:1 - * mapping. - * This would make absolutely no sense with Unicode in - * mind, but do this for ASCII characters since a font - * may lack Unicode mapping info and we don't want to - * end up with having question marks only. - */ - tc = c; - } else { - /* - * Display U+FFFD. If it's not found, display an inverse - * question mark. - */ - tc = conv_uni_to_pc(vc, 0xfffd); - if (tc < 0) { - inverse = true; - tc = conv_uni_to_pc(vc, '?'); - if (tc < 0) - tc = '?'; - - vc_attr = vc_invert_attr(vc); - con_flush(vc, draw); - } - } + tc = vc_get_glyph(vc, tc); + if (tc == -1) + return -1; /* nothing to display */ + if (tc < 0) { + inverse = true; + tc = conv_uni_to_pc(vc, '?'); + if (tc < 0) + tc = '?'; + + vc_attr = vc_invert_attr(vc); + con_flush(vc, draw); } next_c = c; @@ -3028,8 +3104,14 @@ static int vc_con_write_normal(struct vc_data *vc, int tc, int c, tc = conv_uni_to_pc(vc, ' '); if (tc < 0) tc = ' '; - next_c = ' '; + /* + * Store a zero-width space in the Unicode screen given that + * the previous code point is semantically double width. + */ + next_c = UCS_ZWS; } + +out: notify_write(vc, c); if (inverse) @@ -3345,8 +3427,6 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) switch (type) { case TIOCL_SETSEL: - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; return set_selection_user(param, tty); case TIOCL_PASTESEL: if (!capable(CAP_SYS_ADMIN)) @@ -3416,6 +3496,8 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) break; case TIOCL_BLANKEDSCREEN: return console_blanked; + case TIOCL_GETBRACKETEDPASTE: + return get_bracketed_paste(tty); default: return -EINVAL; } @@ -3576,6 +3658,15 @@ static void con_cleanup(struct tty_struct *tty) tty_port_put(&vc->port); } +/* + * We can't deal with anything but the N_TTY ldisc, + * because we can sleep in our write() routine. + */ +static int con_ldisc_ok(struct tty_struct *tty, int ldisc) +{ + return ldisc == N_TTY ? 0 : -EINVAL; +} + static int default_color = 7; /* white */ static int default_italic_color = 2; // green (ASCII) static int default_underline_color = 3; // cyan (ASCII) @@ -3695,6 +3786,7 @@ static const struct tty_operations con_ops = { .resize = vt_resize, .shutdown = con_shutdown, .cleanup = con_cleanup, + .ldisc_ok = con_ldisc_ok, }; static struct cdev vc0_cdev; @@ -4493,7 +4585,7 @@ void do_blank_screen(int entering_gfx) } hide_cursor(vc); - del_timer_sync(&console_timer); + timer_delete_sync(&console_timer); blank_timer_expired = 0; save_screen(vc); @@ -4558,6 +4650,7 @@ void do_unblank_screen(int leaving_gfx) set_palette(vc); set_cursor(vc); vt_event_post(VT_EVENT_UNBLANK, vc->vc_num, vc->vc_num); + notify_update(vc); } EXPORT_SYMBOL(do_unblank_screen); @@ -4598,7 +4691,7 @@ void poke_blanked_console(void) /* This isn't perfectly race free, but a race here would be mostly harmless, * at worst, we'll do a spurious blank and it's unlikely */ - del_timer(&console_timer); + timer_delete(&console_timer); blank_timer_expired = 0; if (ignore_poke || !vc_cons[fg_console].d || vc_cons[fg_console].d->vc_mode == KD_GRAPHICS) @@ -4716,7 +4809,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op) return -EINVAL; if (op->data) { - font.data = kvmalloc(max_font_size, GFP_KERNEL); + font.data = kvzalloc(max_font_size, GFP_KERNEL); if (!font.data) return -ENOMEM; } else diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 4b91072f3a4e..61342e06970a 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -951,6 +951,22 @@ int vt_ioctl(struct tty_struct *tty, (unsigned short __user *)arg); case VT_WAITEVENT: return vt_event_wait_ioctl((struct vt_event __user *)arg); + + case VT_GETCONSIZECSRPOS: + { + struct vt_consizecsrpos concsr; + + console_lock(); + concsr.con_cols = vc->vc_cols; + concsr.con_rows = vc->vc_rows; + concsr.csr_col = vc->state.x; + concsr.csr_row = vc->state.y; + console_unlock(); + if (copy_to_user(up, &concsr, sizeof(concsr))) + return -EFAULT; + return 0; + } + default: return -ENOIOCTLCMD; } @@ -1103,8 +1119,6 @@ long vt_compat_ioctl(struct tty_struct *tty, case VT_WAITACTIVE: case VT_RELDISP: case VT_DISALLOCATE: - case VT_RESIZE: - case VT_RESIZEX: return vt_ioctl(tty, cmd, arg); /* |