1 /*
2  * SPDX-License-Identifier: Apache-2.0
3  *
4  * Copyright (c) 2020 Linumiz
5  * Author: Saravanan Sekar <saravanan@linumiz.com>
6  */
7 
8 #include <zephyr/drivers/pinctrl.h>
9 #include <zephyr/drivers/uart.h>
10 #include <NuMicro.h>
11 #include <string.h>
12 
13 #define DT_DRV_COMPAT nuvoton_numicro_uart
14 
15 struct uart_numicro_config {
16 	UART_T *uart;
17 	uint32_t id_rst;
18 	uint32_t id_clk;
19 	const struct pinctrl_dev_config *pincfg;
20 };
21 
22 struct uart_numicro_data {
23 	const struct device *clock;
24 	struct uart_config ucfg;
25 };
26 
uart_numicro_poll_in(const struct device * dev,unsigned char * c)27 static int uart_numicro_poll_in(const struct device *dev, unsigned char *c)
28 {
29 	const struct uart_numicro_config *config = dev->config;
30 
31 	if ((config->uart->FIFOSTS & UART_FIFOSTS_RXEMPTY_Msk) != 0) {
32 		return -1;
33 	}
34 
35 	*c = (uint8_t)config->uart->DAT;
36 
37 	return 0;
38 }
39 
uart_numicro_poll_out(const struct device * dev,unsigned char c)40 static void uart_numicro_poll_out(const struct device *dev, unsigned char c)
41 {
42 	const struct uart_numicro_config *config = dev->config;
43 
44 	UART_Write(config->uart, &c, 1);
45 }
46 
uart_numicro_err_check(const struct device * dev)47 static int uart_numicro_err_check(const struct device *dev)
48 {
49 	return 0;
50 }
51 
uart_numicro_convert_stopbit(enum uart_config_stop_bits sb)52 static inline int32_t uart_numicro_convert_stopbit(enum uart_config_stop_bits sb)
53 {
54 	switch (sb) {
55 	case UART_CFG_STOP_BITS_1:
56 		return UART_STOP_BIT_1;
57 	case UART_CFG_STOP_BITS_1_5:
58 		return UART_STOP_BIT_1_5;
59 	case UART_CFG_STOP_BITS_2:
60 		return UART_STOP_BIT_2;
61 	default:
62 		return -ENOTSUP;
63 	}
64 };
65 
uart_numicro_convert_datalen(enum uart_config_data_bits db)66 static inline int32_t uart_numicro_convert_datalen(enum uart_config_data_bits db)
67 {
68 	switch (db) {
69 	case UART_CFG_DATA_BITS_5:
70 		return UART_WORD_LEN_5;
71 	case UART_CFG_DATA_BITS_6:
72 		return UART_WORD_LEN_6;
73 	case UART_CFG_DATA_BITS_7:
74 		return UART_WORD_LEN_7;
75 	case UART_CFG_DATA_BITS_8:
76 		return UART_WORD_LEN_8;
77 	default:
78 		return -ENOTSUP;
79 	}
80 }
81 
uart_numicro_convert_parity(enum uart_config_parity parity)82 static inline uint32_t uart_numicro_convert_parity(enum uart_config_parity parity)
83 {
84 	switch (parity) {
85 	case UART_CFG_PARITY_ODD:
86 		return UART_PARITY_ODD;
87 	case UART_CFG_PARITY_EVEN:
88 		return UART_PARITY_EVEN;
89 	case UART_CFG_PARITY_MARK:
90 		return UART_PARITY_MARK;
91 	case UART_CFG_PARITY_SPACE:
92 		return UART_PARITY_SPACE;
93 	case UART_CFG_PARITY_NONE:
94 	default:
95 		return UART_PARITY_NONE;
96 	}
97 }
98 
99 #ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
uart_numicro_configure(const struct device * dev,const struct uart_config * cfg)100 static int uart_numicro_configure(const struct device *dev,
101 				  const struct uart_config *cfg)
102 {
103 	const struct uart_numicro_config *config = dev->config;
104 	struct uart_numicro_data *ddata = dev->data;
105 	int32_t databits, stopbits;
106 	uint32_t parity;
107 
108 	databits = uart_numicro_convert_datalen(cfg->data_bits);
109 	if (databits < 0) {
110 		return databits;
111 	}
112 
113 	stopbits = uart_numicro_convert_stopbit(cfg->stop_bits);
114 	if (stopbits < 0) {
115 		return stopbits;
116 	}
117 
118 	if (cfg->flow_ctrl == UART_CFG_FLOW_CTRL_NONE) {
119 		UART_DisableFlowCtrl(config->uart);
120 	} else if (cfg->flow_ctrl == UART_CFG_FLOW_CTRL_RTS_CTS) {
121 		UART_EnableFlowCtrl(config->uart);
122 	} else {
123 		return -ENOTSUP;
124 	}
125 
126 	parity = uart_numicro_convert_parity(cfg->parity);
127 
128 	UART_SetLineConfig(config->uart, cfg->baudrate, databits, parity,
129 			   stopbits);
130 
131 	memcpy(&ddata->ucfg, cfg, sizeof(*cfg));
132 
133 	return 0;
134 }
135 
uart_numicro_config_get(const struct device * dev,struct uart_config * cfg)136 static int uart_numicro_config_get(const struct device *dev,
137 				   struct uart_config *cfg)
138 {
139 	struct uart_numicro_data *ddata = dev->data;
140 
141 	memcpy(cfg, &ddata->ucfg, sizeof(*cfg));
142 
143 	return 0;
144 }
145 #endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
146 
uart_numicro_init(const struct device * dev)147 static int uart_numicro_init(const struct device *dev)
148 {
149 	const struct uart_numicro_config *config = dev->config;
150 	struct uart_numicro_data *ddata = dev->data;
151 	int err;
152 
153 	SYS_ResetModule(config->id_rst);
154 
155 	SYS_UnlockReg();
156 
157 	/* Enable UART module clock */
158 	CLK_EnableModuleClock(config->id_clk);
159 
160 	/* Select UART0 clock source is PLL */
161 	CLK_SetModuleClock(config->id_clk, CLK_CLKSEL1_UART0SEL_PLL,
162 			   CLK_CLKDIV0_UART0(0));
163 
164 	SYS_LockReg();
165 
166 	err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
167 	if (err != 0) {
168 		return err;
169 	}
170 
171 	UART_Open(config->uart, ddata->ucfg.baudrate);
172 
173 	return 0;
174 }
175 
176 static DEVICE_API(uart, uart_numicro_driver_api) = {
177 	.poll_in          = uart_numicro_poll_in,
178 	.poll_out         = uart_numicro_poll_out,
179 	.err_check        = uart_numicro_err_check,
180 #ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
181 	.configure        = uart_numicro_configure,
182 	.config_get       = uart_numicro_config_get,
183 #endif
184 };
185 
186 #define NUMICRO_INIT(index)						\
187 PINCTRL_DT_INST_DEFINE(index);						\
188 									\
189 static const struct uart_numicro_config uart_numicro_cfg_##index = {	\
190 	.uart = (UART_T *)DT_INST_REG_ADDR(index),			\
191 	.id_rst = UART##index##_RST,					\
192 	.id_clk = UART##index##_MODULE,					\
193 	.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index),		\
194 };									\
195 									\
196 static struct uart_numicro_data uart_numicro_data_##index = {		\
197 	.ucfg = {							\
198 		.baudrate = DT_INST_PROP(index, current_speed),		\
199 	},								\
200 };									\
201 									\
202 DEVICE_DT_INST_DEFINE(index,						\
203 		    uart_numicro_init,					\
204 		    NULL,						\
205 		    &uart_numicro_data_##index,				\
206 		    &uart_numicro_cfg_##index,				\
207 		    PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY,		\
208 		    &uart_numicro_driver_api);
209 
210 DT_INST_FOREACH_STATUS_OKAY(NUMICRO_INIT)
211