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