sercom.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  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. #define SHIFT 32
  48. #define BAUD_INT_MAX 8192
  49. #define BAUD_FP_MAX 8
  50. #if !defined(__DOXYGEN__)
  51. /**
  52. * \internal Configuration structure to save current gclk status.
  53. */
  54. struct _sercom_conf {
  55. /* Status of gclk generator initialization */
  56. bool generator_is_set;
  57. /* Sercom gclk generator used */
  58. enum gclk_generator generator_source;
  59. };
  60. static struct _sercom_conf _sercom_config;
  61. /**
  62. * \internal Calculate 64 bit division, ref can be found in
  63. * http://en.wikipedia.org/wiki/Division_algorithm#Long_division
  64. */
  65. static uint64_t long_division(uint64_t n, uint64_t d)
  66. {
  67. int32_t i;
  68. uint64_t q = 0, r = 0, bit_shift;
  69. for (i = 63; i >= 0; i--) {
  70. bit_shift = (uint64_t)1 << i;
  71. r = r << 1;
  72. if (n & bit_shift) {
  73. r |= 0x01;
  74. }
  75. if (r >= d) {
  76. r = r - d;
  77. q |= bit_shift;
  78. }
  79. }
  80. return q;
  81. }
  82. /**
  83. * \internal Calculate synchronous baudrate value (SPI/UART)
  84. */
  85. enum status_code _sercom_get_sync_baud_val(
  86. const uint32_t baudrate,
  87. const uint32_t external_clock,
  88. uint16_t *const baudvalue)
  89. {
  90. /* Baud value variable */
  91. uint16_t baud_calculated = 0;
  92. uint32_t clock_value = external_clock;
  93. /* Check if baudrate is outside of valid range */
  94. if (baudrate > (external_clock / 2)) {
  95. /* Return with error code */
  96. return STATUS_ERR_BAUDRATE_UNAVAILABLE;
  97. }
  98. /* Calculate BAUD value from clock frequency and baudrate */
  99. clock_value = external_clock / 2;
  100. while (clock_value >= baudrate) {
  101. clock_value = clock_value - baudrate;
  102. baud_calculated++;
  103. }
  104. baud_calculated = baud_calculated - 1;
  105. /* Check if BAUD value is more than 255, which is maximum
  106. * for synchronous mode */
  107. if (baud_calculated > 0xFF) {
  108. /* Return with an error code */
  109. return STATUS_ERR_BAUDRATE_UNAVAILABLE;
  110. } else {
  111. *baudvalue = baud_calculated;
  112. return STATUS_OK;
  113. }
  114. }
  115. /**
  116. * \internal Calculate asynchronous baudrate value (UART)
  117. */
  118. enum status_code _sercom_get_async_baud_val(
  119. const uint32_t baudrate,
  120. const uint32_t peripheral_clock,
  121. uint16_t *const baudval,
  122. enum sercom_asynchronous_operation_mode mode,
  123. enum sercom_asynchronous_sample_num sample_num)
  124. {
  125. /* Temporary variables */
  126. uint64_t ratio = 0;
  127. uint64_t scale = 0;
  128. uint64_t baud_calculated = 0;
  129. uint8_t baud_fp;
  130. uint32_t baud_int = 0;
  131. uint64_t temp1;
  132. /* Check if the baudrate is outside of valid range */
  133. if ((baudrate * sample_num) > peripheral_clock) {
  134. /* Return with error code */
  135. return STATUS_ERR_BAUDRATE_UNAVAILABLE;
  136. }
  137. if(mode == SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC) {
  138. /* Calculate the BAUD value */
  139. temp1 = ((sample_num * (uint64_t)baudrate) << SHIFT);
  140. ratio = long_division(temp1, peripheral_clock);
  141. scale = ((uint64_t)1 << SHIFT) - ratio;
  142. baud_calculated = (65536 * scale) >> SHIFT;
  143. } else if(mode == SERCOM_ASYNC_OPERATION_MODE_FRACTIONAL) {
  144. temp1 = ((uint64_t)baudrate * sample_num);
  145. baud_int = long_division( peripheral_clock, temp1);
  146. if(baud_int > BAUD_INT_MAX) {
  147. return STATUS_ERR_BAUDRATE_UNAVAILABLE;
  148. }
  149. temp1 = long_division( 8 * (uint64_t)peripheral_clock, temp1);
  150. baud_fp = temp1 - 8 * baud_int;
  151. baud_calculated = baud_int | (baud_fp << 13);
  152. }
  153. *baudval = baud_calculated;
  154. return STATUS_OK;
  155. }
  156. #endif
  157. /**
  158. * \brief Set GCLK channel to generator.
  159. *
  160. * This will set the appropriate GCLK channel to the requested GCLK generator.
  161. * This will set the generator for all SERCOM instances, and the user will thus
  162. * only be able to set the same generator that has previously been set, if any.
  163. *
  164. * After the generator has been set the first time, the generator can be changed
  165. * using the \c force_change flag.
  166. *
  167. * \param[in] generator_source The generator to use for SERCOM.
  168. * \param[in] force_change Force change the generator.
  169. *
  170. * \return Status code indicating the GCLK generator change operation.
  171. * \retval STATUS_OK If the generator update request was
  172. * successful.
  173. * \retval STATUS_ERR_ALREADY_INITIALIZED If a generator was already configured
  174. * and the new configuration was not
  175. * forced.
  176. */
  177. enum status_code sercom_set_gclk_generator(
  178. const enum gclk_generator generator_source,
  179. const bool force_change)
  180. {
  181. /* Check if valid option */
  182. if (!_sercom_config.generator_is_set || force_change) {
  183. /* Create and fill a GCLK configuration structure for the new config */
  184. struct system_gclk_chan_config gclk_chan_conf;
  185. system_gclk_chan_get_config_defaults(&gclk_chan_conf);
  186. gclk_chan_conf.source_generator = generator_source;
  187. system_gclk_chan_set_config(SERCOM_GCLK_ID, &gclk_chan_conf);
  188. system_gclk_chan_enable(SERCOM_GCLK_ID);
  189. /* Save config */
  190. _sercom_config.generator_source = generator_source;
  191. _sercom_config.generator_is_set = true;
  192. return STATUS_OK;
  193. } else if (generator_source == _sercom_config.generator_source) {
  194. /* Return status OK if same config */
  195. return STATUS_OK;
  196. }
  197. /* Return invalid config to already initialized GCLK */
  198. return STATUS_ERR_ALREADY_INITIALIZED;
  199. }
  200. /** \internal
  201. * Creates a switch statement case entry to convert a SERCOM instance and pad
  202. * index to the default SERCOM pad MUX setting.
  203. */
  204. #define _SERCOM_PAD_DEFAULTS_CASE(n, pad) \
  205. case (uintptr_t)SERCOM##n: \
  206. switch (pad) { \
  207. case 0: \
  208. return SERCOM##n##_PAD0_DEFAULT; \
  209. case 1: \
  210. return SERCOM##n##_PAD1_DEFAULT; \
  211. case 2: \
  212. return SERCOM##n##_PAD2_DEFAULT; \
  213. case 3: \
  214. return SERCOM##n##_PAD3_DEFAULT; \
  215. } \
  216. break;
  217. /**
  218. * \internal Gets the default PAD pinout for a given SERCOM.
  219. *
  220. * Returns the pinmux settings for the given SERCOM and pad. This is used
  221. * for default configuration of pins.
  222. *
  223. * \param[in] sercom_module Pointer to the SERCOM module
  224. * \param[in] pad PAD to get default pinout for
  225. *
  226. * \returns The default pinmux for the given SERCOM instance and PAD
  227. *
  228. */
  229. uint32_t _sercom_get_default_pad(
  230. Sercom *const sercom_module,
  231. const uint8_t pad)
  232. {
  233. switch ((uintptr_t)sercom_module) {
  234. /* Auto-generate a lookup table for the default SERCOM pad defaults */
  235. MREPEAT(SERCOM_INST_NUM, _SERCOM_PAD_DEFAULTS_CASE, pad)
  236. }
  237. Assert(false);
  238. return 0;
  239. }
  240. /**
  241. * \internal
  242. * Find index of given instance.
  243. *
  244. * \param[in] sercom_instance Instance pointer.
  245. *
  246. * \return Index of given instance.
  247. */
  248. uint8_t _sercom_get_sercom_inst_index(
  249. Sercom *const sercom_instance)
  250. {
  251. /* Save all available SERCOM instances for compare */
  252. Sercom *sercom_instances[SERCOM_INST_NUM] = SERCOM_INSTS;
  253. /* Find index for sercom instance */
  254. for (uint32_t i = 0; i < SERCOM_INST_NUM; i++) {
  255. if ((uintptr_t)sercom_instance == (uintptr_t)sercom_instances[i]) {
  256. return i;
  257. }
  258. }
  259. /* Invalid data given */
  260. Assert(false);
  261. return 0;
  262. }