1 /*
2  * SPDX-License-Identifier: Apache-2.0
3  *
4  * Copyright (c) 2023 Nuvoton Technology Corporation.
5  */
6 
7 #define DT_DRV_COMPAT nuvoton_numaker_spi
8 
9 #include <errno.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/drivers/reset.h>
12 #include <zephyr/drivers/clock_control.h>
13 #include <zephyr/drivers/clock_control/clock_control_numaker.h>
14 #include <zephyr/drivers/pinctrl.h>
15 #include <zephyr/drivers/spi/rtio.h>
16 #include <zephyr/logging/log.h>
17 
18 LOG_MODULE_REGISTER(spi_numaker, CONFIG_SPI_LOG_LEVEL);
19 
20 #include "spi_context.h"
21 #include <NuMicro.h>
22 
23 #define SPI_NUMAKER_TX_NOP 0x00
24 
25 struct spi_numaker_config {
26 	SPI_T *spi;
27 	bool is_qspi;
28 	const struct reset_dt_spec reset;
29 	/* clock configuration */
30 	uint32_t clk_modidx;
31 	uint32_t clk_src;
32 	uint32_t clk_div;
33 	const struct device *clk_dev;
34 	const struct pinctrl_dev_config *pincfg;
35 };
36 
37 struct spi_numaker_data {
38 	struct spi_context ctx;
39 };
40 
41 /*
42  * CPOL/CPHA = 0/0 --> SPI_MODE_0
43  * CPOL/CPHA = 0/1 --> SPI_MODE_1
44  * CPOL/CPHA = 1/0 --> SPI_MODE_2
45  * CPOL/CPHA = 1/1 --> SPI_MODE_3
46  */
47 static const uint32_t smode_tbl[4] = {
48 	SPI_MODE_0, SPI_MODE_1, SPI_MODE_2, SPI_MODE_3
49 };
50 
51 static const uint32_t qsmode_tbl[4] = {
52 	QSPI_MODE_0, QSPI_MODE_1, QSPI_MODE_2, QSPI_MODE_3
53 };
54 
spi_numaker_configure(const struct device * dev,const struct spi_config * config)55 static int spi_numaker_configure(const struct device *dev, const struct spi_config *config)
56 {
57 	int mode;
58 	struct spi_numaker_data *data = dev->data;
59 	const struct spi_numaker_config *dev_cfg = dev->config;
60 
61 	LOG_DBG("%s", __func__);
62 	if (spi_context_configured(&data->ctx, config)) {
63 		return 0;
64 	}
65 
66 	if (SPI_MODE_GET(config->operation) & SPI_MODE_LOOP) {
67 		LOG_ERR("Loop back mode not support");
68 		return -ENOTSUP;
69 	}
70 
71 	if (SPI_OP_MODE_GET(config->operation) == SPI_OP_MODE_SLAVE) {
72 		LOG_ERR("Slave mode not support");
73 		return -ENOTSUP;
74 	}
75 
76 	/* Clear FIFO */
77 	SPI_ClearRxFIFO(dev_cfg->spi);
78 	SPI_ClearTxFIFO(dev_cfg->spi);
79 
80 	if (SPI_MODE_GET(config->operation) & SPI_MODE_CPOL) {
81 		mode = (SPI_MODE_GET(config->operation) & SPI_MODE_CPHA) ? 3 : 2;
82 	} else {
83 		mode = (SPI_MODE_GET(config->operation) & SPI_MODE_CPHA) ? 1 : 0;
84 	}
85 
86 	/* Make SPI module be ready to transfer */
87 	if (dev_cfg->is_qspi) {
88 		QSPI_Open((QSPI_T *)dev_cfg->spi,
89 			  (SPI_OP_MODE_GET(config->operation) == SPI_OP_MODE_SLAVE) ? QSPI_SLAVE
90 										    : QSPI_MASTER,
91 			  qsmode_tbl[mode],
92 			  SPI_WORD_SIZE_GET(config->operation), config->frequency);
93 	} else {
94 		SPI_Open(dev_cfg->spi,
95 			 (SPI_OP_MODE_GET(config->operation) == SPI_OP_MODE_SLAVE) ? SPI_SLAVE
96 										   : SPI_MASTER,
97 			 smode_tbl[mode],
98 			 SPI_WORD_SIZE_GET(config->operation), config->frequency);
99 	}
100 
101 	/* Set Transfer LSB or MSB first */
102 	if ((config->operation) & SPI_TRANSFER_LSB) {
103 		SPI_SET_LSB_FIRST(dev_cfg->spi);
104 	} else {
105 		SPI_SET_MSB_FIRST(dev_cfg->spi);
106 	}
107 
108 	/* full/half duplex */
109 	if (config->operation & SPI_HALF_DUPLEX) {
110 		/* half duplex, which results in 3-wire usage */
111 		SPI_ENABLE_3WIRE_MODE(dev_cfg->spi);
112 	} else {
113 		/* full duplex */
114 		SPI_DISABLE_3WIRE_MODE(dev_cfg->spi);
115 	}
116 
117 	/* Active high CS logic */
118 	if (config->operation & SPI_CS_ACTIVE_HIGH) {
119 		SPI_SET_SS_HIGH(dev_cfg->spi);
120 	} else {
121 		SPI_SET_SS_LOW(dev_cfg->spi);
122 	}
123 
124 	/* Enable the automatic hardware slave select function. Select the SS pin and configure as
125 	 * low-active.
126 	 */
127 	if (data->ctx.num_cs_gpios != 0) {
128 		SPI_EnableAutoSS(dev_cfg->spi, SPI_SS, SPI_SS_ACTIVE_LOW);
129 	} else {
130 		SPI_DisableAutoSS(dev_cfg->spi);
131 	}
132 
133 	/* Be able to set TX/RX FIFO threshold, for ex: SPI_SetFIFO(dev_cfg->spi, 2, 2) */
134 
135 	data->ctx.config = config;
136 
137 	return 0;
138 }
139 
spi_numaker_txrx(const struct device * dev)140 static int spi_numaker_txrx(const struct device *dev)
141 {
142 	struct spi_numaker_data *data = dev->data;
143 	const struct spi_numaker_config *dev_cfg = dev->config;
144 	struct spi_context *ctx = &data->ctx;
145 	uint32_t tx_frame, rx_frame;
146 	uint8_t word_size, spi_dfs;
147 	uint32_t time_out_cnt;
148 
149 	LOG_DBG("%s", __func__);
150 	word_size = SPI_WORD_SIZE_GET(ctx->config->operation);
151 
152 	switch (word_size) {
153 	case 8:
154 		spi_dfs = 1;
155 		break;
156 	case 16:
157 		spi_dfs = 2;
158 		break;
159 	case 24:
160 		spi_dfs = 3;
161 		break;
162 	case 32:
163 		spi_dfs = 4;
164 		break;
165 	default:
166 		spi_dfs = 0;
167 		LOG_ERR("Not support SPI WORD size as [%d] bits", word_size);
168 		return -EIO;
169 	}
170 
171 	LOG_DBG("%s -->word_size [%d]", __func__, word_size);
172 
173 	if (spi_context_tx_on(ctx)) {
174 		tx_frame = ((ctx->tx_buf == NULL) ? SPI_NUMAKER_TX_NOP
175 						  : UNALIGNED_GET((uint8_t *)(data->ctx.tx_buf)));
176 		/* Write to TX register */
177 		SPI_WRITE_TX(dev_cfg->spi, tx_frame);
178 		spi_context_update_tx(ctx, spi_dfs, 1);
179 
180 		/* Check SPI busy status */
181 		time_out_cnt = SystemCoreClock; /* 1 second time-out */
182 		while (SPI_IS_BUSY(dev_cfg->spi)) {
183 			if (--time_out_cnt == 0) {
184 				LOG_ERR("Wait for SPI time-out");
185 				return -EIO;
186 			}
187 		}
188 
189 		LOG_DBG("%s --> TX [0x%x] done", __func__, tx_frame);
190 	} else {
191 		/* Write dummy data to TX register */
192 		SPI_WRITE_TX(dev_cfg->spi, 0x00U);
193 		time_out_cnt = SystemCoreClock; /* 1 second time-out */
194 		while (SPI_IS_BUSY(dev_cfg->spi)) {
195 			if (--time_out_cnt == 0) {
196 				LOG_ERR("Wait for SPI time-out");
197 				return -EIO;
198 			}
199 		}
200 	}
201 
202 	/* Read received data */
203 	if (spi_context_rx_on(ctx)) {
204 		if (SPI_GET_RX_FIFO_COUNT(dev_cfg->spi) > 0) {
205 			rx_frame = SPI_READ_RX(dev_cfg->spi);
206 			if (ctx->rx_buf != NULL) {
207 				UNALIGNED_PUT(rx_frame, (uint8_t *)data->ctx.rx_buf);
208 			}
209 			spi_context_update_rx(ctx, spi_dfs, 1);
210 			LOG_DBG("%s --> RX [0x%x] done", __func__, rx_frame);
211 		}
212 	}
213 
214 	LOG_DBG("%s --> exit", __func__);
215 	return 0;
216 }
217 
218 /* Remain TX/RX Data in spi_context TX/RX buffer */
spi_numaker_remain_words(struct spi_numaker_data * data)219 static bool spi_numaker_remain_words(struct spi_numaker_data *data)
220 {
221 	return spi_context_tx_on(&data->ctx) || spi_context_rx_on(&data->ctx);
222 }
223 
spi_numaker_transceive(const struct device * dev,const struct spi_config * config,const struct spi_buf_set * tx_bufs,const struct spi_buf_set * rx_bufs)224 static int spi_numaker_transceive(const struct device *dev, const struct spi_config *config,
225 				  const struct spi_buf_set *tx_bufs,
226 				  const struct spi_buf_set *rx_bufs)
227 {
228 	struct spi_numaker_data *data = dev->data;
229 	struct spi_context *ctx = &data->ctx;
230 	const struct spi_numaker_config *dev_cfg = dev->config;
231 	int ret;
232 
233 	LOG_DBG("%s", __func__);
234 	spi_context_lock(ctx, false, NULL, NULL, config);
235 	ctx->config = config;
236 
237 	ret = spi_numaker_configure(dev, config);
238 	if (ret < 0) {
239 		goto done;
240 	}
241 
242 	SPI_ENABLE(dev_cfg->spi);
243 
244 	spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, 1);
245 
246 	/* if cs is defined: software cs control, set active true */
247 	if (spi_cs_is_gpio(config)) {
248 		spi_context_cs_control(&data->ctx, true);
249 	}
250 
251 	/* transceive tx/rx data */
252 	do {
253 		ret = spi_numaker_txrx(dev);
254 		if (ret < 0) {
255 			break;
256 		}
257 	} while (spi_numaker_remain_words(data));
258 
259 	/* if cs is defined: software cs control, set active false */
260 	if (spi_cs_is_gpio(config)) {
261 		spi_context_cs_control(&data->ctx, false);
262 	}
263 	SPI_DISABLE(dev_cfg->spi);
264 
265 done:
266 	spi_context_release(ctx, ret);
267 	LOG_DBG("%s --> [%d]", __func__, ret);
268 	return ret;
269 }
270 
spi_numaker_release(const struct device * dev,const struct spi_config * config)271 static int spi_numaker_release(const struct device *dev, const struct spi_config *config)
272 {
273 	struct spi_numaker_data *data = dev->data;
274 	struct spi_context *ctx = &data->ctx;
275 
276 	if (!spi_context_configured(ctx, config)) {
277 		return -EINVAL;
278 	}
279 	spi_context_unlock_unconditionally(ctx);
280 
281 	return 0;
282 }
283 
284 static DEVICE_API(spi, spi_numaker_driver_api) = {
285 	.transceive = spi_numaker_transceive,
286 #ifdef CONFIG_SPI_RTIO
287 	.iodev_submit = spi_rtio_iodev_default_submit,
288 #endif
289 	.release = spi_numaker_release
290 };
291 
spi_numaker_init(const struct device * dev)292 static int spi_numaker_init(const struct device *dev)
293 {
294 	struct spi_numaker_data *data = dev->data;
295 	const struct spi_numaker_config *dev_cfg = dev->config;
296 	int err = 0;
297 	struct numaker_scc_subsys scc_subsys;
298 
299 	SYS_UnlockReg();
300 
301 	/* CLK controller */
302 	memset(&scc_subsys, 0x00, sizeof(scc_subsys));
303 	scc_subsys.subsys_id = NUMAKER_SCC_SUBSYS_ID_PCC;
304 	scc_subsys.pcc.clk_modidx = dev_cfg->clk_modidx;
305 	scc_subsys.pcc.clk_src = dev_cfg->clk_src;
306 	scc_subsys.pcc.clk_div = dev_cfg->clk_div;
307 
308 	/* Equivalent to CLK_EnableModuleClock() */
309 	err = clock_control_on(dev_cfg->clk_dev, (clock_control_subsys_t)&scc_subsys);
310 	if (err != 0) {
311 		goto done;
312 	}
313 	/* Equivalent to CLK_SetModuleClock() */
314 	err = clock_control_configure(dev_cfg->clk_dev, (clock_control_subsys_t)&scc_subsys, NULL);
315 	if (err != 0) {
316 		goto done;
317 	}
318 
319 	err = pinctrl_apply_state(dev_cfg->pincfg, PINCTRL_STATE_DEFAULT);
320 	if (err) {
321 		LOG_ERR("Failed to apply pinctrl state");
322 		goto done;
323 	}
324 
325 	err = spi_context_cs_configure_all(&data->ctx);
326 	if (err < 0) {
327 		goto done;
328 	}
329 
330 	spi_context_unlock_unconditionally(&data->ctx);
331 
332 	/* Reset this module, same as BSP's SYS_ResetModule(id_rst) */
333 	if (!device_is_ready(dev_cfg->reset.dev)) {
334 		LOG_ERR("reset controller not ready");
335 		err = -ENODEV;
336 		goto done;
337 	}
338 
339 	/* Reset SPI to default state */
340 	reset_line_toggle_dt(&dev_cfg->reset);
341 
342 done:
343 	SYS_LockReg();
344 	return err;
345 }
346 
347 #define NUMAKER_SPI_INIT(inst)                                                                     \
348 	PINCTRL_DT_INST_DEFINE(inst);                                                              \
349 	static struct spi_numaker_data spi_numaker_data_##inst = {                                 \
350 		SPI_CONTEXT_INIT_LOCK(spi_numaker_data_##inst, ctx),                               \
351 		SPI_CONTEXT_INIT_SYNC(spi_numaker_data_##inst, ctx),                               \
352 		SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(inst), ctx)};                          \
353 	static struct spi_numaker_config spi_numaker_config_##inst = {                             \
354 		.spi = (SPI_T *)DT_INST_REG_ADDR(inst),                                            \
355 		.is_qspi = DT_INST_NODE_HAS_PROP(inst, qspi),                                      \
356 		.reset = RESET_DT_SPEC_INST_GET(inst),                                             \
357 		.clk_modidx = DT_INST_CLOCKS_CELL(inst, clock_module_index),                       \
358 		.clk_src = DT_INST_CLOCKS_CELL(inst, clock_source),                                \
359 		.clk_div = DT_INST_CLOCKS_CELL(inst, clock_divider),                               \
360 		.clk_dev = DEVICE_DT_GET(DT_PARENT(DT_INST_CLOCKS_CTLR(inst))),                    \
361 		.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst),                                    \
362 	};                                                                                         \
363 	SPI_DEVICE_DT_INST_DEFINE(inst, spi_numaker_init, NULL, &spi_numaker_data_##inst,          \
364 			      &spi_numaker_config_##inst, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY,   \
365 			      &spi_numaker_driver_api);
366 
367 DT_INST_FOREACH_STATUS_OKAY(NUMAKER_SPI_INIT)
368