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/*---------------------------------------------------------------------------*/
64static int (*input_handler)(unsigned char c);
65/*---------------------------------------------------------------------------*/
66static bool
67usable(uint32_t pin) {
68 if(TI_UART_CONF_ENABLE == 0 || pin == IOID_UNUSED) {
69 return false;
70 }
71
72 return true;
73}
74/*---------------------------------------------------------------------------*/
75static void
76power_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 */
98static bool
99accessible(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/*---------------------------------------------------------------------------*/
115static void
116disable_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/*---------------------------------------------------------------------------*/
128static void
129enable_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/*---------------------------------------------------------------------------*/
144static void
145configure(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/*---------------------------------------------------------------------------*/
185static void
186lpm_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. */
244LPM_MODULE(uart_module, NULL, lpm_drop_handler, NULL, LPM_DOMAIN_NONE);
245/*---------------------------------------------------------------------------*/
246static void
247enable(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/*---------------------------------------------------------------------------*/
264void
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/*---------------------------------------------------------------------------*/
294void
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/*---------------------------------------------------------------------------*/
309void
310cc26xx_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/*---------------------------------------------------------------------------*/
343uint8_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/*---------------------------------------------------------------------------*/
359void
360cc26xx_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/** @} */
Header file for the CC13xx/CC26xx UART driver.
#define UART_CTL_UARTEN
UART enable.
Definition: uart.h:179
#define UART_CTL_RXE
UART receive enable.
Definition: uart.h:170
#define UART_LCRH_FEN
UART enable FIFOs.
Definition: uart.h:149
#define UART_CTL_TXE
UART transmit enable.
Definition: uart.h:171
void lpm_pin_set_default_state(uint32_t ioid)
Sets an IOID to a default state.
Definition: lpm.c:566
#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_register_module(lpm_registered_module_t *module)
Register a module for LPM notifications.
Definition: lpm.c:545
void cc26xx_uart_init()
Initialises the UART controller, configures I/O control and interrupts.
Definition: cc26xx-uart.c:265
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
void cc26xx_uart_write_byte(uint8_t c)
Sends a single character down the UART.
Definition: cc26xx-uart.c:295
uint8_t cc26xx_uart_busy(void)
Returns the UART busy status.
Definition: cc26xx-uart.c:344
#define TI_UART_CONF_BAUD_RATE
Default UART0 baud rate.
#define TI_UART_CONF_ENABLE
Enable/Disable UART I/O.
static void input(void)
Process a received 6lowpan packet.
Definition: sicslowpan.c:1833
Header file with declarations for the I/O Control module.
Header file with macros which rename TI CC26xxware functions.
Header file for the cc2538 UART driver.