drivers: serial: pl011: Add support for Ambiq UART

UART controller present in Ambiq SoCs is mostly compatible with PL011, but
requires some quirks that are implemented in this commit:
- the peripheral needs to be powered on first, via the PWRCTRL core,
- peripheral clock needs to be enabled and configured via the CLKEN/CLKSEL.
  registers.

The quirks mechanism was inspired by support for STM32F4 SoC in the
usb_dc_dw driver (fce0b85eca).

Co-authored-by: Mateusz Sierszulski <msierszulski@antmicro.com>
Signed-off-by: Maciej Sobkowski <msobkowski@antmicro.com>
Signed-off-by: Mateusz Sierszulski <msierszulski@antmicro.com>
This commit is contained in:
Maciej Sobkowski 2023-07-03 16:00:15 +02:00 committed by Carles Cufí
commit 60591598e5
2 changed files with 107 additions and 1 deletions

View file

@ -26,6 +26,7 @@
#endif
#include "uart_pl011_registers.h"
#include "uart_pl011_ambiq.h"
struct pl011_config {
DEVICE_MMIO_ROM;
@ -36,6 +37,8 @@ struct pl011_config {
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
uart_irq_config_func_t irq_config_func;
#endif
int (*clk_enable_func)(const struct device *dev, uint32_t clk);
int (*pwr_on_func)(void);
};
/* Device data structure */
@ -304,10 +307,23 @@ static int pl011_init(const struct device *dev)
return ret;
}
#endif
/* Call vendor-specific function to power on the peripheral */
if (config->pwr_on_func != NULL) {
ret = config->pwr_on_func();
}
/* disable the uart */
pl011_disable(dev);
pl011_disable_fifo(dev);
/* Call vendor-specific function to enable clock for the peripheral */
if (config->clk_enable_func != NULL) {
ret = config->clk_enable_func(dev, config->sys_clk_freq);
if (ret) {
return ret;
}
}
/* Set baud rate */
ret = pl011_set_baudrate(dev, config->sys_clk_freq,
data->baud_rate);
@ -358,6 +374,18 @@ static int pl011_init(const struct device *dev)
#define PINCTRL_INIT(n)
#endif /* CONFIG_PINCTRL */
#define PL011_GET_COMPAT_QUIRK_NONE(n) NULL
#define PL011_GET_COMPAT_CLK_QUIRK_0(n) \
COND_CODE_1(DT_NODE_HAS_COMPAT(DT_DRV_INST(n), ambiq_uart), \
(clk_enable_ambiq_uart), \
PL011_GET_COMPAT_QUIRK_NONE(n))
#define PL011_GET_COMPAT_PWR_QUIRK_0(n) \
COND_CODE_1(DT_NODE_HAS_COMPAT(DT_DRV_INST(n), ambiq_uart), \
(pwr_on_ambiq_uart_##n), \
PL011_GET_COMPAT_QUIRK_NONE(n))
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
void pl011_isr(const struct device *dev)
{
@ -393,6 +421,8 @@ void pl011_isr(const struct device *dev)
.sys_clk_freq = DT_INST_PROP_BY_PHANDLE(n, clocks, clock_frequency), \
PINCTRL_INIT(n) \
.irq_config_func = pl011_irq_config_func_##n, \
.clk_enable_func = PL011_GET_COMPAT_CLK_QUIRK_0(n), \
.pwr_on_func = PL011_GET_COMPAT_PWR_QUIRK_0(n), \
};
#else
#define PL011_CONFIG_PORT(n) \
@ -404,7 +434,8 @@ void pl011_isr(const struct device *dev)
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
#define PL011_INIT(n) \
PINCTRL_DEFINE(n) \
PINCTRL_DEFINE(n) \
PL011_QUIRK_AMBIQ_UART_DEFINE(n) \
PL011_CONFIG_PORT(n) \
\
static struct pl011_data pl011_data_port_##n = { \

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2023 Antmicro <www.antmicro.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_SERIAL_UART_PL011_AMBIQ_H_
#define ZEPHYR_DRIVERS_SERIAL_UART_PL011_AMBIQ_H_
#include <zephyr/device.h>
#include <zephyr/kernel.h>
#include "uart_pl011_registers.h"
#define PWRCTRL_MAX_WAIT_US 5
static inline void pl011_ambiq_enable_clk(const struct device *dev)
{
get_uart(dev)->cr |= PL011_CR_AMBIQ_CLKEN;
}
static inline int pl011_ambiq_clk_set(const struct device *dev, uint32_t clk)
{
uint8_t clksel;
switch (clk) {
case 3000000:
clksel = PL011_CR_AMBIQ_CLKSEL_3MHZ;
break;
case 6000000:
clksel = PL011_CR_AMBIQ_CLKSEL_6MHZ;
break;
case 12000000:
clksel = PL011_CR_AMBIQ_CLKSEL_12MHZ;
break;
case 24000000:
clksel = PL011_CR_AMBIQ_CLKSEL_24MHZ;
break;
case 48000000:
clksel = PL011_CR_AMBIQ_CLKSEL_48MHZ;
break;
default:
return -EINVAL;
}
get_uart(dev)->cr |= FIELD_PREP(PL011_CR_AMBIQ_CLKSEL, clksel);
return 0;
}
static inline int clk_enable_ambiq_uart(const struct device *dev, uint32_t clk)
{
pl011_ambiq_enable_clk(dev);
return pl011_ambiq_clk_set(dev, clk);
}
/* Problem: writes to pwrcfg reg take at most PWCTRL_MAX_WAIT_US time to propagate.
* Solution: busy wait for PWCTRL_MAX_WAIT_US microseconds to ensure that register
* writes have propagated.
*/
#define QUIRK_AMBIQ_UART_DEFINE(n) \
static int pwr_on_ambiq_uart_##n(void) \
{ \
uint32_t addr = DT_REG_ADDR(DT_INST_PHANDLE(n, ambiq_pwrcfg)) + \
DT_INST_PHA(n, ambiq_pwrcfg, offset); \
sys_write32((sys_read32(addr) | DT_INST_PHA(n, ambiq_pwrcfg, mask)), addr); \
k_busy_wait(PWRCTRL_MAX_WAIT_US); \
return 0; \
}
#define PL011_QUIRK_AMBIQ_UART_DEFINE(n) \
COND_CODE_1(DT_NODE_HAS_COMPAT(DT_DRV_INST(n), ambiq_uart), (QUIRK_AMBIQ_UART_DEFINE(n)), \
())
#endif /* ZEPHYR_DRIVERS_SERIAL_UART_PL011_AMBIQ_H_ */