Contiki-NG
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.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 
50 #include <stdint.h>
51 #include <stdbool.h>
52 #include <string.h>
53 /*---------------------------------------------------------------------------*/
54 /* Which events to trigger a UART interrupt */
55 #define CC26XX_UART_RX_INTERRUPT_TRIGGERS (UART_INT_RX | UART_INT_RT)
56 
57 /* All interrupt masks */
58 #define CC26XX_UART_INTERRUPT_ALL (UART_INT_OE | UART_INT_BE | UART_INT_PE | \
59  UART_INT_FE | UART_INT_RT | UART_INT_TX | \
60  UART_INT_RX | UART_INT_CTS)
61 /*---------------------------------------------------------------------------*/
62 #define cc26xx_uart_isr UART0IntHandler
63 /*---------------------------------------------------------------------------*/
64 static int (*input_handler)(unsigned char c);
65 /*---------------------------------------------------------------------------*/
66 static bool
67 usable(void)
68 {
69  if(BOARD_IOID_UART_RX == IOID_UNUSED ||
70  BOARD_IOID_UART_TX == IOID_UNUSED ||
72  return false;
73  }
74 
75  return true;
76 }
77 /*---------------------------------------------------------------------------*/
78 static void
79 power_and_clock(void)
80 {
81  /* Power on the SERIAL PD */
82  ti_lib_prcm_power_domain_on(PRCM_DOMAIN_SERIAL);
83  while(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL)
84  != PRCM_DOMAIN_POWER_ON);
85 
86  /* Enable UART clock in active mode */
87  ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_UART0);
88  ti_lib_prcm_load_set();
89  while(!ti_lib_prcm_load_get());
90 }
91 /*---------------------------------------------------------------------------*/
92 /*
93  * Returns 0 if either the SERIAL PD is off, or the PD is on but the run mode
94  * clock is gated. If this function would return 0, accessing UART registers
95  * will return a precise bus fault. If this function returns 1, it is safe to
96  * access UART registers.
97  *
98  * This function only checks the 'run mode' clock gate, since it can only ever
99  * be called with the MCU in run mode.
100  */
101 static bool
102 accessible(void)
103 {
104  /* First, check the PD */
105  if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL)
106  != PRCM_DOMAIN_POWER_ON) {
107  return false;
108  }
109 
110  /* Then check the 'run mode' clock gate */
111  if(!(HWREG(PRCM_BASE + PRCM_O_UARTCLKGR) & PRCM_UARTCLKGR_CLK_EN)) {
112  return false;
113  }
114 
115  return true;
116 }
117 /*---------------------------------------------------------------------------*/
118 static void
119 disable_interrupts(void)
120 {
121  /* Acknowledge UART interrupts */
122  ti_lib_int_disable(INT_UART0_COMB);
123 
124  /* Disable all UART module interrupts */
125  ti_lib_uart_int_disable(UART0_BASE, CC26XX_UART_INTERRUPT_ALL);
126 
127  /* Clear all UART interrupts */
128  ti_lib_uart_int_clear(UART0_BASE, CC26XX_UART_INTERRUPT_ALL);
129 }
130 /*---------------------------------------------------------------------------*/
131 static void
132 enable_interrupts(void)
133 {
134  /* Clear all UART interrupts */
135  ti_lib_uart_int_clear(UART0_BASE, CC26XX_UART_INTERRUPT_ALL);
136 
137  /* Enable RX-related interrupts only if we have an input handler */
138  if(input_handler) {
139  /* Configure which interrupts to generate: FIFO level or after RX timeout */
140  ti_lib_uart_int_enable(UART0_BASE, CC26XX_UART_RX_INTERRUPT_TRIGGERS);
141 
142  /* Acknowledge UART interrupts */
143  ti_lib_int_enable(INT_UART0_COMB);
144  }
145 }
146 /*---------------------------------------------------------------------------*/
147 static void
148 configure(void)
149 {
150  uint32_t ctl_val = UART_CTL_UARTEN | UART_CTL_TXE;
151  /*
152  * Make sure the TX pin is output / high before assigning it to UART control
153  * to avoid falling edge glitches
154  */
155  ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_UART_TX);
156  ti_lib_gpio_set_dio(BOARD_IOID_UART_TX);
157 
158  /*
159  * Map UART signals to the correct GPIO pins and configure them as
160  * hardware controlled.
161  */
162  ti_lib_ioc_pin_type_uart(UART0_BASE, BOARD_IOID_UART_RX, BOARD_IOID_UART_TX,
163  BOARD_IOID_UART_CTS, BOARD_IOID_UART_RTS);
164 
165  /* Configure the UART for 115,200, 8-N-1 operation. */
166  ti_lib_uart_config_set_exp_clk(UART0_BASE, ti_lib_sys_ctrl_clock_get(),
168  (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
169  UART_CONFIG_PAR_NONE));
170 
171  /*
172  * Generate an RX interrupt at FIFO 1/2 full.
173  * We don't really care about the TX interrupt
174  */
175  ti_lib_uart_fifo_level_set(UART0_BASE, UART_FIFO_TX7_8, UART_FIFO_RX4_8);
176 
177  /* Enable FIFOs */
178  HWREG(UART0_BASE + UART_O_LCRH) |= UART_LCRH_FEN;
179 
180  if(input_handler) {
181  ctl_val += UART_CTL_RXE;
182  }
183 
184  /* Enable TX, RX (conditionally), and the UART. */
185  HWREG(UART0_BASE + UART_O_CTL) = ctl_val;
186 }
187 /*---------------------------------------------------------------------------*/
188 static void
189 lpm_drop_handler(uint8_t mode)
190 {
191  /*
192  * First, wait for any outstanding TX to complete. If we have an input
193  * handler, the SERIAL PD will be kept on and the UART module clock will
194  * be enabled under sleep as well as deep sleep. In theory, this means that
195  * we shouldn't lose any outgoing bytes, but we actually do on occasion.
196  * This byte loss may (or may not) be related to the freezing of IO latches
197  * between MCU and AON when we drop to deep sleep. This here is essentially a
198  * workaround
199  */
200  if(accessible() == true) {
201  while(ti_lib_uart_busy(UART0_BASE));
202  }
203 
204  /*
205  * If we have a registered input_handler then we need to retain RX
206  * capability. Thus, if this is not a shutdown notification and we have an
207  * input handler, we do nothing
208  */
209  if((mode != LPM_MODE_SHUTDOWN) && (input_handler != NULL)) {
210  return;
211  }
212 
213  /*
214  * If we reach here, we either don't care about staying awake or we have
215  * received a shutdown notification
216  *
217  * Only touch UART registers if the module is powered and clocked
218  */
219  if(accessible() == true) {
220  /* Disable the module */
221  ti_lib_uart_disable(UART0_BASE);
222 
223  /* Disable all UART interrupts and clear all flags */
224  disable_interrupts();
225  }
226 
227  /*
228  * Always stop the clock in run mode. Also stop in Sleep and Deep Sleep if
229  * this is a request for full shutdown
230  */
231  ti_lib_prcm_peripheral_run_disable(PRCM_PERIPH_UART0);
232  if(mode == LPM_MODE_SHUTDOWN) {
233  ti_lib_prcm_peripheral_sleep_disable(PRCM_PERIPH_UART0);
234  ti_lib_prcm_peripheral_deep_sleep_disable(PRCM_PERIPH_UART0);
235  }
236  ti_lib_prcm_load_set();
237  while(!ti_lib_prcm_load_get());
238 
239  /* Set pins to low leakage configuration in preparation for deep sleep */
240  lpm_pin_set_default_state(BOARD_IOID_UART_TX);
241  lpm_pin_set_default_state(BOARD_IOID_UART_RX);
242  lpm_pin_set_default_state(BOARD_IOID_UART_CTS);
243  lpm_pin_set_default_state(BOARD_IOID_UART_RTS);
244 }
245 /*---------------------------------------------------------------------------*/
246 /* Declare a data structure to register with LPM. */
247 LPM_MODULE(uart_module, NULL, lpm_drop_handler, NULL, LPM_DOMAIN_NONE);
248 /*---------------------------------------------------------------------------*/
249 static void
250 enable(void)
251 {
252  power_and_clock();
253 
254  /* Make sure the peripheral is disabled */
255  ti_lib_uart_disable(UART0_BASE);
256 
257  /* Disable all UART interrupts and clear all flags */
258  disable_interrupts();
259 
260  /* Setup pins, Baud rate and FIFO levels */
261  configure();
262 
263  /* Enable UART interrupts */
264  enable_interrupts();
265 }
266 /*---------------------------------------------------------------------------*/
267 void
269 {
270  bool interrupts_disabled;
271 
272  /* Return early if disabled by user conf or if ports are misconfigured */
273  if(usable() == false) {
274  return;
275  }
276 
277  /* Disable Interrupts */
278  interrupts_disabled = ti_lib_int_master_disable();
279 
280  /* Register ourselves with the LPM module */
281  lpm_register_module(&uart_module);
282 
283  /* Only TX and EN to start with. RX will be enabled only if needed */
284  input_handler = NULL;
285 
286  /*
287  * init() won't actually fire up the UART. We turn it on only when (and if)
288  * it gets requested, either to enable input or to send out a character
289  *
290  * Thus, we simply re-enable processor interrupts here
291  */
292  if(!interrupts_disabled) {
293  ti_lib_int_master_enable();
294  }
295 }
296 /*---------------------------------------------------------------------------*/
297 void
299 {
300  /* Return early if disabled by user conf or if ports are misconfigured */
301  if(usable() == false) {
302  return;
303  }
304 
305  if(accessible() == false) {
306  enable();
307  }
308 
309  ti_lib_uart_char_put(UART0_BASE, c);
310 }
311 /*---------------------------------------------------------------------------*/
312 void
313 cc26xx_uart_set_input(int (*input)(unsigned char c))
314 {
315  input_handler = input;
316 
317  /* Return early if disabled by user conf or if ports are misconfigured */
318  if(usable() == false) {
319  return;
320  }
321 
322  if(input == NULL) {
323  /* Let the SERIAL PD power down */
324  uart_module.domain_lock = LPM_DOMAIN_NONE;
325 
326  /* Disable module clocks under sleep and deep sleep */
327  ti_lib_prcm_peripheral_sleep_disable(PRCM_PERIPH_UART0);
328  ti_lib_prcm_peripheral_deep_sleep_disable(PRCM_PERIPH_UART0);
329  } else {
330  /* Request the SERIAL PD to stay on during deep sleep */
331  uart_module.domain_lock = LPM_DOMAIN_SERIAL;
332 
333  /* Enable module clocks under sleep and deep sleep */
334  ti_lib_prcm_peripheral_sleep_enable(PRCM_PERIPH_UART0);
335  ti_lib_prcm_peripheral_deep_sleep_enable(PRCM_PERIPH_UART0);
336  }
337 
338  ti_lib_prcm_load_set();
339  while(!ti_lib_prcm_load_get());
340 
341  enable();
342 
343  return;
344 }
345 /*---------------------------------------------------------------------------*/
346 uint8_t
348 {
349  /* Return early if disabled by user conf or if ports are misconfigured */
350  if(usable() == false) {
351  return UART_IDLE;
352  }
353 
354  /* If the UART is not accessible, it is not busy */
355  if(accessible() == false) {
356  return UART_IDLE;
357  }
358 
359  return ti_lib_uart_busy(UART0_BASE);
360 }
361 /*---------------------------------------------------------------------------*/
362 void
363 cc26xx_uart_isr(void)
364 {
365  char the_char;
366  uint32_t flags;
367 
368  power_and_clock();
369 
370  /* Read out the masked interrupt status */
371  flags = ti_lib_uart_int_status(UART0_BASE, true);
372 
373  /* Clear all UART interrupt flags */
374  ti_lib_uart_int_clear(UART0_BASE, CC26XX_UART_INTERRUPT_ALL);
375 
376  if((flags & CC26XX_UART_RX_INTERRUPT_TRIGGERS) != 0) {
377  /*
378  * If this was a FIFO RX or an RX timeout, read all bytes available in the
379  * RX FIFO.
380  */
381  while(ti_lib_uart_chars_avail(UART0_BASE)) {
382  the_char = ti_lib_uart_char_get_non_blocking(UART0_BASE);
383 
384  if(input_handler != NULL) {
385  input_handler((unsigned char)the_char);
386  }
387  }
388  }
389 }
390 /*---------------------------------------------------------------------------*/
391 /** @} */
#define CC26XX_UART_CONF_ENABLE
Enable/Disable UART I/O.
Header file for the cc2538 UART driver.
Header file with macros which rename TI CC26xxware functions.
#define UART_CTL_TXE
UART transmit enable.
Definition: uart.h:171
Header file with declarations for the I/O Control module.
void cc26xx_uart_write_byte(uint8_t c)
Sends a single character down the UART.
Definition: cc26xx-uart.c:298
#define UART_LCRH_FEN
UART enable FIFOs.
Definition: uart.h:149
#define UART_CTL_UARTEN
UART enable.
Definition: uart.h:179
void cc26xx_uart_init()
Initialises the UART controller, configures I/O control and interrupts.
Definition: cc26xx-uart.c:268
#define UART_CTL_RXE
UART receive enable.
Definition: uart.h:170
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:313
#define CC26XX_UART_CONF_BAUD_RATE
Default UART0 baud rate.
Header file for the CC13xx/CC26xx UART driver.
uint8_t cc26xx_uart_busy(void)
Returns the UART busy status.
Definition: cc26xx-uart.c:347
#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:560
static void input(void)
Process a received 6lowpan packet.
Definition: sicslowpan.c:1758
void lpm_register_module(lpm_registered_module_t *module)
Register a module for LPM notifications.
Definition: lpm.c:539