diff --git a/arch/mips/src/pic32mz/Kconfig b/arch/mips/src/pic32mz/Kconfig index 23eb3b5904c..11744da9e33 100644 --- a/arch/mips/src/pic32mz/Kconfig +++ b/arch/mips/src/pic32mz/Kconfig @@ -261,6 +261,27 @@ config PIC32MZ_UART6 select UART6_SERIALDRIVER select ARCH_HAVE_SERIAL_TERMIOS +config PIC32MZ_UART_BREAKS + bool "Add TIOxSBRK to support sending Breaks" + depends on PIC32MZ_UART1 || PIC32MZ_UART2 || PIC32MZ_UART3 || PIC32MZ_UART4 || PIC32MZ_UART5 || PIC32MZ_UART6 + default n + ---help--- + Add TIOCxBRK routines to send a line break per the PIC32MZ manual. + The break consists of a start bit, 12 zero bits, and a stop bit. + This is not a BSD compatible break. + +config PIC32MZ_SERIALBRK_BSDCOMPAT + bool "Use GPIO to send Break" + depends on PIC32MZ_UART_BREAKS + default n + ---help--- + Enable using GPIO on the TX pin to send a BSD compatible break: + TIOCSBRK will start the break and TIOCCBRK will end the break. The + current PIC32MZ UARTS have no way to leave the break on (TX=LOW) + because software starts the break and then the hardware + automatically clears the break. This makes it difficult to send a + long break. + config PIC32MZ_ADC bool "ADC1" default n diff --git a/arch/mips/src/pic32mz/hardware/pic32mz_pps.h b/arch/mips/src/pic32mz/hardware/pic32mz_pps.h index cf99ac01078..a1de18429a4 100644 --- a/arch/mips/src/pic32mz/hardware/pic32mz_pps.h +++ b/arch/mips/src/pic32mz/hardware/pic32mz_pps.h @@ -76,6 +76,74 @@ #define __PPS_OUTPUT_REGVAL(a,b) ((uint32_t)(a)) #define PPS_OUTPUT_REGVAL(a) __PPS_OUTPUT_REGVAL(a) +/* The following macro converts from a peripheral output pin mapping to the + * corresponding GPIO port and pin. This allows drivers to do things like + * temporarily change a pin's configuration from peripheral to GPIO to + * override some hardware behavior. Having this macro available to driver + * code relieves the board's include/board.h from redundantly defining both + * the PPS mapping and the GPIO port/pin information. + * + * NOTE: This is written in the same odd macro forms as above for the same + * reason. + */ +#define __PPS_OUTPUT_REGADDR_TO_GPIO(a,b) ((uint32_t)( \ + ((b) == PIC32MZ_RPA14R) ? (GPIO_PORTA | GPIO_PIN14) : \ + ((b) == PIC32MZ_RPA15R) ? (GPIO_PORTA | GPIO_PIN15) : \ + ((b) == PIC32MZ_RPB0R) ? (GPIO_PORTB | GPIO_PIN0) : \ + ((b) == PIC32MZ_RPB10R) ? (GPIO_PORTB | GPIO_PIN10) : \ + ((b) == PIC32MZ_RPB14R) ? (GPIO_PORTB | GPIO_PIN14) : \ + ((b) == PIC32MZ_RPB15R) ? (GPIO_PORTB | GPIO_PIN15) : \ + ((b) == PIC32MZ_RPB1R) ? (GPIO_PORTB | GPIO_PIN1) : \ + ((b) == PIC32MZ_RPB2R) ? (GPIO_PORTB | GPIO_PIN2) : \ + ((b) == PIC32MZ_RPB3R) ? (GPIO_PORTB | GPIO_PIN3) : \ + ((b) == PIC32MZ_RPB5R) ? (GPIO_PORTB | GPIO_PIN5) : \ + ((b) == PIC32MZ_RPB6R) ? (GPIO_PORTB | GPIO_PIN6) : \ + ((b) == PIC32MZ_RPB7R) ? (GPIO_PORTB | GPIO_PIN7) : \ + ((b) == PIC32MZ_RPB8R) ? (GPIO_PORTB | GPIO_PIN8) : \ + ((b) == PIC32MZ_RPB9R) ? (GPIO_PORTB | GPIO_PIN9) : \ + ((b) == PIC32MZ_RPC13R) ? (GPIO_PORTC | GPIO_PIN13) : \ + ((b) == PIC32MZ_RPC14R) ? (GPIO_PORTC | GPIO_PIN14) : \ + ((b) == PIC32MZ_RPC1R) ? (GPIO_PORTC | GPIO_PIN1) : \ + ((b) == PIC32MZ_RPC2R) ? (GPIO_PORTC | GPIO_PIN2) : \ + ((b) == PIC32MZ_RPC3R) ? (GPIO_PORTC | GPIO_PIN3) : \ + ((b) == PIC32MZ_RPC4R) ? (GPIO_PORTC | GPIO_PIN4) : \ + ((b) == PIC32MZ_RPD0R) ? (GPIO_PORTD | GPIO_PIN0) : \ + ((b) == PIC32MZ_RPD10R) ? (GPIO_PORTD | GPIO_PIN10) : \ + ((b) == PIC32MZ_RPD11R) ? (GPIO_PORTD | GPIO_PIN11) : \ + ((b) == PIC32MZ_RPD12R) ? (GPIO_PORTD | GPIO_PIN12) : \ + ((b) == PIC32MZ_RPD14R) ? (GPIO_PORTD | GPIO_PIN14) : \ + ((b) == PIC32MZ_RPD15R) ? (GPIO_PORTD | GPIO_PIN15) : \ + ((b) == PIC32MZ_RPD1R) ? (GPIO_PORTD | GPIO_PIN1) : \ + ((b) == PIC32MZ_RPD2R) ? (GPIO_PORTD | GPIO_PIN2) : \ + ((b) == PIC32MZ_RPD3R) ? (GPIO_PORTD | GPIO_PIN3) : \ + ((b) == PIC32MZ_RPD4R) ? (GPIO_PORTD | GPIO_PIN4) : \ + ((b) == PIC32MZ_RPD5R) ? (GPIO_PORTD | GPIO_PIN5) : \ + ((b) == PIC32MZ_RPD6R) ? (GPIO_PORTD | GPIO_PIN6) : \ + ((b) == PIC32MZ_RPD7R) ? (GPIO_PORTD | GPIO_PIN7) : \ + ((b) == PIC32MZ_RPD9R) ? (GPIO_PORTD | GPIO_PIN9) : \ + ((b) == PIC32MZ_RPE3R) ? (GPIO_PORTE | GPIO_PIN3) : \ + ((b) == PIC32MZ_RPE5R) ? (GPIO_PORTE | GPIO_PIN5) : \ + ((b) == PIC32MZ_RPE8R) ? (GPIO_PORTE | GPIO_PIN8) : \ + ((b) == PIC32MZ_RPE9R) ? (GPIO_PORTE | GPIO_PIN9) : \ + ((b) == PIC32MZ_RPF0R) ? (GPIO_PORTF | GPIO_PIN0) : \ + ((b) == PIC32MZ_RPF12R) ? (GPIO_PORTF | GPIO_PIN12) : \ + ((b) == PIC32MZ_RPF13R) ? (GPIO_PORTF | GPIO_PIN13) : \ + ((b) == PIC32MZ_RPF1R) ? (GPIO_PORTF | GPIO_PIN1) : \ + ((b) == PIC32MZ_RPF2R) ? (GPIO_PORTF | GPIO_PIN2) : \ + ((b) == PIC32MZ_RPF3R) ? (GPIO_PORTF | GPIO_PIN3) : \ + ((b) == PIC32MZ_RPF4R) ? (GPIO_PORTF | GPIO_PIN4) : \ + ((b) == PIC32MZ_RPF5R) ? (GPIO_PORTF | GPIO_PIN5) : \ + ((b) == PIC32MZ_RPF8R) ? (GPIO_PORTF | GPIO_PIN8) : \ + ((b) == PIC32MZ_RPG0R) ? (GPIO_PORTG | GPIO_PIN0) : \ + ((b) == PIC32MZ_RPG1R) ? (GPIO_PORTG | GPIO_PIN1) : \ + ((b) == PIC32MZ_RPG6R) ? (GPIO_PORTG | GPIO_PIN6) : \ + ((b) == PIC32MZ_RPG7R) ? (GPIO_PORTG | GPIO_PIN7) : \ + ((b) == PIC32MZ_RPG8R) ? (GPIO_PORTG | GPIO_PIN8) : \ + ((b) == PIC32MZ_RPG9R) ? (GPIO_PORTG | GPIO_PIN9) : \ + 0 \ + )) +#define PPS_OUTPUT_REGADDR_TO_GPIO(a) __PPS_OUTPUT_REGADDR_TO_GPIO(a) + /**************************************************************************** * Public Types ****************************************************************************/ diff --git a/arch/mips/src/pic32mz/pic32mz_serial.c b/arch/mips/src/pic32mz/pic32mz_serial.c index 47ca3ab225f..d1db65104e3 100644 --- a/arch/mips/src/pic32mz/pic32mz_serial.c +++ b/arch/mips/src/pic32mz/pic32mz_serial.c @@ -49,6 +49,11 @@ #include "hardware/pic32mz_uart.h" #include "pic32mz_lowconsole.h" +#ifdef CONFIG_PIC32MZ_SERIALBRK_BSDCOMPAT +# include "pic32mz_gpio.h" +# include "hardware/pic32mz_pps.h" +#endif + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -232,15 +237,24 @@ struct up_dev_s { - uintptr_t uartbase; /* Base address of UART registers */ - uint32_t baud; /* Configured baud */ - uint8_t irqe; /* Error IRQ associated with this UART (for enable) */ - uint8_t irqrx; /* RX IRQ associated with this UART (for enable) */ - uint8_t irqtx; /* TX IRQ associated with this UART (for enable) */ - uint8_t im; /* Interrupt mask state */ - uint8_t parity; /* 0=none, 1=odd, 2=even */ - uint8_t bits; /* Number of bits (5, 6, 7 or 8) */ - bool stopbits2; /* true: Configure with 2 stop bits instead of 1 */ + uintptr_t uartbase; /* Base address of UART registers */ + uint32_t baud; /* Configured baud */ + uint8_t irqe; /* Error IRQ associated with this UART (for enable) */ + uint8_t irqrx; /* RX IRQ associated with this UART (for enable) */ + uint8_t irqtx; /* TX IRQ associated with this UART (for enable) */ + uint8_t im; /* Interrupt mask state */ + uint8_t parity; /* 0=none, 1=odd, 2=even */ + uint8_t bits; /* Number of bits (5, 6, 7 or 8) */ + bool stopbits2; /* true: Configure with 2 stop bits instead of 1 */ + +#ifdef CONFIG_PIC32MZ_UART_BREAKS + bool brk; /* true: Line break in progress */ +# ifdef CONFIG_PIC32MZ_SERIALBRK_BSDCOMPAT + const uint32_t tx_gpio; /* GPIO config to put TX pin in GPIO mode */ + const uintptr_t tx_pps_reg; /* PPS register to toggle UART/GPIO modes */ + const uint8_t tx_pps_val; /* PPS value to restore pin to UART mode */ +# endif +#endif }; /**************************************************************************** @@ -334,6 +348,16 @@ static struct up_dev_s g_uart1priv = .parity = CONFIG_UART1_PARITY, .bits = CONFIG_UART1_BITS, .stopbits2 = CONFIG_UART1_2STOP, + +#ifdef CONFIG_PIC32MZ_UART_BREAKS + .brk = false, +# ifdef CONFIG_PIC32MZ_SERIALBRK_BSDCOMPAT + .tx_gpio = PPS_OUTPUT_REGADDR_TO_GPIO(BOARD_U1TX_PPS) + | GPIO_OUTPUT | GPIO_VALUE_ZERO, + .tx_pps_reg = PPS_OUTPUT_REGADDR(BOARD_U1TX_PPS), + .tx_pps_val = PPS_OUTPUT_REGVAL(BOARD_U1TX_PPS), +# endif +#endif }; static uart_dev_t g_uart1port = @@ -366,6 +390,16 @@ static struct up_dev_s g_uart2priv = .parity = CONFIG_UART2_PARITY, .bits = CONFIG_UART2_BITS, .stopbits2 = CONFIG_UART2_2STOP, + +#ifdef CONFIG_PIC32MZ_UART_BREAKS + .brk = false, +# ifdef CONFIG_PIC32MZ_SERIALBRK_BSDCOMPAT + .tx_gpio = PPS_OUTPUT_REGADDR_TO_GPIO(BOARD_U2TX_PPS) + | GPIO_OUTPUT | GPIO_VALUE_ZERO, + .tx_pps_reg = PPS_OUTPUT_REGADDR(BOARD_U2TX_PPS), + .tx_pps_val = PPS_OUTPUT_REGVAL(BOARD_U2TX_PPS), +# endif +#endif }; static uart_dev_t g_uart2port = @@ -398,6 +432,16 @@ static struct up_dev_s g_uart3priv = .parity = CONFIG_UART3_PARITY, .bits = CONFIG_UART3_BITS, .stopbits2 = CONFIG_UART3_2STOP, + +#ifdef CONFIG_PIC32MZ_UART_BREAKS + .brk = false, +# ifdef CONFIG_PIC32MZ_SERIALBRK_BSDCOMPAT + .tx_gpio = PPS_OUTPUT_REGADDR_TO_GPIO(BOARD_U3TX_PPS) + | GPIO_OUTPUT | GPIO_VALUE_ZERO, + .tx_pps_reg = PPS_OUTPUT_REGADDR(BOARD_U3TX_PPS), + .tx_pps_val = PPS_OUTPUT_REGVAL(BOARD_U3TX_PPS), +# endif +#endif }; static uart_dev_t g_uart3port = @@ -430,6 +474,16 @@ static struct up_dev_s g_uart4priv = .parity = CONFIG_UART4_PARITY, .bits = CONFIG_UART4_BITS, .stopbits2 = CONFIG_UART4_2STOP, + +#ifdef CONFIG_PIC32MZ_UART_BREAKS + .brk = false, +# ifdef CONFIG_PIC32MZ_SERIALBRK_BSDCOMPAT + .tx_gpio = PPS_OUTPUT_REGADDR_TO_GPIO(BOARD_U4TX_PPS) + | GPIO_OUTPUT | GPIO_VALUE_ZERO, + .tx_pps_reg = PPS_OUTPUT_REGADDR(BOARD_U4TX_PPS), + .tx_pps_val = PPS_OUTPUT_REGVAL(BOARD_U4TX_PPS), +# endif +#endif }; static uart_dev_t g_uart4port = @@ -462,6 +516,16 @@ static struct up_dev_s g_uart5priv = .parity = CONFIG_UART5_PARITY, .bits = CONFIG_UART5_BITS, .stopbits2 = CONFIG_UART5_2STOP, + +#ifdef CONFIG_PIC32MZ_UART_BREAKS + .brk = false, +# ifdef CONFIG_PIC32MZ_SERIALBRK_BSDCOMPAT + .tx_gpio = PPS_OUTPUT_REGADDR_TO_GPIO(BOARD_U5TX_PPS) + | GPIO_OUTPUT | GPIO_VALUE_ZERO, + .tx_pps_reg = PPS_OUTPUT_REGADDR(BOARD_U5TX_PPS), + .tx_pps_val = PPS_OUTPUT_REGVAL(BOARD_U5TX_PPS), +# endif +#endif }; static uart_dev_t g_uart5port = @@ -494,6 +558,16 @@ static struct up_dev_s g_uart6priv = .parity = CONFIG_UART6_PARITY, .bits = CONFIG_UART6_BITS, .stopbits2 = CONFIG_UART6_2STOP, + +#ifdef CONFIG_PIC32MZ_UART_BREAKS + .brk = false, +# ifdef CONFIG_PIC32MZ_SERIALBRK_BSDCOMPAT + .tx_gpio = PPS_OUTPUT_REGADDR_TO_GPIO(BOARD_U6TX_PPS) + | GPIO_OUTPUT | GPIO_VALUE_ZERO, + .tx_pps_reg = PPS_OUTPUT_REGADDR(BOARD_U6TX_PPS), + .tx_pps_val = PPS_OUTPUT_REGVAL(BOARD_U6TX_PPS), +# endif +#endif }; static uart_dev_t g_uart6port = @@ -811,11 +885,12 @@ static int up_interrupt(int irq, void *context, void *arg) static int up_ioctl(struct file *filep, int cmd, unsigned long arg) { -#if defined(CONFIG_SERIAL_TIOCSERGSTRUCT) || defined(CONFIG_SERIAL_TERMIOS) +#if defined(CONFIG_SERIAL_TERMIOS) || defined(CONFIG_SERIAL_TIOCSERGSTRUCT) \ + || defined(CONFIG_PIC32MZ_UART_BREAKS) struct inode *inode = filep->f_inode; struct uart_dev_s *dev = inode->i_private; #endif -#if defined(CONFIG_SERIAL_TERMIOS) +#if defined(CONFIG_SERIAL_TERMIOS) || defined(CONFIG_PIC32MZ_UART_BREAKS) struct up_dev_s *priv = (struct up_dev_s *)dev->priv; #endif int ret = OK; @@ -945,6 +1020,101 @@ static int up_ioctl(struct file *filep, int cmd, unsigned long arg) break; #endif /* CONFIG_SERIAL_TERMIOS */ +#ifdef CONFIG_PIC32MZ_UART_BREAKS +# ifdef CONFIG_PIC32MZ_SERIALBRK_BSDCOMPAT + case TIOCSBRK: /* BSD compatibility: Turn break on, unconditionally */ + { + irqstate_t flags; + + flags = enter_critical_section(); + + /* Disable any further TX activity */ + + priv->brk = true; + up_txint(dev, false); + + /* Configure TX as a GPIO output pin driven low to send break */ + + pic32mz_configgpio(priv->tx_gpio); + putreg32(0, priv->tx_pps_reg); + + leave_critical_section(flags); + } + break; + + case TIOCCBRK: /* BSD compatibility: Turn break off, unconditionally */ + { + irqstate_t flags; + + flags = enter_critical_section(); + + /* Configure TX back to UART */ + + putreg32(priv->tx_pps_val, priv->tx_pps_reg); + + /* Enable further tx activity */ + + priv->brk = false; + up_txint(dev, true); + + leave_critical_section(flags); + } + break; +# else + case TIOCSBRK: /* No BSD compatibility: Turn break on for 12 bit times */ + { + uint32_t regval; + irqstate_t flags; + + flags = enter_critical_section(); + + /* Disable any further TX activity */ + + priv->brk = true; + up_txint(dev, false); + + /* Enable break transmission */ + + regval = up_serialin(priv, PIC32MZ_UART_STA_OFFSET); + regval |= UART_STA_UTXBRK; + up_serialout(priv, PIC32MZ_UART_STA_OFFSET, regval); + + /* A dummy write to TXREG is needed to start sending the break. The + * caller should ensure that there are no pending transmit data in + * the UART FIFO before executing this IOCTL or the break will + * consume a byte of that data instead of the dummy write. + */ + + up_send(dev, 0); + + leave_critical_section(flags); + } + break; + + case TIOCCBRK: /* No BSD compatibility: May turn off break too soon */ + { + irqstate_t flags; + + flags = enter_critical_section(); + + /* Enable further tx activity. We do not clear the UTXBRK bit + * because hardware does it automatically after transmitting the + * break. In fact, the PIC32MZ manual, rev G, section 21.5.4, says: + * "If the user application clears the UTXBRK bit prior to sequence + * completion, unexpected module behavior can result." It should be + * safe to re-enable transmit here because the hardware specifically + * allows to queue up the next character to follow the break. + */ + + priv->brk = false; + up_txint(dev, true); + + leave_critical_section(flags); + } + break; +# endif +#endif + default: ret = -ENOTTY; break; @@ -1075,6 +1245,16 @@ static void up_txint(struct uart_dev_s *dev, bool enable) /* Enable the TX interrupt */ #ifndef CONFIG_SUPPRESS_SERIAL_INTS +# ifdef CONFIG_PIC32MZ_UART_BREAKS + /* Do not enable TX interrupt if line break in progress */ + + if (priv->brk) + { + leave_critical_section(flags); + return; + } +# endif + up_enable_irq(priv->irqtx); ENABLE_TX(im);