--- zzzz-none-000/linux-4.19.183/drivers/tty/serial/amba-pl011.c 2021-03-24 10:07:39.000000000 +0000 +++ bcm63-7530ax-756/linux-4.19.183/drivers/tty/serial/amba-pl011.c 2023-06-28 08:54:20.000000000 +0000 @@ -278,6 +278,9 @@ struct pl011_dmatx_data dmatx; bool dma_probed; #endif +#if defined(CONFIG_AVM_ENHANCED) + atomic_t reflink; +#endif }; static unsigned int pl011_reg_to_offset(const struct uart_amba_port *uap, @@ -306,6 +309,32 @@ writew_relaxed(val, addr); } +#if defined(CONFIG_AVM_FASTIRQ) +#include +#define CLIENT_FIQ_PRIO FIQ_PRIO_MONITOR + +#include +#define __BUILD_AVM_CONTEXT_FUNC(func) firq_##func +#else +#define __BUILD_AVM_CONTEXT_FUNC(func) func +#define is_avm_rte() 0 +#endif + +#if defined(CONFIG_AVM_ENHANCED) +#include "amba-pl011-dectuart.h" + +static int pl011_serial_dectuart_get_char(void); +static void pl011_serial_dectuart_put_char(unsigned char ch); +static void pl011_serial_dectuart_init(unsigned int baud, int mode); +static void pl011_serial_dectuart_exit(void); +static void pl011_serial_console_stop(void); +static void pl011_serial_console_start(void); +#endif + +#if defined(CONFIG_AVM_ENHANCED) +extern unsigned int avm_console_enabled; +#endif + /* * Reads up to 256 characters from the FIFO or until it's empty and * inserts them into the TTY layer. Returns the number of characters @@ -354,6 +383,14 @@ sysrq = uart_handle_sysrq_char(&uap->port, ch & 255); spin_lock(&uap->port.lock); +#if defined(CONFIG_AVM_ENHANCED) + if (uap->port.cons->flags & (CON_ENABLED | CON_CONSDEV)) { + if ((ch & 255) == '\r') { + avm_console_enabled = 1; + } + } +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ + if (!sysrq) uart_insert_char(&uap->port, ch, UART011_DR_OE, ch, flag); } @@ -555,7 +592,7 @@ unsigned long flags; u16 dmacr; - spin_lock_irqsave(&uap->port.lock, flags); + __BUILD_AVM_CONTEXT_FUNC(spin_lock_irqsave)(&uap->port.lock, flags); if (uap->dmatx.queued) dma_unmap_sg(dmatx->chan->device->dev, &dmatx->sg, 1, DMA_TO_DEVICE); @@ -576,7 +613,7 @@ if (!(dmacr & UART011_TXDMAE) || uart_tx_stopped(&uap->port) || uart_circ_empty(&uap->port.state->xmit)) { uap->dmatx.queued = false; - spin_unlock_irqrestore(&uap->port.lock, flags); + __BUILD_AVM_CONTEXT_FUNC(spin_unlock_irqrestore)(&uap->port.lock, flags); return; } @@ -587,7 +624,7 @@ */ pl011_start_tx_pio(uap); - spin_unlock_irqrestore(&uap->port.lock, flags); + __BUILD_AVM_CONTEXT_FUNC(spin_unlock_irqrestore)(&uap->port.lock, flags); } /* @@ -1096,11 +1133,11 @@ if (jiffies_to_msecs(jiffies - dmarx->last_jiffies) > uap->dmarx.poll_timeout) { - spin_lock_irqsave(&uap->port.lock, flags); + __BUILD_AVM_CONTEXT_FUNC(spin_lock_irqsave)(&uap->port.lock, flags); pl011_dma_rx_stop(uap); uap->im |= UART011_RXIM; pl011_write(uap->im, uap, REG_IMSC); - spin_unlock_irqrestore(&uap->port.lock, flags); + __BUILD_AVM_CONTEXT_FUNC(spin_unlock_irqrestore)(&uap->port.lock, flags); uap->dmarx.running = false; dmaengine_terminate_all(rxchan); @@ -1319,6 +1356,9 @@ struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); + if (pl011_dectuart_is_busy(&pl011_dectuart)) + return; + if (!pl011_dma_tx_start(uap)) pl011_start_tx_pio(uap); } @@ -1483,7 +1523,7 @@ unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; int handled = 0; - spin_lock_irqsave(&uap->port.lock, flags); + __BUILD_AVM_CONTEXT_FUNC(spin_lock_irqsave)(&uap->port.lock, flags); status = pl011_read(uap, REG_RIS) & uap->im; if (status) { do { @@ -1513,7 +1553,7 @@ handled = 1; } - spin_unlock_irqrestore(&uap->port.lock, flags); + __BUILD_AVM_CONTEXT_FUNC(spin_unlock_irqrestore)(&uap->port.lock, flags); return IRQ_RETVAL(handled); } @@ -1585,17 +1625,17 @@ unsigned long flags; unsigned int lcr_h; - spin_lock_irqsave(&uap->port.lock, flags); + __BUILD_AVM_CONTEXT_FUNC(spin_lock_irqsave)(&uap->port.lock, flags); lcr_h = pl011_read(uap, REG_LCRH_TX); if (break_state == -1) lcr_h |= UART01x_LCRH_BRK; else lcr_h &= ~UART01x_LCRH_BRK; pl011_write(lcr_h, uap, REG_LCRH_TX); - spin_unlock_irqrestore(&uap->port.lock, flags); + __BUILD_AVM_CONTEXT_FUNC(spin_unlock_irqrestore)(&uap->port.lock, flags); } -#ifdef CONFIG_CONSOLE_POLL +#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_AVM_ENHANCED) static void pl011_quiesce_irqs(struct uart_port *port) { @@ -1651,7 +1691,7 @@ pl011_write(ch, uap, REG_DR); } -#endif /* CONFIG_CONSOLE_POLL */ +#endif /*--- #if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_AVM_ENHANCED) ---*/ static int pl011_hwinit(struct uart_port *port) { @@ -1761,6 +1801,15 @@ container_of(port, struct uart_amba_port, port); unsigned int cr; int retval; +#if defined(CONFIG_AVM_ENHANCED) + unsigned int reflink = atomic_inc_return(&uap->reflink); + + if (reflink > 1) { + pr_debug("%s add reflink %d\n", __func__, reflink); + return 0; + } + pr_debug("%s ttyAMA%d %d\n", __func__, port->line, reflink); +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ retval = pl011_hwinit(port); if (retval) @@ -1872,6 +1921,15 @@ { struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); +#if defined(CONFIG_AVM_ENHANCED) + unsigned int reflink = atomic_dec_return(&uap->reflink); + + if (reflink != 0) { + pr_debug("%s sub reflink %d\n", __func__, reflink); + return; + } + pr_debug("%s ttyAMA%d %d\n", __func__, port->line, reflink); +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ pl011_disable_interrupts(uap); @@ -2004,7 +2062,7 @@ if (uap->fifosize > 1) lcr_h |= UART01x_LCRH_FEN; - spin_lock_irqsave(&port->lock, flags); + __BUILD_AVM_CONTEXT_FUNC(spin_lock_irqsave)(&port->lock, flags); /* * Update the per-port timeout. @@ -2063,7 +2121,7 @@ pl011_write_lcr_h(uap, lcr_h); pl011_write(old_cr, uap, REG_CR); - spin_unlock_irqrestore(&port->lock, flags); + __BUILD_AVM_CONTEXT_FUNC(spin_unlock_irqrestore)(&port->lock, flags); } static void @@ -2081,10 +2139,10 @@ termios->c_cflag &= ~(CMSPAR | CRTSCTS); termios->c_cflag |= CS8 | CLOCAL; - spin_lock_irqsave(&port->lock, flags); + __BUILD_AVM_CONTEXT_FUNC(spin_lock_irqsave)(&port->lock, flags); uart_update_timeout(port, CS8, uap->fixed_baud); pl011_setup_status_masks(port, termios); - spin_unlock_irqrestore(&port->lock, flags); + __BUILD_AVM_CONTEXT_FUNC(spin_unlock_irqrestore)(&port->lock, flags); } static const char *pl011_type(struct uart_port *port) @@ -2212,11 +2270,12 @@ { struct uart_amba_port *uap = amba_ports[co->index]; unsigned int old_cr = 0, new_cr; +#if !defined(CONFIG_BCM_KF_PRINTK_INT_ENABLED) || !defined(CONFIG_BCM_PRINTK_INT_ENABLED) unsigned long flags; int locked = 1; - +#endif clk_enable(uap->clk); - +#if !defined(CONFIG_BCM_KF_PRINTK_INT_ENABLED) || !defined(CONFIG_BCM_PRINTK_INT_ENABLED) local_irq_save(flags); if (uap->port.sysrq) locked = 0; @@ -2224,7 +2283,7 @@ locked = spin_trylock(&uap->port.lock); else spin_lock(&uap->port.lock); - +#endif /* * First save the CR then disable the interrupts */ @@ -2247,11 +2306,11 @@ cpu_relax(); if (!uap->vendor->always_enabled) pl011_write(old_cr, uap, REG_CR); - +#if !defined(CONFIG_BCM_KF_PRINTK_INT_ENABLED) || !defined(CONFIG_BCM_PRINTK_INT_ENABLED) if (locked) spin_unlock(&uap->port.lock); local_irq_restore(flags); - +#endif clk_disable(uap->clk); } @@ -2591,6 +2650,17 @@ amba_ports[index] = uap; +#if defined(CONFIG_AVM_ENHANCED) + pr_info("%s: set dectuart_port to ttyAMA%d\n", __func__, index); + pl011_dectuart.port = &uap->port; + pl011_dectuart.pl011_dectuart_get_char = pl011_serial_dectuart_get_char; + pl011_dectuart.pl011_dectuart_put_char = pl011_serial_dectuart_put_char; + pl011_dectuart.pl011_dectuart_init = pl011_serial_dectuart_init; + pl011_dectuart.pl011_console_stop = pl011_serial_console_stop; + pl011_dectuart.pl011_console_start = pl011_serial_console_start; + pl011_dectuart.pl011_dectuart_exit = pl011_serial_dectuart_exit; +#endif + return 0; } @@ -2835,6 +2905,259 @@ amba_driver_unregister(&pl011_driver); } + +#if defined(CONFIG_AVM_ENHANCED) + +// #define AMBA_PL011_DUMP_REGISTER +#if defined(AMBA_PL011_DUMP_REGISTER) +/** + */ +struct _generic_bit_name { + unsigned int value; + const char *name; +}; + +#define FillField(a, b) {.value = a, .name = b} +static const struct _generic_bit_name cr_field[] = { + FillField(UART011_CR_CTSEN, "CTSEN "), /* CTS hardware flow control */ + FillField(UART011_CR_RTSEN, "RTSEN "), /* RTS hardware flow control */ + FillField(UART011_CR_OUT2, "OUT2 "), /* OUT2 */ + FillField(UART011_CR_OUT1, "OUT1 "), /* OUT1 */ + FillField(UART011_CR_RTS, "RTS "), /* RTS */ + FillField(UART011_CR_DTR, "DTR "), /* DTR */ + FillField(UART011_CR_RXE, "RXE "), /* receive enable */ + FillField(UART011_CR_TXE, "TXE "), /* transmit enable */ + FillField(UART011_CR_LBE, "LBE "), /* loopback enable */ + FillField(0, NULL), +}; + +static const struct _generic_bit_name imsc_field[] = { + FillField(UART011_OEIM, "OEIM "), /* overrun error interrupt mask */ + FillField(UART011_BEIM, "BEIM "), /* break error interrupt mask */ + FillField(UART011_PEIM, "PEIM "), /* parity error interrupt mask */ + FillField(UART011_FEIM, "FEIM "), /* framing error interrupt mask */ + FillField(UART011_RTIM, "RTIM "), /* receive timeout interrupt mask */ + FillField(UART011_TXIM, "TXIM "), /* transmit interrupt mask */ + FillField(UART011_RXIM, "RXIM "), /* receive interrupt mask */ + FillField(UART011_DSRMIM, "DSRMIM "), /* DSR interrupt mask */ + FillField(UART011_DCDMIM, "DCDMIM "), /* DCD interrupt mask */ + FillField(UART011_CTSMIM, "CTSMIM "), /* CTS interrupt mask */ + FillField(UART011_RIMIM, "RIMIM "), /* RI interrupt mask */ + FillField(UART01x_CR_IIRLP, "CR_IIRLP "), /* SIR low power mode */ + FillField(UART01x_CR_SIREN, "SIREN "), /* SIR enable */ + FillField(UART01x_CR_UARTEN, "UARTEN "), /* UART enable */ + FillField(0, NULL), +}; + +static const struct _generic_bit_name dmacr_field[] = { + FillField(UART011_DMAONERR, "DMANOERR "), /* disable dma on error */ + FillField(UART011_TXDMAE, "TXDMAE "), /* enable transmit dma */ + FillField(UART011_RXDMAE, "RXDMAE "), /* enable receive dma */ + FillField(0, NULL), +}; + +static const struct _generic_bit_name fr_field[] = { + FillField(UART011_FR_RI, "FR_RI "), + FillField(UART011_FR_TXFE, "FR_TXFE "), + FillField(UART011_FR_RXFF, "FR_RXFF "), + FillField(UART01x_FR_TXFF, "FR_TXFF "), + FillField(UART01x_FR_RXFE, "FR_RXFE "), + FillField(UART01x_FR_BUSY, "FR_BUSY "), + FillField(UART01x_FR_DCD, "FR_DCD "), + FillField(UART01x_FR_DSR, "FR_DSR "), + FillField(UART01x_FR_CTS, "FR_CTS "), + FillField(0, NULL), +}; + +/** + */ +static char *generic_string_from_bits(char *txt, unsigned int value, const struct _generic_bit_name *pgb) +{ + char *p = txt; + + txt[0] = 0; + while (pgb->name) { + if (pgb->value & value) + p += sprintf(p, "%s", pgb->name); + pgb++; + } + return txt; +} + +#endif/*--- #if defined(AMBA_PL011_DUMP_REGISTER) ---*/ + + +/** + */ +static void pl011_dump_register(const char *prefix __maybe_unused, struct uart_port *port __maybe_unused) +{ +#if defined(AMBA_PL011_DUMP_REGISTER) + char txt[256]; + struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); + + unsigned int imsc = readw(uap->port.membase + UART011_IMSC); + unsigned int dmacr = readw(uap->port.membase + UART011_DMACR); + unsigned int fr = readw(uap->port.membase + UART01x_FR); + unsigned int cr = readw(uap->port.membase + UART011_CR); + + pr_info("%s:\n", prefix); + pr_info("IM = %08x %s\n", imsc, generic_string_from_bits(txt, imsc, imsc_field)); + pr_info("DMACR = %08x %s\n", dmacr, generic_string_from_bits(txt, dmacr, dmacr_field)); + pr_info("FR = %08x %s\n", fr, generic_string_from_bits(txt, fr, fr_field)); + pr_info("CR = %08x %s\n", cr, generic_string_from_bits(txt, cr, cr_field)); +#endif/*--- #if defined(AMBA_PL011_DUMP_REGISTER) ---*/ +} + +/** + */ +static int pl011_serial_dectuart_get_char(void) +{ + struct _pl011_dectuart *pdectuart = &pl011_dectuart; + int c; + + if (!pdectuart->port) + return -ENXIO; + + c = pl011_get_poll_char(pdectuart->port); + if (c == NO_POLL_CHAR) + return -EAGAIN; + return c; +} +/** + */ +static void pl011_serial_dectuart_put_char(unsigned char ch) +{ + struct uart_port *port = pl011_dectuart.port; + + if (port == NULL) { + return; + } + if (pl011_dectuart_is_busy(&pl011_dectuart)) + pl011_put_poll_char(port, ch); +} + +/** + */ +static void pl011_polling_mode(struct _pl011_dectuart *pdectuart) +{ + struct uart_port *port = pdectuart->port; + struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); + unsigned long flags; + + if (port->irq && (pdectuart->irq_off == 0)) { + pdectuart->irq_off = 1; + disable_irq(port->irq); + } + pl011_dump_register("before stop", port); + +#ifdef CONFIG_DMA_ENGINE + del_timer(&uap->dmarx.timer); +#endif + __BUILD_AVM_CONTEXT_FUNC(spin_lock_irqsave)(&port->lock, flags); + if (pdectuart->save_im == 0) { + pdectuart->save_im = uap->im; + } + pl011_stop_tx(port); + pl011_stop_rx(port); + __BUILD_AVM_CONTEXT_FUNC(spin_unlock_irqrestore)(&port->lock, flags); + pl011_dump_register("after stop", port); +} + +/** + */ +static void pl011_irqdma_mode(struct _pl011_dectuart *pdectuart) +{ + struct uart_port *port = pdectuart->port; + struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); + unsigned long flags; + + __BUILD_AVM_CONTEXT_FUNC(spin_lock_irqsave) (&port->lock, flags); + pl011_dump_register("before start", port); + + if (pdectuart->save_im) { + /*--- fixup: not all irq-mask register-bits will restored with trigger-function below ---*/ + uap->im = pdectuart->save_im; + writew(uap->im, uap->port.membase + UART011_IMSC); + pdectuart->save_im = 0; + } + pl011_start_tx(port); + pl011_rx_chars(uap); + + if (port->irq && (pdectuart->irq_off)) { + pdectuart->irq_off = 0; + enable_irq(port->irq); + } + pl011_dump_register("after start", port); + __BUILD_AVM_CONTEXT_FUNC(spin_unlock_irqrestore)(&port->lock, flags); +} + +/** + * mode: != 0 Tx/Rx-Irq an + */ +static void pl011_serial_dectuart_init(unsigned int baud, int mode) +{ + struct _pl011_dectuart *pdectuart = &pl011_dectuart; + struct uart_port *port = pdectuart->port; + + if (port == NULL) { + return; + } + + pr_debug("%s: dect_baud=%u %s busy=%d\n", __func__, baud, + !mode ? "pollmode" : "not supported", + atomic_read(&pdectuart->busy)); + if (mode == 0) { + if ((atomic_inc_return(&pdectuart->busy) == 1) && + (pl011_dectuart_is_shared_uart(pdectuart) == 0)) { + pl011_startup(port); + } + pl011_polling_mode(pdectuart); + uart_set_options(port, NULL, baud, 0, 8, 0); + } +} +/** + */ +static void pl011_serial_console_stop(void) +{ + struct _pl011_dectuart *pdectuart = &pl011_dectuart; + struct uart_port *port = pdectuart->port; + + if (port) { + atomic_set(&pdectuart->busy, 1); + console_stop(port->cons); + } +} + +/** + */ +static void pl011_serial_console_start(void) +{ + struct _pl011_dectuart *pdectuart = &pl011_dectuart; + struct uart_port *port = pdectuart->port; + + if (port) { + atomic_set(&pdectuart->busy, 0); + uart_set_options(port, NULL, 115200, 0, 8, 0); + pl011_irqdma_mode(pdectuart); + console_start(port->cons); + } +} + +/** + */ +static void pl011_serial_dectuart_exit(void) +{ + struct _pl011_dectuart *pdectuart = &pl011_dectuart; + + if (atomic_xchg(&pdectuart->busy, 0)) { + pl011_irqdma_mode(pdectuart); + + pl011_shutdown(pdectuart->port); + } + pr_debug("%s\n", __func__); +} +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ + /* * While this can be a module, if builtin it's most likely the console * So let's leave module_exit but move module_init to an earlier place