serial: add a serial driver for the SAM0 family.
Signed-off-by: Michael Hope <mlhx@google.com>
This commit is contained in:
parent
54c6fbdca7
commit
b85b676ba9
5 changed files with 344 additions and 0 deletions
|
@ -15,5 +15,6 @@ zephyr_sources_if_kconfig(uart_sam.c)
|
|||
zephyr_sources_if_kconfig(usart_sam.c)
|
||||
zephyr_sources_if_kconfig(uart_stellaris.c)
|
||||
zephyr_sources_if_kconfig(uart_stm32.c)
|
||||
zephyr_sources_if_kconfig(uart_sam0.c)
|
||||
|
||||
zephyr_sources_ifdef(CONFIG_USERSPACE uart_handlers.c)
|
||||
|
|
|
@ -101,4 +101,6 @@ source "drivers/serial/Kconfig.gecko"
|
|||
|
||||
source "drivers/serial/Kconfig.msp432p4xx"
|
||||
|
||||
source "drivers/serial/Kconfig.sam0"
|
||||
|
||||
endif
|
||||
|
|
13
drivers/serial/Kconfig.sam0
Normal file
13
drivers/serial/Kconfig.sam0
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Kconfig - Atmel SAM SERCOM configuration options
|
||||
#
|
||||
# Copyright (c) 2017 Google LLC.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig UART_SAM0
|
||||
bool "Atmel SAM0 series SERCOM USART driver"
|
||||
depends on SOC_FAMILY_SAM0
|
||||
select SERIAL_HAS_DRIVER
|
||||
select SERIAL_SUPPORT_INTERRUPT
|
||||
default n
|
||||
help
|
||||
This option enables the SERCOMx USART driver for Atmel SAM0 MCUs.
|
298
drivers/serial/uart_sam0.c
Normal file
298
drivers/serial/uart_sam0.c
Normal file
|
@ -0,0 +1,298 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Google LLC.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <device.h>
|
||||
#include <errno.h>
|
||||
#include <init.h>
|
||||
#include <misc/__assert.h>
|
||||
#include <soc.h>
|
||||
#include <uart.h>
|
||||
|
||||
/* Device constant configuration parameters */
|
||||
struct uart_sam0_dev_cfg {
|
||||
SercomUsart *regs;
|
||||
u32_t baudrate;
|
||||
u32_t ctrla;
|
||||
u32_t pm_apbcmask;
|
||||
u16_t gclk_clkctrl_id;
|
||||
struct soc_gpio_pin pin_rx;
|
||||
struct soc_gpio_pin pin_tx;
|
||||
#if CONFIG_UART_INTERRUPT_DRIVEN
|
||||
void (*irq_config_func)(struct device *dev);
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Device run time data */
|
||||
struct uart_sam0_dev_data {
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
uart_irq_callback_t cb;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define DEV_CFG(dev) \
|
||||
((const struct uart_sam0_dev_cfg *const)(dev)->config->config_info)
|
||||
#define DEV_DATA(dev) ((struct uart_sam0_dev_data * const)(dev)->driver_data)
|
||||
|
||||
static void wait_synchronization(SercomUsart *const usart)
|
||||
{
|
||||
while ((usart->SYNCBUSY.reg & SERCOM_USART_SYNCBUSY_MASK) != 0) {
|
||||
}
|
||||
}
|
||||
|
||||
static int uart_sam0_set_baudrate(SercomUsart *const usart, u32_t baudrate,
|
||||
u32_t clk_freq_hz)
|
||||
{
|
||||
u64_t tmp;
|
||||
u16_t baud;
|
||||
|
||||
tmp = (u64_t)baudrate << 20;
|
||||
tmp = (tmp + (clk_freq_hz >> 1)) / clk_freq_hz;
|
||||
|
||||
/* Verify that the calculated result is within range */
|
||||
if (tmp < 1 || tmp > UINT16_MAX) {
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
baud = 65536 - (u16_t)tmp;
|
||||
usart->BAUD.reg = baud;
|
||||
wait_synchronization(usart);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uart_sam0_init(struct device *dev)
|
||||
{
|
||||
int retval;
|
||||
const struct uart_sam0_dev_cfg *const cfg = DEV_CFG(dev);
|
||||
SercomUsart *const usart = cfg->regs;
|
||||
|
||||
/* Enable the GCLK */
|
||||
GCLK->CLKCTRL.reg =
|
||||
cfg->gclk_clkctrl_id | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_CLKEN;
|
||||
|
||||
/* Enable SERCOM clock in PM */
|
||||
PM->APBCMASK.reg |= cfg->pm_apbcmask;
|
||||
|
||||
/* Connect pins to the peripheral */
|
||||
soc_gpio_configure(&cfg->pin_rx);
|
||||
soc_gpio_configure(&cfg->pin_tx);
|
||||
|
||||
/* Disable all USART interrupts */
|
||||
usart->INTENCLR.reg = SERCOM_USART_INTENCLR_MASK;
|
||||
wait_synchronization(usart);
|
||||
|
||||
/* 8 bits of data, no parity, 1 stop bit in normal mode */
|
||||
usart->CTRLA.reg =
|
||||
cfg->ctrla |
|
||||
/* Internal clock */
|
||||
SERCOM_USART_CTRLA_MODE_USART_INT_CLK
|
||||
/* 16x oversampling with arithmetic baud rate generation */
|
||||
| SERCOM_USART_CTRLA_SAMPR(0) | SERCOM_USART_CTRLA_FORM(0) |
|
||||
SERCOM_USART_CTRLA_CPOL | SERCOM_USART_CTRLA_DORD;
|
||||
wait_synchronization(usart);
|
||||
|
||||
/* Enable receiver and transmitter */
|
||||
usart->CTRLB.reg = SERCOM_SPI_CTRLB_CHSIZE(0) |
|
||||
SERCOM_USART_CTRLB_RXEN | SERCOM_USART_CTRLB_TXEN;
|
||||
wait_synchronization(usart);
|
||||
|
||||
retval = uart_sam0_set_baudrate(usart, cfg->baudrate,
|
||||
SOC_ATMEL_SAM0_GCLK0_FREQ_HZ);
|
||||
if (retval != 0) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
cfg->irq_config_func(dev);
|
||||
#endif
|
||||
|
||||
usart->CTRLA.bit.ENABLE = 1;
|
||||
wait_synchronization(usart);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uart_sam0_poll_in(struct device *dev, unsigned char *c)
|
||||
{
|
||||
SercomUsart *const usart = DEV_CFG(dev)->regs;
|
||||
|
||||
if (!usart->INTFLAG.bit.RXC) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
*c = (unsigned char)usart->DATA.reg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned char uart_sam0_poll_out(struct device *dev, unsigned char c)
|
||||
{
|
||||
SercomUsart *const usart = DEV_CFG(dev)->regs;
|
||||
|
||||
while (!usart->INTFLAG.bit.DRE) {
|
||||
}
|
||||
|
||||
/* send a character */
|
||||
usart->DATA.reg = c;
|
||||
return c;
|
||||
}
|
||||
|
||||
#if CONFIG_UART_INTERRUPT_DRIVEN
|
||||
|
||||
static void uart_sam0_isr(void *arg)
|
||||
{
|
||||
struct device *dev = arg;
|
||||
struct uart_sam0_dev_data *const dev_data = DEV_DATA(dev);
|
||||
|
||||
if (dev_data->cb) {
|
||||
dev_data->cb(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void uart_sam0_irq_tx_disable(struct device *dev)
|
||||
{
|
||||
SercomUsart *const regs = DEV_CFG(dev)->regs;
|
||||
|
||||
regs->INTENCLR.reg = SERCOM_USART_INTENCLR_DRE;
|
||||
}
|
||||
|
||||
static void uart_sam0_irq_rx_enable(struct device *dev)
|
||||
{
|
||||
SercomUsart *const regs = DEV_CFG(dev)->regs;
|
||||
|
||||
regs->INTENSET.reg = SERCOM_USART_INTENSET_RXC;
|
||||
}
|
||||
|
||||
static void uart_sam0_irq_rx_disable(struct device *dev)
|
||||
{
|
||||
SercomUsart *const regs = DEV_CFG(dev)->regs;
|
||||
|
||||
regs->INTENCLR.reg = SERCOM_USART_INTENCLR_RXC;
|
||||
}
|
||||
|
||||
static int uart_sam0_irq_rx_ready(struct device *dev)
|
||||
{
|
||||
SercomUsart *const regs = DEV_CFG(dev)->regs;
|
||||
|
||||
return regs->INTFLAG.bit.RXC != 0;
|
||||
}
|
||||
|
||||
static int uart_sam0_fifo_read(struct device *dev, u8_t *rx_data,
|
||||
const int size)
|
||||
{
|
||||
SercomUsart *const regs = DEV_CFG(dev)->regs;
|
||||
|
||||
if (regs->INTFLAG.bit.RXC) {
|
||||
u8_t ch = regs->DATA.reg;
|
||||
|
||||
if (size >= 1) {
|
||||
*rx_data = ch;
|
||||
return 1;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uart_sam0_irq_is_pending(struct device *dev)
|
||||
{
|
||||
SercomUsart *const regs = DEV_CFG(dev)->regs;
|
||||
|
||||
return (regs->INTENSET.reg & regs->INTFLAG.reg) != 0;
|
||||
}
|
||||
|
||||
static int uart_sam0_irq_update(struct device *dev) { return 1; }
|
||||
|
||||
static void uart_sam0_irq_callback_set(struct device *dev,
|
||||
uart_irq_callback_t cb)
|
||||
{
|
||||
struct uart_sam0_dev_data *const dev_data = DEV_DATA(dev);
|
||||
|
||||
dev_data->cb = cb;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct uart_driver_api uart_sam0_driver_api = {
|
||||
.poll_in = uart_sam0_poll_in,
|
||||
.poll_out = uart_sam0_poll_out,
|
||||
#if CONFIG_UART_INTERRUPT_DRIVEN
|
||||
.fifo_read = uart_sam0_fifo_read,
|
||||
.irq_tx_disable = uart_sam0_irq_tx_disable,
|
||||
.irq_rx_enable = uart_sam0_irq_rx_enable,
|
||||
.irq_rx_disable = uart_sam0_irq_rx_disable,
|
||||
.irq_rx_ready = uart_sam0_irq_rx_ready,
|
||||
.irq_is_pending = uart_sam0_irq_is_pending,
|
||||
.irq_update = uart_sam0_irq_update,
|
||||
.irq_callback_set = uart_sam0_irq_callback_set,
|
||||
#endif
|
||||
};
|
||||
|
||||
#if CONFIG_UART_INTERRUPT_DRIVEN
|
||||
#define UART_SAM0_IRQ_HANDLER_DECL(n) \
|
||||
static void uart_sam0_irq_config_##n(struct device *dev)
|
||||
#define UART_SAM0_IRQ_HANDLER_FUNC(n) \
|
||||
.irq_config_func = uart_sam0_irq_config_##n,
|
||||
#define UART_SAM0_IRQ_HANDLER(n) \
|
||||
static void uart_sam0_irq_config_##n(struct device *dev) \
|
||||
{ \
|
||||
IRQ_CONNECT(CONFIG_UART_SAM0_SERCOM##n##_IRQ, \
|
||||
CONFIG_UART_SAM0_SERCOM##n##_IRQ_PRIORITY, \
|
||||
uart_sam0_isr, DEVICE_GET(uart_sam0_##n), \
|
||||
0); \
|
||||
irq_enable(CONFIG_UART_SAM0_SERCOM##n##_IRQ); \
|
||||
}
|
||||
#else
|
||||
#define UART_SAM0_IRQ_HANDLER_DECL(n)
|
||||
#define UART_SAM0_IRQ_HANDLER_FUNC(n)
|
||||
#define UART_SAM0_IRQ_HANDLER(n)
|
||||
#endif
|
||||
|
||||
#define UART_SAM0_CONFIG_DEFN(n) \
|
||||
static const struct uart_sam0_dev_cfg uart_sam0_config_##n = { \
|
||||
.regs = (SercomUsart *)CONFIG_UART_SAM0_SERCOM##n##_BASE_ADDRESS, \
|
||||
.baudrate = CONFIG_UART_SAM0_SERCOM##n##_CURRENT_SPEED, \
|
||||
.pm_apbcmask = PM_APBCMASK_SERCOM##n, \
|
||||
.gclk_clkctrl_id = GCLK_CLKCTRL_ID_SERCOM##n##_CORE, \
|
||||
.ctrla = SERCOM_USART_CTRLA_RXPO(3) | SERCOM_USART_CTRLA_TXPO(1), \
|
||||
.pin_rx = PIN_UART_SAM0_SERCOM##n##_RX, \
|
||||
.pin_tx = PIN_UART_SAM0_SERCOM##n##_TX, \
|
||||
UART_SAM0_IRQ_HANDLER_FUNC(n) \
|
||||
}
|
||||
|
||||
#define UART_SAM0_DEVICE_INIT(n) \
|
||||
static struct uart_sam0_dev_data uart_sam0_data_##n; \
|
||||
UART_SAM0_IRQ_HANDLER_DECL(n); \
|
||||
UART_SAM0_CONFIG_DEFN(n); \
|
||||
DEVICE_AND_API_INIT(uart_sam0_##n, CONFIG_UART_SAM0_SERCOM##n##_LABEL, \
|
||||
uart_sam0_init, &uart_sam0_data_##n, \
|
||||
&uart_sam0_config_##n, PRE_KERNEL_1, \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
|
||||
&uart_sam0_driver_api); \
|
||||
UART_SAM0_IRQ_HANDLER(n)
|
||||
|
||||
#if CONFIG_UART_SAM0_SERCOM0_BASE_ADDRESS
|
||||
UART_SAM0_DEVICE_INIT(0)
|
||||
#endif
|
||||
|
||||
#if CONFIG_UART_SAM0_SERCOM1_BASE_ADDRESS
|
||||
UART_SAM0_DEVICE_INIT(1)
|
||||
#endif
|
||||
|
||||
#if CONFIG_UART_SAM0_SERCOM2_BASE_ADDRESS
|
||||
UART_SAM0_DEVICE_INIT(2)
|
||||
#endif
|
||||
|
||||
#if CONFIG_UART_SAM0_SERCOM3_BASE_ADDRESS
|
||||
UART_SAM0_DEVICE_INIT(3)
|
||||
#endif
|
||||
|
||||
#if CONFIG_UART_SAM0_SERCOM4_BASE_ADDRESS
|
||||
UART_SAM0_DEVICE_INIT(4)
|
||||
#endif
|
||||
|
||||
#if CONFIG_UART_SAM0_SERCOM5_BASE_ADDRESS
|
||||
UART_SAM0_DEVICE_INIT(5)
|
||||
#endif
|
30
dts/bindings/serial/atmel,sam0-uart.yaml
Normal file
30
dts/bindings/serial/atmel,sam0-uart.yaml
Normal file
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
title: Atmel SAM0 SERCOM UART driver
|
||||
id: atmel,sam0-uart
|
||||
version: 0.1
|
||||
|
||||
description: >
|
||||
This binding gives a base representation of the Atmel SAM0 SERCOM UART driver
|
||||
|
||||
inherits:
|
||||
!include uart.yaml
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
type: string
|
||||
category: required
|
||||
description: compatible strings
|
||||
constraint: "atmel,sam0-uart"
|
||||
|
||||
reg:
|
||||
type: array
|
||||
description: mmio register space
|
||||
generation: define
|
||||
category: required
|
||||
|
||||
interrupts:
|
||||
type: array
|
||||
category: required
|
||||
description: required interrupts
|
||||
generation: define
|
||||
...
|
Loading…
Add table
Add a link
Reference in a new issue