Skip to content

Commit

Permalink
cpu/qn908x: Add support for UART.
Browse files Browse the repository at this point in the history
The QN908x has four FLEXCOMM interfaces that support a subset of UART,
SPI or I2C each one. This patch adds generic support for dealing with
the FLEXCOMM initialization and interrupts and adds a driver for
RX/TX support in UART.

With this patch is now possible to use a shell on the device over UART.
  • Loading branch information
iosabi committed Dec 3, 2020
1 parent 9d46bc7 commit 7624b89
Show file tree
Hide file tree
Showing 8 changed files with 606 additions and 5 deletions.
1 change: 1 addition & 0 deletions boards/qn9080dk/Makefile.features
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ CPU_MODEL = qn9080xhn

# Put defined MCU peripherals here (in alphabetical order)
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
FEATURES_PROVIDED += periph_uart

# Include the common qn908x board features.
include $(RIOTBOARD)/common/qn908x/Makefile.features
17 changes: 16 additions & 1 deletion boards/qn9080dk/include/periph_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,25 @@
extern "C" {
#endif

/**
* @name UART configuration
* @{
*/
static const uart_conf_t uart_config[] = {
{
.dev = USART0,
.rx_pin = GPIO_PIN(PORT_A, 17),
.tx_pin = GPIO_PIN(PORT_A, 16),
},
};

#define UART_NUMOF ARRAY_SIZE(uart_config)
/** @} */


/* put here the board peripherals definitions:
- Available clocks
- Timers
- UARTs
- PWMs
- SPIs
- I2C
Expand Down
7 changes: 3 additions & 4 deletions cpu/qn908x/Makefile.dep
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ USEMODULE += vendor_fsl_clock
# All peripherals use gpio_mux.h
USEMODULE += periph_gpio_mux

# This cpu modules doesn't support UART peripherals yet, so we need to include
# stdio_null.
# TODO: Remove stdio_null once periph_uart is implemented in this module.
USEMODULE += stdio_null
ifneq (,$(filter periph_uart,$(USEMODULE)))
USEMODULE += periph_flexcomm
endif

include $(RIOTCPU)/cortexm_common/Makefile.dep
26 changes: 26 additions & 0 deletions cpu/qn908x/doc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,32 @@ The GPIO driver uses the @ref GPIO_PIN(port, pin) macro to declare pins.
No configuration is necessary.


@defgroup cpu_qn908x_uart NXP QN908x UART
@ingroup cpu_qn908x
@brief NXP QN908x UART driver

There are several FLEXCOMM interfaces in this chip, but only two of these
support UART (FLEXCOMM0 and FLEXCOMM1). The default UART mode is 8n1 and can
be changed with the uart_mode() function. If only RX or only TX is desired, the
other pin can be set to GPIO_UNDEF.

### UART configuration example (for periph_conf.h) ###

static const uart_conf_t uart_config[] = {
{
.dev = USART0,
.rx_pin = GPIO_PIN(PORT_A, 17), /* or 5 */
.tx_pin = GPIO_PIN(PORT_A, 16), /* or 4 */
},
{
.dev = USART1,
.rx_pin = GPIO_PIN(PORT_A, 9), /* or 13 */
.tx_pin = GPIO_PIN(PORT_A, 8), /* or 12 */
},
};
#define UART_NUMOF ARRAY_SIZE(uart_config)


@defgroup cpu_qn908x_wdt NXP QN908x Watchdog timer (WDT)
@ingroup cpu_qn908x
@brief NXP QN908x Watchdog timer (WDT)
Expand Down
67 changes: 67 additions & 0 deletions cpu/qn908x/include/flexcomm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (C) 2020 iosabi
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/

/**
* @ingroup cpu_qn908x
*
* @{
*
* @file
* @brief Flexcomm interface functions.
*
* The FLEXCOMM blocks can operate in different modes such as UART, SPI and I2C,
* but not all modules support all modes. These functions allow to initialize
* and configure the FLEXCOMM, as well as route back the ISRs to the
* corresponding module.
*
* @author iosabi <iosabi@protonmail.com>
*/

#ifndef FLEXCOMM_H
#define FLEXCOMM_H

#include <stdint.h>
#include "periph_cpu.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Flexcomm PSELID values
*
* This value identifies the current function of a FLEXCOMM module.
*/
typedef enum {
FLEXCOMM_ID_UART = 1, /**< UART mode. */
FLEXCOMM_ID_SPI = 2, /**< SPI mode. */
FLEXCOMM_ID_I2C = 3, /**< I2C mode. */
} flexcom_pselid_t;

/**
* @brief Initialize a flexcomm module to operate as the selected mode.
*
* @returns -1 in case of error, otherwise returns the number of flexcomm
* instance initialized, such as 2 for FLEXCOMM2.
*/
int flexcomm_init(FLEXCOMM_Type *dev, flexcom_pselid_t mode);

/**
* @brief Obtain the flexcomm block number (0-based) from the address.
*
* For example, the flexcomm block number of FLEXCOMM2, the pointer to the
* FLEXCOMM_Type block is 2. If an invalid address is passed returns -1.
*/
int flexcomm_instance_from_addr(FLEXCOMM_Type *dev);

#ifdef __cplusplus
}
#endif

#endif /* FLEXCOMM_H */
/** @} */
55 changes: 55 additions & 0 deletions cpu/qn908x/include/periph_cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,61 @@ enum {
GPIO_PORTS_NUMOF /**< overall number of available ports */
};

/**
* @brief UART module configuration options
*
* QN908x doesn't have any UART standalone blocks, but it has two FLEXCOMM
* blocks that can be put in UART mode. The USART_Type* address is one of the
* FLEXCOMM_Type* addresses as well.
*/
typedef struct {
USART_Type *dev; /**< Pointer to module hardware registers */
gpio_t rx_pin; /**< RX pin, GPIO_UNDEF disables RX. */
gpio_t tx_pin; /**< TX pin, GPIO_UNDEF disables TX. */
} uart_conf_t;

/**
* @brief Definition of possible parity modes
*
* These are defined to match the values of the USART->CFG : PARITYSEL bit
* field.
* @{
*/
typedef enum {
UART_PARITY_NONE = 0, /**< no parity */
UART_PARITY_EVEN = 2, /**< even parity */
UART_PARITY_ODD = 3, /**< odd parity */
} uart_parity_t;
#define HAVE_UART_PARITY_T
/** @} */

/**
* @brief Definition of possible data bits lengths in a UART frame
*
* These are defined to match the values of the USART->CFG : DATALEN bit field.
* @{
*/
typedef enum {
UART_DATA_BITS_7 = 0, /**< 7 data bits */
UART_DATA_BITS_8 = 1, /**< 8 data bits */
/* Note: There's a UART_DATA_BITS_9 possible in this hardware. */
} uart_data_bits_t;
#define HAVE_UART_DATA_BITS_T
/** @} */

/**
* @brief Definition of possible stop bits lengths
*
* These are defined to match the values of the USART->CFG : STOPLEN bit field.
* @{
*/
typedef enum {
UART_STOP_BITS_1 = 0, /**< 1 stop bit */
UART_STOP_BITS_2 = 1, /**< 2 stop bits */
} uart_stop_bits_t;
#define HAVE_UART_STOP_BITS_T
/** @} */

#ifdef __cplusplus
}
#endif
Expand Down
145 changes: 145 additions & 0 deletions cpu/qn908x/periph/flexcomm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* Copyright (C) 2020 iosabi
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/

/**
* @ingroup cpu_qn908x
*
* @{
*
* @file
* @brief Flexcomm interrupt dispatch.
*
* @author iosabi <iosabi@protonmail.com>
*
* @}
*/

#include <stdint.h>
#include "cpu.h"
#include "periph_conf.h"
#include "vectors_qn908x.h"
#include "flexcomm.h"

#include "vendor/drivers/fsl_clock.h"

#include "debug.h"

int flexcomm_init(FLEXCOMM_Type *dev, flexcom_pselid_t mode)
{
static const clock_ip_name_t flexcomm_clocks[] = FLEXCOMM_CLOCKS;
int flexcomm_num = flexcomm_instance_from_addr(dev);

if (flexcomm_num < 0 || flexcomm_num >= (int)ARRAY_SIZE(flexcomm_clocks)) {
DEBUG("Invalid flexcomm_num: %d\n", flexcomm_num);
return -1;
}
CLOCK_EnableClock(flexcomm_clocks[flexcomm_num]);
/* Reset the flexcomm. */
SYSCON->RST_SW_SET = 1u << flexcomm_num;
SYSCON->RST_SW_CLR = 1u << flexcomm_num;

/* Check that the mode is present in the FLEXCOMM.
* Bits 4, 5 and 6 tell whether the UART, SPI and I2C respectively are
* present. */
if ((dev->PSELID & (1u << (mode + 3))) == 0) {
DEBUG("Mode %d not present in FLEXCOMM%d\n", (int)mode, flexcomm_num);
return -1;
}

/* This also locks the peripheral so it can't be changed until device or
* peripheral is reset. */
dev->PSELID = (dev->PSELID & ~FLEXCOMM_PSELID_PERSEL_MASK) |
FLEXCOMM_PSELID_LOCK_MASK |
FLEXCOMM_PSELID_PERSEL(mode);
return flexcomm_num;
}

int flexcomm_instance_from_addr(FLEXCOMM_Type *dev)
{
static const FLEXCOMM_Type *flexcomm_addrs[] = FLEXCOMM_BASE_PTRS;

for (uint8_t i = 0; i < ARRAY_SIZE(flexcomm_addrs); i++) {
if (flexcomm_addrs[i] == dev) {
return i;
}
}
DEBUG("Invalid FLEXCOMM address.\n");
return -1;
}

#ifdef MODULE_PERIPH_UART
extern void isr_flexcomm_uart(USART_Type *dev, uint32_t flexcomm_num);
#endif /* MODULE_PERIPH_UART */

#ifdef MODULE_PERIPH_SPI
extern void isr_flexcomm_spi(SPI_Type *dev, uint32_t flexcomm_num);
#endif /* MODULE_PERIPH_SPI */

#ifdef MODULE_PERIPH_I2C
extern void isr_flexcomm_i2c(I2C_Type *dev, uint32_t flexcomm_num);
#endif /* MODULE_PERIPH_I2C */

/**
* @brief General Flexcomm interrupt handler dispatch.
*
* The driver that should get an interrupt from the flexcomm depends on the
* currently configured one, which can be obtained from the PSELID.
*/
static void isr_flexcomm(void *flexcomm, uint32_t flexcomm_num)
{
switch (((FLEXCOMM_Type *)flexcomm)->PSELID & FLEXCOMM_PSELID_PERSEL_MASK) {
#ifdef MODULE_PERIPH_UART
case FLEXCOMM_ID_UART:
isr_flexcomm_uart((USART_Type *)(flexcomm), flexcomm_num);
return;
break;
#endif /* MODULE_PERIPH_UART */
#ifdef MODULE_PERIPH_SPI
case FLEXCOMM_ID_SPI:
isr_flexcomm_spi((SPI_Type *)(flexcomm), flexcomm_num);
return;
break;
#endif /* MODULE_PERIPH_SPI */
#ifdef MODULE_PERIPH_I2C
case FLEXCOMM_ID_I2C:
isr_flexcomm_i2c((I2C_Type *)(flexcomm), flexcomm_num);
return;
break;
#endif /* MODULE_PERIPH_I2C */
default:
cortexm_isr_end();
}
}

#ifdef FLEXCOMM0
void isr_flexcomm0(void)
{
isr_flexcomm(FLEXCOMM0, 0);
}
#endif /* FLEXCOMM0 */

#ifdef FLEXCOMM1
void isr_flexcomm1(void)
{
isr_flexcomm(FLEXCOMM1, 1);
}
#endif /* FLEXCOMM1 */

#ifdef FLEXCOMM2
void isr_flexcomm2(void)
{
isr_flexcomm(FLEXCOMM2, 2);
}
#endif /* FLEXCOMM2 */

#ifdef FLEXCOMM3
void isr_flexcomm3(void)
{
isr_flexcomm(FLEXCOMM3, 3);
}
#endif /* FLEXCOMM3 */

0 comments on commit 7624b89

Please sign in to comment.