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