1 /*
2  * Copyright (c) 2024 Google LLC.
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #define DT_DRV_COMPAT wch_usart
7 
8 #include <errno.h>
9 
10 #include <zephyr/drivers/uart.h>
11 #include <zephyr/drivers/clock_control.h>
12 #include <zephyr/drivers/pinctrl.h>
13 
14 #include <ch32fun.h>
15 
16 struct usart_wch_config {
17 	USART_TypeDef *regs;
18 	const struct device *clock_dev;
19 	uint32_t current_speed;
20 	uint8_t parity;
21 	uint8_t clock_id;
22 	const struct pinctrl_dev_config *pin_cfg;
23 };
24 
25 struct usart_wch_data {
26 };
27 
usart_wch_init(const struct device * dev)28 static int usart_wch_init(const struct device *dev)
29 {
30 	const struct usart_wch_config *config = dev->config;
31 	USART_TypeDef *regs = config->regs;
32 	uint32_t ctlr1 = USART_CTLR1_TE | USART_CTLR1_RE | USART_CTLR1_UE;
33 	uint32_t clock_rate;
34 	clock_control_subsys_t clock_sys = (clock_control_subsys_t *)(uintptr_t)config->clock_id;
35 	uint32_t divn;
36 	int err;
37 
38 	clock_control_on(config->clock_dev, clock_sys);
39 
40 	err = clock_control_get_rate(config->clock_dev, clock_sys, &clock_rate);
41 	if (err != 0) {
42 		return err;
43 	}
44 	divn = (clock_rate + config->current_speed / 2) / config->current_speed;
45 
46 	switch (config->parity) {
47 	case UART_CFG_PARITY_NONE:
48 		break;
49 	case UART_CFG_PARITY_ODD:
50 		ctlr1 |= USART_CTLR1_PCE | USART_CTLR1_PS;
51 		break;
52 	case UART_CFG_PARITY_EVEN:
53 		ctlr1 |= USART_CTLR1_PCE;
54 		break;
55 	default:
56 		return -EINVAL;
57 	}
58 
59 	regs->BRR = divn;
60 	regs->CTLR1 = ctlr1;
61 	regs->CTLR2 = 0;
62 	regs->CTLR3 = 0;
63 
64 	err = pinctrl_apply_state(config->pin_cfg, PINCTRL_STATE_DEFAULT);
65 	if (err != 0) {
66 		return err;
67 	}
68 
69 	return 0;
70 }
71 
usart_wch_poll_in(const struct device * dev,unsigned char * ch)72 static int usart_wch_poll_in(const struct device *dev, unsigned char *ch)
73 {
74 	const struct usart_wch_config *config = dev->config;
75 	USART_TypeDef *regs = config->regs;
76 
77 	if ((regs->STATR & USART_STATR_RXNE) == 0) {
78 		return -1;
79 	}
80 
81 	*ch = regs->DATAR;
82 	return 0;
83 }
84 
usart_wch_poll_out(const struct device * dev,unsigned char ch)85 static void usart_wch_poll_out(const struct device *dev, unsigned char ch)
86 {
87 	const struct usart_wch_config *config = dev->config;
88 	USART_TypeDef *regs = config->regs;
89 
90 	while ((regs->STATR & USART_STATR_TXE) == 0) {
91 	}
92 
93 	regs->DATAR = ch;
94 }
95 
usart_wch_err_check(const struct device * dev)96 static int usart_wch_err_check(const struct device *dev)
97 {
98 	const struct usart_wch_config *config = dev->config;
99 	USART_TypeDef *regs = config->regs;
100 	uint32_t statr = regs->STATR;
101 	enum uart_rx_stop_reason errors = 0;
102 
103 	if ((statr & USART_STATR_PE) != 0) {
104 		errors |= UART_ERROR_PARITY;
105 	}
106 	if ((statr & USART_STATR_FE) != 0) {
107 		errors |= UART_ERROR_FRAMING;
108 	}
109 	if ((statr & USART_STATR_NE) != 0) {
110 		errors |= UART_ERROR_NOISE;
111 	}
112 	if ((statr & USART_STATR_ORE) != 0) {
113 		errors |= UART_ERROR_OVERRUN;
114 	}
115 
116 	return errors;
117 }
118 
119 static DEVICE_API(uart, usart_wch_driver_api) = {
120 	.poll_in = usart_wch_poll_in,
121 	.poll_out = usart_wch_poll_out,
122 	.err_check = usart_wch_err_check,
123 };
124 
125 #define USART_WCH_INIT(idx)                                                                        \
126 	PINCTRL_DT_INST_DEFINE(idx);                                                               \
127 	static struct usart_wch_data usart_wch_##idx##_data;                                       \
128 	static const struct usart_wch_config usart_wch_##idx##_config = {                          \
129 		.regs = (USART_TypeDef *)DT_INST_REG_ADDR(idx),                                    \
130 		.current_speed = DT_INST_PROP(idx, current_speed),                                 \
131 		.parity = DT_INST_ENUM_IDX_OR(idx, parity, UART_CFG_PARITY_NONE),                  \
132 		.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(idx)),                              \
133 		.clock_id = DT_INST_CLOCKS_CELL(idx, id),                                          \
134 		.pin_cfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx),                                    \
135 	};                                                                                         \
136 	DEVICE_DT_INST_DEFINE(idx, &usart_wch_init, NULL, &usart_wch_##idx##_data,                 \
137 			      &usart_wch_##idx##_config, PRE_KERNEL_1,                             \
138 			      CONFIG_SERIAL_INIT_PRIORITY, &usart_wch_driver_api);
139 
140 DT_INST_FOREACH_STATUS_OKAY(USART_WCH_INIT)
141