/**************************************************************************** * arch/arm/src/samd/sam_lowputc.c * * Copyright (C) 2014 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * References: * 1. "Atmel SAM D20J / SAM D20G / SAM D20E ARM-Based Microcontroller * Datasheet", 42129J–SAM–12/2013 * 2. Atmel sample code. This code has an ASF license with is compatible * with the NuttX BSD license, but includes the provision that this * code not be used in non-Atmel products. That sample code was used * only as a reference so I believe that only the NuttX BSD license * applies. * * 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 NuttX nor the names of its 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. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include "up_arch.h" #include "sam_config.h" #include #include "chip/sam_pm.h" #include "chip/sam_gclk.h" #include "chip/sam_usart.h" #include "sam_usart.h" #include "sam_lowputc.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #ifndef OK # define OK 0 #endif /**************************************************************************** * Private Data ****************************************************************************/ /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: sam_wait_synchronization * * Description: * Wait until the SERCOM USART reports that it is synchronized. * ****************************************************************************/ #ifdef SAMD_HAVE_USART static void sam_wait_synchronization(const struct sam_usart_config_s * const config) { while ((getreg16(config->base + SAM_USART_STATUS_OFFSET) & USART_STATUS_SYNCBUSY) != 0); } #endif /**************************************************************************** * Name: sam_usart_configure * * Description: * Configure the SERCOM USART operating mode (as a normal UART). * ****************************************************************************/ #ifdef SAMD_HAVE_USART static inline int sam_usart_configure(const struct sam_usart_config_s * const config) { uint32_t ctrla; uint32_t ctrlb; uint16_t baud; uint64_t tmp; /* Calculate BAUD divider from the source clock frequency and desired. * baud. For asynchronous mode, the formula for the baud generation is * * Fbaud = (Frefclk / 16) * (1 - (BAUD / 65,536)) * * Or, * * BAUD = 65,536 * (1 - 16 * (Fbaud / Fref)) * = 65,536 - 16 * 65,536 * Fbaud / Fref * * Example: Fref = 48MHz and Fbaud = 9600 * * BAUD = 65,326 * Fbaud = 9600 * * Example: Fref = 48MHz and Fbaud = 115,200 * * BAUD = 63,019 * Fbaud = 115,219 */ tmp = (uint64_t)config->baud << 20; tmp = (tmp + (config->frequency >> 1)) / config->frequency; /* Verify that the calculated result is within range */ if (tmp < 1 || tmp > UINT16_MAX) { return -ERANGE; } baud = 65536 - (uint16_t)tmp; /* Disable all USART interrupts */ putreg8(USART_INT_ALL, config->base + SAM_USART_INTENCLR_OFFSET); /* Wait until synchronization is complete */ sam_wait_synchronization(config); /* Set baud divisor */ putreg16((uint16_t)baud, config->base + SAM_USART_BAUD_OFFSET); /* Configure the USART CTRLA and CTRLB registers */ ctrla = (USART_CTRLA_MODE_INTUSART | (uint32_t)config->muxconfig | USART_CTRLA_ASYNCH | USART_CTRLA_CPOL_NORMAL | USART_CTRLA_LSBFIRST); ctrlb = (USART_CTRLB_TXEN | USART_CTRLB_RXEN); /* Set the number of stop bits */ if (config->stopbits2) { ctrlb |= USART_CTRLB_SBMODE; } /* Set the USART word size */ switch (config->bits) { case 5: ctrlb |= USART_CTRLB_CHSIZE_5BITS; break; case 6: ctrlb |= USART_CTRLB_CHSIZE_6BITS; break; case 7: ctrlb |= USART_CTRLB_CHSIZE_7BITS; break; default: case 8: break; case 9: ctrlb |= USART_CTRLB_CHSIZE_9BITS; break; } /* Set parity mode */ switch (config->parity) { default: case 0: /* None */ break; case 1: /* Odd */ ctrlb |= USART_CTRLB_PODD; /* Fall through */ case 2: /* Even */ ctrla |= USART_CTRLA_FORM_PARITY; break; } #if 0 /* Not supported */ /* Set run mode during device sleep */ if (config->runinstandby) { /* Enable in sleep mode */ ctrla |= USART_CTRLA_RUNSTDBY; } #endif /* Wait until synchronization is complete */ sam_wait_synchronization(config); /* Write configuration to CTRLB */ putreg32(ctrlb, config->base + SAM_USART_CTRLB_OFFSET); /* Wait until synchronization is complete */ sam_wait_synchronization(config); /* Write configuration to CTRLA */ putreg32(ctrla, config->base + SAM_USART_CTRLA_OFFSET); return OK; } #endif /**************************************************************************** * Name: sam_pad_configure * * Description: * Configure the SERCOM USART pads. * ****************************************************************************/ #ifdef SAMD_HAVE_USART static inline void sam_pad_configure(const struct sam_usart_config_s * const config) { /* Configure SERCOM pads */ if (config->pad0 != 0) { sam_configport(config->pad0); } if (config->pad1 != 0) { sam_configport(config->pad1); } if (config->pad2 != 0) { sam_configport(config->pad2); } if (config->pad3 != 0) { sam_configport(config->pad3); } } #endif /**************************************************************************** * Name: sam_usart_internal * * Description: * Set the configuration of a SERCOM for provided USART configuration. * This configures the SERCOM as a USART, but does not configure USART * interrupts or enable the USART. * *****************************************************************************/ #ifdef SAMD_HAVE_USART int sam_usart_internal(const struct sam_usart_config_s * const config) { int ret; /* Enable clocking to the SERCOM module in PM */ sercom_enable(config->sercom); /* Configure the GCLKs for the SERCOM module */ sercom_coreclk_configure(config->sercom, config->gclkgen, false); sercom_slowclk_configure(BOARD_SERCOM_SLOW_GCLKGEN); /* Set USART configuration according to the board configuration */ ret = sam_usart_configure(config); if (ret == OK) { /* Configure USART pins */ sam_pad_configure(config); } return ret; } #endif /**************************************************************************** * Name: sam_usart_enable * * Description: * Enable the SERCOM USART (without enabling interrupts). * ****************************************************************************/ #ifdef SAMD_HAVE_USART static inline void sam_usart_enable(const struct sam_usart_config_s * const config) { uintptr_t regaddr; uint32_t regval; /* Wait until synchronization is complete */ sam_wait_synchronization(config); /* Enable USART module */ regaddr = config->base + SAM_USART_CTRLA_OFFSET; regval = getreg32(regaddr); regval |= USART_CTRLA_ENABLE; putreg32(regval, regaddr); } #endif /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: sam_lowsetup * * Description: * Called at the very beginning of _start. Performs low level * initialization. * ****************************************************************************/ void sam_lowsetup(void) { #ifdef HAVE_SERIAL_CONSOLE /* Configure and enable the console USART */ VERIFY(sam_usart_internal(&g_consoleconfig)); sam_usart_enable(&g_consoleconfig); #endif } /**************************************************************************** * Name: sam_usart_initialize * * Description: * Set the configuration of a SERCOM for provided USART configuration. * This configures the SERCOM as a USART, but does not configure USART * interrupts or enable the USART. * *****************************************************************************/ #ifdef SAMD_HAVE_USART int sam_usart_initialize(const struct sam_usart_config_s * const config) { irqstate_t flags; int ret; /* Reset the SERCOM so that we know that it is in its initial state */ flags = irqsave(); sam_usart_reset(config); /* Just invoke the internal implementation, but with interrupts disabled * so that the operation is atomic. */ ret = sam_usart_internal(config); irqrestore(flags); return ret; } #endif /**************************************************************************** * Name: sam_usart_reset * * Description: * Reset the USART SERCOM. This restores all SERCOM register to the * initial state and disables the SERCOM. * *****************************************************************************/ #ifdef SAMD_HAVE_USART void sam_usart_reset(const struct sam_usart_config_s * const config) { uintptr_t regaddr = config->base + SAM_USART_CTRLA_OFFSET; uint32_t regval; /* Reset the SERCOM by setting the SWRST bit in the CTRLA register. When * the reset completes, the SERCOM will registers will be restored to there * initial state and the SERCOM will be disabled. */ regval = getreg32(regaddr); regval |= USART_CTRLA_SWRST; putreg32(regval, regaddr); /* Wait for the reset to complete */ while ((getreg32(regaddr) & USART_CTRLA_SWRST) != 0); } #endif /**************************************************************************** * Name: sam_lowputc * * Description: * Output one character to the USART using a simple polling method. * *****************************************************************************/ #ifdef HAVE_SERIAL_CONSOLE void sam_lowputc(uint32_t ch) { uintptr_t base = g_consoleconfig.base; uintptr_t intflag = base + SAM_USART_INTFLAG_OFFSET; /* Wait for the USART to be ready for new TX data */ while ((getreg8(intflag) & USART_INT_DRE) == 0); /* Wait until synchronization is complete */ sam_wait_synchronization(&g_consoleconfig); /* Write data to USART module */ putreg16((uint16_t)ch, base + SAM_USART_DATA_OFFSET); /* Wait until data is sent */ while ((getreg8(intflag) & USART_INT_TXC) == 0); } #endif