18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* sunzilog.c: Zilog serial driver for Sparc systems. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Driver for Zilog serial chips found on Sun workstations and 58c2ecf20Sopenharmony_ci * servers. This driver could actually be made more generic. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This is based on the old drivers/sbus/char/zs.c code. A lot 88c2ecf20Sopenharmony_ci * of code has been simply moved over directly from there but 98c2ecf20Sopenharmony_ci * much has been rewritten. Credits therefore go out to Eddie 108c2ecf20Sopenharmony_ci * C. Dost, Pete Zaitcev, Ted Ts'o and Alex Buell for their 118c2ecf20Sopenharmony_ci * work there. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Copyright (C) 2002, 2006, 2007 David S. Miller (davem@davemloft.net) 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/errno.h> 198c2ecf20Sopenharmony_ci#include <linux/delay.h> 208c2ecf20Sopenharmony_ci#include <linux/tty.h> 218c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 228c2ecf20Sopenharmony_ci#include <linux/major.h> 238c2ecf20Sopenharmony_ci#include <linux/string.h> 248c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 258c2ecf20Sopenharmony_ci#include <linux/ioport.h> 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci#include <linux/circ_buf.h> 288c2ecf20Sopenharmony_ci#include <linux/serial.h> 298c2ecf20Sopenharmony_ci#include <linux/sysrq.h> 308c2ecf20Sopenharmony_ci#include <linux/console.h> 318c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 328c2ecf20Sopenharmony_ci#ifdef CONFIG_SERIO 338c2ecf20Sopenharmony_ci#include <linux/serio.h> 348c2ecf20Sopenharmony_ci#endif 358c2ecf20Sopenharmony_ci#include <linux/init.h> 368c2ecf20Sopenharmony_ci#include <linux/of_device.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include <asm/io.h> 398c2ecf20Sopenharmony_ci#include <asm/irq.h> 408c2ecf20Sopenharmony_ci#include <asm/prom.h> 418c2ecf20Sopenharmony_ci#include <asm/setup.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#include <linux/serial_core.h> 448c2ecf20Sopenharmony_ci#include <linux/sunserialcore.h> 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#include "sunzilog.h" 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* On 32-bit sparcs we need to delay after register accesses 498c2ecf20Sopenharmony_ci * to accommodate sun4 systems, but we do not need to flush writes. 508c2ecf20Sopenharmony_ci * On 64-bit sparc we only need to flush single writes to ensure 518c2ecf20Sopenharmony_ci * completion. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci#ifndef CONFIG_SPARC64 548c2ecf20Sopenharmony_ci#define ZSDELAY() udelay(5) 558c2ecf20Sopenharmony_ci#define ZSDELAY_LONG() udelay(20) 568c2ecf20Sopenharmony_ci#define ZS_WSYNC(channel) do { } while (0) 578c2ecf20Sopenharmony_ci#else 588c2ecf20Sopenharmony_ci#define ZSDELAY() 598c2ecf20Sopenharmony_ci#define ZSDELAY_LONG() 608c2ecf20Sopenharmony_ci#define ZS_WSYNC(__channel) \ 618c2ecf20Sopenharmony_ci readb(&((__channel)->control)) 628c2ecf20Sopenharmony_ci#endif 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define ZS_CLOCK 4915200 /* Zilog input clock rate. */ 658c2ecf20Sopenharmony_ci#define ZS_CLOCK_DIVISOR 16 /* Divisor this driver uses. */ 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* 688c2ecf20Sopenharmony_ci * We wrap our port structure around the generic uart_port. 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_cistruct uart_sunzilog_port { 718c2ecf20Sopenharmony_ci struct uart_port port; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* IRQ servicing chain. */ 748c2ecf20Sopenharmony_ci struct uart_sunzilog_port *next; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* Current values of Zilog write registers. */ 778c2ecf20Sopenharmony_ci unsigned char curregs[NUM_ZSREGS]; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci unsigned int flags; 808c2ecf20Sopenharmony_ci#define SUNZILOG_FLAG_CONS_KEYB 0x00000001 818c2ecf20Sopenharmony_ci#define SUNZILOG_FLAG_CONS_MOUSE 0x00000002 828c2ecf20Sopenharmony_ci#define SUNZILOG_FLAG_IS_CONS 0x00000004 838c2ecf20Sopenharmony_ci#define SUNZILOG_FLAG_IS_KGDB 0x00000008 848c2ecf20Sopenharmony_ci#define SUNZILOG_FLAG_MODEM_STATUS 0x00000010 858c2ecf20Sopenharmony_ci#define SUNZILOG_FLAG_IS_CHANNEL_A 0x00000020 868c2ecf20Sopenharmony_ci#define SUNZILOG_FLAG_REGS_HELD 0x00000040 878c2ecf20Sopenharmony_ci#define SUNZILOG_FLAG_TX_STOPPED 0x00000080 888c2ecf20Sopenharmony_ci#define SUNZILOG_FLAG_TX_ACTIVE 0x00000100 898c2ecf20Sopenharmony_ci#define SUNZILOG_FLAG_ESCC 0x00000200 908c2ecf20Sopenharmony_ci#define SUNZILOG_FLAG_ISR_HANDLER 0x00000400 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci unsigned int cflag; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci unsigned char parity_mask; 958c2ecf20Sopenharmony_ci unsigned char prev_status; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#ifdef CONFIG_SERIO 988c2ecf20Sopenharmony_ci struct serio serio; 998c2ecf20Sopenharmony_ci int serio_open; 1008c2ecf20Sopenharmony_ci#endif 1018c2ecf20Sopenharmony_ci}; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic void sunzilog_putchar(struct uart_port *port, int ch); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci#define ZILOG_CHANNEL_FROM_PORT(PORT) ((struct zilog_channel __iomem *)((PORT)->membase)) 1068c2ecf20Sopenharmony_ci#define UART_ZILOG(PORT) ((struct uart_sunzilog_port *)(PORT)) 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#define ZS_IS_KEYB(UP) ((UP)->flags & SUNZILOG_FLAG_CONS_KEYB) 1098c2ecf20Sopenharmony_ci#define ZS_IS_MOUSE(UP) ((UP)->flags & SUNZILOG_FLAG_CONS_MOUSE) 1108c2ecf20Sopenharmony_ci#define ZS_IS_CONS(UP) ((UP)->flags & SUNZILOG_FLAG_IS_CONS) 1118c2ecf20Sopenharmony_ci#define ZS_IS_KGDB(UP) ((UP)->flags & SUNZILOG_FLAG_IS_KGDB) 1128c2ecf20Sopenharmony_ci#define ZS_WANTS_MODEM_STATUS(UP) ((UP)->flags & SUNZILOG_FLAG_MODEM_STATUS) 1138c2ecf20Sopenharmony_ci#define ZS_IS_CHANNEL_A(UP) ((UP)->flags & SUNZILOG_FLAG_IS_CHANNEL_A) 1148c2ecf20Sopenharmony_ci#define ZS_REGS_HELD(UP) ((UP)->flags & SUNZILOG_FLAG_REGS_HELD) 1158c2ecf20Sopenharmony_ci#define ZS_TX_STOPPED(UP) ((UP)->flags & SUNZILOG_FLAG_TX_STOPPED) 1168c2ecf20Sopenharmony_ci#define ZS_TX_ACTIVE(UP) ((UP)->flags & SUNZILOG_FLAG_TX_ACTIVE) 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* Reading and writing Zilog8530 registers. The delays are to make this 1198c2ecf20Sopenharmony_ci * driver work on the Sun4 which needs a settling delay after each chip 1208c2ecf20Sopenharmony_ci * register access, other machines handle this in hardware via auxiliary 1218c2ecf20Sopenharmony_ci * flip-flops which implement the settle time we do in software. 1228c2ecf20Sopenharmony_ci * 1238c2ecf20Sopenharmony_ci * The port lock must be held and local IRQs must be disabled 1248c2ecf20Sopenharmony_ci * when {read,write}_zsreg is invoked. 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_cistatic unsigned char read_zsreg(struct zilog_channel __iomem *channel, 1278c2ecf20Sopenharmony_ci unsigned char reg) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci unsigned char retval; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci writeb(reg, &channel->control); 1328c2ecf20Sopenharmony_ci ZSDELAY(); 1338c2ecf20Sopenharmony_ci retval = readb(&channel->control); 1348c2ecf20Sopenharmony_ci ZSDELAY(); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return retval; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic void write_zsreg(struct zilog_channel __iomem *channel, 1408c2ecf20Sopenharmony_ci unsigned char reg, unsigned char value) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci writeb(reg, &channel->control); 1438c2ecf20Sopenharmony_ci ZSDELAY(); 1448c2ecf20Sopenharmony_ci writeb(value, &channel->control); 1458c2ecf20Sopenharmony_ci ZSDELAY(); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void sunzilog_clear_fifo(struct zilog_channel __iomem *channel) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci int i; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci for (i = 0; i < 32; i++) { 1538c2ecf20Sopenharmony_ci unsigned char regval; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci regval = readb(&channel->control); 1568c2ecf20Sopenharmony_ci ZSDELAY(); 1578c2ecf20Sopenharmony_ci if (regval & Rx_CH_AV) 1588c2ecf20Sopenharmony_ci break; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci regval = read_zsreg(channel, R1); 1618c2ecf20Sopenharmony_ci readb(&channel->data); 1628c2ecf20Sopenharmony_ci ZSDELAY(); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (regval & (PAR_ERR | Rx_OVR | CRC_ERR)) { 1658c2ecf20Sopenharmony_ci writeb(ERR_RES, &channel->control); 1668c2ecf20Sopenharmony_ci ZSDELAY(); 1678c2ecf20Sopenharmony_ci ZS_WSYNC(channel); 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci/* This function must only be called when the TX is not busy. The UART 1738c2ecf20Sopenharmony_ci * port lock must be held and local interrupts disabled. 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_cistatic int __load_zsregs(struct zilog_channel __iomem *channel, unsigned char *regs) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci int i; 1788c2ecf20Sopenharmony_ci int escc; 1798c2ecf20Sopenharmony_ci unsigned char r15; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* Let pending transmits finish. */ 1828c2ecf20Sopenharmony_ci for (i = 0; i < 1000; i++) { 1838c2ecf20Sopenharmony_ci unsigned char stat = read_zsreg(channel, R1); 1848c2ecf20Sopenharmony_ci if (stat & ALL_SNT) 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci udelay(100); 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci writeb(ERR_RES, &channel->control); 1908c2ecf20Sopenharmony_ci ZSDELAY(); 1918c2ecf20Sopenharmony_ci ZS_WSYNC(channel); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci sunzilog_clear_fifo(channel); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* Disable all interrupts. */ 1968c2ecf20Sopenharmony_ci write_zsreg(channel, R1, 1978c2ecf20Sopenharmony_ci regs[R1] & ~(RxINT_MASK | TxINT_ENAB | EXT_INT_ENAB)); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* Set parity, sync config, stop bits, and clock divisor. */ 2008c2ecf20Sopenharmony_ci write_zsreg(channel, R4, regs[R4]); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* Set misc. TX/RX control bits. */ 2038c2ecf20Sopenharmony_ci write_zsreg(channel, R10, regs[R10]); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* Set TX/RX controls sans the enable bits. */ 2068c2ecf20Sopenharmony_ci write_zsreg(channel, R3, regs[R3] & ~RxENAB); 2078c2ecf20Sopenharmony_ci write_zsreg(channel, R5, regs[R5] & ~TxENAB); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* Synchronous mode config. */ 2108c2ecf20Sopenharmony_ci write_zsreg(channel, R6, regs[R6]); 2118c2ecf20Sopenharmony_ci write_zsreg(channel, R7, regs[R7]); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* Don't mess with the interrupt vector (R2, unused by us) and 2148c2ecf20Sopenharmony_ci * master interrupt control (R9). We make sure this is setup 2158c2ecf20Sopenharmony_ci * properly at probe time then never touch it again. 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* Disable baud generator. */ 2198c2ecf20Sopenharmony_ci write_zsreg(channel, R14, regs[R14] & ~BRENAB); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* Clock mode control. */ 2228c2ecf20Sopenharmony_ci write_zsreg(channel, R11, regs[R11]); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* Lower and upper byte of baud rate generator divisor. */ 2258c2ecf20Sopenharmony_ci write_zsreg(channel, R12, regs[R12]); 2268c2ecf20Sopenharmony_ci write_zsreg(channel, R13, regs[R13]); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* Now rewrite R14, with BRENAB (if set). */ 2298c2ecf20Sopenharmony_ci write_zsreg(channel, R14, regs[R14]); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* External status interrupt control. */ 2328c2ecf20Sopenharmony_ci write_zsreg(channel, R15, (regs[R15] | WR7pEN) & ~FIFOEN); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* ESCC Extension Register */ 2358c2ecf20Sopenharmony_ci r15 = read_zsreg(channel, R15); 2368c2ecf20Sopenharmony_ci if (r15 & 0x01) { 2378c2ecf20Sopenharmony_ci write_zsreg(channel, R7, regs[R7p]); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* External status interrupt and FIFO control. */ 2408c2ecf20Sopenharmony_ci write_zsreg(channel, R15, regs[R15] & ~WR7pEN); 2418c2ecf20Sopenharmony_ci escc = 1; 2428c2ecf20Sopenharmony_ci } else { 2438c2ecf20Sopenharmony_ci /* Clear FIFO bit case it is an issue */ 2448c2ecf20Sopenharmony_ci regs[R15] &= ~FIFOEN; 2458c2ecf20Sopenharmony_ci escc = 0; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* Reset external status interrupts. */ 2498c2ecf20Sopenharmony_ci write_zsreg(channel, R0, RES_EXT_INT); /* First Latch */ 2508c2ecf20Sopenharmony_ci write_zsreg(channel, R0, RES_EXT_INT); /* Second Latch */ 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* Rewrite R3/R5, this time without enables masked. */ 2538c2ecf20Sopenharmony_ci write_zsreg(channel, R3, regs[R3]); 2548c2ecf20Sopenharmony_ci write_zsreg(channel, R5, regs[R5]); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* Rewrite R1, this time without IRQ enabled masked. */ 2578c2ecf20Sopenharmony_ci write_zsreg(channel, R1, regs[R1]); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return escc; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/* Reprogram the Zilog channel HW registers with the copies found in the 2638c2ecf20Sopenharmony_ci * software state struct. If the transmitter is busy, we defer this update 2648c2ecf20Sopenharmony_ci * until the next TX complete interrupt. Else, we do it right now. 2658c2ecf20Sopenharmony_ci * 2668c2ecf20Sopenharmony_ci * The UART port lock must be held and local interrupts disabled. 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_cistatic void sunzilog_maybe_update_regs(struct uart_sunzilog_port *up, 2698c2ecf20Sopenharmony_ci struct zilog_channel __iomem *channel) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci if (!ZS_REGS_HELD(up)) { 2728c2ecf20Sopenharmony_ci if (ZS_TX_ACTIVE(up)) { 2738c2ecf20Sopenharmony_ci up->flags |= SUNZILOG_FLAG_REGS_HELD; 2748c2ecf20Sopenharmony_ci } else { 2758c2ecf20Sopenharmony_ci __load_zsregs(channel, up->curregs); 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic void sunzilog_change_mouse_baud(struct uart_sunzilog_port *up) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci unsigned int cur_cflag = up->cflag; 2838c2ecf20Sopenharmony_ci int brg, new_baud; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci up->cflag &= ~CBAUD; 2868c2ecf20Sopenharmony_ci up->cflag |= suncore_mouse_baud_cflag_next(cur_cflag, &new_baud); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci brg = BPS_TO_BRG(new_baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); 2898c2ecf20Sopenharmony_ci up->curregs[R12] = (brg & 0xff); 2908c2ecf20Sopenharmony_ci up->curregs[R13] = (brg >> 8) & 0xff; 2918c2ecf20Sopenharmony_ci sunzilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(&up->port)); 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic void sunzilog_kbdms_receive_chars(struct uart_sunzilog_port *up, 2958c2ecf20Sopenharmony_ci unsigned char ch, int is_break) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci if (ZS_IS_KEYB(up)) { 2988c2ecf20Sopenharmony_ci /* Stop-A is handled by drivers/char/keyboard.c now. */ 2998c2ecf20Sopenharmony_ci#ifdef CONFIG_SERIO 3008c2ecf20Sopenharmony_ci if (up->serio_open) 3018c2ecf20Sopenharmony_ci serio_interrupt(&up->serio, ch, 0); 3028c2ecf20Sopenharmony_ci#endif 3038c2ecf20Sopenharmony_ci } else if (ZS_IS_MOUSE(up)) { 3048c2ecf20Sopenharmony_ci int ret = suncore_mouse_baud_detection(ch, is_break); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci switch (ret) { 3078c2ecf20Sopenharmony_ci case 2: 3088c2ecf20Sopenharmony_ci sunzilog_change_mouse_baud(up); 3098c2ecf20Sopenharmony_ci fallthrough; 3108c2ecf20Sopenharmony_ci case 1: 3118c2ecf20Sopenharmony_ci break; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci case 0: 3148c2ecf20Sopenharmony_ci#ifdef CONFIG_SERIO 3158c2ecf20Sopenharmony_ci if (up->serio_open) 3168c2ecf20Sopenharmony_ci serio_interrupt(&up->serio, ch, 0); 3178c2ecf20Sopenharmony_ci#endif 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic struct tty_port * 3248c2ecf20Sopenharmony_cisunzilog_receive_chars(struct uart_sunzilog_port *up, 3258c2ecf20Sopenharmony_ci struct zilog_channel __iomem *channel) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct tty_port *port = NULL; 3288c2ecf20Sopenharmony_ci unsigned char ch, r1, flag; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (up->port.state != NULL) /* Unopened serial console */ 3318c2ecf20Sopenharmony_ci port = &up->port.state->port; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci for (;;) { 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci r1 = read_zsreg(channel, R1); 3368c2ecf20Sopenharmony_ci if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) { 3378c2ecf20Sopenharmony_ci writeb(ERR_RES, &channel->control); 3388c2ecf20Sopenharmony_ci ZSDELAY(); 3398c2ecf20Sopenharmony_ci ZS_WSYNC(channel); 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci ch = readb(&channel->control); 3438c2ecf20Sopenharmony_ci ZSDELAY(); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* This funny hack depends upon BRK_ABRT not interfering 3468c2ecf20Sopenharmony_ci * with the other bits we care about in R1. 3478c2ecf20Sopenharmony_ci */ 3488c2ecf20Sopenharmony_ci if (ch & BRK_ABRT) 3498c2ecf20Sopenharmony_ci r1 |= BRK_ABRT; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (!(ch & Rx_CH_AV)) 3528c2ecf20Sopenharmony_ci break; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci ch = readb(&channel->data); 3558c2ecf20Sopenharmony_ci ZSDELAY(); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci ch &= up->parity_mask; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (unlikely(ZS_IS_KEYB(up)) || unlikely(ZS_IS_MOUSE(up))) { 3608c2ecf20Sopenharmony_ci sunzilog_kbdms_receive_chars(up, ch, 0); 3618c2ecf20Sopenharmony_ci continue; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* A real serial line, record the character and status. */ 3658c2ecf20Sopenharmony_ci flag = TTY_NORMAL; 3668c2ecf20Sopenharmony_ci up->port.icount.rx++; 3678c2ecf20Sopenharmony_ci if (r1 & (BRK_ABRT | PAR_ERR | Rx_OVR | CRC_ERR)) { 3688c2ecf20Sopenharmony_ci if (r1 & BRK_ABRT) { 3698c2ecf20Sopenharmony_ci r1 &= ~(PAR_ERR | CRC_ERR); 3708c2ecf20Sopenharmony_ci up->port.icount.brk++; 3718c2ecf20Sopenharmony_ci if (uart_handle_break(&up->port)) 3728c2ecf20Sopenharmony_ci continue; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci else if (r1 & PAR_ERR) 3758c2ecf20Sopenharmony_ci up->port.icount.parity++; 3768c2ecf20Sopenharmony_ci else if (r1 & CRC_ERR) 3778c2ecf20Sopenharmony_ci up->port.icount.frame++; 3788c2ecf20Sopenharmony_ci if (r1 & Rx_OVR) 3798c2ecf20Sopenharmony_ci up->port.icount.overrun++; 3808c2ecf20Sopenharmony_ci r1 &= up->port.read_status_mask; 3818c2ecf20Sopenharmony_ci if (r1 & BRK_ABRT) 3828c2ecf20Sopenharmony_ci flag = TTY_BREAK; 3838c2ecf20Sopenharmony_ci else if (r1 & PAR_ERR) 3848c2ecf20Sopenharmony_ci flag = TTY_PARITY; 3858c2ecf20Sopenharmony_ci else if (r1 & CRC_ERR) 3868c2ecf20Sopenharmony_ci flag = TTY_FRAME; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci if (uart_handle_sysrq_char(&up->port, ch) || !port) 3898c2ecf20Sopenharmony_ci continue; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (up->port.ignore_status_mask == 0xff || 3928c2ecf20Sopenharmony_ci (r1 & up->port.ignore_status_mask) == 0) { 3938c2ecf20Sopenharmony_ci tty_insert_flip_char(port, ch, flag); 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci if (r1 & Rx_OVR) 3968c2ecf20Sopenharmony_ci tty_insert_flip_char(port, 0, TTY_OVERRUN); 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return port; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic void sunzilog_status_handle(struct uart_sunzilog_port *up, 4038c2ecf20Sopenharmony_ci struct zilog_channel __iomem *channel) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci unsigned char status; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci status = readb(&channel->control); 4088c2ecf20Sopenharmony_ci ZSDELAY(); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci writeb(RES_EXT_INT, &channel->control); 4118c2ecf20Sopenharmony_ci ZSDELAY(); 4128c2ecf20Sopenharmony_ci ZS_WSYNC(channel); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (status & BRK_ABRT) { 4158c2ecf20Sopenharmony_ci if (ZS_IS_MOUSE(up)) 4168c2ecf20Sopenharmony_ci sunzilog_kbdms_receive_chars(up, 0, 1); 4178c2ecf20Sopenharmony_ci if (ZS_IS_CONS(up)) { 4188c2ecf20Sopenharmony_ci /* Wait for BREAK to deassert to avoid potentially 4198c2ecf20Sopenharmony_ci * confusing the PROM. 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_ci while (1) { 4228c2ecf20Sopenharmony_ci status = readb(&channel->control); 4238c2ecf20Sopenharmony_ci ZSDELAY(); 4248c2ecf20Sopenharmony_ci if (!(status & BRK_ABRT)) 4258c2ecf20Sopenharmony_ci break; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci sun_do_break(); 4288c2ecf20Sopenharmony_ci return; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (ZS_WANTS_MODEM_STATUS(up)) { 4338c2ecf20Sopenharmony_ci if (status & SYNC) 4348c2ecf20Sopenharmony_ci up->port.icount.dsr++; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* The Zilog just gives us an interrupt when DCD/CTS/etc. change. 4378c2ecf20Sopenharmony_ci * But it does not tell us which bit has changed, we have to keep 4388c2ecf20Sopenharmony_ci * track of this ourselves. 4398c2ecf20Sopenharmony_ci */ 4408c2ecf20Sopenharmony_ci if ((status ^ up->prev_status) ^ DCD) 4418c2ecf20Sopenharmony_ci uart_handle_dcd_change(&up->port, 4428c2ecf20Sopenharmony_ci (status & DCD)); 4438c2ecf20Sopenharmony_ci if ((status ^ up->prev_status) ^ CTS) 4448c2ecf20Sopenharmony_ci uart_handle_cts_change(&up->port, 4458c2ecf20Sopenharmony_ci (status & CTS)); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci wake_up_interruptible(&up->port.state->port.delta_msr_wait); 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci up->prev_status = status; 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic void sunzilog_transmit_chars(struct uart_sunzilog_port *up, 4548c2ecf20Sopenharmony_ci struct zilog_channel __iomem *channel) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct circ_buf *xmit; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (ZS_IS_CONS(up)) { 4598c2ecf20Sopenharmony_ci unsigned char status = readb(&channel->control); 4608c2ecf20Sopenharmony_ci ZSDELAY(); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* TX still busy? Just wait for the next TX done interrupt. 4638c2ecf20Sopenharmony_ci * 4648c2ecf20Sopenharmony_ci * It can occur because of how we do serial console writes. It would 4658c2ecf20Sopenharmony_ci * be nice to transmit console writes just like we normally would for 4668c2ecf20Sopenharmony_ci * a TTY line. (ie. buffered and TX interrupt driven). That is not 4678c2ecf20Sopenharmony_ci * easy because console writes cannot sleep. One solution might be 4688c2ecf20Sopenharmony_ci * to poll on enough port->xmit space becoming free. -DaveM 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_ci if (!(status & Tx_BUF_EMP)) 4718c2ecf20Sopenharmony_ci return; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci up->flags &= ~SUNZILOG_FLAG_TX_ACTIVE; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (ZS_REGS_HELD(up)) { 4778c2ecf20Sopenharmony_ci __load_zsregs(channel, up->curregs); 4788c2ecf20Sopenharmony_ci up->flags &= ~SUNZILOG_FLAG_REGS_HELD; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (ZS_TX_STOPPED(up)) { 4828c2ecf20Sopenharmony_ci up->flags &= ~SUNZILOG_FLAG_TX_STOPPED; 4838c2ecf20Sopenharmony_ci goto ack_tx_int; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (up->port.x_char) { 4878c2ecf20Sopenharmony_ci up->flags |= SUNZILOG_FLAG_TX_ACTIVE; 4888c2ecf20Sopenharmony_ci writeb(up->port.x_char, &channel->data); 4898c2ecf20Sopenharmony_ci ZSDELAY(); 4908c2ecf20Sopenharmony_ci ZS_WSYNC(channel); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci up->port.icount.tx++; 4938c2ecf20Sopenharmony_ci up->port.x_char = 0; 4948c2ecf20Sopenharmony_ci return; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (up->port.state == NULL) 4988c2ecf20Sopenharmony_ci goto ack_tx_int; 4998c2ecf20Sopenharmony_ci xmit = &up->port.state->xmit; 5008c2ecf20Sopenharmony_ci if (uart_circ_empty(xmit)) 5018c2ecf20Sopenharmony_ci goto ack_tx_int; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (uart_tx_stopped(&up->port)) 5048c2ecf20Sopenharmony_ci goto ack_tx_int; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci up->flags |= SUNZILOG_FLAG_TX_ACTIVE; 5078c2ecf20Sopenharmony_ci writeb(xmit->buf[xmit->tail], &channel->data); 5088c2ecf20Sopenharmony_ci ZSDELAY(); 5098c2ecf20Sopenharmony_ci ZS_WSYNC(channel); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); 5128c2ecf20Sopenharmony_ci up->port.icount.tx++; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) 5158c2ecf20Sopenharmony_ci uart_write_wakeup(&up->port); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ciack_tx_int: 5208c2ecf20Sopenharmony_ci writeb(RES_Tx_P, &channel->control); 5218c2ecf20Sopenharmony_ci ZSDELAY(); 5228c2ecf20Sopenharmony_ci ZS_WSYNC(channel); 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic irqreturn_t sunzilog_interrupt(int irq, void *dev_id) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = dev_id; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci while (up) { 5308c2ecf20Sopenharmony_ci struct zilog_channel __iomem *channel 5318c2ecf20Sopenharmony_ci = ZILOG_CHANNEL_FROM_PORT(&up->port); 5328c2ecf20Sopenharmony_ci struct tty_port *port; 5338c2ecf20Sopenharmony_ci unsigned char r3; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci spin_lock(&up->port.lock); 5368c2ecf20Sopenharmony_ci r3 = read_zsreg(channel, R3); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* Channel A */ 5398c2ecf20Sopenharmony_ci port = NULL; 5408c2ecf20Sopenharmony_ci if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { 5418c2ecf20Sopenharmony_ci writeb(RES_H_IUS, &channel->control); 5428c2ecf20Sopenharmony_ci ZSDELAY(); 5438c2ecf20Sopenharmony_ci ZS_WSYNC(channel); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (r3 & CHARxIP) 5468c2ecf20Sopenharmony_ci port = sunzilog_receive_chars(up, channel); 5478c2ecf20Sopenharmony_ci if (r3 & CHAEXT) 5488c2ecf20Sopenharmony_ci sunzilog_status_handle(up, channel); 5498c2ecf20Sopenharmony_ci if (r3 & CHATxIP) 5508c2ecf20Sopenharmony_ci sunzilog_transmit_chars(up, channel); 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci spin_unlock(&up->port.lock); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (port) 5558c2ecf20Sopenharmony_ci tty_flip_buffer_push(port); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* Channel B */ 5588c2ecf20Sopenharmony_ci up = up->next; 5598c2ecf20Sopenharmony_ci channel = ZILOG_CHANNEL_FROM_PORT(&up->port); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci spin_lock(&up->port.lock); 5628c2ecf20Sopenharmony_ci port = NULL; 5638c2ecf20Sopenharmony_ci if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { 5648c2ecf20Sopenharmony_ci writeb(RES_H_IUS, &channel->control); 5658c2ecf20Sopenharmony_ci ZSDELAY(); 5668c2ecf20Sopenharmony_ci ZS_WSYNC(channel); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (r3 & CHBRxIP) 5698c2ecf20Sopenharmony_ci port = sunzilog_receive_chars(up, channel); 5708c2ecf20Sopenharmony_ci if (r3 & CHBEXT) 5718c2ecf20Sopenharmony_ci sunzilog_status_handle(up, channel); 5728c2ecf20Sopenharmony_ci if (r3 & CHBTxIP) 5738c2ecf20Sopenharmony_ci sunzilog_transmit_chars(up, channel); 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci spin_unlock(&up->port.lock); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (port) 5788c2ecf20Sopenharmony_ci tty_flip_buffer_push(port); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci up = up->next; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci/* A convenient way to quickly get R0 status. The caller must _not_ hold the 5878c2ecf20Sopenharmony_ci * port lock, it is acquired here. 5888c2ecf20Sopenharmony_ci */ 5898c2ecf20Sopenharmony_cistatic __inline__ unsigned char sunzilog_read_channel_status(struct uart_port *port) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci struct zilog_channel __iomem *channel; 5928c2ecf20Sopenharmony_ci unsigned char status; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci channel = ZILOG_CHANNEL_FROM_PORT(port); 5958c2ecf20Sopenharmony_ci status = readb(&channel->control); 5968c2ecf20Sopenharmony_ci ZSDELAY(); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci return status; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci/* The port lock is not held. */ 6028c2ecf20Sopenharmony_cistatic unsigned int sunzilog_tx_empty(struct uart_port *port) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci unsigned long flags; 6058c2ecf20Sopenharmony_ci unsigned char status; 6068c2ecf20Sopenharmony_ci unsigned int ret; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci status = sunzilog_read_channel_status(port); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (status & Tx_BUF_EMP) 6158c2ecf20Sopenharmony_ci ret = TIOCSER_TEMT; 6168c2ecf20Sopenharmony_ci else 6178c2ecf20Sopenharmony_ci ret = 0; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci return ret; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci/* The port lock is held and interrupts are disabled. */ 6238c2ecf20Sopenharmony_cistatic unsigned int sunzilog_get_mctrl(struct uart_port *port) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci unsigned char status; 6268c2ecf20Sopenharmony_ci unsigned int ret; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci status = sunzilog_read_channel_status(port); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci ret = 0; 6318c2ecf20Sopenharmony_ci if (status & DCD) 6328c2ecf20Sopenharmony_ci ret |= TIOCM_CAR; 6338c2ecf20Sopenharmony_ci if (status & SYNC) 6348c2ecf20Sopenharmony_ci ret |= TIOCM_DSR; 6358c2ecf20Sopenharmony_ci if (status & CTS) 6368c2ecf20Sopenharmony_ci ret |= TIOCM_CTS; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci return ret; 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci/* The port lock is held and interrupts are disabled. */ 6428c2ecf20Sopenharmony_cistatic void sunzilog_set_mctrl(struct uart_port *port, unsigned int mctrl) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = 6458c2ecf20Sopenharmony_ci container_of(port, struct uart_sunzilog_port, port); 6468c2ecf20Sopenharmony_ci struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); 6478c2ecf20Sopenharmony_ci unsigned char set_bits, clear_bits; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci set_bits = clear_bits = 0; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (mctrl & TIOCM_RTS) 6528c2ecf20Sopenharmony_ci set_bits |= RTS; 6538c2ecf20Sopenharmony_ci else 6548c2ecf20Sopenharmony_ci clear_bits |= RTS; 6558c2ecf20Sopenharmony_ci if (mctrl & TIOCM_DTR) 6568c2ecf20Sopenharmony_ci set_bits |= DTR; 6578c2ecf20Sopenharmony_ci else 6588c2ecf20Sopenharmony_ci clear_bits |= DTR; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* NOTE: Not subject to 'transmitter active' rule. */ 6618c2ecf20Sopenharmony_ci up->curregs[R5] |= set_bits; 6628c2ecf20Sopenharmony_ci up->curregs[R5] &= ~clear_bits; 6638c2ecf20Sopenharmony_ci write_zsreg(channel, R5, up->curregs[R5]); 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci/* The port lock is held and interrupts are disabled. */ 6678c2ecf20Sopenharmony_cistatic void sunzilog_stop_tx(struct uart_port *port) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = 6708c2ecf20Sopenharmony_ci container_of(port, struct uart_sunzilog_port, port); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci up->flags |= SUNZILOG_FLAG_TX_STOPPED; 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci/* The port lock is held and interrupts are disabled. */ 6768c2ecf20Sopenharmony_cistatic void sunzilog_start_tx(struct uart_port *port) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = 6798c2ecf20Sopenharmony_ci container_of(port, struct uart_sunzilog_port, port); 6808c2ecf20Sopenharmony_ci struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); 6818c2ecf20Sopenharmony_ci unsigned char status; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci up->flags |= SUNZILOG_FLAG_TX_ACTIVE; 6848c2ecf20Sopenharmony_ci up->flags &= ~SUNZILOG_FLAG_TX_STOPPED; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci status = readb(&channel->control); 6878c2ecf20Sopenharmony_ci ZSDELAY(); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci /* TX busy? Just wait for the TX done interrupt. */ 6908c2ecf20Sopenharmony_ci if (!(status & Tx_BUF_EMP)) 6918c2ecf20Sopenharmony_ci return; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci /* Send the first character to jump-start the TX done 6948c2ecf20Sopenharmony_ci * IRQ sending engine. 6958c2ecf20Sopenharmony_ci */ 6968c2ecf20Sopenharmony_ci if (port->x_char) { 6978c2ecf20Sopenharmony_ci writeb(port->x_char, &channel->data); 6988c2ecf20Sopenharmony_ci ZSDELAY(); 6998c2ecf20Sopenharmony_ci ZS_WSYNC(channel); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci port->icount.tx++; 7028c2ecf20Sopenharmony_ci port->x_char = 0; 7038c2ecf20Sopenharmony_ci } else { 7048c2ecf20Sopenharmony_ci struct circ_buf *xmit = &port->state->xmit; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci if (uart_circ_empty(xmit)) 7078c2ecf20Sopenharmony_ci return; 7088c2ecf20Sopenharmony_ci writeb(xmit->buf[xmit->tail], &channel->data); 7098c2ecf20Sopenharmony_ci ZSDELAY(); 7108c2ecf20Sopenharmony_ci ZS_WSYNC(channel); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); 7138c2ecf20Sopenharmony_ci port->icount.tx++; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) 7168c2ecf20Sopenharmony_ci uart_write_wakeup(&up->port); 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci/* The port lock is held. */ 7218c2ecf20Sopenharmony_cistatic void sunzilog_stop_rx(struct uart_port *port) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = UART_ZILOG(port); 7248c2ecf20Sopenharmony_ci struct zilog_channel __iomem *channel; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci if (ZS_IS_CONS(up)) 7278c2ecf20Sopenharmony_ci return; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci channel = ZILOG_CHANNEL_FROM_PORT(port); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci /* Disable all RX interrupts. */ 7328c2ecf20Sopenharmony_ci up->curregs[R1] &= ~RxINT_MASK; 7338c2ecf20Sopenharmony_ci sunzilog_maybe_update_regs(up, channel); 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci/* The port lock is held. */ 7378c2ecf20Sopenharmony_cistatic void sunzilog_enable_ms(struct uart_port *port) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = 7408c2ecf20Sopenharmony_ci container_of(port, struct uart_sunzilog_port, port); 7418c2ecf20Sopenharmony_ci struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); 7428c2ecf20Sopenharmony_ci unsigned char new_reg; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci new_reg = up->curregs[R15] | (DCDIE | SYNCIE | CTSIE); 7458c2ecf20Sopenharmony_ci if (new_reg != up->curregs[R15]) { 7468c2ecf20Sopenharmony_ci up->curregs[R15] = new_reg; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci /* NOTE: Not subject to 'transmitter active' rule. */ 7498c2ecf20Sopenharmony_ci write_zsreg(channel, R15, up->curregs[R15] & ~WR7pEN); 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci} 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci/* The port lock is not held. */ 7548c2ecf20Sopenharmony_cistatic void sunzilog_break_ctl(struct uart_port *port, int break_state) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = 7578c2ecf20Sopenharmony_ci container_of(port, struct uart_sunzilog_port, port); 7588c2ecf20Sopenharmony_ci struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); 7598c2ecf20Sopenharmony_ci unsigned char set_bits, clear_bits, new_reg; 7608c2ecf20Sopenharmony_ci unsigned long flags; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci set_bits = clear_bits = 0; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci if (break_state) 7658c2ecf20Sopenharmony_ci set_bits |= SND_BRK; 7668c2ecf20Sopenharmony_ci else 7678c2ecf20Sopenharmony_ci clear_bits |= SND_BRK; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci new_reg = (up->curregs[R5] | set_bits) & ~clear_bits; 7728c2ecf20Sopenharmony_ci if (new_reg != up->curregs[R5]) { 7738c2ecf20Sopenharmony_ci up->curregs[R5] = new_reg; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci /* NOTE: Not subject to 'transmitter active' rule. */ 7768c2ecf20Sopenharmony_ci write_zsreg(channel, R5, up->curregs[R5]); 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 7808c2ecf20Sopenharmony_ci} 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_cistatic void __sunzilog_startup(struct uart_sunzilog_port *up) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci struct zilog_channel __iomem *channel; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci channel = ZILOG_CHANNEL_FROM_PORT(&up->port); 7878c2ecf20Sopenharmony_ci up->prev_status = readb(&channel->control); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci /* Enable receiver and transmitter. */ 7908c2ecf20Sopenharmony_ci up->curregs[R3] |= RxENAB; 7918c2ecf20Sopenharmony_ci up->curregs[R5] |= TxENAB; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci up->curregs[R1] |= EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB; 7948c2ecf20Sopenharmony_ci sunzilog_maybe_update_regs(up, channel); 7958c2ecf20Sopenharmony_ci} 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_cistatic int sunzilog_startup(struct uart_port *port) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = UART_ZILOG(port); 8008c2ecf20Sopenharmony_ci unsigned long flags; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci if (ZS_IS_CONS(up)) 8038c2ecf20Sopenharmony_ci return 0; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 8068c2ecf20Sopenharmony_ci __sunzilog_startup(up); 8078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 8088c2ecf20Sopenharmony_ci return 0; 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci/* 8128c2ecf20Sopenharmony_ci * The test for ZS_IS_CONS is explained by the following e-mail: 8138c2ecf20Sopenharmony_ci ***** 8148c2ecf20Sopenharmony_ci * From: Russell King <rmk@arm.linux.org.uk> 8158c2ecf20Sopenharmony_ci * Date: Sun, 8 Dec 2002 10:18:38 +0000 8168c2ecf20Sopenharmony_ci * 8178c2ecf20Sopenharmony_ci * On Sun, Dec 08, 2002 at 02:43:36AM -0500, Pete Zaitcev wrote: 8188c2ecf20Sopenharmony_ci * > I boot my 2.5 boxes using "console=ttyS0,9600" argument, 8198c2ecf20Sopenharmony_ci * > and I noticed that something is not right with reference 8208c2ecf20Sopenharmony_ci * > counting in this case. It seems that when the console 8218c2ecf20Sopenharmony_ci * > is open by kernel initially, this is not accounted 8228c2ecf20Sopenharmony_ci * > as an open, and uart_startup is not called. 8238c2ecf20Sopenharmony_ci * 8248c2ecf20Sopenharmony_ci * That is correct. We are unable to call uart_startup when the serial 8258c2ecf20Sopenharmony_ci * console is initialised because it may need to allocate memory (as 8268c2ecf20Sopenharmony_ci * request_irq does) and the memory allocators may not have been 8278c2ecf20Sopenharmony_ci * initialised. 8288c2ecf20Sopenharmony_ci * 8298c2ecf20Sopenharmony_ci * 1. initialise the port into a state where it can send characters in the 8308c2ecf20Sopenharmony_ci * console write method. 8318c2ecf20Sopenharmony_ci * 8328c2ecf20Sopenharmony_ci * 2. don't do the actual hardware shutdown in your shutdown() method (but 8338c2ecf20Sopenharmony_ci * do the normal software shutdown - ie, free irqs etc) 8348c2ecf20Sopenharmony_ci ***** 8358c2ecf20Sopenharmony_ci */ 8368c2ecf20Sopenharmony_cistatic void sunzilog_shutdown(struct uart_port *port) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = UART_ZILOG(port); 8398c2ecf20Sopenharmony_ci struct zilog_channel __iomem *channel; 8408c2ecf20Sopenharmony_ci unsigned long flags; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci if (ZS_IS_CONS(up)) 8438c2ecf20Sopenharmony_ci return; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci channel = ZILOG_CHANNEL_FROM_PORT(port); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci /* Disable receiver and transmitter. */ 8508c2ecf20Sopenharmony_ci up->curregs[R3] &= ~RxENAB; 8518c2ecf20Sopenharmony_ci up->curregs[R5] &= ~TxENAB; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci /* Disable all interrupts and BRK assertion. */ 8548c2ecf20Sopenharmony_ci up->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); 8558c2ecf20Sopenharmony_ci up->curregs[R5] &= ~SND_BRK; 8568c2ecf20Sopenharmony_ci sunzilog_maybe_update_regs(up, channel); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 8598c2ecf20Sopenharmony_ci} 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci/* Shared by TTY driver and serial console setup. The port lock is held 8628c2ecf20Sopenharmony_ci * and local interrupts are disabled. 8638c2ecf20Sopenharmony_ci */ 8648c2ecf20Sopenharmony_cistatic void 8658c2ecf20Sopenharmony_cisunzilog_convert_to_zs(struct uart_sunzilog_port *up, unsigned int cflag, 8668c2ecf20Sopenharmony_ci unsigned int iflag, int brg) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci up->curregs[R10] = NRZ; 8708c2ecf20Sopenharmony_ci up->curregs[R11] = TCBR | RCBR; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci /* Program BAUD and clock source. */ 8738c2ecf20Sopenharmony_ci up->curregs[R4] &= ~XCLK_MASK; 8748c2ecf20Sopenharmony_ci up->curregs[R4] |= X16CLK; 8758c2ecf20Sopenharmony_ci up->curregs[R12] = brg & 0xff; 8768c2ecf20Sopenharmony_ci up->curregs[R13] = (brg >> 8) & 0xff; 8778c2ecf20Sopenharmony_ci up->curregs[R14] = BRSRC | BRENAB; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci /* Character size, stop bits, and parity. */ 8808c2ecf20Sopenharmony_ci up->curregs[R3] &= ~RxN_MASK; 8818c2ecf20Sopenharmony_ci up->curregs[R5] &= ~TxN_MASK; 8828c2ecf20Sopenharmony_ci switch (cflag & CSIZE) { 8838c2ecf20Sopenharmony_ci case CS5: 8848c2ecf20Sopenharmony_ci up->curregs[R3] |= Rx5; 8858c2ecf20Sopenharmony_ci up->curregs[R5] |= Tx5; 8868c2ecf20Sopenharmony_ci up->parity_mask = 0x1f; 8878c2ecf20Sopenharmony_ci break; 8888c2ecf20Sopenharmony_ci case CS6: 8898c2ecf20Sopenharmony_ci up->curregs[R3] |= Rx6; 8908c2ecf20Sopenharmony_ci up->curregs[R5] |= Tx6; 8918c2ecf20Sopenharmony_ci up->parity_mask = 0x3f; 8928c2ecf20Sopenharmony_ci break; 8938c2ecf20Sopenharmony_ci case CS7: 8948c2ecf20Sopenharmony_ci up->curregs[R3] |= Rx7; 8958c2ecf20Sopenharmony_ci up->curregs[R5] |= Tx7; 8968c2ecf20Sopenharmony_ci up->parity_mask = 0x7f; 8978c2ecf20Sopenharmony_ci break; 8988c2ecf20Sopenharmony_ci case CS8: 8998c2ecf20Sopenharmony_ci default: 9008c2ecf20Sopenharmony_ci up->curregs[R3] |= Rx8; 9018c2ecf20Sopenharmony_ci up->curregs[R5] |= Tx8; 9028c2ecf20Sopenharmony_ci up->parity_mask = 0xff; 9038c2ecf20Sopenharmony_ci break; 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci up->curregs[R4] &= ~0x0c; 9068c2ecf20Sopenharmony_ci if (cflag & CSTOPB) 9078c2ecf20Sopenharmony_ci up->curregs[R4] |= SB2; 9088c2ecf20Sopenharmony_ci else 9098c2ecf20Sopenharmony_ci up->curregs[R4] |= SB1; 9108c2ecf20Sopenharmony_ci if (cflag & PARENB) 9118c2ecf20Sopenharmony_ci up->curregs[R4] |= PAR_ENAB; 9128c2ecf20Sopenharmony_ci else 9138c2ecf20Sopenharmony_ci up->curregs[R4] &= ~PAR_ENAB; 9148c2ecf20Sopenharmony_ci if (!(cflag & PARODD)) 9158c2ecf20Sopenharmony_ci up->curregs[R4] |= PAR_EVEN; 9168c2ecf20Sopenharmony_ci else 9178c2ecf20Sopenharmony_ci up->curregs[R4] &= ~PAR_EVEN; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci up->port.read_status_mask = Rx_OVR; 9208c2ecf20Sopenharmony_ci if (iflag & INPCK) 9218c2ecf20Sopenharmony_ci up->port.read_status_mask |= CRC_ERR | PAR_ERR; 9228c2ecf20Sopenharmony_ci if (iflag & (IGNBRK | BRKINT | PARMRK)) 9238c2ecf20Sopenharmony_ci up->port.read_status_mask |= BRK_ABRT; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci up->port.ignore_status_mask = 0; 9268c2ecf20Sopenharmony_ci if (iflag & IGNPAR) 9278c2ecf20Sopenharmony_ci up->port.ignore_status_mask |= CRC_ERR | PAR_ERR; 9288c2ecf20Sopenharmony_ci if (iflag & IGNBRK) { 9298c2ecf20Sopenharmony_ci up->port.ignore_status_mask |= BRK_ABRT; 9308c2ecf20Sopenharmony_ci if (iflag & IGNPAR) 9318c2ecf20Sopenharmony_ci up->port.ignore_status_mask |= Rx_OVR; 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci if ((cflag & CREAD) == 0) 9358c2ecf20Sopenharmony_ci up->port.ignore_status_mask = 0xff; 9368c2ecf20Sopenharmony_ci} 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci/* The port lock is not held. */ 9398c2ecf20Sopenharmony_cistatic void 9408c2ecf20Sopenharmony_cisunzilog_set_termios(struct uart_port *port, struct ktermios *termios, 9418c2ecf20Sopenharmony_ci struct ktermios *old) 9428c2ecf20Sopenharmony_ci{ 9438c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = 9448c2ecf20Sopenharmony_ci container_of(port, struct uart_sunzilog_port, port); 9458c2ecf20Sopenharmony_ci unsigned long flags; 9468c2ecf20Sopenharmony_ci int baud, brg; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci baud = uart_get_baud_rate(port, termios, old, 1200, 76800); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci spin_lock_irqsave(&up->port.lock, flags); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci sunzilog_convert_to_zs(up, termios->c_cflag, termios->c_iflag, brg); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci if (UART_ENABLE_MS(&up->port, termios->c_cflag)) 9578c2ecf20Sopenharmony_ci up->flags |= SUNZILOG_FLAG_MODEM_STATUS; 9588c2ecf20Sopenharmony_ci else 9598c2ecf20Sopenharmony_ci up->flags &= ~SUNZILOG_FLAG_MODEM_STATUS; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci up->cflag = termios->c_cflag; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci sunzilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(port)); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci uart_update_timeout(port, termios->c_cflag, baud); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&up->port.lock, flags); 9688c2ecf20Sopenharmony_ci} 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_cistatic const char *sunzilog_type(struct uart_port *port) 9718c2ecf20Sopenharmony_ci{ 9728c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = UART_ZILOG(port); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci return (up->flags & SUNZILOG_FLAG_ESCC) ? "zs (ESCC)" : "zs"; 9758c2ecf20Sopenharmony_ci} 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci/* We do not request/release mappings of the registers here, this 9788c2ecf20Sopenharmony_ci * happens at early serial probe time. 9798c2ecf20Sopenharmony_ci */ 9808c2ecf20Sopenharmony_cistatic void sunzilog_release_port(struct uart_port *port) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci} 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_cistatic int sunzilog_request_port(struct uart_port *port) 9858c2ecf20Sopenharmony_ci{ 9868c2ecf20Sopenharmony_ci return 0; 9878c2ecf20Sopenharmony_ci} 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci/* These do not need to do anything interesting either. */ 9908c2ecf20Sopenharmony_cistatic void sunzilog_config_port(struct uart_port *port, int flags) 9918c2ecf20Sopenharmony_ci{ 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci/* We do not support letting the user mess with the divisor, IRQ, etc. */ 9958c2ecf20Sopenharmony_cistatic int sunzilog_verify_port(struct uart_port *port, struct serial_struct *ser) 9968c2ecf20Sopenharmony_ci{ 9978c2ecf20Sopenharmony_ci return -EINVAL; 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci#ifdef CONFIG_CONSOLE_POLL 10018c2ecf20Sopenharmony_cistatic int sunzilog_get_poll_char(struct uart_port *port) 10028c2ecf20Sopenharmony_ci{ 10038c2ecf20Sopenharmony_ci unsigned char ch, r1; 10048c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = 10058c2ecf20Sopenharmony_ci container_of(port, struct uart_sunzilog_port, port); 10068c2ecf20Sopenharmony_ci struct zilog_channel __iomem *channel 10078c2ecf20Sopenharmony_ci = ZILOG_CHANNEL_FROM_PORT(&up->port); 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci r1 = read_zsreg(channel, R1); 10118c2ecf20Sopenharmony_ci if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) { 10128c2ecf20Sopenharmony_ci writeb(ERR_RES, &channel->control); 10138c2ecf20Sopenharmony_ci ZSDELAY(); 10148c2ecf20Sopenharmony_ci ZS_WSYNC(channel); 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci ch = readb(&channel->control); 10188c2ecf20Sopenharmony_ci ZSDELAY(); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci /* This funny hack depends upon BRK_ABRT not interfering 10218c2ecf20Sopenharmony_ci * with the other bits we care about in R1. 10228c2ecf20Sopenharmony_ci */ 10238c2ecf20Sopenharmony_ci if (ch & BRK_ABRT) 10248c2ecf20Sopenharmony_ci r1 |= BRK_ABRT; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci if (!(ch & Rx_CH_AV)) 10278c2ecf20Sopenharmony_ci return NO_POLL_CHAR; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci ch = readb(&channel->data); 10308c2ecf20Sopenharmony_ci ZSDELAY(); 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci ch &= up->parity_mask; 10338c2ecf20Sopenharmony_ci return ch; 10348c2ecf20Sopenharmony_ci} 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_cistatic void sunzilog_put_poll_char(struct uart_port *port, 10378c2ecf20Sopenharmony_ci unsigned char ch) 10388c2ecf20Sopenharmony_ci{ 10398c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = 10408c2ecf20Sopenharmony_ci container_of(port, struct uart_sunzilog_port, port); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci sunzilog_putchar(&up->port, ch); 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ci#endif /* CONFIG_CONSOLE_POLL */ 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_cistatic const struct uart_ops sunzilog_pops = { 10478c2ecf20Sopenharmony_ci .tx_empty = sunzilog_tx_empty, 10488c2ecf20Sopenharmony_ci .set_mctrl = sunzilog_set_mctrl, 10498c2ecf20Sopenharmony_ci .get_mctrl = sunzilog_get_mctrl, 10508c2ecf20Sopenharmony_ci .stop_tx = sunzilog_stop_tx, 10518c2ecf20Sopenharmony_ci .start_tx = sunzilog_start_tx, 10528c2ecf20Sopenharmony_ci .stop_rx = sunzilog_stop_rx, 10538c2ecf20Sopenharmony_ci .enable_ms = sunzilog_enable_ms, 10548c2ecf20Sopenharmony_ci .break_ctl = sunzilog_break_ctl, 10558c2ecf20Sopenharmony_ci .startup = sunzilog_startup, 10568c2ecf20Sopenharmony_ci .shutdown = sunzilog_shutdown, 10578c2ecf20Sopenharmony_ci .set_termios = sunzilog_set_termios, 10588c2ecf20Sopenharmony_ci .type = sunzilog_type, 10598c2ecf20Sopenharmony_ci .release_port = sunzilog_release_port, 10608c2ecf20Sopenharmony_ci .request_port = sunzilog_request_port, 10618c2ecf20Sopenharmony_ci .config_port = sunzilog_config_port, 10628c2ecf20Sopenharmony_ci .verify_port = sunzilog_verify_port, 10638c2ecf20Sopenharmony_ci#ifdef CONFIG_CONSOLE_POLL 10648c2ecf20Sopenharmony_ci .poll_get_char = sunzilog_get_poll_char, 10658c2ecf20Sopenharmony_ci .poll_put_char = sunzilog_put_poll_char, 10668c2ecf20Sopenharmony_ci#endif 10678c2ecf20Sopenharmony_ci}; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_cistatic int uart_chip_count; 10708c2ecf20Sopenharmony_cistatic struct uart_sunzilog_port *sunzilog_port_table; 10718c2ecf20Sopenharmony_cistatic struct zilog_layout __iomem **sunzilog_chip_regs; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_cistatic struct uart_sunzilog_port *sunzilog_irq_chain; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_cistatic struct uart_driver sunzilog_reg = { 10768c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 10778c2ecf20Sopenharmony_ci .driver_name = "sunzilog", 10788c2ecf20Sopenharmony_ci .dev_name = "ttyS", 10798c2ecf20Sopenharmony_ci .major = TTY_MAJOR, 10808c2ecf20Sopenharmony_ci}; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_cistatic int __init sunzilog_alloc_tables(int num_sunzilog) 10838c2ecf20Sopenharmony_ci{ 10848c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up; 10858c2ecf20Sopenharmony_ci unsigned long size; 10868c2ecf20Sopenharmony_ci int num_channels = num_sunzilog * 2; 10878c2ecf20Sopenharmony_ci int i; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci size = num_channels * sizeof(struct uart_sunzilog_port); 10908c2ecf20Sopenharmony_ci sunzilog_port_table = kzalloc(size, GFP_KERNEL); 10918c2ecf20Sopenharmony_ci if (!sunzilog_port_table) 10928c2ecf20Sopenharmony_ci return -ENOMEM; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci for (i = 0; i < num_channels; i++) { 10958c2ecf20Sopenharmony_ci up = &sunzilog_port_table[i]; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci spin_lock_init(&up->port.lock); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci if (i == 0) 11008c2ecf20Sopenharmony_ci sunzilog_irq_chain = up; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci if (i < num_channels - 1) 11038c2ecf20Sopenharmony_ci up->next = up + 1; 11048c2ecf20Sopenharmony_ci else 11058c2ecf20Sopenharmony_ci up->next = NULL; 11068c2ecf20Sopenharmony_ci } 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci size = num_sunzilog * sizeof(struct zilog_layout __iomem *); 11098c2ecf20Sopenharmony_ci sunzilog_chip_regs = kzalloc(size, GFP_KERNEL); 11108c2ecf20Sopenharmony_ci if (!sunzilog_chip_regs) { 11118c2ecf20Sopenharmony_ci kfree(sunzilog_port_table); 11128c2ecf20Sopenharmony_ci sunzilog_irq_chain = NULL; 11138c2ecf20Sopenharmony_ci return -ENOMEM; 11148c2ecf20Sopenharmony_ci } 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci return 0; 11178c2ecf20Sopenharmony_ci} 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_cistatic void sunzilog_free_tables(void) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci kfree(sunzilog_port_table); 11228c2ecf20Sopenharmony_ci sunzilog_irq_chain = NULL; 11238c2ecf20Sopenharmony_ci kfree(sunzilog_chip_regs); 11248c2ecf20Sopenharmony_ci} 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci#define ZS_PUT_CHAR_MAX_DELAY 2000 /* 10 ms */ 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_cistatic void sunzilog_putchar(struct uart_port *port, int ch) 11298c2ecf20Sopenharmony_ci{ 11308c2ecf20Sopenharmony_ci struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); 11318c2ecf20Sopenharmony_ci int loops = ZS_PUT_CHAR_MAX_DELAY; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci /* This is a timed polling loop so do not switch the explicit 11348c2ecf20Sopenharmony_ci * udelay with ZSDELAY as that is a NOP on some platforms. -DaveM 11358c2ecf20Sopenharmony_ci */ 11368c2ecf20Sopenharmony_ci do { 11378c2ecf20Sopenharmony_ci unsigned char val = readb(&channel->control); 11388c2ecf20Sopenharmony_ci if (val & Tx_BUF_EMP) { 11398c2ecf20Sopenharmony_ci ZSDELAY(); 11408c2ecf20Sopenharmony_ci break; 11418c2ecf20Sopenharmony_ci } 11428c2ecf20Sopenharmony_ci udelay(5); 11438c2ecf20Sopenharmony_ci } while (--loops); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci writeb(ch, &channel->data); 11468c2ecf20Sopenharmony_ci ZSDELAY(); 11478c2ecf20Sopenharmony_ci ZS_WSYNC(channel); 11488c2ecf20Sopenharmony_ci} 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci#ifdef CONFIG_SERIO 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(sunzilog_serio_lock); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_cistatic int sunzilog_serio_write(struct serio *serio, unsigned char ch) 11558c2ecf20Sopenharmony_ci{ 11568c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = serio->port_data; 11578c2ecf20Sopenharmony_ci unsigned long flags; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci spin_lock_irqsave(&sunzilog_serio_lock, flags); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci sunzilog_putchar(&up->port, ch); 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sunzilog_serio_lock, flags); 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci return 0; 11668c2ecf20Sopenharmony_ci} 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_cistatic int sunzilog_serio_open(struct serio *serio) 11698c2ecf20Sopenharmony_ci{ 11708c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = serio->port_data; 11718c2ecf20Sopenharmony_ci unsigned long flags; 11728c2ecf20Sopenharmony_ci int ret; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci spin_lock_irqsave(&sunzilog_serio_lock, flags); 11758c2ecf20Sopenharmony_ci if (!up->serio_open) { 11768c2ecf20Sopenharmony_ci up->serio_open = 1; 11778c2ecf20Sopenharmony_ci ret = 0; 11788c2ecf20Sopenharmony_ci } else 11798c2ecf20Sopenharmony_ci ret = -EBUSY; 11808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sunzilog_serio_lock, flags); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci return ret; 11838c2ecf20Sopenharmony_ci} 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_cistatic void sunzilog_serio_close(struct serio *serio) 11868c2ecf20Sopenharmony_ci{ 11878c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = serio->port_data; 11888c2ecf20Sopenharmony_ci unsigned long flags; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci spin_lock_irqsave(&sunzilog_serio_lock, flags); 11918c2ecf20Sopenharmony_ci up->serio_open = 0; 11928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sunzilog_serio_lock, flags); 11938c2ecf20Sopenharmony_ci} 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci#endif /* CONFIG_SERIO */ 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci#ifdef CONFIG_SERIAL_SUNZILOG_CONSOLE 11988c2ecf20Sopenharmony_cistatic void 11998c2ecf20Sopenharmony_cisunzilog_console_write(struct console *con, const char *s, unsigned int count) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = &sunzilog_port_table[con->index]; 12028c2ecf20Sopenharmony_ci unsigned long flags; 12038c2ecf20Sopenharmony_ci int locked = 1; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci if (up->port.sysrq || oops_in_progress) 12068c2ecf20Sopenharmony_ci locked = spin_trylock_irqsave(&up->port.lock, flags); 12078c2ecf20Sopenharmony_ci else 12088c2ecf20Sopenharmony_ci spin_lock_irqsave(&up->port.lock, flags); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci uart_console_write(&up->port, s, count, sunzilog_putchar); 12118c2ecf20Sopenharmony_ci udelay(2); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci if (locked) 12148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&up->port.lock, flags); 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_cistatic int __init sunzilog_console_setup(struct console *con, char *options) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = &sunzilog_port_table[con->index]; 12208c2ecf20Sopenharmony_ci unsigned long flags; 12218c2ecf20Sopenharmony_ci int baud, brg; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci if (up->port.type != PORT_SUNZILOG) 12248c2ecf20Sopenharmony_ci return -EINVAL; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci printk(KERN_INFO "Console: ttyS%d (SunZilog zs%d)\n", 12278c2ecf20Sopenharmony_ci (sunzilog_reg.minor - 64) + con->index, con->index); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci /* Get firmware console settings. */ 12308c2ecf20Sopenharmony_ci sunserial_console_termios(con, up->port.dev->of_node); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci /* Firmware console speed is limited to 150-->38400 baud so 12338c2ecf20Sopenharmony_ci * this hackish cflag thing is OK. 12348c2ecf20Sopenharmony_ci */ 12358c2ecf20Sopenharmony_ci switch (con->cflag & CBAUD) { 12368c2ecf20Sopenharmony_ci case B150: baud = 150; break; 12378c2ecf20Sopenharmony_ci case B300: baud = 300; break; 12388c2ecf20Sopenharmony_ci case B600: baud = 600; break; 12398c2ecf20Sopenharmony_ci case B1200: baud = 1200; break; 12408c2ecf20Sopenharmony_ci case B2400: baud = 2400; break; 12418c2ecf20Sopenharmony_ci case B4800: baud = 4800; break; 12428c2ecf20Sopenharmony_ci default: case B9600: baud = 9600; break; 12438c2ecf20Sopenharmony_ci case B19200: baud = 19200; break; 12448c2ecf20Sopenharmony_ci case B38400: baud = 38400; break; 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci spin_lock_irqsave(&up->port.lock, flags); 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci up->curregs[R15] |= BRKIE; 12528c2ecf20Sopenharmony_ci sunzilog_convert_to_zs(up, con->cflag, 0, brg); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); 12558c2ecf20Sopenharmony_ci __sunzilog_startup(up); 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&up->port.lock, flags); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci return 0; 12608c2ecf20Sopenharmony_ci} 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_cistatic struct console sunzilog_console_ops = { 12638c2ecf20Sopenharmony_ci .name = "ttyS", 12648c2ecf20Sopenharmony_ci .write = sunzilog_console_write, 12658c2ecf20Sopenharmony_ci .device = uart_console_device, 12668c2ecf20Sopenharmony_ci .setup = sunzilog_console_setup, 12678c2ecf20Sopenharmony_ci .flags = CON_PRINTBUFFER, 12688c2ecf20Sopenharmony_ci .index = -1, 12698c2ecf20Sopenharmony_ci .data = &sunzilog_reg, 12708c2ecf20Sopenharmony_ci}; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_cistatic inline struct console *SUNZILOG_CONSOLE(void) 12738c2ecf20Sopenharmony_ci{ 12748c2ecf20Sopenharmony_ci return &sunzilog_console_ops; 12758c2ecf20Sopenharmony_ci} 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci#else 12788c2ecf20Sopenharmony_ci#define SUNZILOG_CONSOLE() (NULL) 12798c2ecf20Sopenharmony_ci#endif 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_cistatic void sunzilog_init_kbdms(struct uart_sunzilog_port *up) 12828c2ecf20Sopenharmony_ci{ 12838c2ecf20Sopenharmony_ci int baud, brg; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci if (up->flags & SUNZILOG_FLAG_CONS_KEYB) { 12868c2ecf20Sopenharmony_ci up->cflag = B1200 | CS8 | CLOCAL | CREAD; 12878c2ecf20Sopenharmony_ci baud = 1200; 12888c2ecf20Sopenharmony_ci } else { 12898c2ecf20Sopenharmony_ci up->cflag = B4800 | CS8 | CLOCAL | CREAD; 12908c2ecf20Sopenharmony_ci baud = 4800; 12918c2ecf20Sopenharmony_ci } 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci up->curregs[R15] |= BRKIE; 12948c2ecf20Sopenharmony_ci brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); 12958c2ecf20Sopenharmony_ci sunzilog_convert_to_zs(up, up->cflag, 0, brg); 12968c2ecf20Sopenharmony_ci sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); 12978c2ecf20Sopenharmony_ci __sunzilog_startup(up); 12988c2ecf20Sopenharmony_ci} 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci#ifdef CONFIG_SERIO 13018c2ecf20Sopenharmony_cistatic void sunzilog_register_serio(struct uart_sunzilog_port *up) 13028c2ecf20Sopenharmony_ci{ 13038c2ecf20Sopenharmony_ci struct serio *serio = &up->serio; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci serio->port_data = up; 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci serio->id.type = SERIO_RS232; 13088c2ecf20Sopenharmony_ci if (up->flags & SUNZILOG_FLAG_CONS_KEYB) { 13098c2ecf20Sopenharmony_ci serio->id.proto = SERIO_SUNKBD; 13108c2ecf20Sopenharmony_ci strlcpy(serio->name, "zskbd", sizeof(serio->name)); 13118c2ecf20Sopenharmony_ci } else { 13128c2ecf20Sopenharmony_ci serio->id.proto = SERIO_SUN; 13138c2ecf20Sopenharmony_ci serio->id.extra = 1; 13148c2ecf20Sopenharmony_ci strlcpy(serio->name, "zsms", sizeof(serio->name)); 13158c2ecf20Sopenharmony_ci } 13168c2ecf20Sopenharmony_ci strlcpy(serio->phys, 13178c2ecf20Sopenharmony_ci ((up->flags & SUNZILOG_FLAG_CONS_KEYB) ? 13188c2ecf20Sopenharmony_ci "zs/serio0" : "zs/serio1"), 13198c2ecf20Sopenharmony_ci sizeof(serio->phys)); 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci serio->write = sunzilog_serio_write; 13228c2ecf20Sopenharmony_ci serio->open = sunzilog_serio_open; 13238c2ecf20Sopenharmony_ci serio->close = sunzilog_serio_close; 13248c2ecf20Sopenharmony_ci serio->dev.parent = up->port.dev; 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci serio_register_port(serio); 13278c2ecf20Sopenharmony_ci} 13288c2ecf20Sopenharmony_ci#endif 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_cistatic void sunzilog_init_hw(struct uart_sunzilog_port *up) 13318c2ecf20Sopenharmony_ci{ 13328c2ecf20Sopenharmony_ci struct zilog_channel __iomem *channel; 13338c2ecf20Sopenharmony_ci unsigned long flags; 13348c2ecf20Sopenharmony_ci int baud, brg; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci channel = ZILOG_CHANNEL_FROM_PORT(&up->port); 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci spin_lock_irqsave(&up->port.lock, flags); 13398c2ecf20Sopenharmony_ci if (ZS_IS_CHANNEL_A(up)) { 13408c2ecf20Sopenharmony_ci write_zsreg(channel, R9, FHWRES); 13418c2ecf20Sopenharmony_ci ZSDELAY_LONG(); 13428c2ecf20Sopenharmony_ci (void) read_zsreg(channel, R0); 13438c2ecf20Sopenharmony_ci } 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci if (up->flags & (SUNZILOG_FLAG_CONS_KEYB | 13468c2ecf20Sopenharmony_ci SUNZILOG_FLAG_CONS_MOUSE)) { 13478c2ecf20Sopenharmony_ci up->curregs[R1] = EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB; 13488c2ecf20Sopenharmony_ci up->curregs[R4] = PAR_EVEN | X16CLK | SB1; 13498c2ecf20Sopenharmony_ci up->curregs[R3] = RxENAB | Rx8; 13508c2ecf20Sopenharmony_ci up->curregs[R5] = TxENAB | Tx8; 13518c2ecf20Sopenharmony_ci up->curregs[R6] = 0x00; /* SDLC Address */ 13528c2ecf20Sopenharmony_ci up->curregs[R7] = 0x7E; /* SDLC Flag */ 13538c2ecf20Sopenharmony_ci up->curregs[R9] = NV; 13548c2ecf20Sopenharmony_ci up->curregs[R7p] = 0x00; 13558c2ecf20Sopenharmony_ci sunzilog_init_kbdms(up); 13568c2ecf20Sopenharmony_ci /* Only enable interrupts if an ISR handler available */ 13578c2ecf20Sopenharmony_ci if (up->flags & SUNZILOG_FLAG_ISR_HANDLER) 13588c2ecf20Sopenharmony_ci up->curregs[R9] |= MIE; 13598c2ecf20Sopenharmony_ci write_zsreg(channel, R9, up->curregs[R9]); 13608c2ecf20Sopenharmony_ci } else { 13618c2ecf20Sopenharmony_ci /* Normal serial TTY. */ 13628c2ecf20Sopenharmony_ci up->parity_mask = 0xff; 13638c2ecf20Sopenharmony_ci up->curregs[R1] = EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB; 13648c2ecf20Sopenharmony_ci up->curregs[R4] = PAR_EVEN | X16CLK | SB1; 13658c2ecf20Sopenharmony_ci up->curregs[R3] = RxENAB | Rx8; 13668c2ecf20Sopenharmony_ci up->curregs[R5] = TxENAB | Tx8; 13678c2ecf20Sopenharmony_ci up->curregs[R6] = 0x00; /* SDLC Address */ 13688c2ecf20Sopenharmony_ci up->curregs[R7] = 0x7E; /* SDLC Flag */ 13698c2ecf20Sopenharmony_ci up->curregs[R9] = NV; 13708c2ecf20Sopenharmony_ci up->curregs[R10] = NRZ; 13718c2ecf20Sopenharmony_ci up->curregs[R11] = TCBR | RCBR; 13728c2ecf20Sopenharmony_ci baud = 9600; 13738c2ecf20Sopenharmony_ci brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); 13748c2ecf20Sopenharmony_ci up->curregs[R12] = (brg & 0xff); 13758c2ecf20Sopenharmony_ci up->curregs[R13] = (brg >> 8) & 0xff; 13768c2ecf20Sopenharmony_ci up->curregs[R14] = BRSRC | BRENAB; 13778c2ecf20Sopenharmony_ci up->curregs[R15] = FIFOEN; /* Use FIFO if on ESCC */ 13788c2ecf20Sopenharmony_ci up->curregs[R7p] = TxFIFO_LVL | RxFIFO_LVL; 13798c2ecf20Sopenharmony_ci if (__load_zsregs(channel, up->curregs)) { 13808c2ecf20Sopenharmony_ci up->flags |= SUNZILOG_FLAG_ESCC; 13818c2ecf20Sopenharmony_ci } 13828c2ecf20Sopenharmony_ci /* Only enable interrupts if an ISR handler available */ 13838c2ecf20Sopenharmony_ci if (up->flags & SUNZILOG_FLAG_ISR_HANDLER) 13848c2ecf20Sopenharmony_ci up->curregs[R9] |= MIE; 13858c2ecf20Sopenharmony_ci write_zsreg(channel, R9, up->curregs[R9]); 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&up->port.lock, flags); 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci#ifdef CONFIG_SERIO 13918c2ecf20Sopenharmony_ci if (up->flags & (SUNZILOG_FLAG_CONS_KEYB | 13928c2ecf20Sopenharmony_ci SUNZILOG_FLAG_CONS_MOUSE)) 13938c2ecf20Sopenharmony_ci sunzilog_register_serio(up); 13948c2ecf20Sopenharmony_ci#endif 13958c2ecf20Sopenharmony_ci} 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_cistatic int zilog_irq; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_cistatic int zs_probe(struct platform_device *op) 14008c2ecf20Sopenharmony_ci{ 14018c2ecf20Sopenharmony_ci static int kbm_inst, uart_inst; 14028c2ecf20Sopenharmony_ci int inst; 14038c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up; 14048c2ecf20Sopenharmony_ci struct zilog_layout __iomem *rp; 14058c2ecf20Sopenharmony_ci int keyboard_mouse = 0; 14068c2ecf20Sopenharmony_ci int err; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci if (of_find_property(op->dev.of_node, "keyboard", NULL)) 14098c2ecf20Sopenharmony_ci keyboard_mouse = 1; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci /* uarts must come before keyboards/mice */ 14128c2ecf20Sopenharmony_ci if (keyboard_mouse) 14138c2ecf20Sopenharmony_ci inst = uart_chip_count + kbm_inst; 14148c2ecf20Sopenharmony_ci else 14158c2ecf20Sopenharmony_ci inst = uart_inst; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci sunzilog_chip_regs[inst] = of_ioremap(&op->resource[0], 0, 14188c2ecf20Sopenharmony_ci sizeof(struct zilog_layout), 14198c2ecf20Sopenharmony_ci "zs"); 14208c2ecf20Sopenharmony_ci if (!sunzilog_chip_regs[inst]) 14218c2ecf20Sopenharmony_ci return -ENOMEM; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci rp = sunzilog_chip_regs[inst]; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci if (!zilog_irq) 14268c2ecf20Sopenharmony_ci zilog_irq = op->archdata.irqs[0]; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci up = &sunzilog_port_table[inst * 2]; 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci /* Channel A */ 14318c2ecf20Sopenharmony_ci up[0].port.mapbase = op->resource[0].start + 0x00; 14328c2ecf20Sopenharmony_ci up[0].port.membase = (void __iomem *) &rp->channelA; 14338c2ecf20Sopenharmony_ci up[0].port.iotype = UPIO_MEM; 14348c2ecf20Sopenharmony_ci up[0].port.irq = op->archdata.irqs[0]; 14358c2ecf20Sopenharmony_ci up[0].port.uartclk = ZS_CLOCK; 14368c2ecf20Sopenharmony_ci up[0].port.fifosize = 1; 14378c2ecf20Sopenharmony_ci up[0].port.ops = &sunzilog_pops; 14388c2ecf20Sopenharmony_ci up[0].port.type = PORT_SUNZILOG; 14398c2ecf20Sopenharmony_ci up[0].port.flags = 0; 14408c2ecf20Sopenharmony_ci up[0].port.line = (inst * 2) + 0; 14418c2ecf20Sopenharmony_ci up[0].port.dev = &op->dev; 14428c2ecf20Sopenharmony_ci up[0].flags |= SUNZILOG_FLAG_IS_CHANNEL_A; 14438c2ecf20Sopenharmony_ci up[0].port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_SUNZILOG_CONSOLE); 14448c2ecf20Sopenharmony_ci if (keyboard_mouse) 14458c2ecf20Sopenharmony_ci up[0].flags |= SUNZILOG_FLAG_CONS_KEYB; 14468c2ecf20Sopenharmony_ci sunzilog_init_hw(&up[0]); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci /* Channel B */ 14498c2ecf20Sopenharmony_ci up[1].port.mapbase = op->resource[0].start + 0x04; 14508c2ecf20Sopenharmony_ci up[1].port.membase = (void __iomem *) &rp->channelB; 14518c2ecf20Sopenharmony_ci up[1].port.iotype = UPIO_MEM; 14528c2ecf20Sopenharmony_ci up[1].port.irq = op->archdata.irqs[0]; 14538c2ecf20Sopenharmony_ci up[1].port.uartclk = ZS_CLOCK; 14548c2ecf20Sopenharmony_ci up[1].port.fifosize = 1; 14558c2ecf20Sopenharmony_ci up[1].port.ops = &sunzilog_pops; 14568c2ecf20Sopenharmony_ci up[1].port.type = PORT_SUNZILOG; 14578c2ecf20Sopenharmony_ci up[1].port.flags = 0; 14588c2ecf20Sopenharmony_ci up[1].port.line = (inst * 2) + 1; 14598c2ecf20Sopenharmony_ci up[1].port.dev = &op->dev; 14608c2ecf20Sopenharmony_ci up[1].flags |= 0; 14618c2ecf20Sopenharmony_ci up[1].port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_SUNZILOG_CONSOLE); 14628c2ecf20Sopenharmony_ci if (keyboard_mouse) 14638c2ecf20Sopenharmony_ci up[1].flags |= SUNZILOG_FLAG_CONS_MOUSE; 14648c2ecf20Sopenharmony_ci sunzilog_init_hw(&up[1]); 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci if (!keyboard_mouse) { 14678c2ecf20Sopenharmony_ci if (sunserial_console_match(SUNZILOG_CONSOLE(), op->dev.of_node, 14688c2ecf20Sopenharmony_ci &sunzilog_reg, up[0].port.line, 14698c2ecf20Sopenharmony_ci false)) 14708c2ecf20Sopenharmony_ci up->flags |= SUNZILOG_FLAG_IS_CONS; 14718c2ecf20Sopenharmony_ci err = uart_add_one_port(&sunzilog_reg, &up[0].port); 14728c2ecf20Sopenharmony_ci if (err) { 14738c2ecf20Sopenharmony_ci of_iounmap(&op->resource[0], 14748c2ecf20Sopenharmony_ci rp, sizeof(struct zilog_layout)); 14758c2ecf20Sopenharmony_ci return err; 14768c2ecf20Sopenharmony_ci } 14778c2ecf20Sopenharmony_ci if (sunserial_console_match(SUNZILOG_CONSOLE(), op->dev.of_node, 14788c2ecf20Sopenharmony_ci &sunzilog_reg, up[1].port.line, 14798c2ecf20Sopenharmony_ci false)) 14808c2ecf20Sopenharmony_ci up->flags |= SUNZILOG_FLAG_IS_CONS; 14818c2ecf20Sopenharmony_ci err = uart_add_one_port(&sunzilog_reg, &up[1].port); 14828c2ecf20Sopenharmony_ci if (err) { 14838c2ecf20Sopenharmony_ci uart_remove_one_port(&sunzilog_reg, &up[0].port); 14848c2ecf20Sopenharmony_ci of_iounmap(&op->resource[0], 14858c2ecf20Sopenharmony_ci rp, sizeof(struct zilog_layout)); 14868c2ecf20Sopenharmony_ci return err; 14878c2ecf20Sopenharmony_ci } 14888c2ecf20Sopenharmony_ci uart_inst++; 14898c2ecf20Sopenharmony_ci } else { 14908c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Keyboard at MMIO 0x%llx (irq = %d) " 14918c2ecf20Sopenharmony_ci "is a %s\n", 14928c2ecf20Sopenharmony_ci dev_name(&op->dev), 14938c2ecf20Sopenharmony_ci (unsigned long long) up[0].port.mapbase, 14948c2ecf20Sopenharmony_ci op->archdata.irqs[0], sunzilog_type(&up[0].port)); 14958c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Mouse at MMIO 0x%llx (irq = %d) " 14968c2ecf20Sopenharmony_ci "is a %s\n", 14978c2ecf20Sopenharmony_ci dev_name(&op->dev), 14988c2ecf20Sopenharmony_ci (unsigned long long) up[1].port.mapbase, 14998c2ecf20Sopenharmony_ci op->archdata.irqs[0], sunzilog_type(&up[1].port)); 15008c2ecf20Sopenharmony_ci kbm_inst++; 15018c2ecf20Sopenharmony_ci } 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci platform_set_drvdata(op, &up[0]); 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci return 0; 15068c2ecf20Sopenharmony_ci} 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_cistatic void zs_remove_one(struct uart_sunzilog_port *up) 15098c2ecf20Sopenharmony_ci{ 15108c2ecf20Sopenharmony_ci if (ZS_IS_KEYB(up) || ZS_IS_MOUSE(up)) { 15118c2ecf20Sopenharmony_ci#ifdef CONFIG_SERIO 15128c2ecf20Sopenharmony_ci serio_unregister_port(&up->serio); 15138c2ecf20Sopenharmony_ci#endif 15148c2ecf20Sopenharmony_ci } else 15158c2ecf20Sopenharmony_ci uart_remove_one_port(&sunzilog_reg, &up->port); 15168c2ecf20Sopenharmony_ci} 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_cistatic int zs_remove(struct platform_device *op) 15198c2ecf20Sopenharmony_ci{ 15208c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = platform_get_drvdata(op); 15218c2ecf20Sopenharmony_ci struct zilog_layout __iomem *regs; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci zs_remove_one(&up[0]); 15248c2ecf20Sopenharmony_ci zs_remove_one(&up[1]); 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci regs = sunzilog_chip_regs[up[0].port.line / 2]; 15278c2ecf20Sopenharmony_ci of_iounmap(&op->resource[0], regs, sizeof(struct zilog_layout)); 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci return 0; 15308c2ecf20Sopenharmony_ci} 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_cistatic const struct of_device_id zs_match[] = { 15338c2ecf20Sopenharmony_ci { 15348c2ecf20Sopenharmony_ci .name = "zs", 15358c2ecf20Sopenharmony_ci }, 15368c2ecf20Sopenharmony_ci {}, 15378c2ecf20Sopenharmony_ci}; 15388c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, zs_match); 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_cistatic struct platform_driver zs_driver = { 15418c2ecf20Sopenharmony_ci .driver = { 15428c2ecf20Sopenharmony_ci .name = "zs", 15438c2ecf20Sopenharmony_ci .of_match_table = zs_match, 15448c2ecf20Sopenharmony_ci }, 15458c2ecf20Sopenharmony_ci .probe = zs_probe, 15468c2ecf20Sopenharmony_ci .remove = zs_remove, 15478c2ecf20Sopenharmony_ci}; 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_cistatic int __init sunzilog_init(void) 15508c2ecf20Sopenharmony_ci{ 15518c2ecf20Sopenharmony_ci struct device_node *dp; 15528c2ecf20Sopenharmony_ci int err; 15538c2ecf20Sopenharmony_ci int num_keybms = 0; 15548c2ecf20Sopenharmony_ci int num_sunzilog = 0; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci for_each_node_by_name(dp, "zs") { 15578c2ecf20Sopenharmony_ci num_sunzilog++; 15588c2ecf20Sopenharmony_ci if (of_find_property(dp, "keyboard", NULL)) 15598c2ecf20Sopenharmony_ci num_keybms++; 15608c2ecf20Sopenharmony_ci } 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci if (num_sunzilog) { 15638c2ecf20Sopenharmony_ci err = sunzilog_alloc_tables(num_sunzilog); 15648c2ecf20Sopenharmony_ci if (err) 15658c2ecf20Sopenharmony_ci goto out; 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci uart_chip_count = num_sunzilog - num_keybms; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci err = sunserial_register_minors(&sunzilog_reg, 15708c2ecf20Sopenharmony_ci uart_chip_count * 2); 15718c2ecf20Sopenharmony_ci if (err) 15728c2ecf20Sopenharmony_ci goto out_free_tables; 15738c2ecf20Sopenharmony_ci } 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci err = platform_driver_register(&zs_driver); 15768c2ecf20Sopenharmony_ci if (err) 15778c2ecf20Sopenharmony_ci goto out_unregister_uart; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci if (zilog_irq) { 15808c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = sunzilog_irq_chain; 15818c2ecf20Sopenharmony_ci err = request_irq(zilog_irq, sunzilog_interrupt, IRQF_SHARED, 15828c2ecf20Sopenharmony_ci "zs", sunzilog_irq_chain); 15838c2ecf20Sopenharmony_ci if (err) 15848c2ecf20Sopenharmony_ci goto out_unregister_driver; 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci /* Enable Interrupts */ 15878c2ecf20Sopenharmony_ci while (up) { 15888c2ecf20Sopenharmony_ci struct zilog_channel __iomem *channel; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci /* printk (KERN_INFO "Enable IRQ for ZILOG Hardware %p\n", up); */ 15918c2ecf20Sopenharmony_ci channel = ZILOG_CHANNEL_FROM_PORT(&up->port); 15928c2ecf20Sopenharmony_ci up->flags |= SUNZILOG_FLAG_ISR_HANDLER; 15938c2ecf20Sopenharmony_ci up->curregs[R9] |= MIE; 15948c2ecf20Sopenharmony_ci write_zsreg(channel, R9, up->curregs[R9]); 15958c2ecf20Sopenharmony_ci up = up->next; 15968c2ecf20Sopenharmony_ci } 15978c2ecf20Sopenharmony_ci } 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ciout: 16008c2ecf20Sopenharmony_ci return err; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ciout_unregister_driver: 16038c2ecf20Sopenharmony_ci platform_driver_unregister(&zs_driver); 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ciout_unregister_uart: 16068c2ecf20Sopenharmony_ci if (num_sunzilog) { 16078c2ecf20Sopenharmony_ci sunserial_unregister_minors(&sunzilog_reg, num_sunzilog); 16088c2ecf20Sopenharmony_ci sunzilog_reg.cons = NULL; 16098c2ecf20Sopenharmony_ci } 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ciout_free_tables: 16128c2ecf20Sopenharmony_ci sunzilog_free_tables(); 16138c2ecf20Sopenharmony_ci goto out; 16148c2ecf20Sopenharmony_ci} 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_cistatic void __exit sunzilog_exit(void) 16178c2ecf20Sopenharmony_ci{ 16188c2ecf20Sopenharmony_ci platform_driver_unregister(&zs_driver); 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci if (zilog_irq) { 16218c2ecf20Sopenharmony_ci struct uart_sunzilog_port *up = sunzilog_irq_chain; 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci /* Disable Interrupts */ 16248c2ecf20Sopenharmony_ci while (up) { 16258c2ecf20Sopenharmony_ci struct zilog_channel __iomem *channel; 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci /* printk (KERN_INFO "Disable IRQ for ZILOG Hardware %p\n", up); */ 16288c2ecf20Sopenharmony_ci channel = ZILOG_CHANNEL_FROM_PORT(&up->port); 16298c2ecf20Sopenharmony_ci up->flags &= ~SUNZILOG_FLAG_ISR_HANDLER; 16308c2ecf20Sopenharmony_ci up->curregs[R9] &= ~MIE; 16318c2ecf20Sopenharmony_ci write_zsreg(channel, R9, up->curregs[R9]); 16328c2ecf20Sopenharmony_ci up = up->next; 16338c2ecf20Sopenharmony_ci } 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci free_irq(zilog_irq, sunzilog_irq_chain); 16368c2ecf20Sopenharmony_ci zilog_irq = 0; 16378c2ecf20Sopenharmony_ci } 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci if (sunzilog_reg.nr) { 16408c2ecf20Sopenharmony_ci sunserial_unregister_minors(&sunzilog_reg, sunzilog_reg.nr); 16418c2ecf20Sopenharmony_ci sunzilog_free_tables(); 16428c2ecf20Sopenharmony_ci } 16438c2ecf20Sopenharmony_ci} 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_cimodule_init(sunzilog_init); 16468c2ecf20Sopenharmony_cimodule_exit(sunzilog_exit); 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ciMODULE_AUTHOR("David S. Miller"); 16498c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sun Zilog serial port driver"); 16508c2ecf20Sopenharmony_ciMODULE_VERSION("2.0"); 16518c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1652