/** | |
* \file | |
* | |
* \brief SAM D20 Serial Peripheral Interface Driver | |
* | |
* Copyright (C) 2012-2013 Atmel Corporation. All rights reserved. | |
* | |
* \asf_license_start | |
* | |
* \page License | |
* | |
* 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. The name of Atmel may not be used to endorse or promote products derived | |
* from this software without specific prior written permission. | |
* | |
* 4. This software may only be redistributed and used in connection with an | |
* Atmel microcontroller product. | |
* | |
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED | |
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE | |
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. | |
* | |
* \asf_license_stop | |
* | |
*/ | |
#include "sercom.h" | |
#define SHIFT 32 | |
#if !defined(__DOXYGEN__) | |
/** | |
* \internal Configuration structure to save current gclk status. | |
*/ | |
struct _sercom_conf { | |
/* Status of gclk generator initialization. */ | |
bool generator_is_set; | |
/* Sercom gclk generator used. */ | |
enum gclk_generator generator_source; | |
}; | |
static struct _sercom_conf _sercom_config; | |
/** | |
* \internal Calculate synchronous baudrate value (SPI/UART) | |
*/ | |
enum status_code _sercom_get_sync_baud_val( | |
const uint32_t baudrate, | |
const uint32_t external_clock, | |
uint16_t *const baudvalue) | |
{ | |
/* Baud value variable */ | |
uint16_t baud_calculated = 0; | |
/* Check if baudrate is outside of valid range. */ | |
if (baudrate > (external_clock / 2)) { | |
/* Return with error code */ | |
return STATUS_ERR_BAUDRATE_UNAVAILABLE; | |
} | |
/* Calculate BAUD value from clock frequency and baudrate */ | |
baud_calculated = (external_clock / (2 * baudrate)) - 1; | |
/* Check if BAUD value is more than 255, which is maximum | |
* for synchronous mode */ | |
if (baud_calculated > 0xFF) { | |
/* Return with an error code */ | |
return STATUS_ERR_BAUDRATE_UNAVAILABLE; | |
} else { | |
*baudvalue = baud_calculated; | |
return STATUS_OK; | |
} | |
} | |
/** | |
* \internal Calculate asynchronous baudrate value (UART) | |
*/ | |
enum status_code _sercom_get_async_baud_val( | |
const uint32_t baudrate, | |
const uint32_t peripheral_clock, | |
uint16_t *const baudval) | |
{ | |
/* Temporary variables */ | |
uint64_t ratio = 0; | |
uint64_t scale = 0; | |
uint64_t baud_calculated = 0; | |
/* Check if the baudrate is outside of valid range */ | |
if ((baudrate * 16) >= peripheral_clock) { | |
/* Return with error code */ | |
return STATUS_ERR_BAUDRATE_UNAVAILABLE; | |
} | |
/* Calculate the BAUD value */ | |
ratio = ((16 * (uint64_t)baudrate) << SHIFT) / peripheral_clock; | |
scale = ((uint64_t)1 << SHIFT) - ratio; | |
baud_calculated = (65536 * scale) >> SHIFT; | |
*baudval = baud_calculated; | |
return STATUS_OK; | |
} | |
#endif | |
/** | |
* \brief Set GCLK channel to generator. | |
* | |
* This will set the appropriate GCLK channel to the requested GCLK generator. | |
* This will set the generator for all SERCOM instances, and the user will thus | |
* only be able to set the same generator that has previously been set, if any. | |
* | |
* After the generator has been set the first time, the generator can be changed | |
* using the \c force_change flag. | |
* | |
* \param[in] generator_source The generator to use for SERCOM. | |
* \param[in] force_change Force change the generator. | |
* | |
* \return Status code indicating the GCLK generator change operation. | |
* \retval STATUS_OK If the generator update request was | |
* successful. | |
* \retval STATUS_ERR_ALREADY_INITIALIZED If a generator was already configured | |
* and the new configuration was not | |
* forced. | |
*/ | |
enum status_code sercom_set_gclk_generator( | |
const enum gclk_generator generator_source, | |
const bool force_change) | |
{ | |
/* Check if valid option. */ | |
if (!_sercom_config.generator_is_set || force_change) { | |
/* Create and fill a GCLK configuration structure for the new config. */ | |
struct system_gclk_chan_config gclk_chan_conf; | |
system_gclk_chan_get_config_defaults(&gclk_chan_conf); | |
gclk_chan_conf.source_generator = generator_source; | |
system_gclk_chan_set_config(SERCOM_GCLK_ID, &gclk_chan_conf); | |
system_gclk_chan_enable(SERCOM_GCLK_ID); | |
/* Save config. */ | |
_sercom_config.generator_source = generator_source; | |
_sercom_config.generator_is_set = true; | |
return STATUS_OK; | |
} else if (generator_source == _sercom_config.generator_source) { | |
/* Return status OK if same config. */ | |
return STATUS_OK; | |
} | |
/* Return invalid config to already initialized GCLK. */ | |
return STATUS_ERR_ALREADY_INITIALIZED; | |
} | |
/** \internal | |
* Creates a switch statement case entry to convert a SERCOM instance and pad | |
* index to the default SERCOM pad MUX setting. | |
*/ | |
#define _SERCOM_PAD_DEFAULTS_CASE(n, pad) \ | |
case (uintptr_t)SERCOM##n: \ | |
switch (pad) { \ | |
case 0: \ | |
return SERCOM##n##_PAD0_DEFAULT; \ | |
case 1: \ | |
return SERCOM##n##_PAD1_DEFAULT; \ | |
case 2: \ | |
return SERCOM##n##_PAD2_DEFAULT; \ | |
case 3: \ | |
return SERCOM##n##_PAD3_DEFAULT; \ | |
} \ | |
break; | |
/** | |
* \internal Gets the default PAD pinout for a given SERCOM. | |
* | |
* Returns the PINMUX settings for the given SERCOM and pad. This is used | |
* for default configuration of pins. | |
* | |
* \param[in] sercom_module Pointer to the SERCOM module | |
* \param[in] pad PAD to get default pinout for | |
* | |
* \returns The default PINMUX for the given SERCOM instance and PAD | |
* | |
*/ | |
uint32_t _sercom_get_default_pad( | |
Sercom *const sercom_module, | |
const uint8_t pad) | |
{ | |
switch ((uintptr_t)sercom_module) { | |
/* Auto-generate a lookup table for the default SERCOM pad defaults */ | |
MREPEAT(SERCOM_INST_NUM, _SERCOM_PAD_DEFAULTS_CASE, pad) | |
} | |
Assert(false); | |
return 0; | |
} |