Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2016, Texas Instruments Incorporated
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/* See www.ti.com/lit/pdf/swru367, section 6, for CC3200 UART info. */

#include <kernel.h>
#include <arch/cpu.h>
#include <uart.h>

/* Driverlib includes */
#include <inc/hw_types.h>
#include <driverlib/rom.h>
#include <driverlib/rom_map.h>
#include <driverlib/prcm.h>
#include <driverlib/uart.h>

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 *)UART_CC32XX_BASE_ADDRESS,
	.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,
		    POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
		    (void *)&uart_cc32xx_driver_api);