/* * Copyright 2001-2006 by egnite Software GmbH * Copyright 2009 by egnite GmbH * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * For additional information see http://www.ethernut.de/ */ /* * $Id: usartat91.c 5472 2013-12-06 00:16:28Z olereinhardt $ * * Revision 1.01 2009/09/20 ulrichprinz * Added support for using DBGU as limited standard USART. */ #include #ifdef UART_USES_NPL #include #endif #include #include #include #include #include /* ** Initial USART settings. */ #ifndef UART_INIT_BAUDRATE #define UART_INIT_BAUDRATE 115200 #endif /* ** Add empty placeholders for undefined pin configurations. */ #ifndef UART_RXTX_PINS_ENABLE #define UART_RXTX_PINS_ENABLE() #endif #ifndef UART_HDX_PIN_ENABLE #define UART_HDX_PIN_ENABLE() #endif #ifndef UART_HDX_RX #define UART_HDX_RX() #endif #ifndef UART_HDX_TX #define UART_HDX_TX() #endif #ifndef UART_RTS_PIN_ENABLE #define UART_RTS_PIN_ENABLE() #endif #ifndef UART_RTS_ON #define UART_RTS_ON() #endif #ifndef UART_RTS_OFF #define UART_RTS_OFF() #endif #ifndef UART_CTS_PIN_ENABLE #define UART_CTS_PIN_ENABLE() #endif #ifndef UART_CTS_IS_ON #define UART_CTS_IS_ON() (1) #endif #ifndef UART_MODEM_PINS_ENABLE #define UART_MODEM_PINS_ENABLE() #endif /* ** GPIO controlled half duplex pin. */ #if defined(UART_HDX_BIT) && defined(UART_HDX_PIO_ID) #undef UART_HDX_RX #undef UART_HDX_TX #undef UART_HDX_PIN_ENABLE #undef GPIO_ID #define GPIO_ID UART_HDX_PIO_ID #include static INLINE void UART_HDX_PIN_ENABLE(void) { GPIO_ENABLE(UART_HDX_BIT); GPIO_OUTPUT(UART_HDX_BIT); } #ifdef UART_HDX_FLIP_BIT static INLINE void UART_HDX_RX(void) { GPIO_SET_HI(UART_HDX_BIT); } static INLINE void UART_HDX_TX(void) { GPIO_SET_LO(UART_HDX_BIT); } #else static INLINE void UART_HDX_RX(void) { GPIO_SET_LO(UART_HDX_BIT); } static INLINE void UART_HDX_TX(void) { GPIO_SET_HI(UART_HDX_BIT); } #endif #elif defined(UART_USES_NPL) #undef UART_HDX_RX #undef UART_HDX_TX #if US_ID == US1_ID #define UART_HDX_RX() if (inr(NPL_RSCR) & NPL_RSUS1P) sbi(NPL_RSCR, NPL_RSRTS_BIT) #define UART_HDX_TX() if (inr(NPL_RSCR) & NPL_RSUS1P) cbi(NPL_RSCR, NPL_RSRTS_BIT) #else #define UART_HDX_RX() if ((inr(NPL_RSCR) & NPL_RSUS1P) == 0) sbi(NPL_RSCR, NPL_RSRTS_BIT) #define UART_HDX_TX() if ((inr(NPL_RSCR) & NPL_RSUS1P) == 0) cbi(NPL_RSCR, NPL_RSRTS_BIT) #endif #endif /* UART_HDX_BIT */ /* ** GPIO controlled RTS pin. */ #if defined(UART_RTS_BIT) && defined(UART_RTS_PIO_ID) #undef UART_RTS_ON #undef UART_RTS_OFF #undef UART_RTS_PIN_ENABLE #undef GPIO_ID #define GPIO_ID UART_RTS_PIO_ID #include static INLINE void UART_RTS_PIN_ENABLE(void) { \ GPIO_ENABLE(UART_RTS_BIT); \ GPIO_OUTPUT(UART_RTS_BIT); \ } #ifdef UART_RTS_FLIP_BIT static INLINE void UART_RTS_ON(void) { GPIO_SET_HI(UART_RTS_BIT); } static INLINE void UART_RTS_OFF(void) { GPIO_SET_LO(UART_RTS_BIT); } #else static INLINE void UART_RTS_ON(void) { GPIO_SET_LO(UART_RTS_BIT); } static INLINE void UART_RTS_OFF(void) { GPIO_SET_HI(UART_RTS_BIT); } #endif #elif defined(UART_USES_NPL) #undef UART_RTS_ON #undef UART_RTS_OFF #if US_ID == US1_ID #define UART_RTS_ON() if (inr(NPL_RSCR) & NPL_RSUS1P) sbi(NPL_RSCR, NPL_RSRTS_BIT) #define UART_RTS_OFF() if (inr(NPL_RSCR) & NPL_RSUS1P) cbi(NPL_RSCR, NPL_RSRTS_BIT) #else #define UART_RTS_ON() if ((inr(NPL_RSCR) & NPL_RSUS1P) == 0) sbi(NPL_RSCR, NPL_RSRTS_BIT) #define UART_RTS_OFF() if ((inr(NPL_RSCR) & NPL_RSUS1P) == 0) cbi(NPL_RSCR, NPL_RSRTS_BIT) #endif #endif /* ** GPIO monitored CTS pin. */ #if defined(UART_CTS_BIT) && defined(UART_CTS_PIO_ID) #undef UART_CTS_IS_ON #undef UART_CTS_PIN_ENABLE #undef GPIO_ID #define GPIO_ID UART_CTS_PIO_ID #include #ifdef UART_CTS_FLIP_BIT static INLINE int UART_CTS_IS_ON(void) { return GPIO_GET(UART_CTS_BIT); } #else static INLINE int UART_CTS_IS_ON(void) { return !GPIO_GET(UART_CTS_BIT); } #endif static INLINE void UART_CTS_PIN_ENABLE(void) { \ GPIO_ENABLE(UART_CTS_BIT); \ GPIO_INPUT(UART_CTS_BIT); \ GPIO_PULLUP_ON(UART_CTS_BIT); \ } #elif defined(UART_USES_NPL) #undef UART_CTS_IS_ON static INLINE int UART_CTS_IS_ON(void) { outw(NPL_SCR, NPL_RSCTS); return bit_is_set(NPL_SLR, NPL_RSCTS_BIT); } #endif /* ** Determine if we use built-in hardware handshake. */ #if !defined(USE_BUILT_IN_HARDWARE_HANDSHAKE) #if defined(UART_HARDWARE_HANDSHAKE) && \ defined(US_MODE_HWHANDSHAKE) && \ !defined(UART_RTS_BIT) && \ !defined(UART_CTS_BIT) && \ !defined(UART_USES_NPL) #define USE_BUILT_IN_HARDWARE_HANDSHAKE 1 #else #define USE_BUILT_IN_HARDWARE_HANDSHAKE 0 #endif #endif /* ** Determine if we use built-in half duplex mode. */ #if !defined(USE_BUILT_IN_HALF_DUPLEX) #if defined(UART_HARDWARE_HANDSHAKE) && !defined(UART_HDX_BIT) && !defined(UART_USES_NPL) #define USE_BUILT_IN_HALF_DUPLEX 1 #else #define USE_BUILT_IN_HALF_DUPLEX 0 #endif #endif /* Some function renaming for IRQ handling on uarts * that do not have a separate IRQ instance but a * merged one with others like DBU on SYS-IRQ or * external uarts on GPIO-IRQs */ #if (US_ID==SYSC_ID) #define NutUartIrqRegister NutRegisterSysIrqHandler #define NutUartIrqEnable NutSysIrqEnable #define NutUartIrqDisable NutSysIrqDisable #else #define NutUartIrqRegister NutRegisterIrqHandler #define NutUartIrqEnable NutIrqEnable #define NutUartIrqDisable NutIrqDisable #endif /*! * \addtogroup xgNutArchArmAt91Us */ /*@{*/ /* \brief ASCII code for software flow control, starts transmitter. */ #define ASCII_XON 0x11 /* \brief ASCII code for software flow control, stops transmitter. */ #define ASCII_XOFF 0x13 /* \brief XON transmit pending flag. */ #define XON_PENDING 0x10 /* \brief XOFF transmit pending flag. */ #define XOFF_PENDING 0x20 /* \brief XOFF sent flag. */ #define XOFF_SENT 0x40 /* \brief XOFF received flag. */ #define XOFF_RCVD 0x80 /*! * \brief Receiver error flags. */ static unsigned int rx_errors; /*! * \brief Enables software flow control if not equal zero. */ static uint_fast8_t flow_control; /*! * \brief Transmit address frame, if not zero. */ static uint_fast8_t tx_aframe; /*! * \brief Half duplex control flag. * * Set to 1 if half duplex is enabled. Contains zero otherwise. */ static uint_fast8_t hdx_control; /*! * \brief Enables RTS control if not equal zero. * * This variable exists only if the hardware configuration defines a * port bit to control the RTS signal. */ static uint_fast8_t rts_control; /*! * \brief Enables CTS sense if not equal zero. * * This variable exists only if the hardware configuration defines a * port bit to sense the CTS signal. */ static uint_fast8_t cts_sense; #if defined(UART_CTS_BIT) || defined(UART_USES_NPL) /*! * \brief USARTn CTS sense interrupt handler. * * This interrupt routine is called when the CTS line level is low. * Typical line drivers negate the signal, thus driving our port * low when CTS is active. * * This routine exists only if the hardware configuration defines a * port bit to sense the CTS signal. */ static void At91UsartCts(void *arg) { /* Disable CTS sense interrupt. */ #if defined(UART_USES_NPL) NplIrqDisable(&sig_RSCTS); /* Enable transmit interrupt. */ outr(USARTn_BASE + US_IER_OFF, US_TXRDY); #else if (UART_CTS_IS_ON()) { GpioIrqDisable(&UART_CTS_SIGNAL, UART_CTS_BIT); /* Enable transmit interrupt. */ outr(USARTn_BASE + US_IER_OFF, US_TXRDY); } #endif } #endif #if USE_BUILT_IN_HALF_DUPLEX == 0 /* * \brief USARTn transmitter empty interrupt handler. * * Used with half duplex communication to switch from tranmit to receive * mode after the last character has been transmitted. * * \param arg Pointer to the transmitter ring buffer. */ static void At91UsartTxEmpty(RINGBUF *rbf) { /* * Check if half duplex mode has been enabled and if all characters * had been sent out. */ if (hdx_control && (rbf->rbf_cnt == 0)) { /* * Switch to receiver mode: * Enable USART receive, disable transmit. * Disable TX-Empty IRQ */ outr(USARTn_BASE + US_CR_OFF, US_RXEN | US_TXDIS); #if defined(UART_HDX_BIT) || defined(UART_USES_NPL) UART_HDX_RX(); #endif } } #endif /* * \brief USARTn transmitter ready interrupt handler. * * \param rbf Pointer to the transmitter ring buffer. */ static void At91UsartTxReady(RINGBUF *rbf) { register uint8_t *cp = rbf->rbf_tail; /* * Process pending software flow controls first. */ if (flow_control & (XON_PENDING | XOFF_PENDING)) { if (flow_control & XON_PENDING) { outr(USARTn_BASE + US_THR_OFF, ASCII_XOFF); flow_control |= XOFF_SENT; } else { outr(USARTn_BASE + US_THR_OFF, ASCII_XON); flow_control &= ~XOFF_SENT; } flow_control &= ~(XON_PENDING | XOFF_PENDING); return; } if (flow_control & XOFF_RCVD) { /* * If XOFF has been received, we disable the transmit interrupts * and return without sending anything. */ outr(USARTn_BASE + US_IDR_OFF, US_TXRDY); return; } /* * Check if we have more bytes to transmit. */ if (rbf->rbf_cnt) { #if defined(UART_CTS_BIT) || defined(UART_USES_NPL) /* * If CTS has been disabled, we disable the transmit interrupts, * enable CTS interrupts and return without sending anything. */ if (cts_sense && !UART_CTS_IS_ON()) { outr(USARTn_BASE + US_IDR_OFF, US_TXRDY); #if defined(UART_CTS_BIT) GpioIrqEnable(&UART_CTS_SIGNAL, UART_CTS_BIT); #else NplIrqEnable(&sig_RSCTS); #endif return; } #endif /* * Send address in multidrop mode. */ if (tx_aframe) { outr(USARTn_BASE + US_CR_OFF, US_SENDA); } /* Start transmission of the next character. */ outr(USARTn_BASE + US_THR_OFF, *cp); /* Decrement the number of available bytes in the buffer. */ rbf->rbf_cnt--; /* Wrap around the buffer pointer if we reached its end. */ if (++cp == rbf->rbf_last) { cp = rbf->rbf_start; } rbf->rbf_tail = cp; #if USE_BUILT_IN_HALF_DUPLEX == 0 /* * If software half duplex enabled, we need TX-Empty IRQ for * detection if last bit of last byte transmission is finished. */ if( hdx_control && rbf->rbf_cnt == 0) { outr(USARTn_BASE + US_IER_OFF, US_TXEMPTY); } #endif /* Send an event if we reached the low watermark. */ if (rbf->rbf_cnt == rbf->rbf_lwm) { NutEventPostFromIrq(&rbf->rbf_que); NutSelectWakeupFromIrq(rbf->wq_list, WQ_FLAG_WRITE); } } /* * Nothing left to transmit, disable interrupt. */ else { /* Disable transmit interrupts. */ outr(USARTn_BASE + US_IDR_OFF, US_TXRDY); /* Send an event to inform the upper level. */ NutEventPostFromIrq(&rbf->rbf_que); NutSelectWakeupFromIrq(rbf->wq_list, WQ_FLAG_WRITE); } } /* * \brief USARTn receiver ready interrupt handler. * * * \param rbf Pointer to the receiver ring buffer. */ static void At91UsartRxReady(RINGBUF *rbf) { register size_t cnt; register uint8_t ch; /* * We read the received character as early as possible to avoid overflows * caused by interrupt latency. */ ch = inb(USARTn_BASE + US_RHR_OFF); /* Collect receiver errors. */ rx_errors |= inr(USARTn_BASE + US_CSR_OFF) & (US_OVRE | US_FRAME | US_PARE); /* * Handle software handshake. We have to do this before checking the * buffer, because flow control must work in write-only mode, where * there is no receive buffer. */ if (flow_control) { /* XOFF character disables transmit interrupts. */ if (ch == ASCII_XOFF) { outr(USARTn_BASE + US_IDR_OFF, US_TXRDY); flow_control |= XOFF_RCVD; return; } /* XON enables transmit interrupts. */ else if (ch == ASCII_XON) { outr(USARTn_BASE + US_IER_OFF, US_TXRDY); flow_control &= ~XOFF_RCVD; return; } } /* * Check buffer overflow. */ cnt = rbf->rbf_cnt; if (cnt >= rbf->rbf_siz) { rx_errors |= US_OVRE; return; } /* Wake up waiting threads if this is the first byte in the buffer. */ if (cnt++ == 0){ NutEventPostFromIrq(&rbf->rbf_que); NutSelectWakeupFromIrq(rbf->wq_list, WQ_FLAG_READ); } /* * Check the high watermark for software handshake. If the number of * buffered bytes is equal or above this mark, then send XOFF. */ else if (flow_control) { if(cnt >= rbf->rbf_hwm) { if((flow_control & XOFF_SENT) == 0) { if (inr(USARTn_BASE + US_CSR_OFF) & US_TXRDY) { outb(USARTn_BASE + US_THR_OFF, ASCII_XOFF); flow_control |= XOFF_SENT; flow_control &= ~XOFF_PENDING; } else { flow_control |= XOFF_PENDING; } } } } #if defined(UART_RTS_BIT) || defined(UART_USES_NPL) /* * Check the high watermark for GPIO hardware handshake. If the * number of buffered bytes is equal or above this mark, then * disable RTS. */ else if (rts_control && cnt >= rbf->rbf_hwm) { UART_RTS_OFF(); } #endif /* * Store the character and increment and the ring buffer pointer. */ *rbf->rbf_head++ = ch; if (rbf->rbf_head == rbf->rbf_last) { rbf->rbf_head = rbf->rbf_start; } /* Update the ring buffer counter. */ rbf->rbf_cnt = cnt; } /*! * \brief USART interrupt handler. * * \param arg Pointer to the device specific control block. */ static void At91UsartInterrupt(void *arg) { USARTDCB *dcb = (USARTDCB *)arg; unsigned int csr = inr(USARTn_BASE + US_CSR_OFF); if (csr & US_RXRDY) { At91UsartRxReady(&dcb->dcb_rx_rbf); } if (csr & US_TXRDY) { At91UsartTxReady(&dcb->dcb_tx_rbf); } #if USE_BUILT_IN_HALF_DUPLEX == 0 if (csr & US_TXEMPTY) { At91UsartTxEmpty(&dcb->dcb_tx_rbf); } #endif } /*! * \brief Carefully enable USART hardware functions. * * Always enabale transmitter and receiver, even on read-only or * write-only mode. So we can support software flow control. */ static void At91UsartEnable(void) { /* Enable UART receiver and transmitter. */ outr(USARTn_BASE + US_CR_OFF, US_RXEN | US_TXEN); /* Globally enable UART interrupts. */ NutUartIrqEnable(&SIG_UART); } /*! * \brief Carefully disable USART hardware functions. * * This routine is called before changing cruical settings like * baudrate, frame format etc. * * The previous version uses a 10ms delay to make sure, that any * incoming or outgoing character is processed. However, this time * depends on the baudrate. * * In fact we do not need to take care of incoming characters, * when changing such settings. * * For outgoing characters however, settings may be changed on * the fly and we should wait, until the last character transmitted * with the old settings has left the shift register. While TXRDY * is set when the holding register is empty, TXEMPTY is set when the * shift register is empty. The bad news is, that both are zero, if * the transmitter is disabled. We are not able to determine this * state. So we check TXRDY first and, if set, wait for any character * currently in the shift register. */ static void At91UsartDisable(void) { /* Globally disable UART interrupts. */ NutUartIrqDisable(&SIG_UART); /* Wait until all bits had been shifted out. */ if (inr(USARTn_BASE + US_CSR_OFF) & US_TXRDY) { while((inr(USARTn_BASE + US_CSR_OFF) & US_TXEMPTY) == 0); } /* Disable USART transmit and receive. */ outr(USARTn_BASE + US_CR_OFF, US_RXDIS | US_TXDIS); } /*! * \brief Query the USART hardware for the selected speed. * * This function is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \return The currently selected baudrate. */ static uint32_t At91UsartGetSpeed(void) { unsigned int cs; uint32_t clk; clk = NutClockGet(NUT_HWCLK_PERIPHERAL); cs = inr(USARTn_BASE + US_MR_OFF) & US_CLKS; if (cs == US_CLKS_MCK8) { clk /= 8; } else if (cs != US_CLKS_MCK) { clk = 0; } return clk / (16UL * (inr(USARTn_BASE + US_BRGR_OFF) & 0xFFFF)); } /*! * \brief Set the USART hardware bit rate. * * This function is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \param rate Number of bits per second. * * \return 0 on success, -1 otherwise. */ static int At91UsartSetSpeed(uint32_t rate) { At91UsartDisable(); #if !(USARTn_BASE == DBGU_BASE) outr(USARTn_BASE + US_BRGR_OFF, (NutClockGet(NUT_HWCLK_PERIPHERAL) / (8 * (rate)) + 1) / 2); #else #if defined(AT91_PLL_MAINCK) outr(DBGU_BRGR, (At91GetMasterClock() / (8 * rate) + 1) / 2); #else outr(DBGU_BRGR, (NutGetCpuClock() / (8 * rate) + 1) / 2); #endif #endif At91UsartEnable(); return 0; } /*! * \brief Query the USART hardware for the number of data bits. * * This function is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \return The number of data bits set. */ static uint8_t At91UsartGetDataBits(void) { #if !(USARTn_BASE == DBGU_BASE) unsigned int val = inr(USARTn_BASE + US_MR_OFF); if ((val & US_PAR) == US_PAR_MULTIDROP) { val = 9; } else { val &= US_CHRL; if (val == US_CHRL_5) { val = 5; } else if (val == US_CHRL_6) { val = 6; } else if (val == US_CHRL_7) { val = 7; } else { val = 8; } } return (uint8_t)val; #else /* Not supported by DBGU, always 8 bits */ return 8; #endif } /*! * \brief Set the USART hardware to the number of data bits. * * This function is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \return 0 on success, -1 otherwise. */ static int At91UsartSetDataBits(uint8_t bits) { #if !(USARTn_BASE == DBGU_BASE) unsigned int val = inr(USARTn_BASE + US_MR_OFF); if (bits == 9) { val &= ~US_PAR; val |= US_PAR_MULTIDROP; } else { val &= ~US_CHRL; if (bits == 5) { val |= US_CHRL_5; } else if (bits == 6) { val |= US_CHRL_6; } else if (bits == 7) { val |= US_CHRL_7; } else if (bits == 8) { val |= US_CHRL_8; } } At91UsartDisable(); outr(USARTn_BASE + US_MR_OFF, val); At91UsartEnable(); /* * Verify the result. */ if (At91UsartGetDataBits() != bits) { return -1; } return 0; #else /* Not supported by DBGU, always 8 bits */ return -1; #endif } /*! * \brief Query the USART hardware for the parity mode. * * This routine is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \return Parity mode, either 0 (disabled), 1 (odd), 2 (even) or 9 (multidrop). */ static uint8_t At91UsartGetParity(void) { unsigned int val = inr(USARTn_BASE + US_MR_OFF) & US_PAR; if ((val & US_PAR) == US_PAR_MULTIDROP) { val = 9; } else { if (val == US_PAR_ODD) { val = 1; } else if (val == US_PAR_EVEN) { val = 2; } else { val = 0; } } return (uint8_t)val; } /*! * \brief Set the USART hardware to the specified parity mode. * * This routine is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \param mode 0 (disabled), 1 (odd) or 2 (even) * * \return 0 on success, -1 otherwise. */ static int At91UsartSetParity(uint8_t mode) { unsigned int val = inr(USARTn_BASE + US_MR_OFF) & ~US_PAR; switch (mode) { case 0: val |= US_PAR_NO; break; case 1: val |= US_PAR_ODD; break; case 2: val |= US_PAR_EVEN; break; } At91UsartDisable(); outr(USARTn_BASE + US_MR_OFF, val); At91UsartEnable(); /* * Verify the result. */ if (At91UsartGetParity() != mode) { return -1; } return 0; } /*! * \brief Query the USART hardware for the number of stop bits. * * This routine is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \return The number of stop bits set, either 1, 2 or 3 (1.5 bits). */ static uint8_t At91UsartGetStopBits(void) { #if !(USARTn_BASE == DBGU_BASE) unsigned int val = inr(USARTn_BASE + US_MR_OFF) & US_NBSTOP; if (val == US_NBSTOP_1) { val = 1; } else if (val == US_NBSTOP_2) { val = 2; } else { val = 3; } return (uint8_t)val; #else /* Not supported by DBGU, always 1 */ return 1; #endif } /*! * \brief Set the USART hardware to the number of stop bits. * * This routine is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \return 0 on success, -1 otherwise. */ static int At91UsartSetStopBits(uint8_t bits) { #if !(USARTn_BASE == DBGU_BASE) unsigned int val = inr(USARTn_BASE + US_MR_OFF) & ~US_NBSTOP; switch(bits) { case 1: val |= US_NBSTOP_1; break; case 2: val |= US_NBSTOP_2; break; case 3: val |= US_NBSTOP_1_5; break; } At91UsartDisable(); outr(USARTn_BASE + US_MR_OFF, val); At91UsartEnable(); /* * Verify the result. */ if (At91UsartGetStopBits() != bits) { return -1; } #endif return 0; } /*! * \brief Query the USART hardware status. * * \return Status flags. */ static uint32_t At91UsartGetStatus(void) { uint32_t rc = 0; #if USE_BUILT_IN_HARDWARE_HANDSHAKE uint32_t csr = inr(USARTn_BASE + US_CSR_OFF); #endif /* * Set receiver error flags. */ if ((rx_errors & US_FRAME) != 0) { rc |= UART_FRAMINGERROR; } if ((rx_errors & US_OVRE) != 0) { rc |= UART_OVERRUNERROR; } if ((rx_errors & US_PARE) != 0) { rc |= UART_PARITYERROR; } /* * Determine software handshake status. The flow control status may * change during interrupt, but this doesn't really hurt us. */ if (flow_control) { if (flow_control & XOFF_SENT) { rc |= UART_RXDISABLED; } if (flow_control & XOFF_RCVD) { rc |= UART_TXDISABLED; } } /* * Determine hardware handshake control status. */ #if USE_BUILT_IN_HARDWARE_HANDSHAKE /* How to find out? */ #elif defined(UART_RTS_BIT) /* No definition of the status register? */ #elif defined(UART_USES_NPL) if (bit_is_set(NPL_RSCR, NPL_RSRTS_BIT)) { rc |= UART_RTSDISABLED; if (rts_control) { rc |= UART_RXDISABLED; } } else { rc |= UART_RTSENABLED; } #endif /* * Determine hardware handshake sense status. */ #if USE_BUILT_IN_HARDWARE_HANDSHAKE if (csr & US_CTS) { rc |= UART_CTSDISABLED; if (cts_sense) { rc |= UART_RXDISABLED; } } else { rc |= UART_CTSENABLED; } #else if (UART_CTS_IS_ON()) { rc |= UART_CTSENABLED; } else { rc |= UART_CTSDISABLED; if (cts_sense) { rc |= UART_RXDISABLED; } } #endif /* * Determine hardware modem sense status. */ #if 0 /* I'm confused. Awful flag mismatch? Why do we have uart.h and usart.h? */ if (csr & US_RI) { rc |= UART_RIDISABLED; } else { rc |= UART_RIENABLED; } if (csr & US_DSR) { rc |= UART_DSRDISABLED; } else { rc |= UART_DSRENABLED; } if (csr & US_DCD) { rc |= UART_DCDDISABLED; } else { rc |= UART_DCDENABLED; } #endif /* * If transmitter and receiver haven't been detected disabled by any * of the checks above, then they are probably enabled. */ if ((rc & UART_RXDISABLED) == 0) { rc |= UART_RXENABLED; } if ((rc & UART_TXDISABLED) == 0) { rc |= UART_TXENABLED; } /* * Process multidrop setting. */ if (tx_aframe) { rc |= UART_TXADDRFRAME; } else { rc |= UART_TXNORMFRAME; } return rc; } /*! * \brief Set the USART hardware status. * * \param flags Status flags. * * \return 0 on success, -1 otherwise. */ static int At91UsartSetStatus(uint32_t flags) { /* * Process software handshake control. */ if (flow_control) { /* Access to the flow control status must be atomic. */ NutUartIrqDisable(&SIG_UART); /* * Enabling or disabling the receiver means to behave like * having sent a XON or XOFF character resp. */ if (flags & UART_RXENABLED) { flow_control &= ~XOFF_SENT; } else if (flags & UART_RXDISABLED) { flow_control |= XOFF_SENT; } /* * Enabling or disabling the transmitter means to behave like * having received a XON or XOFF character resp. */ if (flags & UART_TXENABLED) { flow_control &= ~XOFF_RCVD; } else if (flags & UART_TXDISABLED) { flow_control |= XOFF_RCVD; } NutUartIrqEnable(&SIG_UART); } /* * Process hardware handshake control. */ #if USE_BUILT_IN_HARDWARE_HANDSHAKE /* Built-in hardware. */ if (rts_control) { if (flags & UART_RXDISABLED) { outr(USARTn_BASE + US_CR_OFF, US_RTSDIS); } if (flags & UART_RXENABLED) { outr(USARTn_BASE + US_CR_OFF, US_RTSEN); } } if (flags & UART_RTSDISABLED) { outr(USARTn_BASE + US_CR_OFF, US_RTSDIS); } if (flags & UART_RTSENABLED) { outr(USARTn_BASE + US_CR_OFF, US_RTSEN); } #else /* Manually controlled via GPIO. */ if (rts_control) { if (flags & UART_RXDISABLED) { UART_RTS_OFF(); } if (flags & UART_RXENABLED) { UART_RTS_ON(); } } if (flags & UART_RTSDISABLED) { UART_RTS_OFF(); } if (flags & UART_RTSENABLED) { UART_RTS_ON(); } #endif /* * Process hardware modem control. */ #if USE_BUILT_IN_MODEM_CONTROL /* Built-in hardware. */ if (flags & UART_DTRDISABLED) { outr(USARTn_BASE + US_CR_OFF, US_DTRDIS); } if (flags & UART_DTRENABLED) { outr(USARTn_BASE + US_CR_OFF, US_DTREN); } #endif /* * Process multidrop setting. */ if (flags & UART_TXADDRFRAME) { tx_aframe = 1; } if (flags & UART_TXNORMFRAME) { tx_aframe = 0; } /* * Clear UART receive errors. */ if (flags & UART_ERRORS) { outr(USARTn_BASE + US_CR_OFF, US_RSTSTA); } /* * Verify the result. */ if ((At91UsartGetStatus() & ~UART_ERRORS) != flags) { return -1; } return 0; } /*! * \brief Query the USART hardware for synchronous mode. * * This function is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * Not implemented for the AT91 USART. Always returns 0. * * \return Or-ed combination of \ref UART_SYNC, \ref UART_MASTER, * \ref UART_NCLOCK and \ref UART_HIGHSPEED. */ static uint8_t At91UsartGetClockMode(void) { return 0; } /*! * \brief Set asynchronous or synchronous mode. * * This function is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * Not implemented for the AT91 USART. Always returns -1. * * \param mode Must be an or-ed combination of USART_SYNC, USART_MASTER, * USART_NCLOCK and USART_HIGHSPEED. * * \return 0 on success, -1 otherwise. */ static int At91UsartSetClockMode(uint8_t mode) { return -1; } /*! * \brief Query flow control mode. * * This routine is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \return See UsartIOCtl(). */ static uint32_t At91UsartGetFlowControl(void) { uint32_t rc = 0; if (flow_control) { rc |= USART_MF_XONXOFF; } else { rc &= ~USART_MF_XONXOFF; } #if defined(UART_RTS_BIT) || defined(UART_USES_NPL) || defined(UART_HARDWARE_HANDSHAKE) if (rts_control) { rc |= USART_MF_RTSCONTROL; } else { rc &= ~USART_MF_RTSCONTROL; } #endif #if defined(UART_CTS_BIT) || defined(UART_USES_NPL) || defined(UART_HARDWARE_HANDSHAKE) if (cts_sense) { rc |= USART_MF_CTSSENSE; } else { rc &= ~USART_MF_CTSSENSE; } #endif if (hdx_control) { rc |= USART_MF_HALFDUPLEX; } else { rc &= ~USART_MF_HALFDUPLEX; } return rc; } /*! * \brief Set flow control mode. * * This function is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \param flags See UsartIOCtl(). * * \return 0 on success, -1 otherwise. */ static int At91UsartSetFlowControl(uint32_t flags) { #if USE_BUILT_IN_HARDWARE_HANDSHAKE || USE_BUILT_IN_HALF_DUPLEX uint32_t mr = inr(USARTn_BASE + US_MR_OFF) & ~US_MODE; #endif /* * Set software handshake mode. */ NutUartIrqDisable(&SIG_UART); if (flags & USART_MF_XONXOFF) { if(flow_control == 0) { flow_control = 1 | XOFF_SENT; /* force XON to be sent on next read */ } } else { flow_control = 0; } NutUartIrqEnable(&SIG_UART); /* * Set RTS control mode. */ UART_RTS_PIN_ENABLE(); UART_RTS_ON(); if (flags & USART_MF_RTSCONTROL) { rts_control = 1; #if USE_BUILT_IN_HARDWARE_HANDSHAKE outr(USARTn_BASE + US_MR_OFF, mr | US_MODE_HWHANDSHAKE); cts_sense = 1; #endif } else if (rts_control) { rts_control = 0; #if USE_BUILT_IN_HARDWARE_HANDSHAKE outr(USARTn_BASE + US_MR_OFF, mr); cts_sense = 0; #endif } /* * Set CTS sense mode. */ if (flags & USART_MF_CTSSENSE) { cts_sense = 1; UART_CTS_PIN_ENABLE(); #if USE_BUILT_IN_HARDWARE_HANDSHAKE rts_control = 1; outr(USARTn_BASE + US_MR_OFF, mr | US_MODE_HWHANDSHAKE); #endif } else if (cts_sense) { cts_sense = 0; #if USE_BUILT_IN_HARDWARE_HANDSHAKE rts_control = 0; outr(USARTn_BASE + US_MR_OFF, mr); #endif } /* * Set half duplex mode. */ if (flags & USART_MF_HALFDUPLEX) { /* Mark half duplex mode enabled. */ hdx_control = 1; /* Enable half duplex pin. */ UART_HDX_PIN_ENABLE(); UART_HDX_RX(); #if USE_BUILT_IN_HALF_DUPLEX /* Enable built-in RS-485 mode. */ outr(USARTn_BASE + US_MR_OFF, mr | US_MODE_RS485); #else /* Enable USART receive, disable transmit. */ outr(USARTn_BASE + US_CR_OFF, US_RXEN | US_TXDIS); /* Enable transmit complete interrupt. */ outr(USARTn_BASE + US_IDR_OFF, US_TXEMPTY); #endif } else if (hdx_control) { hdx_control = 0; #if USE_BUILT_IN_HALF_DUPLEX /* Disable built-in RS-485 mode. */ outr(USARTn_BASE + US_MR_OFF, inr(USARTn_BASE + US_MR_OFF) & ~US_MODE); #else /* Enable USART receive and transmit. */ outr(USARTn_BASE + US_CR_OFF, US_RXEN | US_TXEN); /* Disable transmit complete interrupt. */ outr(USARTn_BASE + US_IDR_OFF, US_TXEMPTY); #endif } /* No need to call At91UsartGetFlowControl. The result is obvious, ** because we do not exclude unsupported modes yet. */ return 0; } /*! * \brief Start the USART transmitter hardware. * * The upper level USART driver will call this function through the * USARTDCB jump table each time it added one or more bytes to the * transmit buffer. */ static void At91UsartTxStart(void) { #if USE_BUILT_IN_HALF_DUPLEX == 0 if (hdx_control) { #if defined(UART_HDX_BIT) || defined(UART_USES_NPL) UART_HDX_TX(); #endif /* Disable USART receive, enable transmit. */ outr(USARTn_BASE + US_CR_OFF, US_RXDIS | US_TXEN); } #endif /* Enable transmit interrupts. */ outr(USARTn_BASE + US_IER_OFF, US_TXRDY); } /*! * \brief Start the USART receiver hardware. * * The upper level USART driver will call this function through the * USARTDCB jump table each time it removed enough bytes from the * receive buffer. Enough means, that the number of bytes left in * the buffer is below the low watermark. */ static void At91UsartRxStart(void) { /* * Do any required software flow control. */ if (flow_control && (flow_control & XOFF_SENT) != 0) { NutUartIrqDisable(&SIG_UART); if ((inr(USARTn_BASE + US_CSR_OFF) & US_TXRDY)) { outr(USARTn_BASE + US_THR_OFF, ASCII_XON); flow_control &= ~XON_PENDING; } else { flow_control |= XON_PENDING; } flow_control &= ~(XOFF_SENT | XOFF_PENDING); NutUartIrqEnable(&SIG_UART); } /* Enable RTS. */ UART_RTS_ON(); /* Enable receive interrupts. */ outr(USARTn_BASE + US_IER_OFF, US_RXRDY); } /* * \brief Initialize the USART hardware driver. * * This function is called during device registration by the upper level * USART driver through the USARTDCB jump table. * * \return 0 on success, -1 otherwise. */ static int At91UsartInit(void) { /* * Register receive and transmit interrupts. */ if (NutUartIrqRegister(&SIG_UART, At91UsartInterrupt, &dcb_usart)) { return -1; } /* * Register CTS sense interrupts. */ #if defined(UART_CTS_BIT) if (GpioRegisterIrqHandler(&sig_GPIO1, UART_CTS_BIT, At91UsartCts, NULL)) { return -1; } #if defined(PS_PCER) outr(PS_PCER, _BV(UART_CTS_PIO_ID)); #elif defined(PMC_PCER) outr(PMC_PCER, _BV(UART_CTS_PIO_ID)); #endif #elif defined(UART_USES_NPL) if (NplRegisterIrqHandler(&sig_RSCTS, At91UsartCts, NULL)) { return -1; } #endif /* Enable UART clock. */ #if defined(US_ID) #if defined(PS_PCER) outr(PS_PCER, _BV(US_ID)); #elif defined(PMC_PCER) outr(PMC_PCER, _BV(US_ID)); #endif #endif /* Disable GPIO on UART tx/rx pins. */ UART_RXTX_PINS_ENABLE(); /* Reset UART. */ outr(USARTn_BASE + US_CR_OFF, US_RSTRX | US_RSTTX | US_RXDIS | US_TXDIS); /* Disable all UART interrupts. */ outr(USARTn_BASE + US_IDR_OFF, 0xFFFFFFFF); #if defined(US_RCR_OFF) && defined(US_TCR_OFF) /* Clear UART PDC counter registers. */ outr(USARTn_BASE + US_RCR_OFF, 0); outr(USARTn_BASE + US_TCR_OFF, 0); #endif /* Set UART baud rate generator register. */ At91UsartSetSpeed( UART_INIT_BAUDRATE); /* Set UART mode to 8 data bits, no parity and 1 stop bit. */ outr(USARTn_BASE + US_MR_OFF, US_CHMODE_NORMAL | US_CHRL_8 | US_PAR_NO | US_NBSTOP_1); return 0; } /* * \brief Deinitialize the USART hardware driver. * * This function is called during device deregistration by the upper * level USART driver through the USARTDCB jump table. * * \return 0 on success, -1 otherwise. */ static int At91UsartDeinit(void) { /* Deregister receive and transmit interrupts. */ NutUartIrqRegister(&SIG_UART, 0, 0); /* Reset UART. */ outr(USARTn_BASE + US_CR_OFF, US_RSTRX | US_RSTTX | US_RXDIS | US_TXDIS); /* Disable all UART interrupts. */ outr(USARTn_BASE + US_IDR_OFF, 0xFFFFFFFF); /* Disable UART clock. */ #if defined (US_ID) #if defined(PS_PCDR) outr(PS_PCDR, _BV(US_ID)); #elif defined(PMC_PCDR) outr(PMC_PCDR, _BV(US_ID)); #endif #endif /* * Disabling flow control shouldn't be required here, because it's up * to the upper level to do this on the last close or during * deregistration. */ hdx_control = 0; UART_HDX_RX(); cts_sense = 0; rts_control = 0; return 0; } /*@}*/