162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/interrupt.h> 362306a36Sopenharmony_ci#include <linux/ioport.h> 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include "spk_types.h" 662306a36Sopenharmony_ci#include "speakup.h" 762306a36Sopenharmony_ci#include "spk_priv.h" 862306a36Sopenharmony_ci#include "serialio.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/serial_core.h> 1162306a36Sopenharmony_ci/* WARNING: Do not change this to <linux/serial.h> without testing that 1262306a36Sopenharmony_ci * SERIAL_PORT_DFNS does get defined to the appropriate value. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci#include <asm/serial.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#ifndef SERIAL_PORT_DFNS 1762306a36Sopenharmony_ci#define SERIAL_PORT_DFNS 1862306a36Sopenharmony_ci#endif 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic void start_serial_interrupt(int irq); 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic const struct old_serial_port rs_table[] = { 2362306a36Sopenharmony_ci SERIAL_PORT_DFNS 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic const struct old_serial_port *serstate; 2762306a36Sopenharmony_cistatic int timeouts; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic int spk_serial_out(struct spk_synth *in_synth, const char ch); 3062306a36Sopenharmony_cistatic void spk_serial_send_xchar(struct spk_synth *in_synth, char ch); 3162306a36Sopenharmony_cistatic void spk_serial_tiocmset(struct spk_synth *in_synth, unsigned int set, unsigned int clear); 3262306a36Sopenharmony_cistatic unsigned char spk_serial_in(struct spk_synth *in_synth); 3362306a36Sopenharmony_cistatic unsigned char spk_serial_in_nowait(struct spk_synth *in_synth); 3462306a36Sopenharmony_cistatic void spk_serial_flush_buffer(struct spk_synth *in_synth); 3562306a36Sopenharmony_cistatic int spk_serial_wait_for_xmitr(struct spk_synth *in_synth); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct spk_io_ops spk_serial_io_ops = { 3862306a36Sopenharmony_ci .synth_out = spk_serial_out, 3962306a36Sopenharmony_ci .send_xchar = spk_serial_send_xchar, 4062306a36Sopenharmony_ci .tiocmset = spk_serial_tiocmset, 4162306a36Sopenharmony_ci .synth_in = spk_serial_in, 4262306a36Sopenharmony_ci .synth_in_nowait = spk_serial_in_nowait, 4362306a36Sopenharmony_ci .flush_buffer = spk_serial_flush_buffer, 4462306a36Sopenharmony_ci .wait_for_xmitr = spk_serial_wait_for_xmitr, 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_serial_io_ops); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ciconst struct old_serial_port *spk_serial_init(int index) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci int baud = 9600, quot = 0; 5162306a36Sopenharmony_ci unsigned int cval = 0; 5262306a36Sopenharmony_ci int cflag = CREAD | HUPCL | CLOCAL | B9600 | CS8; 5362306a36Sopenharmony_ci const struct old_serial_port *ser; 5462306a36Sopenharmony_ci int err; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (index >= ARRAY_SIZE(rs_table)) { 5762306a36Sopenharmony_ci pr_info("no port info for ttyS%d\n", index); 5862306a36Sopenharmony_ci return NULL; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci ser = rs_table + index; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* Divisor, byte size and parity */ 6362306a36Sopenharmony_ci quot = ser->baud_base / baud; 6462306a36Sopenharmony_ci cval = cflag & (CSIZE | CSTOPB); 6562306a36Sopenharmony_ci#if defined(__powerpc__) || defined(__alpha__) 6662306a36Sopenharmony_ci cval >>= 8; 6762306a36Sopenharmony_ci#else /* !__powerpc__ && !__alpha__ */ 6862306a36Sopenharmony_ci cval >>= 4; 6962306a36Sopenharmony_ci#endif /* !__powerpc__ && !__alpha__ */ 7062306a36Sopenharmony_ci if (cflag & PARENB) 7162306a36Sopenharmony_ci cval |= UART_LCR_PARITY; 7262306a36Sopenharmony_ci if (!(cflag & PARODD)) 7362306a36Sopenharmony_ci cval |= UART_LCR_EPAR; 7462306a36Sopenharmony_ci if (synth_request_region(ser->port, 8)) { 7562306a36Sopenharmony_ci /* try to take it back. */ 7662306a36Sopenharmony_ci pr_info("Ports not available, trying to steal them\n"); 7762306a36Sopenharmony_ci __release_region(&ioport_resource, ser->port, 8); 7862306a36Sopenharmony_ci err = synth_request_region(ser->port, 8); 7962306a36Sopenharmony_ci if (err) { 8062306a36Sopenharmony_ci pr_warn("Unable to allocate port at %x, errno %i", 8162306a36Sopenharmony_ci ser->port, err); 8262306a36Sopenharmony_ci return NULL; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* Disable UART interrupts, set DTR and RTS high 8762306a36Sopenharmony_ci * and set speed. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci outb(cval | UART_LCR_DLAB, ser->port + UART_LCR); /* set DLAB */ 9062306a36Sopenharmony_ci outb(quot & 0xff, ser->port + UART_DLL); /* LS of divisor */ 9162306a36Sopenharmony_ci outb(quot >> 8, ser->port + UART_DLM); /* MS of divisor */ 9262306a36Sopenharmony_ci outb(cval, ser->port + UART_LCR); /* reset DLAB */ 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* Turn off Interrupts */ 9562306a36Sopenharmony_ci outb(0, ser->port + UART_IER); 9662306a36Sopenharmony_ci outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* If we read 0xff from the LSR, there is no UART here. */ 9962306a36Sopenharmony_ci if (inb(ser->port + UART_LSR) == 0xff) { 10062306a36Sopenharmony_ci synth_release_region(ser->port, 8); 10162306a36Sopenharmony_ci serstate = NULL; 10262306a36Sopenharmony_ci return NULL; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci mdelay(1); 10662306a36Sopenharmony_ci speakup_info.port_tts = ser->port; 10762306a36Sopenharmony_ci serstate = ser; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci start_serial_interrupt(ser->irq); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return ser; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic irqreturn_t synth_readbuf_handler(int irq, void *dev_id) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci unsigned long flags; 11762306a36Sopenharmony_ci int c; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 12062306a36Sopenharmony_ci while (inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR) { 12162306a36Sopenharmony_ci c = inb_p(speakup_info.port_tts + UART_RX); 12262306a36Sopenharmony_ci synth->read_buff_add((u_char)c); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 12562306a36Sopenharmony_ci return IRQ_HANDLED; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void start_serial_interrupt(int irq) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci int rv; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (!synth->read_buff_add) 13362306a36Sopenharmony_ci return; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci rv = request_irq(irq, synth_readbuf_handler, IRQF_SHARED, 13662306a36Sopenharmony_ci "serial", (void *)synth_readbuf_handler); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (rv) 13962306a36Sopenharmony_ci pr_err("Unable to request Speakup serial I R Q\n"); 14062306a36Sopenharmony_ci /* Set MCR */ 14162306a36Sopenharmony_ci outb(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2, 14262306a36Sopenharmony_ci speakup_info.port_tts + UART_MCR); 14362306a36Sopenharmony_ci /* Turn on Interrupts */ 14462306a36Sopenharmony_ci outb(UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI, 14562306a36Sopenharmony_ci speakup_info.port_tts + UART_IER); 14662306a36Sopenharmony_ci inb(speakup_info.port_tts + UART_LSR); 14762306a36Sopenharmony_ci inb(speakup_info.port_tts + UART_RX); 14862306a36Sopenharmony_ci inb(speakup_info.port_tts + UART_IIR); 14962306a36Sopenharmony_ci inb(speakup_info.port_tts + UART_MSR); 15062306a36Sopenharmony_ci outb(1, speakup_info.port_tts + UART_FCR); /* Turn FIFO On */ 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic void spk_serial_send_xchar(struct spk_synth *synth, char ch) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci int timeout = SPK_XMITR_TIMEOUT; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci while (spk_serial_tx_busy()) { 15862306a36Sopenharmony_ci if (!--timeout) 15962306a36Sopenharmony_ci break; 16062306a36Sopenharmony_ci udelay(1); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci outb(ch, speakup_info.port_tts); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic void spk_serial_tiocmset(struct spk_synth *in_synth, unsigned int set, unsigned int clear) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci int old = inb(speakup_info.port_tts + UART_MCR); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci outb((old & ~clear) | set, speakup_info.port_tts + UART_MCR); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciint spk_serial_synth_probe(struct spk_synth *synth) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci const struct old_serial_port *ser; 17562306a36Sopenharmony_ci int failed = 0; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if ((synth->ser >= SPK_LO_TTY) && (synth->ser <= SPK_HI_TTY)) { 17862306a36Sopenharmony_ci ser = spk_serial_init(synth->ser); 17962306a36Sopenharmony_ci if (!ser) { 18062306a36Sopenharmony_ci failed = -1; 18162306a36Sopenharmony_ci } else { 18262306a36Sopenharmony_ci outb_p(0, ser->port); 18362306a36Sopenharmony_ci mdelay(1); 18462306a36Sopenharmony_ci outb_p('\r', ser->port); 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci } else { 18762306a36Sopenharmony_ci failed = -1; 18862306a36Sopenharmony_ci pr_warn("ttyS%i is an invalid port\n", synth->ser); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci if (failed) { 19162306a36Sopenharmony_ci pr_info("%s: not found\n", synth->long_name); 19262306a36Sopenharmony_ci return -ENODEV; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci pr_info("%s: ttyS%i, Driver Version %s\n", 19562306a36Sopenharmony_ci synth->long_name, synth->ser, synth->version); 19662306a36Sopenharmony_ci synth->alive = 1; 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_serial_synth_probe); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_civoid spk_stop_serial_interrupt(void) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci if (speakup_info.port_tts == 0) 20462306a36Sopenharmony_ci return; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (!synth->read_buff_add) 20762306a36Sopenharmony_ci return; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* Turn off interrupts */ 21062306a36Sopenharmony_ci outb(0, speakup_info.port_tts + UART_IER); 21162306a36Sopenharmony_ci /* Free IRQ */ 21262306a36Sopenharmony_ci free_irq(serstate->irq, (void *)synth_readbuf_handler); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_stop_serial_interrupt); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic int spk_serial_wait_for_xmitr(struct spk_synth *in_synth) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci int tmout = SPK_XMITR_TIMEOUT; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if ((in_synth->alive) && (timeouts >= NUM_DISABLE_TIMEOUTS)) { 22162306a36Sopenharmony_ci pr_warn("%s: too many timeouts, deactivating speakup\n", 22262306a36Sopenharmony_ci in_synth->long_name); 22362306a36Sopenharmony_ci in_synth->alive = 0; 22462306a36Sopenharmony_ci /* No synth any more, so nobody will restart TTYs, and we thus 22562306a36Sopenharmony_ci * need to do it ourselves. Now that there is no synth we can 22662306a36Sopenharmony_ci * let application flood anyway 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci speakup_start_ttys(); 22962306a36Sopenharmony_ci timeouts = 0; 23062306a36Sopenharmony_ci return 0; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci while (spk_serial_tx_busy()) { 23362306a36Sopenharmony_ci if (--tmout == 0) { 23462306a36Sopenharmony_ci pr_warn("%s: timed out (tx busy)\n", 23562306a36Sopenharmony_ci in_synth->long_name); 23662306a36Sopenharmony_ci timeouts++; 23762306a36Sopenharmony_ci return 0; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci udelay(1); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci tmout = SPK_CTS_TIMEOUT; 24262306a36Sopenharmony_ci while (!((inb_p(speakup_info.port_tts + UART_MSR)) & UART_MSR_CTS)) { 24362306a36Sopenharmony_ci /* CTS */ 24462306a36Sopenharmony_ci if (--tmout == 0) { 24562306a36Sopenharmony_ci timeouts++; 24662306a36Sopenharmony_ci return 0; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci udelay(1); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci timeouts = 0; 25162306a36Sopenharmony_ci return 1; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic unsigned char spk_serial_in(struct spk_synth *in_synth) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci int tmout = SPK_SERIAL_TIMEOUT; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci while (!(inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR)) { 25962306a36Sopenharmony_ci if (--tmout == 0) { 26062306a36Sopenharmony_ci pr_warn("time out while waiting for input.\n"); 26162306a36Sopenharmony_ci return 0xff; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci udelay(1); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci return inb_p(speakup_info.port_tts + UART_RX); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic unsigned char spk_serial_in_nowait(struct spk_synth *in_synth) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci unsigned char lsr; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci lsr = inb_p(speakup_info.port_tts + UART_LSR); 27362306a36Sopenharmony_ci if (!(lsr & UART_LSR_DR)) 27462306a36Sopenharmony_ci return 0; 27562306a36Sopenharmony_ci return inb_p(speakup_info.port_tts + UART_RX); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic void spk_serial_flush_buffer(struct spk_synth *in_synth) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci /* TODO: flush the UART 16550 buffer */ 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic int spk_serial_out(struct spk_synth *in_synth, const char ch) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci if (in_synth->alive && spk_serial_wait_for_xmitr(in_synth)) { 28662306a36Sopenharmony_ci outb_p(ch, speakup_info.port_tts); 28762306a36Sopenharmony_ci return 1; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci return 0; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ciconst char *spk_serial_synth_immediate(struct spk_synth *synth, 29362306a36Sopenharmony_ci const char *buff) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci u_char ch; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci while ((ch = *buff)) { 29862306a36Sopenharmony_ci if (ch == '\n') 29962306a36Sopenharmony_ci ch = synth->procspeech; 30062306a36Sopenharmony_ci if (spk_serial_wait_for_xmitr(synth)) 30162306a36Sopenharmony_ci outb(ch, speakup_info.port_tts); 30262306a36Sopenharmony_ci else 30362306a36Sopenharmony_ci return buff; 30462306a36Sopenharmony_ci buff++; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci return NULL; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_serial_synth_immediate); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_civoid spk_serial_release(struct spk_synth *synth) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci spk_stop_serial_interrupt(); 31362306a36Sopenharmony_ci if (speakup_info.port_tts == 0) 31462306a36Sopenharmony_ci return; 31562306a36Sopenharmony_ci synth_release_region(speakup_info.port_tts, 8); 31662306a36Sopenharmony_ci speakup_info.port_tts = 0; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spk_serial_release); 319