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