/*! * Copyright (C) 2001-2010 by egnite Software 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/ */ /* * $Log: usart.c,v $ * */ #include #include #include #include #include #include #include #include #include // Work around missing defines in some AVR32 models #if !defined(AVR32_USART_CSR_MASK) #define AVR32_USART_CSR_MASK 0xFFFFFFFF #endif #if !defined(AVR32_USART_IDR_MASK) #define AVR32_USART_IDR_MASK 0xFFFFFFFF #endif /*! * \addtogroup xgNutArchAvr32Usart */ /*@{*/ /* \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 ureg_t 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; #ifdef UART_HDX_BIT /* define in cfg/modem.h */ #ifdef UART_HDX_FLIP_BIT /* same as RTS toggle by Windows NT driver */ #define UART_HDX_TX cbi #define UART_HDX_RX sbi #else /* previous usage by Ethernut */ #define UART_HDX_TX sbi #define UART_HDX_RX cbi #endif #endif #ifdef UART_HDX_BIT /*! * \brief Enables half duplex control if not equal zero. * * This variable exists only if the hardware configuration defines a * port bit to switch between receive and transmit mode. */ static uint_fast8_t hdx_control; #endif //#if defined(UART_RTS_BIT) || defined(US_MODE_HWHANDSHAKE) /*! * \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; //#endif //#if defined(UART_CTS_BIT) || defined(US_MODE_HWHANDSHAKE) /*! * \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; //#endif #ifdef UART_CTS_BIT /*! * \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 Avr32UsartCts(void *arg) { /* Enable transmit interrupt. */ //sbi(UCSRnB, UDRIE); /* Disable CTS sense interrupt. */ //cbi(EIMSK, UART_CTS_BIT); } #endif #ifdef UART_HDX_BIT /* * \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. * * This routine exists only if the hardware configuration defines a * port bit to switch between receive and transmit mode. * * \param arg Pointer to the transmitter ring buffer. */ static void Avr32UsartTxEmpty(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. */ UART_HDX_RX(UART_HDX_PORT, UART_HDX_BIT); } } #endif /* * \brief USARTn transmitter ready interrupt handler. * * \param rbf Pointer to the transmitter ring buffer. */ static void Avr32UsartTxReady(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 + AVR32_USART_THR, (ASCII_XOFF << AVR32_USART_THR_TXCHR_OFFSET) & AVR32_USART_THR_TXCHR_MASK); flow_control |= XOFF_SENT; } else { outr(USARTn_BASE + AVR32_USART_THR, (ASCII_XON << AVR32_USART_THR_TXCHR_OFFSET) & AVR32_USART_THR_TXCHR_MASK); 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 + AVR32_USART_IDR, (1 << AVR32_USART_IDR_TXRDY_OFFSET) & AVR32_USART_IDR_TXRDY_MASK); inr(USARTn_BASE + AVR32_USART_CSR); return; } if (rbf->rbf_cnt) { #ifdef UART_CTS_BIT /* * If CTS has been disabled, we disable the transmit interrupts * and return without sending anything. */ if (cts_sense && bit_is_set(UART_CTS_PIN, UART_CTS_BIT)) { outr(USARTn_BASE + AVR32_USART_IDR, (1 << AVR32_USART_IDR_TXRDY_OFFSET) & AVR32_USART_IDR_TXRDY_MASK); inr(USARTn_BASE + AVR32_USART_CSR); // sbi(EIMSK, UART_CTS_BIT); return; } #endif rbf->rbf_cnt--; /* * Send address in multidrop mode. */ if (tx_aframe) { outr(USARTn_BASE + AVR32_USART_CR, inr( USARTn_BASE + AVR32_USART_CR ) | AVR32_USART_CR_SENDA_MASK); } /* * Start transmission of the next character. */ outr(USARTn_BASE + AVR32_USART_THR, (*cp << AVR32_USART_THR_TXCHR_OFFSET) & AVR32_USART_THR_TXCHR_MASK); /* * Wrap around the buffer pointer if we reached its end. */ if (++cp == rbf->rbf_last) { cp = rbf->rbf_start; } rbf->rbf_tail = cp; if (rbf->rbf_cnt == rbf->rbf_lwm) { NutEventPostFromIrq(&rbf->rbf_que); } } /* * Nothing left to transmit, disable interrupt. */ else { outr(USARTn_BASE + AVR32_USART_IDR, (1 << AVR32_USART_IDR_TXRDY_OFFSET) & AVR32_USART_IDR_TXRDY_MASK); inr(USARTn_BASE + AVR32_USART_CSR); rbf->rbf_cnt = 0; NutEventPostFromIrq(&rbf->rbf_que); } } /* * \brief USARTn receiver ready interrupt handler. * * * \param rbf Pointer to the receiver ring buffer. */ static void Avr32UsartRxReady(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 = inr(USARTn_BASE + AVR32_USART_RHR) & AVR32_USART_RHR_RXCHR_MASK >> AVR32_USART_RHR_RXCHR_OFFSET; /* Collect receiver errors. */ rx_errors |= inr(USARTn_BASE + AVR32_USART_CSR) & (AVR32_USART_CSR_OVRE_MASK | AVR32_USART_CSR_FRAME_MASK | AVR32_USART_CSR_PARE_MASK); /* * 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 + AVR32_USART_IDR, (1 << AVR32_USART_IDR_TXRDY_OFFSET) & AVR32_USART_IDR_TXRDY_MASK); inr(USARTn_BASE + AVR32_USART_CSR); flow_control |= XOFF_RCVD; return; } /* XON enables transmit interrupts. */ else if (ch == ASCII_XON) { outr(USARTn_BASE + AVR32_USART_IER, (1 << AVR32_USART_IDR_TXRDY_OFFSET) & AVR32_USART_IDR_TXRDY_MASK); flow_control &= ~XOFF_RCVD; return; } } /* * Check buffer overflow. */ cnt = rbf->rbf_cnt; if (cnt >= rbf->rbf_siz) { rx_errors |= AVR32_USART_CSR_OVRE_MASK; return; } /* Wake up waiting threads if this is the first byte in the buffer. */ if (cnt++ == 0) { NutEventPostFromIrq(&rbf->rbf_que); } /* * Check the high watermark for software handshake. If the number of * buffered bytes is 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 + AVR32_USART_CSR) & AVR32_USART_CSR_TXRDY_MASK) { outb(USARTn_BASE + AVR32_USART_THR, ASCII_XOFF); flow_control |= XOFF_SENT; flow_control &= ~XOFF_PENDING; } else { flow_control |= XOFF_PENDING; } } } } #ifdef UART_RTS_BIT /* * Check the high watermark for hardware handshake. If the number of * buffered bytes is above this mark, then disable RTS. */ else if (rts_control && cnt >= rbf->rbf_hwm) { sbi(UART_RTS_PORT, UART_RTS_BIT); } #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 Avr32UsartInterrupt(void *arg) { USARTDCB *dcb = (USARTDCB *) arg; ureg_t csr = inr(USARTn_BASE + AVR32_USART_CSR) & AVR32_USART_CSR_MASK; if (csr & AVR32_USART_CSR_RXRDY_MASK) { Avr32UsartRxReady(&dcb->dcb_rx_rbf); } if (csr & AVR32_USART_CSR_TXRDY_MASK) { Avr32UsartTxReady(&dcb->dcb_tx_rbf); } #ifdef UART_HDX_BIT if (csr & AVR32_USART_CSR_TXEMPTY_MASK) { Avr32UsartTxEmpty(&dcb->dcb_tx_rbf); } #endif /* UART_HDX_BIT */ } /*! * \brief Carefully enable USART hardware functions. * * Always enable transmitter and receiver, even on read-only or * write-only mode. So we can support software flow control. */ static void Avr32UsartEnable(void) { NutEnterCritical(); /* Enable UART receiver and transmitter. */ outr(USARTn_BASE + AVR32_USART_CR, inr(USARTn_BASE + AVR32_USART_CR) | AVR32_USART_CR_RXEN_MASK | AVR32_USART_CR_TXEN_MASK); /* Enable UART receiver and transmitter interrupts. */ outr(USARTn_BASE + AVR32_USART_IER, AVR32_USART_IER_RXRDY_MASK | AVR32_USART_IER_TXRDY_MASK); //NutIrqEnable(&SIG_UART); #ifdef UART_HDX_BIT if (hdx_control) { /* Enable transmit complete interrupt. */ outr(USARTn_BASE + AVR32_USART_IER, AVR32_USART_IER_TXEMPTY_MASK); } #endif NutExitCritical(); } /*! * \brief Carefully disable USART hardware functions. */ static void Avr32UsartDisable(void) { /* * Disable USART interrupts. */ NutEnterCritical(); outr(USARTn_BASE + AVR32_USART_IDR, AVR32_USART_IDR_MASK); inr(USARTn_BASE + AVR32_USART_CSR); NutExitCritical(); /* * Allow incoming or outgoing character to finish. */ NutDelay(10); /* * Disable USART transmit and receive. */ outr(USARTn_BASE + AVR32_USART_CR, inr(USARTn_BASE + AVR32_USART_CR) | AVR32_USART_CR_RXDIS_MASK | AVR32_USART_CR_TXDIS_MASK); } /*! * \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 Avr32UsartGetSpeed(void) { uint32_t clk = NutClockGet(NUT_HWCLK_PERIPHERAL_A); uint32_t cd = inr(USARTn_BASE + AVR32_USART_BRGR) & AVR32_USART_BRGR_CD_MASK >> AVR32_USART_BRGR_CD_OFFSET; if (inr(USARTn_BASE + AVR32_USART_BRGR) & AVR32_USART_BRGR_FP_MASK) { cd += ((inr(USARTn_BASE + AVR32_USART_BRGR) & AVR32_USART_BRGR_FP_MASK) >> AVR32_USART_BRGR_FP_OFFSET) / 8; } return clk / (8UL * (2 - ((inr(USARTn_BASE + AVR32_USART_MR) & AVR32_USART_MR_OVER_MASK) >> AVR32_USART_MR_OVER_OFFSET)) * cd); } /*! * \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 Avr32UsartSetSpeed(uint32_t rate) { const unsigned long pba_hz = NutClockGet(NUT_HWCLK_PERIPHERAL_A); const unsigned int over = (pba_hz >= 16 * rate) ? 16 : 8; const unsigned int cd_fp = ((1 << AVR32_USART_BRGR_FP_SIZE) * pba_hz + (over * rate) / 2) / (over * rate); const unsigned int cd = cd_fp >> AVR32_USART_BRGR_FP_SIZE; const unsigned int fp = cd_fp & ((1 << AVR32_USART_BRGR_FP_SIZE) - 1); if (cd < 1 || cd > (1 << AVR32_USART_BRGR_CD_SIZE) - 1) return -1; Avr32UsartDisable(); outr(USARTn_BASE + AVR32_USART_MR, (inr(USARTn_BASE + AVR32_USART_MR) & ~(AVR32_USART_MR_USCLKS_MASK | AVR32_USART_MR_SYNC_MASK | AVR32_USART_MR_OVER_MASK)) | AVR32_USART_MR_USCLKS_MCK << AVR32_USART_MR_USCLKS_OFFSET | ((over == 16) ? AVR32_USART_MR_OVER_X16 : AVR32_USART_MR_OVER_X8) << AVR32_USART_MR_OVER_OFFSET); outr(USARTn_BASE + AVR32_USART_BRGR, cd << AVR32_USART_BRGR_CD_OFFSET | fp << AVR32_USART_BRGR_FP_OFFSET); Avr32UsartEnable(); 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 Avr32UsartGetDataBits(void) { uint8_t val; if (inr(USARTn_BASE + AVR32_USART_MR) & AVR32_USART_MR_MODE9_MASK) { val = 9; } else { val = ((inr(USARTn_BASE + AVR32_USART_MR) & AVR32_USART_MR_CHRL_MASK) >> AVR32_USART_MR_CHRL_OFFSET) + 5; } return (uint8_t) val; } /*! * \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 Avr32UsartSetDataBits(uint8_t bits) { Avr32UsartDisable(); if (bits == 9) { /* Character length set to 9 bits. MODE9 dominates CHRL. */ outr(USARTn_BASE + AVR32_USART_MR, inr(USARTn_BASE + AVR32_USART_MR) | AVR32_USART_MR_MODE9_MASK); } else { outr(USARTn_BASE + AVR32_USART_MR, inr(USARTn_BASE + AVR32_USART_MR) | (bits - 5) << AVR32_USART_MR_CHRL_OFFSET); } Avr32UsartEnable(); /* * Verify the result. */ if (Avr32UsartGetDataBits() != bits) { return -1; } return 0; } /*! * \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 Avr32UsartGetParity(void) { uint8_t val; if (inr(USARTn_BASE + AVR32_USART_MR) & AVR32_USART_MR_MODE9_MASK) { val = 9; } else { if (((inr(USARTn_BASE + AVR32_USART_MR) & AVR32_USART_MR_PAR_MASK) >> AVR32_USART_MR_PAR_OFFSET) == AVR32_USART_MR_PAR_ODD) { val = 1; } else if (((inr(USARTn_BASE + AVR32_USART_MR) & AVR32_USART_MR_PAR_MASK) >> AVR32_USART_MR_PAR_OFFSET) == AVR32_USART_MR_PAR_EVEN) { val = 2; } else { val = 0; } } return 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 Avr32UsartSetParity(uint8_t mode) { Avr32UsartDisable(); switch (mode) { case 0: outr(USARTn_BASE + AVR32_USART_MR, AVR32_USART_MR_PAR_NONE << AVR32_USART_MR_PAR_OFFSET); break; case 1: outr(USARTn_BASE + AVR32_USART_MR, AVR32_USART_MR_PAR_ODD << AVR32_USART_MR_PAR_OFFSET); break; case 2: outr(USARTn_BASE + AVR32_USART_MR, AVR32_USART_MR_PAR_EVEN << AVR32_USART_MR_PAR_OFFSET); break; } Avr32UsartEnable(); /* * Verify the result. */ if (Avr32UsartGetParity() != 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 Avr32UsartGetStopBits(void) { switch (inr(USARTn_BASE + AVR32_USART_MR) & AVR32_USART_MR_NBSTOP_MASK >> AVR32_USART_MR_NBSTOP_OFFSET) { case AVR32_USART_MR_NBSTOP_1: return 1; case AVR32_USART_MR_NBSTOP_2: return 2; case AVR32_USART_MR_NBSTOP_1_5: return 3; } return 0; } /*! * \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 Avr32UsartSetStopBits(uint8_t bits) { Avr32UsartDisable(); switch (bits) { case 1: outr(USARTn_BASE + AVR32_USART_MR, inr(USARTn_BASE + AVR32_USART_MR) | AVR32_USART_MR_NBSTOP_1 << AVR32_USART_NBSTOP_OFFSET); break; case 2: outr(USARTn_BASE + AVR32_USART_MR, inr(USARTn_BASE + AVR32_USART_MR) | AVR32_USART_MR_NBSTOP_2 << AVR32_USART_NBSTOP_OFFSET); break; case 3: outr(USARTn_BASE + AVR32_USART_MR, inr(USARTn_BASE + AVR32_USART_MR) | AVR32_USART_MR_NBSTOP_1_5 << AVR32_USART_NBSTOP_OFFSET); break; } Avr32UsartEnable(); /* * Verify the result. */ if (Avr32UsartGetStopBits() != bits) { return -1; } return 0; } /*! * \brief Query the USART hardware status. * * \return Status flags. */ static uint32_t Avr32UsartGetStatus(void) { uint32_t rc = 0; #if defined(US_MODE_HWHANDSHAKE) uint32_t csr = inr(USARTn_BASE + AVR32_USART_CSR); #endif /* * Set receiver error flags. */ if ((rx_errors & AVR32_USART_CSR_FRAME_MASK) != 0) { rc |= UART_FRAMINGERROR; } if ((rx_errors & AVR32_USART_CSR_OVRE_MASK) != 0) { rc |= UART_OVERRUNERROR; } if ((rx_errors & AVR32_USART_CSR_PARE_MASK) != 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 defined(UART_RTS_BIT) if (bit_is_set(UART_RTS_PORT, UART_RTS_BIT)) { rc |= UART_RTSDISABLED; if (rts_control) { rc |= UART_RXDISABLED; } } else { rc |= UART_RTSENABLED; } #elif defined(US_MODE_HWHANDSHAKE) /* How to find out? */ #endif /* * Determine hardware handshake sense status. */ #ifdef UART_CTS_BIT if (bit_is_set(UART_CTS_PIN, UART_CTS_BIT)) { rc |= UART_CTSDISABLED; if (cts_sense) { rc |= UART_RXDISABLED; } } else { rc |= UART_CTSENABLED; } #elif defined(US_MODE_HWHANDSHAKE) if (csr & AVR32_USART_CSR_CTS_MASK) { rc |= UART_CTSDISABLED; if (cts_sense) { rc |= UART_RXDISABLED; } } else { rc |= UART_CTSENABLED; } #endif /* * Determine hardware modem sense status. */ #if defined(US_MODE_HWHANDSHAKE) && 0 /* I'm confused. Awful flag mismatch? Why do we have uart.h and usart.h? */ if (csr & AVR32_USART_CSR_RI_MASK) { rc |= UART_RIDISABLED; } else { rc |= UART_RIENABLED; } if (csr & AVR32_USART_CSR_DSR_MASK) { rc |= UART_DSRDISABLED; } else { rc |= UART_DSRENABLED; } if (csr & AVR32_USART_CSR_DCD_MASK) { 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 Avr32UsartSetStatus(uint32_t flags) { /* * Process software handshake control. */ if (flow_control) { /* Access to the flow control status must be atomic. */ NutEnterCritical(); /* * 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; } NutExitCritical(); } /* * Process hardware handshake control. */ #if defined(UART_RTS_BIT) /* Manually controlled via GPIO. */ if (rts_control) { if (flags & UART_RXDISABLED) { sbi(UART_RTS_PORT, UART_RTS_BIT); } if (flags & UART_RXENABLED) { cbi(UART_RTS_PORT, UART_RTS_BIT); } } if (flags & UART_RTSDISABLED) { sbi(UART_RTS_PORT, UART_RTS_BIT); } if (flags & UART_RTSENABLED) { cbi(UART_RTS_PORT, UART_RTS_BIT); } #elif defined(US_MODE_HWHANDSHAKE) /* Build in hardware. */ if (rts_control) { if (flags & UART_RXDISABLED) { USARTn_BASE.CR.rtsdis = 1; } if (flags & UART_RXENABLED) { USARTn_BASE.CR.rtsen = 1; } } if (flags & UART_RTSDISABLED) { USARTn_BASE.CR.rtsdis = 1; } if (flags & UART_RTSENABLED) { USARTn_BASE.CR.rtsen = 1; } #endif /* * Process hardware modem control. */ #if defined(UART_DTR_BIT) /* Manually controlled via GPIO. */ if (flags & UART_DTRDISABLED) { sbi(UART_DTR_PORT, UART_DTR_BIT); } if (flags & UART_DTRENABLED) { cbi(UART_DTR_PORT, UART_DTR_BIT); } #elif defined(US_MODE_HWHANDSHAKE) /* Build in hardware. */ if (flags & UART_DTRDISABLED) { USARTn_BASE.CR.dtrdis = 1; } if (flags & UART_DTRENABLED) { USARTn_BASE.CR.dtren = 1; } #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 + AVR32_USART_CR, inr(USARTn_BASE + AVR32_USART_CR) | AVR32_USART_CR_RSTSTA_MASK); } /* * Verify the result. */ if ((Avr32UsartGetStatus() & ~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. * * \return Or-ed combination of \ref UART_SYNC, \ref UART_MASTER, * \ref UART_NCLOCK and \ref UART_HIGHSPEED. */ static uint8_t Avr32UsartGetClockMode(void) { uint8_t rc = 0; return rc; } /*! * \brief Set asynchronous or synchronous mode. * * This function is called by ioctl function of the upper level USART * driver through the USARTDCB jump table. * * \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 Avr32UsartSetClockMode(uint8_t mode) { /* * Verify the result. */ if (Avr32UsartGetClockMode() != mode) { return -1; } return 0; } /*! * \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 Avr32UsartGetFlowControl(void) { uint32_t rc = 0; if (flow_control) { rc |= USART_MF_XONXOFF; } else { rc &= ~USART_MF_XONXOFF; } #ifdef UART_RTS_BIT if (rts_control) { rc |= USART_MF_RTSCONTROL; } else { rc &= ~USART_MF_RTSCONTROL; } #endif #ifdef UART_CTS_BIT if (cts_sense) { rc |= USART_MF_CTSSENSE; } else { rc &= ~USART_MF_CTSSENSE; } #endif #ifdef UART_HDX_BIT if (hdx_control) { rc |= USART_MF_HALFDUPLEX; } else { rc &= ~USART_MF_HALFDUPLEX; } #endif 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 Avr32UsartSetFlowControl(uint32_t flags) { /* * Set software handshake mode. */ if (flags & USART_MF_XONXOFF) { if (flow_control == 0) { NutEnterCritical(); flow_control = 1 | XOFF_SENT; /* force XON to be sent on next read */ NutExitCritical(); } } else { NutEnterCritical(); flow_control = 0; NutExitCritical(); } /* * Set RTS control mode. */ if (flags & USART_MF_RTSCONTROL) { #if defined(UART_RTS_BIT) sbi(UART_RTS_PORT, UART_RTS_BIT); sbi(UART_RTS_DDR, UART_RTS_BIT); rts_control = 1; #endif } else if (rts_control) { rts_control = 0; #if defined(UART_RTS_BIT) cbi(UART_RTS_DDR, UART_RTS_BIT); #endif } /* * Set CTS sense mode. */ if (flags & USART_MF_CTSSENSE) { #if defined(UART_CTS_BIT) /* Register CTS sense interrupt. */ if (NutRegisterIrqHandler(&UART_CTS_SIGNAL, Avr32UsartCts, 0)) { return -1; } sbi(UART_CTS_PORT, UART_CTS_BIT); cbi(UART_CTS_DDR, UART_CTS_BIT); cts_sense = 1; #elif defined(US_MODE_HWHANDSHAKE) outr(USARTn_BASE + AVR32_USART_MR, inr(USARTn_BASE + AVR32_USART_MR) | (AVR32_USART_MR_MODE_HARDWARE << AVR32_USART_MR_MODE_OFFSET) ); cts_sense = 1; rts_control = 1; #endif } else if (cts_sense) { #if defined(UART_CTS_BIT) /* Deregister CTS sense interrupt. */ NutRegisterIrqHandler(&UART_CTS_SIGNAL, 0, 0); cbi(UART_CTS_DDR, UART_CTS_BIT); #elif defined(US_MODE_HWHANDSHAKE) outr(USARTn_BASE + AVR32_USART_MR, inr(USARTn_BASE + AVR32_USART_MR) | (AVR32_USART_MR_MODE_NORMAL << AVR32_USART_MR_MODE_OFFSET) ); rts_control = 0; #endif cts_sense = 0; } #ifdef UART_HDX_BIT /* * Set half duplex mode. */ if (flags & USART_MF_HALFDUPLEX) { /* Register transmit complete interrupt. */ if (NutRegisterIrqHandler(&sig_UART_TRANS, Avr32UsartTxComplete, &dcb_usart.dcb_rx_rbf)) { return -1; } /* Initially enable the receiver. */ UART_HDX_RX(UART_HDX_PORT, UART_HDX_BIT); sbi(UART_HDX_DDR, UART_HDX_BIT); hdx_control = 1; /* Enable transmit complete interrupt. */ sbi(UCSRnB, TXCIE); } else if (hdx_control) { hdx_control = 0; /* disable transmit complete interrupt */ cbi(UCSRnB, TXCIE); /* Deregister transmit complete interrupt. */ NutRegisterIrqHandler(&sig_UART_TRANS, 0, 0); cbi(UART_HDX_DDR, UART_HDX_BIT); } #endif /* * Verify the result. */ if (Avr32UsartGetFlowControl() != flags) { return -1; } 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 Avr32UsartTxStart(void) { #ifdef UART_HDX_BIT if (hdx_control) { /* Enable half duplex transmitter. */ UART_HDX_TX(UART_HDX_PORT, UART_HDX_BIT); } #endif /* Enable transmit interrupts. */ outr(USARTn_BASE + AVR32_USART_IER, AVR32_USART_IER_TXRDY_MASK); } /*! * \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 Avr32UsartRxStart(void) { /* * Do any required software flow control. */ if (flow_control && (flow_control & XOFF_SENT) != 0) { NutEnterCritical(); if (inr(USARTn_BASE + AVR32_USART_CSR) & AVR32_USART_CSR_TXRDY) { outr(USARTn_BASE + AVR32_USART_THR, (ASCII_XON << AVR32_USART_THR_TXCHR_OFFSET) & AVR32_USART_THR_TXCHR_MASK ); flow_control &= ~XON_PENDING; } else { flow_control |= XON_PENDING; } flow_control &= ~(XOFF_SENT | XOFF_PENDING); NutExitCritical(); } #ifdef UART_RTS_BIT if (rts_control) { /* Enable RTS. */ cbi(UART_RTS_PORT, UART_RTS_BIT); } #endif } /* * \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 Avr32UsartInit(void) { /* * Register receive and transmit interrupts. */ if (NutRegisterIrqHandler(&SIG_UART, Avr32UsartInterrupt, &dcb_usart)) { return -1; } /* Disable GPIO on UART tx/rx pins. */ gpio_enable_module_pin(USART_RX_PIN, USART_RX_FUNCTION); gpio_enable_module_pin(USART_TX_PIN, USART_TX_FUNCTION); /* Disable all USART interrupts. ** Interrupts needed should be set explicitly on every reset. */ outr(USARTn_BASE + AVR32_USART_IDR, AVR32_USART_IDR_MASK); inr(USARTn_BASE + AVR32_USART_CSR); /* Reset mode and other registers that could cause unpredictable behavior after reset. */ outr(USARTn_BASE + AVR32_USART_MR, 0); outr(USARTn_BASE + AVR32_USART_RTOR, 0); outr(USARTn_BASE + AVR32_USART_TTGR, 0); /* Shutdown TX and RX (will be re-enabled when setup has successfully completed), ** reset status bits and turn off DTR and RTS. */ outr(USARTn_BASE + AVR32_USART_CR, AVR32_USART_CR_RSTRX_MASK | AVR32_USART_CR_RSTTX_MASK | AVR32_USART_CR_RSTSTA_MASK | AVR32_USART_CR_RSTIT_MASK | AVR32_USART_CR_RSTNACK_MASK | #ifdef AVR32_USART_CR_DTRDIS_MASK AVR32_USART_CR_DTRDIS_MASK | #endif AVR32_USART_CR_RTSDIS_MASK); outr(USARTn_BASE + AVR32_USART_MR, inr(USARTn_BASE + AVR32_USART_MR) | (8 - 5) << AVR32_USART_MR_CHRL_OFFSET | /* 8 bit character length */ AVR32_USART_MR_PAR_NONE << AVR32_USART_MR_PAR_OFFSET | /* No parity */ AVR32_USART_MR_CHMODE_NORMAL << AVR32_USART_MR_CHMODE_OFFSET | AVR32_USART_MR_NBSTOP_1 << AVR32_USART_MR_NBSTOP_OFFSET); /* Set normal mode. */ outr(USARTn_BASE + AVR32_USART_MR, (inr(USARTn_BASE + AVR32_USART_MR) & ~AVR32_USART_MR_MODE_MASK) | AVR32_USART_MR_MODE_NORMAL << AVR32_USART_MR_MODE_OFFSET); /* Enable input and output. */ outr(USARTn_BASE + AVR32_USART_CR, inr(USARTn_BASE + AVR32_USART_CR) | AVR32_USART_CR_RXEN_MASK | AVR32_USART_CR_TXEN_MASK); 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 Avr32UsartDeinit(void) { /* Deregister receive and transmit interrupts. */ NutRegisterIrqHandler(&SIG_UART, 0, 0); /* Shutdown TX and RX (will be re-enabled when setup has successfully completed), ** reset status bits and turn off DTR and RTS. */ outr(USARTn_BASE + AVR32_USART_CR, AVR32_USART_CR_RSTRX_MASK | AVR32_USART_CR_RSTTX_MASK | AVR32_USART_CR_RSTSTA_MASK | AVR32_USART_CR_RSTIT_MASK | AVR32_USART_CR_RSTNACK_MASK | #ifdef AVR32_USART_CR_DTRDIS_MASK AVR32_USART_CR_DTRDIS_MASK | #endif AVR32_USART_CR_RTSDIS_MASK); /* Disable all UART interrupts. */ outr(USARTn_BASE + AVR32_USART_IDR, AVR32_USART_IDR_MASK); inr(USARTn_BASE + AVR32_USART_CSR); /* Disable UART clock. */ /* Enable GPIO on UART tx/rx pins. */ /* * 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. */ #ifdef UART_HDX_BIT /* Deregister transmit complete interrupt. */ if (hdx_control) { hdx_control = 0; NutRegisterIrqHandler(&sig_UART_TRANS, 0, 0); } #endif #ifdef UART_CTS_BIT if (cts_sense) { cts_sense = 0; cbi(UART_CTS_DDR, UART_CTS_BIT); /* Deregister CTS sense interrupt. */ NutRegisterIrqHandler(&UART_CTS_SIGNAL, 0, 0); } #endif #ifdef UART_RTS_BIT if (rts_control) { rts_control = 0; cbi(UART_RTS_DDR, UART_RTS_BIT); } #endif return 0; } /*@}*/