18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 38c2ecf20Sopenharmony_ci#include <linux/ioport.h> 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include "spk_types.h" 68c2ecf20Sopenharmony_ci#include "speakup.h" 78c2ecf20Sopenharmony_ci#include "spk_priv.h" 88c2ecf20Sopenharmony_ci#include "serialio.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/serial_core.h> 118c2ecf20Sopenharmony_ci/* WARNING: Do not change this to <linux/serial.h> without testing that 128c2ecf20Sopenharmony_ci * SERIAL_PORT_DFNS does get defined to the appropriate value. 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci#include <asm/serial.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#ifndef SERIAL_PORT_DFNS 178c2ecf20Sopenharmony_ci#define SERIAL_PORT_DFNS 188c2ecf20Sopenharmony_ci#endif 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic void start_serial_interrupt(int irq); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic const struct old_serial_port rs_table[] = { 238c2ecf20Sopenharmony_ci SERIAL_PORT_DFNS 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic const struct old_serial_port *serstate; 278c2ecf20Sopenharmony_cistatic int timeouts; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int spk_serial_out(struct spk_synth *in_synth, const char ch); 308c2ecf20Sopenharmony_cistatic void spk_serial_send_xchar(char ch); 318c2ecf20Sopenharmony_cistatic void spk_serial_tiocmset(unsigned int set, unsigned int clear); 328c2ecf20Sopenharmony_cistatic unsigned char spk_serial_in(void); 338c2ecf20Sopenharmony_cistatic unsigned char spk_serial_in_nowait(void); 348c2ecf20Sopenharmony_cistatic void spk_serial_flush_buffer(void); 358c2ecf20Sopenharmony_cistatic int spk_serial_wait_for_xmitr(struct spk_synth *in_synth); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct spk_io_ops spk_serial_io_ops = { 388c2ecf20Sopenharmony_ci .synth_out = spk_serial_out, 398c2ecf20Sopenharmony_ci .send_xchar = spk_serial_send_xchar, 408c2ecf20Sopenharmony_ci .tiocmset = spk_serial_tiocmset, 418c2ecf20Sopenharmony_ci .synth_in = spk_serial_in, 428c2ecf20Sopenharmony_ci .synth_in_nowait = spk_serial_in_nowait, 438c2ecf20Sopenharmony_ci .flush_buffer = spk_serial_flush_buffer, 448c2ecf20Sopenharmony_ci .wait_for_xmitr = spk_serial_wait_for_xmitr, 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_serial_io_ops); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ciconst struct old_serial_port *spk_serial_init(int index) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci int baud = 9600, quot = 0; 518c2ecf20Sopenharmony_ci unsigned int cval = 0; 528c2ecf20Sopenharmony_ci int cflag = CREAD | HUPCL | CLOCAL | B9600 | CS8; 538c2ecf20Sopenharmony_ci const struct old_serial_port *ser; 548c2ecf20Sopenharmony_ci int err; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (index >= ARRAY_SIZE(rs_table)) { 578c2ecf20Sopenharmony_ci pr_info("no port info for ttyS%d\n", index); 588c2ecf20Sopenharmony_ci return NULL; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci ser = rs_table + index; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* Divisor, bytesize and parity */ 638c2ecf20Sopenharmony_ci quot = ser->baud_base / baud; 648c2ecf20Sopenharmony_ci cval = cflag & (CSIZE | CSTOPB); 658c2ecf20Sopenharmony_ci#if defined(__powerpc__) || defined(__alpha__) 668c2ecf20Sopenharmony_ci cval >>= 8; 678c2ecf20Sopenharmony_ci#else /* !__powerpc__ && !__alpha__ */ 688c2ecf20Sopenharmony_ci cval >>= 4; 698c2ecf20Sopenharmony_ci#endif /* !__powerpc__ && !__alpha__ */ 708c2ecf20Sopenharmony_ci if (cflag & PARENB) 718c2ecf20Sopenharmony_ci cval |= UART_LCR_PARITY; 728c2ecf20Sopenharmony_ci if (!(cflag & PARODD)) 738c2ecf20Sopenharmony_ci cval |= UART_LCR_EPAR; 748c2ecf20Sopenharmony_ci if (synth_request_region(ser->port, 8)) { 758c2ecf20Sopenharmony_ci /* try to take it back. */ 768c2ecf20Sopenharmony_ci pr_info("Ports not available, trying to steal them\n"); 778c2ecf20Sopenharmony_ci __release_region(&ioport_resource, ser->port, 8); 788c2ecf20Sopenharmony_ci err = synth_request_region(ser->port, 8); 798c2ecf20Sopenharmony_ci if (err) { 808c2ecf20Sopenharmony_ci pr_warn("Unable to allocate port at %x, errno %i", 818c2ecf20Sopenharmony_ci ser->port, err); 828c2ecf20Sopenharmony_ci return NULL; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* Disable UART interrupts, set DTR and RTS high 878c2ecf20Sopenharmony_ci * and set speed. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci outb(cval | UART_LCR_DLAB, ser->port + UART_LCR); /* set DLAB */ 908c2ecf20Sopenharmony_ci outb(quot & 0xff, ser->port + UART_DLL); /* LS of divisor */ 918c2ecf20Sopenharmony_ci outb(quot >> 8, ser->port + UART_DLM); /* MS of divisor */ 928c2ecf20Sopenharmony_ci outb(cval, ser->port + UART_LCR); /* reset DLAB */ 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* Turn off Interrupts */ 958c2ecf20Sopenharmony_ci outb(0, ser->port + UART_IER); 968c2ecf20Sopenharmony_ci outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* If we read 0xff from the LSR, there is no UART here. */ 998c2ecf20Sopenharmony_ci if (inb(ser->port + UART_LSR) == 0xff) { 1008c2ecf20Sopenharmony_ci synth_release_region(ser->port, 8); 1018c2ecf20Sopenharmony_ci serstate = NULL; 1028c2ecf20Sopenharmony_ci return NULL; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci mdelay(1); 1068c2ecf20Sopenharmony_ci speakup_info.port_tts = ser->port; 1078c2ecf20Sopenharmony_ci serstate = ser; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci start_serial_interrupt(ser->irq); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return ser; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic irqreturn_t synth_readbuf_handler(int irq, void *dev_id) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci unsigned long flags; 1178c2ecf20Sopenharmony_ci int c; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 1208c2ecf20Sopenharmony_ci while (inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR) { 1218c2ecf20Sopenharmony_ci c = inb_p(speakup_info.port_tts + UART_RX); 1228c2ecf20Sopenharmony_ci synth->read_buff_add((u_char)c); 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 1258c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void start_serial_interrupt(int irq) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci int rv; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (!synth->read_buff_add) 1338c2ecf20Sopenharmony_ci return; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci rv = request_irq(irq, synth_readbuf_handler, IRQF_SHARED, 1368c2ecf20Sopenharmony_ci "serial", (void *)synth_readbuf_handler); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (rv) 1398c2ecf20Sopenharmony_ci pr_err("Unable to request Speakup serial I R Q\n"); 1408c2ecf20Sopenharmony_ci /* Set MCR */ 1418c2ecf20Sopenharmony_ci outb(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2, 1428c2ecf20Sopenharmony_ci speakup_info.port_tts + UART_MCR); 1438c2ecf20Sopenharmony_ci /* Turn on Interrupts */ 1448c2ecf20Sopenharmony_ci outb(UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI, 1458c2ecf20Sopenharmony_ci speakup_info.port_tts + UART_IER); 1468c2ecf20Sopenharmony_ci inb(speakup_info.port_tts + UART_LSR); 1478c2ecf20Sopenharmony_ci inb(speakup_info.port_tts + UART_RX); 1488c2ecf20Sopenharmony_ci inb(speakup_info.port_tts + UART_IIR); 1498c2ecf20Sopenharmony_ci inb(speakup_info.port_tts + UART_MSR); 1508c2ecf20Sopenharmony_ci outb(1, speakup_info.port_tts + UART_FCR); /* Turn FIFO On */ 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic void spk_serial_send_xchar(char ch) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci int timeout = SPK_XMITR_TIMEOUT; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci while (spk_serial_tx_busy()) { 1588c2ecf20Sopenharmony_ci if (!--timeout) 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci udelay(1); 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci outb(ch, speakup_info.port_tts); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic void spk_serial_tiocmset(unsigned int set, unsigned int clear) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci int old = inb(speakup_info.port_tts + UART_MCR); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci outb((old & ~clear) | set, speakup_info.port_tts + UART_MCR); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciint spk_serial_synth_probe(struct spk_synth *synth) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci const struct old_serial_port *ser; 1758c2ecf20Sopenharmony_ci int failed = 0; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if ((synth->ser >= SPK_LO_TTY) && (synth->ser <= SPK_HI_TTY)) { 1788c2ecf20Sopenharmony_ci ser = spk_serial_init(synth->ser); 1798c2ecf20Sopenharmony_ci if (!ser) { 1808c2ecf20Sopenharmony_ci failed = -1; 1818c2ecf20Sopenharmony_ci } else { 1828c2ecf20Sopenharmony_ci outb_p(0, ser->port); 1838c2ecf20Sopenharmony_ci mdelay(1); 1848c2ecf20Sopenharmony_ci outb_p('\r', ser->port); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci } else { 1878c2ecf20Sopenharmony_ci failed = -1; 1888c2ecf20Sopenharmony_ci pr_warn("ttyS%i is an invalid port\n", synth->ser); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci if (failed) { 1918c2ecf20Sopenharmony_ci pr_info("%s: not found\n", synth->long_name); 1928c2ecf20Sopenharmony_ci return -ENODEV; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci pr_info("%s: ttyS%i, Driver Version %s\n", 1958c2ecf20Sopenharmony_ci synth->long_name, synth->ser, synth->version); 1968c2ecf20Sopenharmony_ci synth->alive = 1; 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_serial_synth_probe); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_civoid spk_stop_serial_interrupt(void) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci if (speakup_info.port_tts == 0) 2048c2ecf20Sopenharmony_ci return; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (!synth->read_buff_add) 2078c2ecf20Sopenharmony_ci return; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* Turn off interrupts */ 2108c2ecf20Sopenharmony_ci outb(0, speakup_info.port_tts + UART_IER); 2118c2ecf20Sopenharmony_ci /* Free IRQ */ 2128c2ecf20Sopenharmony_ci free_irq(serstate->irq, (void *)synth_readbuf_handler); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_stop_serial_interrupt); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic int spk_serial_wait_for_xmitr(struct spk_synth *in_synth) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci int tmout = SPK_XMITR_TIMEOUT; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if ((in_synth->alive) && (timeouts >= NUM_DISABLE_TIMEOUTS)) { 2218c2ecf20Sopenharmony_ci pr_warn("%s: too many timeouts, deactivating speakup\n", 2228c2ecf20Sopenharmony_ci in_synth->long_name); 2238c2ecf20Sopenharmony_ci in_synth->alive = 0; 2248c2ecf20Sopenharmony_ci /* No synth any more, so nobody will restart TTYs, and we thus 2258c2ecf20Sopenharmony_ci * need to do it ourselves. Now that there is no synth we can 2268c2ecf20Sopenharmony_ci * let application flood anyway 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_ci speakup_start_ttys(); 2298c2ecf20Sopenharmony_ci timeouts = 0; 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci while (spk_serial_tx_busy()) { 2338c2ecf20Sopenharmony_ci if (--tmout == 0) { 2348c2ecf20Sopenharmony_ci pr_warn("%s: timed out (tx busy)\n", 2358c2ecf20Sopenharmony_ci in_synth->long_name); 2368c2ecf20Sopenharmony_ci timeouts++; 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci udelay(1); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci tmout = SPK_CTS_TIMEOUT; 2428c2ecf20Sopenharmony_ci while (!((inb_p(speakup_info.port_tts + UART_MSR)) & UART_MSR_CTS)) { 2438c2ecf20Sopenharmony_ci /* CTS */ 2448c2ecf20Sopenharmony_ci if (--tmout == 0) { 2458c2ecf20Sopenharmony_ci timeouts++; 2468c2ecf20Sopenharmony_ci return 0; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci udelay(1); 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci timeouts = 0; 2518c2ecf20Sopenharmony_ci return 1; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic unsigned char spk_serial_in(void) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci int tmout = SPK_SERIAL_TIMEOUT; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci while (!(inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR)) { 2598c2ecf20Sopenharmony_ci if (--tmout == 0) { 2608c2ecf20Sopenharmony_ci pr_warn("time out while waiting for input.\n"); 2618c2ecf20Sopenharmony_ci return 0xff; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci udelay(1); 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci return inb_p(speakup_info.port_tts + UART_RX); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic unsigned char spk_serial_in_nowait(void) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci unsigned char lsr; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci lsr = inb_p(speakup_info.port_tts + UART_LSR); 2738c2ecf20Sopenharmony_ci if (!(lsr & UART_LSR_DR)) 2748c2ecf20Sopenharmony_ci return 0; 2758c2ecf20Sopenharmony_ci return inb_p(speakup_info.port_tts + UART_RX); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic void spk_serial_flush_buffer(void) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci /* TODO: flush the UART 16550 buffer */ 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int spk_serial_out(struct spk_synth *in_synth, const char ch) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci if (in_synth->alive && spk_serial_wait_for_xmitr(in_synth)) { 2868c2ecf20Sopenharmony_ci outb_p(ch, speakup_info.port_tts); 2878c2ecf20Sopenharmony_ci return 1; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci return 0; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ciconst char *spk_serial_synth_immediate(struct spk_synth *synth, 2938c2ecf20Sopenharmony_ci const char *buff) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci u_char ch; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci while ((ch = *buff)) { 2988c2ecf20Sopenharmony_ci if (ch == '\n') 2998c2ecf20Sopenharmony_ci ch = synth->procspeech; 3008c2ecf20Sopenharmony_ci if (spk_serial_wait_for_xmitr(synth)) 3018c2ecf20Sopenharmony_ci outb(ch, speakup_info.port_tts); 3028c2ecf20Sopenharmony_ci else 3038c2ecf20Sopenharmony_ci return buff; 3048c2ecf20Sopenharmony_ci buff++; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci return NULL; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_serial_synth_immediate); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_civoid spk_serial_release(void) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci spk_stop_serial_interrupt(); 3138c2ecf20Sopenharmony_ci if (speakup_info.port_tts == 0) 3148c2ecf20Sopenharmony_ci return; 3158c2ecf20Sopenharmony_ci synth_release_region(speakup_info.port_tts, 8); 3168c2ecf20Sopenharmony_ci speakup_info.port_tts = 0; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_serial_release); 319