Contiki 3.x
cc26xx-uart.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holder nor the names of its
14  * contributors may be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28  * OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 /*---------------------------------------------------------------------------*/
31 /**
32  * \addtogroup cc26xx-uart
33  * @{
34  *
35  * \file
36  * Implementation of the CC13xx/CC26xx UART driver.
37  */
38 /*---------------------------------------------------------------------------*/
39 #include "contiki-conf.h"
40 #include "cc26xx-uart.h"
41 #include "hw_types.h"
42 #include "hw_memmap.h"
43 #include "sys_ctrl.h"
44 #include "prcm.h"
45 #include "ioc.h"
46 #include "uart.h"
47 #include "lpm.h"
48 #include "ti-lib.h"
49 #include "sys/energest.h"
50 
51 #include <stdint.h>
52 #include <stdbool.h>
53 #include <string.h>
54 /*---------------------------------------------------------------------------*/
55 /* Which events to trigger a UART interrupt */
56 #define CC26XX_UART_RX_INTERRUPT_TRIGGERS (UART_INT_RX | UART_INT_RT)
57 
58 /* All interrupt masks */
59 #define CC26XX_UART_INTERRUPT_ALL (UART_INT_OE | UART_INT_BE | UART_INT_PE | \
60  UART_INT_FE | UART_INT_RT | UART_INT_TX | \
61  UART_INT_RX | UART_INT_CTS)
62 /*---------------------------------------------------------------------------*/
63 #define cc26xx_uart_isr UART0IntHandler
64 /*---------------------------------------------------------------------------*/
65 static int (*input_handler)(unsigned char c);
66 /*---------------------------------------------------------------------------*/
67 static bool
68 usable(void)
69 {
70  if(BOARD_IOID_UART_RX == IOID_UNUSED ||
71  BOARD_IOID_UART_TX == IOID_UNUSED ||
73  return false;
74  }
75 
76  return true;
77 }
78 /*---------------------------------------------------------------------------*/
79 static void
80 power_and_clock(void)
81 {
82  /* Power on the SERIAL PD */
83  ti_lib_prcm_power_domain_on(PRCM_DOMAIN_SERIAL);
84  while(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL)
85  != PRCM_DOMAIN_POWER_ON);
86 
87  /* Enable UART clock in active mode */
88  ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_UART0);
89  ti_lib_prcm_load_set();
90  while(!ti_lib_prcm_load_get());
91 }
92 /*---------------------------------------------------------------------------*/
93 /*
94  * Returns 0 if either the SERIAL PD is off, or the PD is on but the run mode
95  * clock is gated. If this function would return 0, accessing UART registers
96  * will return a precise bus fault. If this function returns 1, it is safe to
97  * access UART registers.
98  *
99  * This function only checks the 'run mode' clock gate, since it can only ever
100  * be called with the MCU in run mode.
101  */
102 static bool
103 accessible(void)
104 {
105  /* First, check the PD */
106  if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL)
107  != PRCM_DOMAIN_POWER_ON) {
108  return false;
109  }
110 
111  /* Then check the 'run mode' clock gate */
112  if(!(HWREG(PRCM_BASE + PRCM_O_UARTCLKGR) & PRCM_UARTCLKGR_CLK_EN)) {
113  return false;
114  }
115 
116  return true;
117 }
118 /*---------------------------------------------------------------------------*/
119 static void
120 disable_interrupts(void)
121 {
122  /* Acknowledge UART interrupts */
123  ti_lib_int_disable(INT_UART0);
124 
125  /* Disable all UART module interrupts */
126  ti_lib_uart_int_disable(UART0_BASE, CC26XX_UART_INTERRUPT_ALL);
127 
128  /* Clear all UART interrupts */
129  ti_lib_uart_int_clear(UART0_BASE, CC26XX_UART_INTERRUPT_ALL);
130 }
131 /*---------------------------------------------------------------------------*/
132 static void
133 enable_interrupts(void)
134 {
135  /* Clear all UART interrupts */
136  ti_lib_uart_int_clear(UART0_BASE, CC26XX_UART_INTERRUPT_ALL);
137 
138  /* Enable RX-related interrupts only if we have an input handler */
139  if(input_handler) {
140  /* Configure which interrupts to generate: FIFO level or after RX timeout */
141  ti_lib_uart_int_enable(UART0_BASE, CC26XX_UART_RX_INTERRUPT_TRIGGERS);
142 
143  /* Acknowledge UART interrupts */
144  ti_lib_int_enable(INT_UART0);
145  }
146 }
147 /*---------------------------------------------------------------------------*/
148 static void
149 configure(void)
150 {
151  uint32_t ctl_val = UART_CTL_UARTEN | UART_CTL_TXE;
152  /*
153  * Make sure the TX pin is output / high before assigning it to UART control
154  * to avoid falling edge glitches
155  */
156  ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_UART_TX);
157  ti_lib_gpio_pin_write(BOARD_UART_TX, 1);
158 
159  /*
160  * Map UART signals to the correct GPIO pins and configure them as
161  * hardware controlled.
162  */
163  ti_lib_ioc_pin_type_uart(UART0_BASE, BOARD_IOID_UART_RX, BOARD_IOID_UART_TX,
164  BOARD_IOID_UART_CTS, BOARD_IOID_UART_RTS);
165 
166  /* Configure the UART for 115,200, 8-N-1 operation. */
167  ti_lib_uart_config_set_exp_clk(UART0_BASE, ti_lib_sys_ctrl_clock_get(),
169  (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
170  UART_CONFIG_PAR_NONE));
171 
172  /*
173  * Generate an RX interrupt at FIFO 1/2 full.
174  * We don't really care about the TX interrupt
175  */
176  ti_lib_uart_fifo_level_set(UART0_BASE, UART_FIFO_TX7_8, UART_FIFO_RX4_8);
177 
178  /* Enable FIFOs */
179  HWREG(UART0_BASE + UART_O_LCRH) |= UART_LCRH_FEN;
180 
181  if(input_handler) {
182  ctl_val += UART_CTL_RXE;
183  }
184 
185  /* Enable TX, RX (conditionally), and the UART. */
186  HWREG(UART0_BASE + UART_O_CTL) = ctl_val;
187 }
188 /*---------------------------------------------------------------------------*/
189 static void
190 lpm_drop_handler(uint8_t mode)
191 {
192  /*
193  * First, wait for any outstanding TX to complete. If we have an input
194  * handler, the SERIAL PD will be kept on and the UART module clock will
195  * be enabled under sleep as well as deep sleep. In theory, this means that
196  * we shouldn't lose any outgoing bytes, but we actually do on occasion.
197  * This byte loss may (or may not) be related to the freezing of IO latches
198  * between MCU and AON when we drop to deep sleep. This here is essentially a
199  * workaround
200  */
201  if(accessible() == true) {
202  while(ti_lib_uart_busy(UART0_BASE));
203  }
204 
205  /*
206  * If we have a registered input_handler then we need to retain RX
207  * capability. Thus, if this is not a shutdown notification and we have an
208  * input handler, we do nothing
209  */
210  if((mode != LPM_MODE_SHUTDOWN) && (input_handler != NULL)) {
211  return;
212  }
213 
214  /*
215  * If we reach here, we either don't care about staying awake or we have
216  * received a shutdown notification
217  *
218  * Only touch UART registers if the module is powered and clocked
219  */
220  if(accessible() == true) {
221  /* Disable the module */
222  ti_lib_uart_disable(UART0_BASE);
223 
224  /* Disable all UART interrupts and clear all flags */
225  disable_interrupts();
226  }
227 
228  /*
229  * Always stop the clock in run mode. Also stop in Sleep and Deep Sleep if
230  * this is a request for full shutdown
231  */
232  ti_lib_prcm_peripheral_run_disable(PRCM_PERIPH_UART0);
233  if(mode == LPM_MODE_SHUTDOWN) {
234  ti_lib_prcm_peripheral_sleep_disable(PRCM_PERIPH_UART0);
235  ti_lib_prcm_peripheral_deep_sleep_disable(PRCM_PERIPH_UART0);
236  }
237  ti_lib_prcm_load_set();
238  while(!ti_lib_prcm_load_get());
239 
240  /* Set pins to low leakage configuration in preparation for deep sleep */
241  lpm_pin_set_default_state(BOARD_IOID_UART_TX);
242  lpm_pin_set_default_state(BOARD_IOID_UART_RX);
243  lpm_pin_set_default_state(BOARD_IOID_UART_CTS);
244  lpm_pin_set_default_state(BOARD_IOID_UART_RTS);
245 }
246 /*---------------------------------------------------------------------------*/
247 /* Declare a data structure to register with LPM. */
248 LPM_MODULE(uart_module, NULL, lpm_drop_handler, NULL, LPM_DOMAIN_NONE);
249 /*---------------------------------------------------------------------------*/
250 static void
251 enable(void)
252 {
253  power_and_clock();
254 
255  /* Make sure the peripheral is disabled */
256  ti_lib_uart_disable(UART0_BASE);
257 
258  /* Disable all UART interrupts and clear all flags */
259  disable_interrupts();
260 
261  /* Setup pins, Baud rate and FIFO levels */
262  configure();
263 
264  /* Enable UART interrupts */
265  enable_interrupts();
266 }
267 /*---------------------------------------------------------------------------*/
268 void
270 {
271  bool interrupts_disabled;
272 
273  /* Return early if disabled by user conf or if ports are misconfigured */
274  if(usable() == false) {
275  return;
276  }
277 
278  /* Disable Interrupts */
279  interrupts_disabled = ti_lib_int_master_disable();
280 
281  /* Register ourselves with the LPM module */
282  lpm_register_module(&uart_module);
283 
284  /* Only TX and EN to start with. RX will be enabled only if needed */
285  input_handler = NULL;
286 
287  /*
288  * init() won't actually fire up the UART. We turn it on only when (and if)
289  * it gets requested, either to enable input or to send out a character
290  *
291  * Thus, we simply re-enable processor interrupts here
292  */
293  if(!interrupts_disabled) {
294  ti_lib_int_master_enable();
295  }
296 }
297 /*---------------------------------------------------------------------------*/
298 void
300 {
301  /* Return early if disabled by user conf or if ports are misconfigured */
302  if(usable() == false) {
303  return;
304  }
305 
306  if(accessible() == false) {
307  enable();
308  }
309 
310  ti_lib_uart_char_put(UART0_BASE, c);
311 }
312 /*---------------------------------------------------------------------------*/
313 void
314 cc26xx_uart_set_input(int (*input)(unsigned char c))
315 {
316  input_handler = input;
317 
318  /* Return early if disabled by user conf or if ports are misconfigured */
319  if(usable() == false) {
320  return;
321  }
322 
323  if(input == NULL) {
324  /* Let the SERIAL PD power down */
325  uart_module.domain_lock = LPM_DOMAIN_NONE;
326 
327  /* Disable module clocks under sleep and deep sleep */
328  ti_lib_prcm_peripheral_sleep_disable(PRCM_PERIPH_UART0);
329  ti_lib_prcm_peripheral_deep_sleep_disable(PRCM_PERIPH_UART0);
330  } else {
331  /* Request the SERIAL PD to stay on during deep sleep */
332  uart_module.domain_lock = LPM_DOMAIN_SERIAL;
333 
334  /* Enable module clocks under sleep and deep sleep */
335  ti_lib_prcm_peripheral_sleep_enable(PRCM_PERIPH_UART0);
336  ti_lib_prcm_peripheral_deep_sleep_enable(PRCM_PERIPH_UART0);
337  }
338 
339  ti_lib_prcm_load_set();
340  while(!ti_lib_prcm_load_get());
341 
342  enable();
343 
344  return;
345 }
346 /*---------------------------------------------------------------------------*/
347 uint8_t
349 {
350  /* Return early if disabled by user conf or if ports are misconfigured */
351  if(usable() == false) {
352  return UART_IDLE;
353  }
354 
355  /* If the UART is not accessible, it is not busy */
356  if(accessible() == false) {
357  return UART_IDLE;
358  }
359 
360  return ti_lib_uart_busy(UART0_BASE);
361 }
362 /*---------------------------------------------------------------------------*/
363 void
364 cc26xx_uart_isr(void)
365 {
366  char the_char;
367  uint32_t flags;
368 
369  ENERGEST_ON(ENERGEST_TYPE_IRQ);
370 
371  power_and_clock();
372 
373  /* Read out the masked interrupt status */
374  flags = ti_lib_uart_int_status(UART0_BASE, true);
375 
376  /* Clear all UART interrupt flags */
377  ti_lib_uart_int_clear(UART0_BASE, CC26XX_UART_INTERRUPT_ALL);
378 
379  if((flags & CC26XX_UART_RX_INTERRUPT_TRIGGERS) != 0) {
380  /*
381  * If this was a FIFO RX or an RX timeout, read all bytes available in the
382  * RX FIFO.
383  */
384  while(ti_lib_uart_chars_avail(UART0_BASE)) {
385  the_char = ti_lib_uart_char_get_non_blocking(UART0_BASE);
386 
387  if(input_handler != NULL) {
388  input_handler((unsigned char)the_char);
389  }
390  }
391  }
392 
393  ENERGEST_OFF(ENERGEST_TYPE_IRQ);
394 }
395 /*---------------------------------------------------------------------------*/
396 /** @} */
void cc26xx_uart_init()
Initialises the UART controller, configures I/O control and interrupts.
Definition: cc26xx-uart.c:269
Header file with macros which rename TI CC26xxware functions.
uint8_t cc26xx_uart_busy(void)
Returns the UART busy status.
Definition: cc26xx-uart.c:348
#define CC26XX_UART_CONF_BAUD_RATE
Default UART0 baud rate.
Definition: contiki-conf.h:259
static void input(void)
Process a received 6lowpan packet.
Definition: sicslowpan.c:1503
#define CC26XX_UART_CONF_ENABLE
Enable/Disable UART I/O.
Definition: contiki-conf.h:255
void cc26xx_uart_write_byte(uint8_t c)
Sends a single character down the UART.
Definition: cc26xx-uart.c:299
Header file for the energy estimation mechanism
void lpm_register_module(lpm_registered_module_t *module)
Register a module for LPM notifications.
Definition: lpm.c:498
#define UART_LCRH_FEN
UART enable FIFOs.
Definition: uart.h:149
#define LPM_MODULE(n, m, s, w, l)
Declare a variable to be used in order to get notifications from LPM.
Definition: lpm.h:92
void lpm_pin_set_default_state(uint32_t ioid)
Sets an IOID to a default state.
Definition: lpm.c:519
#define NULL
The null pointer.
Header file with declarations for the I/O Control module.
#define UART_CTL_TXE
UART transmit enable.
Definition: uart.h:171
#define UART_CTL_UARTEN
UART enable.
Definition: uart.h:179
#define UART0_BASE
Peripheral UART0 base address.
Definition: MKL25Z4.h:7195
#define UART_CTL_RXE
UART receive enable.
Definition: uart.h:170
Header file for the CC13xx/CC26xx UART driver.
void cc26xx_uart_set_input(int(*input)(unsigned char c))
Assigns a callback to be called when the UART receives a byte.
Definition: cc26xx-uart.c:314