1 /*
2  * Copyright (c) 2024 Ambiq <www.ambiq.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ambiq_spid
8 
9 #include <zephyr/logging/log.h>
10 LOG_MODULE_REGISTER(spi_ambiq_spid);
11 
12 #include <zephyr/drivers/spi.h>
13 #include <zephyr/drivers/pinctrl.h>
14 #include <zephyr/kernel.h>
15 #include <zephyr/sys/byteorder.h>
16 #include <zephyr/pm/device.h>
17 #include <zephyr/pm/policy.h>
18 #include <zephyr/pm/device_runtime.h>
19 
20 #include <stdlib.h>
21 #include <errno.h>
22 #include "spi_context.h"
23 #include <am_mcu_apollo.h>
24 
25 #define AMBIQ_SPID_PWRCTRL_MAX_WAIT_US 5
26 
27 typedef int (*ambiq_spi_pwr_func_t)(void);
28 
29 struct spi_ambiq_config {
30 	const struct gpio_dt_spec int_gpios;
31 	uint32_t base;
32 	int size;
33 	const struct pinctrl_dev_config *pcfg;
34 	ambiq_spi_pwr_func_t pwr_func;
35 	void (*irq_config_func)(void);
36 };
37 
38 struct spi_ambiq_data {
39 	struct spi_context ctx;
40 	am_hal_ios_config_t ios_cfg;
41 	void *ios_handler;
42 	int inst_idx;
43 	struct k_sem spim_wrcmp_sem;
44 };
45 
46 #define AMBIQ_SPID_TX_BUFSIZE_MAX 1023
47 uint8_t ambiq_spid_sram_buffer[AMBIQ_SPID_TX_BUFSIZE_MAX];
48 
49 #define AMBIQ_SPID_DUMMY_BYTE   0
50 #define AMBIQ_SPID_DUMMY_LENGTH 16
51 static uint8_t ambiq_spid_dummy_buffer[2][AMBIQ_SPID_DUMMY_LENGTH];
52 
53 #define AMBIQ_SPID_WORD_SIZE 8
54 
55 #define AMBIQ_SPID_FIFO_BASE   0x78
56 #define AMBIQ_SPID_FIFO_END    0x100
57 #define AMBIQ_SPID_FIFO_LENGTH (AMBIQ_SPID_FIFO_END - AMBIQ_SPID_FIFO_BASE)
58 
59 #define AMBIQ_SPID_INT_ERR (AM_HAL_IOS_INT_FOVFL | AM_HAL_IOS_INT_FUNDFL | AM_HAL_IOS_INT_FRDERR)
60 
61 #define AMBIQ_SPID_XCMP_INT                                                                        \
62 	(AM_HAL_IOS_INT_XCMPWR | AM_HAL_IOS_INT_XCMPWF | AM_HAL_IOS_INT_XCMPRR |                   \
63 	 AM_HAL_IOS_INT_XCMPRF)
64 
spi_ambiq_reset(const struct device * dev)65 static void spi_ambiq_reset(const struct device *dev)
66 {
67 	struct spi_ambiq_data *data = dev->data;
68 	struct spi_context *ctx = &data->ctx;
69 
70 	/* cancel timed out transaction */
71 	am_hal_ios_disable(data->ios_handler);
72 	/* NULL config to trigger reconfigure on next xfer */
73 	ctx->config = NULL;
74 	/* signal any thread waiting on sync semaphore */
75 	spi_context_complete(ctx, dev, -ETIMEDOUT);
76 }
77 
spi_ambiq_inform(const struct device * dev)78 static void spi_ambiq_inform(const struct device *dev)
79 {
80 	const struct spi_ambiq_config *cfg = dev->config;
81 	/* Inform the controller */
82 	gpio_pin_set_dt(&cfg->int_gpios, 1);
83 	gpio_pin_set_dt(&cfg->int_gpios, 0);
84 }
85 
spi_ambiq_isr(const struct device * dev)86 static void spi_ambiq_isr(const struct device *dev)
87 {
88 	uint32_t ui32Status;
89 	struct spi_ambiq_data *data = dev->data;
90 
91 	am_hal_ios_interrupt_status_get(data->ios_handler, false, &ui32Status);
92 	am_hal_ios_interrupt_clear(data->ios_handler, ui32Status);
93 	if (ui32Status & AM_HAL_IOS_INT_XCMPWR) {
94 		k_sem_give(&data->spim_wrcmp_sem);
95 	}
96 }
97 
spi_config(const struct device * dev,const struct spi_config * config)98 static int spi_config(const struct device *dev, const struct spi_config *config)
99 {
100 	struct spi_ambiq_data *data = dev->data;
101 	struct spi_context *ctx = &(data->ctx);
102 	int ret = 0;
103 
104 	data->ios_cfg.ui32InterfaceSelect = AM_HAL_IOS_USE_SPI;
105 
106 	if (spi_context_configured(ctx, config)) {
107 		/* Already configured. No need to do it again. */
108 		return 0;
109 	}
110 
111 	if (SPI_WORD_SIZE_GET(config->operation) != AMBIQ_SPID_WORD_SIZE) {
112 		LOG_ERR("Word size must be %d", AMBIQ_SPID_WORD_SIZE);
113 		return -ENOTSUP;
114 	}
115 
116 	if ((config->operation & SPI_LINES_MASK) != SPI_LINES_SINGLE) {
117 		LOG_ERR("Only supports single mode");
118 		return -ENOTSUP;
119 	}
120 
121 	if (config->operation & SPI_LOCK_ON) {
122 		LOG_ERR("Lock On not supported");
123 		return -ENOTSUP;
124 	}
125 
126 	if (config->operation & SPI_TRANSFER_LSB) {
127 		LOG_ERR("LSB first not supported");
128 		return -ENOTSUP;
129 	}
130 
131 	if (config->operation & SPI_MODE_CPOL) {
132 		if (config->operation & SPI_MODE_CPHA) {
133 			data->ios_cfg.ui32InterfaceSelect |= AM_HAL_IOS_SPIMODE_3;
134 		} else {
135 			data->ios_cfg.ui32InterfaceSelect |= AM_HAL_IOS_SPIMODE_2;
136 		}
137 	} else {
138 		if (config->operation & SPI_MODE_CPHA) {
139 			data->ios_cfg.ui32InterfaceSelect |= AM_HAL_IOS_SPIMODE_1;
140 		} else {
141 			data->ios_cfg.ui32InterfaceSelect |= AM_HAL_IOS_SPIMODE_0;
142 		}
143 	}
144 
145 	if (config->operation & SPI_OP_MODE_MASTER) {
146 		LOG_ERR("Controller mode not supported");
147 		return -ENOTSUP;
148 	}
149 
150 	if (config->operation & SPI_MODE_LOOP) {
151 		LOG_ERR("Loopback mode not supported");
152 		return -ENOTSUP;
153 	}
154 
155 	if (spi_cs_is_gpio(config)) {
156 		LOG_ERR("CS control via GPIO is not supported");
157 		return -EINVAL;
158 	}
159 
160 	/* Eliminate the "read-only" section, so an external controller can use the
161 	 * entire "direct write" section.
162 	 */
163 	data->ios_cfg.ui32ROBase = AMBIQ_SPID_FIFO_BASE;
164 	/* Making the "FIFO" section as big as possible. */
165 	data->ios_cfg.ui32FIFOBase = AMBIQ_SPID_FIFO_BASE;
166 	/* We don't need any RAM space, so extend the FIFO all the way to the end
167 	 * of the LRAM.
168 	 */
169 	data->ios_cfg.ui32RAMBase = AMBIQ_SPID_FIFO_END;
170 	/* FIFO Threshold - set to half the size */
171 	data->ios_cfg.ui32FIFOThreshold = AMBIQ_SPID_FIFO_LENGTH >> 1;
172 
173 	data->ios_cfg.pui8SRAMBuffer = ambiq_spid_sram_buffer,
174 	data->ios_cfg.ui32SRAMBufferCap = AMBIQ_SPID_TX_BUFSIZE_MAX,
175 
176 	ctx->config = config;
177 
178 	ret = am_hal_ios_configure(data->ios_handler, &data->ios_cfg);
179 
180 	return ret;
181 }
182 
spi_ambiq_xfer(const struct device * dev,const struct spi_config * config)183 static int spi_ambiq_xfer(const struct device *dev, const struct spi_config *config)
184 {
185 	struct spi_ambiq_data *data = dev->data;
186 	struct spi_context *ctx = &data->ctx;
187 	int ret = 0;
188 	uint32_t chunk, num_written, num_read, used_space = 0;
189 
190 	while (1) {
191 		/* First send out all data */
192 		if (spi_context_tx_on(ctx)) {
193 			spi_ambiq_inform(dev);
194 			chunk = (ctx->tx_len > AMBIQ_SPID_TX_BUFSIZE_MAX)
195 					? AMBIQ_SPID_TX_BUFSIZE_MAX
196 					: ctx->tx_len;
197 			am_hal_ios_fifo_space_used(data->ios_handler, &used_space);
198 			/* Controller done reading the last block signalled
199 			 * Check if any more data available
200 			 */
201 			if (!used_space) {
202 				if (ctx->tx_buf) {
203 					/*  Copy data into FIFO */
204 					ret = am_hal_ios_fifo_write(data->ios_handler,
205 								    (uint8_t *)ctx->tx_buf, chunk,
206 								    &num_written);
207 				} else {
208 					uint32_t dummy_written = 0;
209 
210 					num_written = 0;
211 					/*  Copy dummy into FIFO */
212 					while (chunk) {
213 						uint32_t size = (chunk > AMBIQ_SPID_DUMMY_LENGTH)
214 									? AMBIQ_SPID_DUMMY_LENGTH
215 									: chunk;
216 						ret = am_hal_ios_fifo_write(
217 							data->ios_handler,
218 							ambiq_spid_dummy_buffer[0], size,
219 							&dummy_written);
220 						num_written += dummy_written;
221 						chunk -= dummy_written;
222 					}
223 				}
224 				if (ret != 0) {
225 					LOG_ERR("SPID write error: %d", ret);
226 					goto end;
227 				}
228 				spi_context_update_tx(ctx, 1, num_written);
229 			}
230 		} else if (spi_context_rx_on(ctx)) {
231 			/* Wait for controller write complete interrupt */
232 			(void)k_sem_take(&data->spim_wrcmp_sem, K_FOREVER);
233 			/* Read out the first byte as packet length */
234 			num_read = am_hal_ios_pui8LRAM[0];
235 			uint32_t size, offset = 0;
236 
237 			while (spi_context_rx_on(ctx)) {
238 				/* There's no data in the LRAM any more */
239 				if (!num_read) {
240 					spi_ambiq_inform(dev);
241 					goto end;
242 				}
243 				if (ctx->rx_buf) {
244 					size = MIN(num_read, ctx->rx_len);
245 					/* Read data from LRAM */
246 					memcpy(ctx->rx_buf,
247 					       (uint8_t *)&am_hal_ios_pui8LRAM[1 + offset], size);
248 				} else {
249 					size = MIN(num_read, AMBIQ_SPID_DUMMY_LENGTH);
250 					/*  Consume data from LRAM */
251 					memcpy(ambiq_spid_dummy_buffer[1],
252 					       (uint8_t *)&am_hal_ios_pui8LRAM[1 + offset], size);
253 				}
254 				num_read -= size;
255 				offset += size;
256 				spi_context_update_rx(ctx, 1, size);
257 			}
258 		} else {
259 			break;
260 		}
261 	}
262 
263 end:
264 	if (ret != 0) {
265 		spi_ambiq_reset(dev);
266 	} else {
267 		spi_context_complete(ctx, dev, ret);
268 	}
269 
270 	return ret;
271 }
272 
spi_ambiq_transceive(const struct device * dev,const struct spi_config * config,const struct spi_buf_set * tx_bufs,const struct spi_buf_set * rx_bufs)273 static int spi_ambiq_transceive(const struct device *dev, const struct spi_config *config,
274 				const struct spi_buf_set *tx_bufs,
275 				const struct spi_buf_set *rx_bufs)
276 {
277 	struct spi_ambiq_data *data = dev->data;
278 	int ret;
279 
280 	if (!tx_bufs && !rx_bufs) {
281 		return 0;
282 	}
283 
284 	ret = pm_device_runtime_get(dev);
285 	if (ret < 0) {
286 		LOG_ERR("pm_device_runtime_get failed: %d", ret);
287 	}
288 
289 	/* context setup */
290 	spi_context_lock(&data->ctx, false, NULL, NULL, config);
291 
292 	ret = spi_config(dev, config);
293 	if (ret) {
294 		spi_context_release(&data->ctx, ret);
295 		return ret;
296 	}
297 
298 	spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, 1);
299 
300 	ret = spi_ambiq_xfer(dev, config);
301 
302 	spi_context_release(&data->ctx, ret);
303 
304 	/* Use async put to avoid useless device suspension/resumption
305 	 * when doing consecutive transmission.
306 	 */
307 	ret = pm_device_runtime_put_async(dev, K_MSEC(2));
308 	if (ret < 0) {
309 		LOG_ERR("pm_device_runtime_put failed: %d", ret);
310 	}
311 
312 	return ret;
313 }
314 
spi_ambiq_release(const struct device * dev,const struct spi_config * config)315 static int spi_ambiq_release(const struct device *dev, const struct spi_config *config)
316 {
317 	struct spi_ambiq_data *data = dev->data;
318 
319 	if (!spi_context_configured(&data->ctx, config)) {
320 		return -EINVAL;
321 	}
322 
323 	spi_context_unlock_unconditionally(&data->ctx);
324 
325 	return 0;
326 }
327 
328 static DEVICE_API(spi, spi_ambiq_driver_api) = {
329 	.transceive = spi_ambiq_transceive,
330 	.release = spi_ambiq_release,
331 };
332 
spi_ambiq_init(const struct device * dev)333 static int spi_ambiq_init(const struct device *dev)
334 {
335 	struct spi_ambiq_data *data = dev->data;
336 	const struct spi_ambiq_config *cfg = dev->config;
337 	int ret = 0;
338 
339 	if (AM_HAL_STATUS_SUCCESS !=
340 	    am_hal_ios_initialize((cfg->base - IOSLAVE_BASE) / cfg->size, &data->ios_handler)) {
341 		LOG_ERR("Fail to initialize SPID\n");
342 		return -ENXIO;
343 	}
344 
345 	ret = cfg->pwr_func();
346 
347 	ret |= pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
348 	if (ret < 0) {
349 		LOG_ERR("Fail to config SPID pins\n");
350 		goto end;
351 	}
352 
353 	memset(ambiq_spid_dummy_buffer[0], AMBIQ_SPID_DUMMY_BYTE, AMBIQ_SPID_DUMMY_LENGTH);
354 
355 	am_hal_ios_interrupt_clear(data->ios_handler, AM_HAL_IOS_INT_ALL);
356 	am_hal_ios_interrupt_enable(data->ios_handler, AMBIQ_SPID_INT_ERR | AM_HAL_IOS_INT_IOINTW |
357 							       AMBIQ_SPID_XCMP_INT);
358 	cfg->irq_config_func();
359 
360 end:
361 	if (ret < 0) {
362 		am_hal_ios_uninitialize(data->ios_handler);
363 	} else {
364 		spi_context_unlock_unconditionally(&data->ctx);
365 	}
366 	return ret;
367 }
368 
369 #ifdef CONFIG_PM_DEVICE
spi_ambiq_pm_action(const struct device * dev,enum pm_device_action action)370 static int spi_ambiq_pm_action(const struct device *dev, enum pm_device_action action)
371 {
372 	struct spi_ambiq_data *data = dev->data;
373 	uint32_t ret;
374 	am_hal_sysctrl_power_state_e status;
375 
376 	switch (action) {
377 	case PM_DEVICE_ACTION_RESUME:
378 		status = AM_HAL_SYSCTRL_WAKE;
379 		break;
380 	case PM_DEVICE_ACTION_SUSPEND:
381 		status = AM_HAL_SYSCTRL_DEEPSLEEP;
382 		break;
383 	default:
384 		return -ENOTSUP;
385 	}
386 
387 	ret = am_hal_ios_power_ctrl(data->ios_handler, status, true);
388 	if (ret != AM_HAL_STATUS_SUCCESS) {
389 		LOG_ERR("am_hal_ios_power_ctrl failed: %d", ret);
390 		return -EPERM;
391 	} else {
392 		return 0;
393 	}
394 }
395 #endif /* CONFIG_PM_DEVICE */
396 
397 #define AMBIQ_SPID_INIT(n)                                                                         \
398 	PINCTRL_DT_INST_DEFINE(n);                                                                 \
399 	static int pwr_on_ambiq_spi_##n(void)                                                      \
400 	{                                                                                          \
401 		uint32_t addr = DT_REG_ADDR(DT_INST_PHANDLE(n, ambiq_pwrcfg)) +                    \
402 				DT_INST_PHA(n, ambiq_pwrcfg, offset);                              \
403 		sys_write32((sys_read32(addr) | DT_INST_PHA(n, ambiq_pwrcfg, mask)), addr);        \
404 		k_busy_wait(AMBIQ_SPID_PWRCTRL_MAX_WAIT_US);                                       \
405 		return 0;                                                                          \
406 	}                                                                                          \
407 	static void spi_irq_config_func_##n(void)                                                  \
408 	{                                                                                          \
409 		IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), spi_ambiq_isr,              \
410 			    DEVICE_DT_INST_GET(n), 0);                                             \
411 		irq_enable(DT_INST_IRQN(n));                                                       \
412 	};                                                                                         \
413 	static struct spi_ambiq_data spi_ambiq_data##n = {                                         \
414 		SPI_CONTEXT_INIT_LOCK(spi_ambiq_data##n, ctx),                                     \
415 		SPI_CONTEXT_INIT_SYNC(spi_ambiq_data##n, ctx),                                     \
416 		.spim_wrcmp_sem = Z_SEM_INITIALIZER(spi_ambiq_data##n.spim_wrcmp_sem, 0, 1),       \
417 		.inst_idx = n};                                                                    \
418 	static const struct spi_ambiq_config spi_ambiq_config##n = {                               \
419 		.int_gpios = GPIO_DT_SPEC_INST_GET(n, int_gpios),                                  \
420 		.base = DT_INST_REG_ADDR(n),                                                       \
421 		.size = DT_INST_REG_SIZE(n),                                                       \
422 		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n),                                         \
423 		.irq_config_func = spi_irq_config_func_##n,                                        \
424 		.pwr_func = pwr_on_ambiq_spi_##n};                                                 \
425 	PM_DEVICE_DT_INST_DEFINE(n, spi_ambiq_pm_action);                                          \
426 	SPI_DEVICE_DT_INST_DEFINE(n, spi_ambiq_init, PM_DEVICE_DT_INST_GET(n), &spi_ambiq_data##n, \
427 				  &spi_ambiq_config##n, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY,     \
428 				  &spi_ambiq_driver_api);
429 
430 DT_INST_FOREACH_STATUS_OKAY(AMBIQ_SPID_INIT)
431