Skip to content

Commit 128f202

Browse files
committed
drivers: serial: Implement UART driver for WCH CH5xx series
This commit introduces UART driver for CH5xx series, supporting both polling and interrupt-driven mode. Signed-off-by: Chen Xingyu <[email protected]>
1 parent 4d436d3 commit 128f202

File tree

7 files changed

+429
-0
lines changed

7 files changed

+429
-0
lines changed

drivers/serial/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ zephyr_library_sources_ifdef(CONFIG_UART_ENE_KB1200 uart_ene_kb1200.c)
7777
zephyr_library_sources_ifdef(CONFIG_UART_RZT2M uart_rzt2m.c)
7878
zephyr_library_sources_ifdef(CONFIG_UART_RA8_SCI_B uart_renesas_ra8_sci_b.c)
7979
zephyr_library_sources_ifdef(CONFIG_UART_SI32_USART uart_si32_usart.c)
80+
zephyr_library_sources_ifdef(CONFIG_UART_CH5XX uart_ch5xx.c)
8081
zephyr_library_sources_ifdef(CONFIG_USERSPACE uart_handlers.c)
8182
zephyr_library_sources_ifdef(CONFIG_UART_SCI_RA uart_renesas_ra_sci.c)
8283

drivers/serial/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,4 +282,6 @@ rsource "Kconfig.renesas_ra8"
282282

283283
source "drivers/serial/Kconfig.si32"
284284

285+
source "drivers/serial/Kconfig.ch32v"
286+
285287
endif # SERIAL

drivers/serial/Kconfig.ch32v

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright (c) 2023-2024 Chen Xingyu <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config UART_CH5XX
5+
bool "WCH CH5xx UART driver"
6+
default y
7+
depends on DT_HAS_WCH_CH5XX_UART_ENABLED
8+
select SERIAL_HAS_DRIVER
9+
select SERIAL_SUPPORT_INTERRUPT
10+
select PINCTRL
11+
help
12+
Enable WCH CH5xx UART driver.

drivers/serial/uart_ch5xx.c

Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
/*
2+
* Copyright (c) 2023-2024 Chen Xingyu <[email protected]>
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#define DT_DRV_COMPAT wch_ch5xx_uart
7+
8+
#include <zephyr/arch/cpu.h>
9+
#include <zephyr/irq.h>
10+
#include <zephyr/device.h>
11+
#include <zephyr/drivers/uart.h>
12+
#include <zephyr/drivers/pinctrl.h>
13+
14+
#include <zephyr/logging/log.h>
15+
LOG_MODULE_REGISTER(uart_ch5xx, CONFIG_UART_LOG_LEVEL);
16+
17+
#define UART_R8_MCR(base) (base + 0x00)
18+
#define UART_R8_IER(base) (base + 0x01)
19+
#define UART_R8_FCR(base) (base + 0x02)
20+
#define UART_R8_LCR(base) (base + 0x03)
21+
#define UART_R8_IIR(base) (base + 0x04)
22+
#define UART_R8_LSR(base) (base + 0x05)
23+
#define UART_R8_MSR(base) (base + 0x06)
24+
#define UART_R8_RBR(base) (base + 0x08)
25+
#define UART_R8_THR(base) (base + 0x08)
26+
#define UART_R8_RFC(base) (base + 0x0A)
27+
#define UART_R8_TFC(base) (base + 0x0B)
28+
#define UART_R16_DL(base) (base + 0x0C)
29+
#define UART_R8_DIV(base) (base + 0x0E)
30+
31+
/* UART_R8_MCR */
32+
#define MCR_DTR BIT(0)
33+
#define MCR_RTS BIT(1)
34+
#define MCR_OUT1 BIT(2)
35+
#define MCR_INT_OE BIT(3)
36+
#define MCR_LOOP BIT(4)
37+
#define MCR_AU_FLOW_EN BIT(5)
38+
#define MCR_TNOW BIT(6)
39+
#define MCR_HALF BIT(7)
40+
41+
/* UART_R8_IER */
42+
#define IER_RECV_RDY BIT(0)
43+
#define IER_THR_EMPTY BIT(1)
44+
#define IER_LINE_STAT BIT(2)
45+
#define IER_MODEM_CHG BIT(3)
46+
#define IER_DTR_EN BIT(4)
47+
#define IER_RTS_EN BIT(5)
48+
#define IER_TXD_EN BIT(6)
49+
#define IER_RESET BIT(7)
50+
51+
/* UART_R8_FCR */
52+
#define FCR_FIFO_EN BIT(0)
53+
#define FCR_RX_FIFO_CLR BIT(1)
54+
#define FCR_TX_FIFO_CLR BIT(2)
55+
#define FCR_FIFO_TRIG_1 (0 << 6)
56+
#define FCR_FIFO_TRIG_2 (1 << 6)
57+
#define FCR_FIFO_TRIG_4 (2 << 6)
58+
#define FCR_FIFO_TRIG_7 (3 << 6)
59+
#define FCR_FIFO_TRIG_MASK (BIT_MASK(2) << 6)
60+
61+
/* UART_R8_LCR */
62+
#define LCR_WORD_SZ_5 (0 << 0)
63+
#define LCR_WORD_SZ_6 (1 << 0)
64+
#define LCR_WORD_SZ_7 (2 << 0)
65+
#define LCR_WORD_SZ_8 (3 << 0)
66+
#define LCR_WORD_SZ_MASK (BIT_MASK(2) << 0)
67+
#define LCR_STOP_BIT BIT(2)
68+
#define LCR_PAR_EN BIT(3)
69+
#define LCR_PAR_MOD_ODD (0 << 4)
70+
#define LCR_PAR_MOD_EVEN (1 << 4)
71+
#define LCR_PAR_MOD_MASK (2 << 4)
72+
#define LCR_PAR_MOD_SPACE (3 << 4)
73+
#define LCR_PAR_MOD_MARK (BIT_MASK(2) << 4)
74+
#define LCR_BREAK_EN BIT(6)
75+
76+
/* UART_R8_IIR */
77+
#define IIR_INT_MASK (BIT_MASK(4) << 0)
78+
#define IIR_INT_NOINT (0x1)
79+
#define IIR_INT_ADDR (0xe)
80+
#define IIR_INT_LSR (0x6)
81+
#define IIR_INT_RBR_AVAIL (0x4)
82+
#define IIR_INT_RBR_TIMEOUT (0xc)
83+
#define IIR_INT_THR_EMPTY (0x2)
84+
#define IIR_INT_MSR_CHG (0x0)
85+
86+
/* UART_R8_LSR */
87+
#define LSR_DATA_RDY BIT(0)
88+
#define LSR_OVER_ERR BIT(1)
89+
#define LSR_PAR_ERR BIT(2)
90+
#define LSR_FRAME_ERR BIT(3)
91+
#define LSR_BREAK_ERR BIT(4)
92+
#define LSR_TX_FIFO_EMP BIT(5)
93+
#define LSR_TX_ALL_EMP BIT(6)
94+
#define LSR_ERR_RX_FIFO BIT(7)
95+
96+
#define UART_FIFO_SIZE 8
97+
98+
struct uart_ch5xx_config {
99+
void (*irq_config_func)(void);
100+
mem_addr_t base;
101+
const struct pinctrl_dev_config *pcfg;
102+
uint32_t sys_clk_freq;
103+
uint32_t baud_rate;
104+
};
105+
106+
struct uart_ch5xx_data {
107+
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
108+
uart_irq_callback_user_data_t cb;
109+
void *cb_data;
110+
#endif
111+
};
112+
113+
static int uart_ch5xx_poll_in(const struct device *dev, unsigned char *p_char)
114+
{
115+
const struct uart_ch5xx_config *cfg = dev->config;
116+
117+
/* Wait for RX FIFO available */
118+
while (sys_read8(UART_R8_RFC(cfg->base)) == 0) {
119+
}
120+
121+
*p_char = sys_read8(UART_R8_RBR(cfg->base));
122+
123+
return 0;
124+
}
125+
126+
static void uart_ch5xx_poll_out(const struct device *dev, unsigned char out_char)
127+
{
128+
const struct uart_ch5xx_config *cfg = dev->config;
129+
130+
/* Wait for TX FIFO available */
131+
while (sys_read8(UART_R8_TFC(cfg->base)) == UART_FIFO_SIZE) {
132+
}
133+
134+
sys_write8(out_char, UART_R8_THR(cfg->base));
135+
}
136+
137+
#if defined(CONFIG_UART_INTERRUPT_DRIVEN)
138+
static int uart_ch5xx_fifo_fill(const struct device *dev, const uint8_t *tx_data, int len)
139+
{
140+
const struct uart_ch5xx_config *cfg = dev->config;
141+
int sent;
142+
143+
for (sent = 0; sent < len && sys_read8(UART_R8_TFC(cfg->base)) < UART_FIFO_SIZE; sent++) {
144+
sys_write8(tx_data[sent], UART_R8_THR(cfg->base));
145+
}
146+
147+
return sent;
148+
}
149+
150+
static int uart_ch5xx_fifo_read(const struct device *dev, uint8_t *rx_data, const int size)
151+
{
152+
const struct uart_ch5xx_config *cfg = dev->config;
153+
int read;
154+
155+
for (read = 0; read < size && sys_read8(UART_R8_RFC(cfg->base)) > 0; read++) {
156+
rx_data[read] = sys_read8(UART_R8_RBR(cfg->base));
157+
}
158+
159+
return read;
160+
}
161+
162+
static void uart_ch5xx_irq_tx_enable(const struct device *dev)
163+
{
164+
const struct uart_ch5xx_config *cfg = dev->config;
165+
uint32_t regval;
166+
167+
regval = sys_read8(UART_R8_IER(cfg->base));
168+
regval |= IER_THR_EMPTY;
169+
sys_write8(regval, UART_R8_IER(cfg->base));
170+
}
171+
172+
static void uart_ch5xx_irq_tx_disable(const struct device *dev)
173+
{
174+
const struct uart_ch5xx_config *cfg = dev->config;
175+
uint32_t regval;
176+
177+
regval = sys_read8(UART_R8_IER(cfg->base));
178+
regval &= ~IER_THR_EMPTY;
179+
sys_write8(regval, UART_R8_IER(cfg->base));
180+
}
181+
182+
static int uart_ch5xx_irq_tx_ready(const struct device *dev)
183+
{
184+
const struct uart_ch5xx_config *cfg = dev->config;
185+
186+
return sys_read8(UART_R8_TFC(cfg->base)) < UART_FIFO_SIZE;
187+
}
188+
189+
static void uart_ch5xx_irq_rx_enable(const struct device *dev)
190+
{
191+
const struct uart_ch5xx_config *cfg = dev->config;
192+
uint32_t regval;
193+
194+
regval = sys_read8(UART_R8_IER(cfg->base));
195+
regval |= IER_RECV_RDY;
196+
sys_write8(regval, UART_R8_IER(cfg->base));
197+
}
198+
199+
static void uart_ch5xx_irq_rx_disable(const struct device *dev)
200+
{
201+
const struct uart_ch5xx_config *cfg = dev->config;
202+
uint32_t regval;
203+
204+
regval = sys_read8(UART_R8_IER(cfg->base));
205+
regval &= ~IER_RECV_RDY;
206+
sys_write8(regval, UART_R8_IER(cfg->base));
207+
}
208+
209+
static int uart_ch5xx_irq_rx_ready(const struct device *dev)
210+
{
211+
const struct uart_ch5xx_config *cfg = dev->config;
212+
213+
return sys_read8(UART_R8_RFC(cfg->base)) > 0;
214+
}
215+
216+
static int uart_ch5xx_irq_is_pending(const struct device *dev)
217+
{
218+
const struct uart_ch5xx_config *cfg = dev->config;
219+
uint32_t regval;
220+
221+
regval = sys_read8(UART_R8_IIR(cfg->base)) & IIR_INT_MASK;
222+
223+
return regval != IIR_INT_NOINT;
224+
}
225+
226+
static int uart_ch5xx_irq_update(const struct device *dev)
227+
{
228+
return 1;
229+
}
230+
231+
static void uart_ch5xx_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb,
232+
void *user_data)
233+
{
234+
struct uart_ch5xx_data *data = dev->data;
235+
236+
data->cb = cb;
237+
data->cb_data = user_data;
238+
}
239+
240+
static void uart_ch5xx_isr(const struct device *dev)
241+
{
242+
const struct uart_ch5xx_config *cfg = dev->config;
243+
struct uart_ch5xx_data *data = dev->data;
244+
uint32_t regval;
245+
246+
if (data->cb) {
247+
data->cb(dev, data->cb_data);
248+
}
249+
250+
regval = sys_read8(UART_R8_IIR(cfg->base));
251+
switch (regval & IIR_INT_MASK) {
252+
case IIR_INT_NOINT:
253+
case IIR_INT_ADDR:
254+
break;
255+
case IIR_INT_LSR:
256+
if (sys_read8(UART_R8_LSR(cfg->base)) & LSR_ERR_RX_FIFO) {
257+
uart_irq_err_enable(dev);
258+
}
259+
break;
260+
case IIR_INT_RBR_AVAIL:
261+
if (sys_read8(UART_R8_LSR(cfg->base)) & LSR_DATA_RDY) {
262+
uart_irq_rx_ready(dev);
263+
}
264+
break;
265+
case IIR_INT_RBR_TIMEOUT:
266+
if (sys_read8(UART_R8_LSR(cfg->base)) & LSR_DATA_RDY) {
267+
uart_irq_rx_ready(dev);
268+
}
269+
break;
270+
case IIR_INT_THR_EMPTY:
271+
uart_irq_tx_ready(dev);
272+
break;
273+
default:
274+
break;
275+
}
276+
}
277+
#endif
278+
279+
static int uart_ch5xx_init(const struct device *dev)
280+
{
281+
const struct uart_ch5xx_config *cfg = dev->config;
282+
uint32_t regval;
283+
int ret;
284+
285+
regval = 10 * cfg->sys_clk_freq * 2 / 16 / cfg->baud_rate;
286+
regval = (regval + 5) / 10;
287+
sys_write8(1, UART_R8_DIV(cfg->base));
288+
sys_write16(regval, UART_R16_DL(cfg->base));
289+
290+
sys_write8(FCR_FIFO_EN | FCR_RX_FIFO_CLR | FCR_TX_FIFO_CLR, UART_R8_FCR(cfg->base));
291+
292+
sys_write8(LCR_WORD_SZ_8, UART_R8_LCR(cfg->base));
293+
294+
sys_write8(IER_TXD_EN, UART_R8_IER(cfg->base));
295+
296+
ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
297+
if (ret < 0) {
298+
return ret;
299+
}
300+
301+
#if defined(CONFIG_UART_INTERRUPT_DRIVEN)
302+
regval = sys_read8(UART_R8_MCR(cfg->base));
303+
regval |= MCR_INT_OE;
304+
sys_write8(regval, UART_R8_MCR(cfg->base));
305+
306+
cfg->irq_config_func();
307+
#endif
308+
309+
return 0;
310+
}
311+
312+
static const struct uart_driver_api uart_ch5xx_api = {
313+
.poll_in = uart_ch5xx_poll_in,
314+
.poll_out = uart_ch5xx_poll_out,
315+
#if defined(CONFIG_UART_INTERRUPT_DRIVEN)
316+
.fifo_fill = uart_ch5xx_fifo_fill,
317+
.fifo_read = uart_ch5xx_fifo_read,
318+
.irq_tx_enable = uart_ch5xx_irq_tx_enable,
319+
.irq_tx_disable = uart_ch5xx_irq_tx_disable,
320+
.irq_tx_ready = uart_ch5xx_irq_tx_ready,
321+
.irq_rx_enable = uart_ch5xx_irq_rx_enable,
322+
.irq_rx_disable = uart_ch5xx_irq_rx_disable,
323+
.irq_rx_ready = uart_ch5xx_irq_rx_ready,
324+
.irq_is_pending = uart_ch5xx_irq_is_pending,
325+
.irq_update = uart_ch5xx_irq_update,
326+
.irq_callback_set = uart_ch5xx_irq_callback_set,
327+
#endif
328+
};
329+
330+
#define UART_CH5XX_INST(n) \
331+
static struct uart_ch5xx_data uart_ch5xx_data_##n; \
332+
\
333+
static void uart_ch5xx_irq_config_func_##n(void) \
334+
{ \
335+
IF_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN, \
336+
(IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), uart_ch5xx_isr, \
337+
DEVICE_DT_INST_GET(n), 0); \
338+
irq_enable(DT_INST_IRQN(n));)) \
339+
} \
340+
\
341+
PINCTRL_DT_INST_DEFINE(n); \
342+
\
343+
static const struct uart_ch5xx_config uart_ch5xx_cfg_##n = { \
344+
.irq_config_func = uart_ch5xx_irq_config_func_##n, \
345+
.base = DT_INST_REG_ADDR(n), \
346+
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
347+
.sys_clk_freq = DT_INST_PROP_BY_PHANDLE(n, clocks, clock_frequency), \
348+
.baud_rate = DT_INST_PROP(n, current_speed), \
349+
}; \
350+
\
351+
DEVICE_DT_INST_DEFINE(n, uart_ch5xx_init, NULL, &uart_ch5xx_data_##n, &uart_ch5xx_cfg_##n, \
352+
PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, &uart_ch5xx_api);
353+
354+
DT_INST_FOREACH_STATUS_OKAY(UART_CH5XX_INST)

0 commit comments

Comments
 (0)