diff --git a/arch/arm/soc/ti_simplelink/cc32xx/Kconfig.defconfig.cc3200 b/arch/arm/soc/ti_simplelink/cc32xx/Kconfig.defconfig.cc3200 index 91d67328043..0c332140116 100644 --- a/arch/arm/soc/ti_simplelink/cc32xx/Kconfig.defconfig.cc3200 +++ b/arch/arm/soc/ti_simplelink/cc32xx/Kconfig.defconfig.cc3200 @@ -29,4 +29,9 @@ config NUM_IRQS # This includes the NWP interrupt default 179 +if UART_CC32XX +config UART_CC32XX_IRQ_PRI + default 3 +endif # UART_CC32XX + endif # SOC_CC3200 diff --git a/boards/arm/cc3200_launchxl/cc3200_launchxl_defconfig b/boards/arm/cc3200_launchxl/cc3200_launchxl_defconfig index 66dc1923f68..5160ca52c55 100644 --- a/boards/arm/cc3200_launchxl/cc3200_launchxl_defconfig +++ b/boards/arm/cc3200_launchxl/cc3200_launchxl_defconfig @@ -7,6 +7,16 @@ CONFIG_CORTEX_M_SYSTICK=y CONFIG_FLASH=n CONFIG_FLASH_BASE_ADDRESS=0x00000000 CONFIG_XIP=n +CONFIG_PRINTK=y + +# enable uart driver +CONFIG_SERIAL=y +CONFIG_SERIAL_HAS_DRIVER=y +CONFIG_UART_CC32XX=y + +# enable console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y # Enable CC3200 SDK driver files CONFIG_CC3200SDK_BUILTIN=y diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 3a9721c23a5..d625638f2ca 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -83,4 +83,6 @@ source "drivers/serial/Kconfig.nrf5" source "drivers/serial/Kconfig.altera_jtag" +source "drivers/serial/Kconfig.cc32xx" + endif diff --git a/drivers/serial/Kconfig.cc32xx b/drivers/serial/Kconfig.cc32xx new file mode 100644 index 00000000000..163b1c0cdfd --- /dev/null +++ b/drivers/serial/Kconfig.cc32xx @@ -0,0 +1,26 @@ +menuconfig UART_CC32XX + depends on SOC_SERIES_CC32XX + bool "CC32XX UART driver" + default n + select SERIAL_HAS_DRIVER + depends on SOC_FAMILY_TISIMPLELINK + help + This option enables the CC32XX UART driver, for UART_0. + +config UART_CC32XX_NAME + string "Device Name for CC32XX UART" + default "UART_0" + depends on UART_CC32XX + help + This is the UART's device name binding. + +config UART_CC32XX_IRQ_PRI + depends on UART_CC32XX + int "IRQ priority from UART controller" + +config UART_CC32XX_BAUDRATE + depends on UART_CC32XX + int "UART_0 baud rate" + default 115200 + help + This option sets the baud rate for the CC32XX UART. diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index f5c1052fc6e..5e375df802a 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_UART_QMSI) += uart_qmsi.o obj-$(CONFIG_UART_STM32) += uart_stm32.o obj-$(CONFIG_UART_NRF5) += uart_nrf5.o obj-$(CONFIG_UART_ALTERA_JTAG) += uart_altera_jtag.o +obj-$(CONFIG_UART_CC32XX) += uart_cc32xx.o diff --git a/drivers/serial/uart_cc32xx.c b/drivers/serial/uart_cc32xx.c new file mode 100644 index 00000000000..9e95dd24646 --- /dev/null +++ b/drivers/serial/uart_cc32xx.c @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2016, Texas Instruments Incorporated + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* See www.ti.com/lit/pdf/swru367, section 6, for CC3200 UART info. */ + +#include +#include +#include + +/* Driverlib includes */ +#include +#include +#include +#include +#include +#include +#include + +/* Note: Zephyr uses exception numbers, vs the IRQ #s used by the CC3200 SDK */ +#define EXCEPTION_UARTA0 5 /* (INT_UARTA0 - 16) = (21-16) */ + +struct uart_cc32xx_dev_data_t { +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + uart_irq_callback_t cb; /**< Callback function pointer */ +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +}; + +#define DEV_CFG(dev) \ + ((const struct uart_device_config * const)(dev)->config->config_info) +#define DEV_DATA(dev) \ + ((struct uart_cc32xx_dev_data_t * const)(dev)->driver_data) + +/* Forward decls: */ +static struct device DEVICE_NAME_GET(uart_cc32xx_0); + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN +static void uart_cc32xx_isr(void *arg); +#endif + +static const struct uart_device_config uart_cc32xx_dev_cfg_0 = { + .base = (void *)UARTA0_BASE, + .sys_clk_freq = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC, +}; + +static struct uart_cc32xx_dev_data_t uart_cc32xx_dev_data_0 = { +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .cb = NULL, +#endif +}; + +/* + * CC32XX UART has a configurable FIFO length, from 1 to 8 characters. + * However, the Zephyr console driver, and the Zephyr uart sample test, assume + * a RX FIFO depth of one: meaning, one interrupt == one character received. + * Keeping with this assumption, this driver leaves the FIFOs disabled, + * and at depth 1. + */ +static int uart_cc32xx_init(struct device *dev) +{ + const struct uart_device_config *config = DEV_CFG(dev); + + MAP_PRCMPeripheralReset(PRCM_UARTA0); + + /* This also calls MAP_UARTEnable() to enable the FIFOs: */ + MAP_UARTConfigSetExpClk((unsigned long)config->base, + MAP_PRCMPeripheralClockGet(PRCM_UARTA0), + CONFIG_UART_CC32XX_BAUDRATE, + (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE + | UART_CONFIG_PAR_NONE)); + MAP_UARTFlowControlSet((unsigned long)config->base, + UART_FLOWCONTROL_NONE); + /* Re-disable the FIFOs: */ + MAP_UARTFIFODisable((unsigned long)config->base); + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + /* Clear any pending UART interrupts: we only care about RX, TX: */ + MAP_UARTIntClear((unsigned long)config->base, + (UART_INT_RX | UART_INT_TX)); + + IRQ_CONNECT(EXCEPTION_UARTA0, + CONFIG_UART_CC32XX_IRQ_PRI, + uart_cc32xx_isr, DEVICE_GET(uart_cc32xx_0), + 0); + irq_enable(EXCEPTION_UARTA0); +#endif + return 0; +} + +static int uart_cc32xx_poll_in(struct device *dev, unsigned char *c) +{ + const struct uart_device_config *config = DEV_CFG(dev); + + if (MAP_UARTCharsAvail((unsigned long)config->base)) { + *c = MAP_UARTCharGetNonBlocking((unsigned long)config->base); + } else { + return (-1); + } + return 0; +} + +static unsigned char uart_cc32xx_poll_out(struct device *dev, unsigned char c) +{ + const struct uart_device_config *config = DEV_CFG(dev); + + MAP_UARTCharPut((unsigned long)config->base, c); + + return c; +} + +static int uart_cc32xx_err_check(struct device *dev) +{ + const struct uart_device_config *config = DEV_CFG(dev); + unsigned long cc32xx_errs = 0L; + unsigned int z_err = 0; + + cc32xx_errs = MAP_UARTRxErrorGet((unsigned long)config->base); + + /* Map cc3200 SDK uart.h defines to zephyr uart.h defines */ + z_err = ((cc32xx_errs & UART_RXERROR_OVERRUN) ? + UART_ERROR_OVERRUN : 0) | + ((cc32xx_errs & UART_RXERROR_BREAK) ? UART_ERROR_BREAK : 0) | + ((cc32xx_errs & UART_RXERROR_PARITY) ? UART_ERROR_PARITY : 0) | + ((cc32xx_errs & UART_RXERROR_FRAMING) ? UART_ERROR_FRAMING : 0); + + MAP_UARTRxErrorClear((unsigned long)config->base); + + return (int)z_err; +} + +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + +static int uart_cc32xx_fifo_fill(struct device *dev, const uint8_t *tx_data, + int size) +{ + const struct uart_device_config *config = DEV_CFG(dev); + unsigned int num_tx = 0; + + while ((size - num_tx) > 0) { + /* Send a character */ + if (MAP_UARTCharPutNonBlocking((unsigned long)config->base, + tx_data[num_tx])) { + num_tx++; + } else { + break; + } + } + + return (int)num_tx; +} + +static int uart_cc32xx_fifo_read(struct device *dev, uint8_t *rx_data, + const int size) +{ + const struct uart_device_config *config = DEV_CFG(dev); + unsigned int num_rx = 0; + + while (((size - num_rx) > 0) && + MAP_UARTCharsAvail((unsigned long)config->base)) { + + /* Receive a character */ + rx_data[num_rx++] = + MAP_UARTCharGetNonBlocking((unsigned long)config->base); + } + + return num_rx; +} + +static void uart_cc32xx_irq_tx_enable(struct device *dev) +{ + const struct uart_device_config *config = DEV_CFG(dev); + + MAP_UARTIntEnable((unsigned long)config->base, UART_INT_TX); +} + +static void uart_cc32xx_irq_tx_disable(struct device *dev) +{ + const struct uart_device_config *config = DEV_CFG(dev); + + MAP_UARTIntDisable((unsigned long)config->base, UART_INT_TX); +} + +static int uart_cc32xx_irq_tx_ready(struct device *dev) +{ + const struct uart_device_config *config = DEV_CFG(dev); + unsigned int int_status; + + int_status = MAP_UARTIntStatus((unsigned long)config->base, 1); + + return (int_status & UART_INT_TX); +} + +static void uart_cc32xx_irq_rx_enable(struct device *dev) +{ + const struct uart_device_config *config = DEV_CFG(dev); + + /* FIFOs are left disabled from reset, so UART_INT_RT flag not used. */ + MAP_UARTIntEnable((unsigned long)config->base, UART_INT_RX); +} + +static void uart_cc32xx_irq_rx_disable(struct device *dev) +{ + const struct uart_device_config *config = DEV_CFG(dev); + + MAP_UARTIntDisable((unsigned long)config->base, UART_INT_RX); +} + +static int uart_cc32xx_irq_tx_empty(struct device *dev) +{ + const struct uart_device_config *config = DEV_CFG(dev); + + return (!MAP_UARTBusy((unsigned long)config->base)); +} + +static int uart_cc32xx_irq_rx_ready(struct device *dev) +{ + const struct uart_device_config *config = DEV_CFG(dev); + unsigned int int_status; + + int_status = MAP_UARTIntStatus((unsigned long)config->base, 1); + + return (int_status & UART_INT_RX); +} + +static void uart_cc32xx_irq_err_enable(struct device *dev) +{ + /* Not yet used in zephyr */ +} + +static void uart_cc32xx_irq_err_disable(struct device *dev) +{ + /* Not yet used in zephyr */ +} + +static int uart_cc32xx_irq_is_pending(struct device *dev) +{ + const struct uart_device_config *config = DEV_CFG(dev); + unsigned int int_status; + + int_status = MAP_UARTIntStatus((unsigned long)config->base, 1); + + return (int_status & (UART_INT_TX | UART_INT_RX)); +} + +static int uart_cc32xx_irq_update(struct device *dev) +{ + return 1; +} + +static void uart_cc32xx_irq_callback_set(struct device *dev, + uart_irq_callback_t cb) +{ + struct uart_cc32xx_dev_data_t * const dev_data = DEV_DATA(dev); + + dev_data->cb = cb; +} + +/** + * @brief Interrupt service routine. + * + * This simply calls the callback function, if one exists. + * + * Note: CC32XX UART Tx interrupts when ready to send; Rx interrupts when char + * received. + * + * @param arg Argument to ISR. + * + * @return N/A + */ +static void uart_cc32xx_isr(void *arg) +{ + struct device *dev = arg; + const struct uart_device_config *config = DEV_CFG(dev); + struct uart_cc32xx_dev_data_t * const dev_data = DEV_DATA(dev); + + unsigned long intStatus = MAP_UARTIntStatus((unsigned long)config->base, + 1); + + if (dev_data->cb) { + dev_data->cb(dev); + } + /* + * Clear interrupts only after cb called, as Zephyr UART clients expect + * to check interrupt status during the callback. + */ + MAP_UARTIntClear((unsigned long)config->base, intStatus); +} +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ + +static const struct uart_driver_api uart_cc32xx_driver_api = { + .poll_in = uart_cc32xx_poll_in, + .poll_out = uart_cc32xx_poll_out, + .err_check = uart_cc32xx_err_check, +#ifdef CONFIG_UART_INTERRUPT_DRIVEN + .fifo_fill = uart_cc32xx_fifo_fill, + .fifo_read = uart_cc32xx_fifo_read, + .irq_tx_enable = uart_cc32xx_irq_tx_enable, + .irq_tx_disable = uart_cc32xx_irq_tx_disable, + .irq_tx_ready = uart_cc32xx_irq_tx_ready, + .irq_rx_enable = uart_cc32xx_irq_rx_enable, + .irq_rx_disable = uart_cc32xx_irq_rx_disable, + .irq_tx_empty = uart_cc32xx_irq_tx_empty, + .irq_rx_ready = uart_cc32xx_irq_rx_ready, + .irq_err_enable = uart_cc32xx_irq_err_enable, + .irq_err_disable = uart_cc32xx_irq_err_disable, + .irq_is_pending = uart_cc32xx_irq_is_pending, + .irq_update = uart_cc32xx_irq_update, + .irq_callback_set = uart_cc32xx_irq_callback_set, +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +}; + +DEVICE_AND_API_INIT(uart_cc32xx_0, CONFIG_UART_CONSOLE_ON_DEV_NAME, + uart_cc32xx_init, &uart_cc32xx_dev_data_0, + &uart_cc32xx_dev_cfg_0, + SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + (void *)&uart_cc32xx_driver_api);