1 /**
2 * \file
3 *
4 * \brief SAM Serial Peripheral Interface Driver
5 *
6 * Copyright (C) 2012-2016 Atmel Corporation. All rights reserved.
7 *
8 * \asf_license_start
9 *
10 * \page License
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions are met:
14 *
15 * 1. Redistributions of source code must retain the above copyright notice,
16 * this list of conditions and the following disclaimer.
17 *
18 * 2. Redistributions in binary form must reproduce the above copyright notice,
19 * this list of conditions and the following disclaimer in the documentation
20 * and/or other materials provided with the distribution.
21 *
22 * 3. The name of Atmel may not be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 *
25 * 4. This software may only be redistributed and used in connection with an
26 * Atmel microcontroller product.
27 *
28 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
29 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
30 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
31 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
32 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
37 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38 * POSSIBILITY OF SUCH DAMAGE.
39 *
40 * \asf_license_stop
41 *
42 */
43 /*
44 * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
45 */
46 #include "sercom.h"
47
48 #define SHIFT 32
49 #define BAUD_INT_MAX 8192
50 #define BAUD_FP_MAX 8
51
52 #if !defined(__DOXYGEN__)
53 /**
54 * \internal Configuration structure to save current gclk status.
55 */
56 struct _sercom_conf {
57 /* Status of gclk generator initialization */
58 bool generator_is_set;
59 /* Sercom gclk generator used */
60 enum gclk_generator generator_source;
61 };
62
63 static struct _sercom_conf _sercom_config;
64
65
66 /**
67 * \internal Calculate 64 bit division, ref can be found in
68 * http://en.wikipedia.org/wiki/Division_algorithm#Long_division
69 */
long_division(uint64_t n,uint64_t d)70 static uint64_t long_division(uint64_t n, uint64_t d)
71 {
72 int32_t i;
73 uint64_t q = 0, r = 0, bit_shift;
74 for (i = 63; i >= 0; i--) {
75 bit_shift = (uint64_t)1 << i;
76
77 r = r << 1;
78
79 if (n & bit_shift) {
80 r |= 0x01;
81 }
82
83 if (r >= d) {
84 r = r - d;
85 q |= bit_shift;
86 }
87 }
88
89 return q;
90 }
91
92 /**
93 * \internal Calculate synchronous baudrate value (SPI/UART)
94 */
_sercom_get_sync_baud_val(const uint32_t baudrate,const uint32_t external_clock,uint16_t * const baudvalue)95 enum status_code _sercom_get_sync_baud_val(
96 const uint32_t baudrate,
97 const uint32_t external_clock,
98 uint16_t *const baudvalue)
99 {
100 /* Baud value variable */
101 uint16_t baud_calculated = 0;
102 uint32_t clock_value = external_clock;
103
104
105 /* Check if baudrate is outside of valid range */
106 if (baudrate > (external_clock / 2)) {
107 /* Return with error code */
108 return STATUS_ERR_BAUDRATE_UNAVAILABLE;
109 }
110
111 /* Calculate BAUD value from clock frequency and baudrate */
112 clock_value = external_clock / 2;
113 while (clock_value >= baudrate) {
114 clock_value = clock_value - baudrate;
115 baud_calculated++;
116 }
117 baud_calculated = baud_calculated - 1;
118
119 /* Check if BAUD value is more than 255, which is maximum
120 * for synchronous mode */
121 if (baud_calculated > 0xFF) {
122 /* Return with an error code */
123 return STATUS_ERR_BAUDRATE_UNAVAILABLE;
124 } else {
125 *baudvalue = baud_calculated;
126 return STATUS_OK;
127 }
128 }
129
130 /**
131 * \internal Calculate asynchronous baudrate value (UART)
132 */
_sercom_get_async_baud_val(const uint32_t baudrate,const uint32_t peripheral_clock,uint16_t * const baudval,enum sercom_asynchronous_operation_mode mode,enum sercom_asynchronous_sample_num sample_num)133 enum status_code _sercom_get_async_baud_val(
134 const uint32_t baudrate,
135 const uint32_t peripheral_clock,
136 uint16_t *const baudval,
137 enum sercom_asynchronous_operation_mode mode,
138 enum sercom_asynchronous_sample_num sample_num)
139 {
140 /* Temporary variables */
141 uint64_t ratio = 0;
142 uint64_t scale = 0;
143 uint64_t baud_calculated = 0;
144 uint8_t baud_fp;
145 uint32_t baud_int = 0;
146 uint64_t temp1;
147
148 /* Check if the baudrate is outside of valid range */
149 if ((baudrate * sample_num) > peripheral_clock) {
150 /* Return with error code */
151 return STATUS_ERR_BAUDRATE_UNAVAILABLE;
152 }
153
154 if(mode == SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC) {
155 /* Calculate the BAUD value */
156 temp1 = ((sample_num * (uint64_t)baudrate) << SHIFT);
157 ratio = long_division(temp1, peripheral_clock);
158 scale = ((uint64_t)1 << SHIFT) - ratio;
159 baud_calculated = (65536 * scale) >> SHIFT;
160 } else if(mode == SERCOM_ASYNC_OPERATION_MODE_FRACTIONAL) {
161 temp1 = ((uint64_t)baudrate * sample_num);
162 baud_int = long_division( peripheral_clock, temp1);
163 if(baud_int > BAUD_INT_MAX) {
164 return STATUS_ERR_BAUDRATE_UNAVAILABLE;
165 }
166 temp1 = long_division( 8 * (uint64_t)peripheral_clock, temp1);
167 baud_fp = temp1 - 8 * baud_int;
168 baud_calculated = baud_int | (baud_fp << 13);
169 }
170
171 *baudval = baud_calculated;
172 return STATUS_OK;
173 }
174 #endif
175
176 /**
177 * \brief Set GCLK channel to generator.
178 *
179 * This will set the appropriate GCLK channel to the requested GCLK generator.
180 * This will set the generator for all SERCOM instances, and the user will thus
181 * only be able to set the same generator that has previously been set, if any.
182 *
183 * After the generator has been set the first time, the generator can be changed
184 * using the \c force_change flag.
185 *
186 * \param[in] generator_source The generator to use for SERCOM.
187 * \param[in] force_change Force change the generator.
188 *
189 * \return Status code indicating the GCLK generator change operation.
190 * \retval STATUS_OK If the generator update request was
191 * successful.
192 * \retval STATUS_ERR_ALREADY_INITIALIZED If a generator was already configured
193 * and the new configuration was not
194 * forced.
195 */
sercom_set_gclk_generator(const enum gclk_generator generator_source,const bool force_change)196 enum status_code sercom_set_gclk_generator(
197 const enum gclk_generator generator_source,
198 const bool force_change)
199 {
200 /* Check if valid option */
201 if (!_sercom_config.generator_is_set || force_change) {
202 /* Create and fill a GCLK configuration structure for the new config */
203 struct system_gclk_chan_config gclk_chan_conf;
204 system_gclk_chan_get_config_defaults(&gclk_chan_conf);
205 gclk_chan_conf.source_generator = generator_source;
206 system_gclk_chan_set_config(SERCOM_GCLK_ID, &gclk_chan_conf);
207 system_gclk_chan_enable(SERCOM_GCLK_ID);
208
209 /* Save config */
210 _sercom_config.generator_source = generator_source;
211 _sercom_config.generator_is_set = true;
212
213 return STATUS_OK;
214 } else if (generator_source == _sercom_config.generator_source) {
215 /* Return status OK if same config */
216 return STATUS_OK;
217 }
218
219 /* Return invalid config to already initialized GCLK */
220 return STATUS_ERR_ALREADY_INITIALIZED;
221 }
222
223 /** \internal
224 * Creates a switch statement case entry to convert a SERCOM instance and pad
225 * index to the default SERCOM pad MUX setting.
226 */
227 #define _SERCOM_PAD_DEFAULTS_CASE(n, pad) \
228 case (uintptr_t)SERCOM##n: \
229 switch (pad) { \
230 case 0: \
231 return SERCOM##n##_PAD0_DEFAULT; \
232 case 1: \
233 return SERCOM##n##_PAD1_DEFAULT; \
234 case 2: \
235 return SERCOM##n##_PAD2_DEFAULT; \
236 case 3: \
237 return SERCOM##n##_PAD3_DEFAULT; \
238 } \
239 break;
240
241 /**
242 * \internal Gets the default PAD pinout for a given SERCOM.
243 *
244 * Returns the pinmux settings for the given SERCOM and pad. This is used
245 * for default configuration of pins.
246 *
247 * \param[in] sercom_module Pointer to the SERCOM module
248 * \param[in] pad PAD to get default pinout for
249 *
250 * \returns The default pinmux for the given SERCOM instance and PAD
251 *
252 */
_sercom_get_default_pad(Sercom * const sercom_module,const uint8_t pad)253 uint32_t _sercom_get_default_pad(
254 Sercom *const sercom_module,
255 const uint8_t pad)
256 {
257 switch ((uintptr_t)sercom_module) {
258 /* Auto-generate a lookup table for the default SERCOM pad defaults */
259 MREPEAT(SERCOM_INST_NUM, _SERCOM_PAD_DEFAULTS_CASE, pad)
260 }
261
262 Assert(false);
263 return 0;
264 }
265
266 /**
267 * \internal
268 * Find index of given instance.
269 *
270 * \param[in] sercom_instance Instance pointer.
271 *
272 * \return Index of given instance.
273 */
_sercom_get_sercom_inst_index(Sercom * const sercom_instance)274 uint8_t _sercom_get_sercom_inst_index(
275 Sercom *const sercom_instance)
276 {
277 /* Save all available SERCOM instances for compare */
278 Sercom *sercom_instances[SERCOM_INST_NUM] = SERCOM_INSTS;
279
280 /* Find index for sercom instance */
281 for (uint32_t i = 0; i < SERCOM_INST_NUM; i++) {
282 if ((uintptr_t)sercom_instance == (uintptr_t)sercom_instances[i]) {
283 return i;
284 }
285 }
286
287 /* Invalid data given */
288 Assert(false);
289 return 0;
290 }
291