1 /*
2 * Copyright (c) 2024 Ambiq Micro Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Some Ambiq Apollox Blue SOC (e.g. Apollo3 Blue) uses internal designed BLEIF module which is
7 * different from the general IOM module for SPI transceiver. The called HAL API will also be
8 * independent. This driver is implemented for the BLEIF module usage scenarios.
9 */
10
11 #define DT_DRV_COMPAT ambiq_spi_bleif
12
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(spi_ambiq_bleif);
15
16 #include <zephyr/drivers/spi.h>
17 #include <zephyr/drivers/spi/rtio.h>
18 #include <zephyr/drivers/pinctrl.h>
19 #include <zephyr/kernel.h>
20 #include <zephyr/sys/byteorder.h>
21 #include <stdlib.h>
22 #include <errno.h>
23 #include "spi_context.h"
24 #include <am_mcu_apollo.h>
25
26 #define PWRCTRL_MAX_WAIT_US 5
27
28 typedef int (*ambiq_spi_pwr_func_t)(void);
29
30 struct spi_ambiq_config {
31 uint32_t base;
32 int size;
33 const struct pinctrl_dev_config *pcfg;
34 ambiq_spi_pwr_func_t pwr_func;
35 };
36
37 struct spi_ambiq_data {
38 struct spi_context ctx;
39 am_hal_ble_config_t ble_cfg;
40 void *BLEhandle;
41 };
42
43 #define SPI_BASE (((const struct spi_ambiq_config *)(dev)->config)->base)
44 #define REG_STAT 0x268
45 #define SPI_STAT(dev) (SPI_BASE + REG_STAT)
46 #define SPI_WORD_SIZE 8
47
spi_config(const struct device * dev,const struct spi_config * config)48 static int spi_config(const struct device *dev, const struct spi_config *config)
49 {
50 struct spi_ambiq_data *data = dev->data;
51 struct spi_context *ctx = &(data->ctx);
52
53 int ret = 0;
54
55 if (spi_context_configured(ctx, config)) {
56 /* Already configured. No need to do it again. */
57 return 0;
58 }
59
60 if (SPI_WORD_SIZE_GET(config->operation) != SPI_WORD_SIZE) {
61 LOG_ERR("Word size must be %d", SPI_WORD_SIZE);
62 return -ENOTSUP;
63 }
64
65 if ((config->operation & SPI_LINES_MASK) != SPI_LINES_SINGLE) {
66 LOG_ERR("Only supports single mode");
67 return -ENOTSUP;
68 }
69
70 if (config->operation & SPI_LOCK_ON) {
71 LOG_ERR("Lock On not supported");
72 return -ENOTSUP;
73 }
74
75 if (config->operation & SPI_TRANSFER_LSB) {
76 LOG_ERR("LSB first not supported");
77 return -ENOTSUP;
78 }
79
80 if (config->operation & SPI_OP_MODE_SLAVE) {
81 LOG_ERR("Slave mode not supported");
82 return -ENOTSUP;
83 }
84 if (config->operation & SPI_MODE_LOOP) {
85 LOG_ERR("Loopback mode not supported");
86 return -ENOTSUP;
87 }
88
89 /* We consider only the default configuration defined in HAL is tested and stable. */
90 data->ble_cfg = am_hal_ble_default_config;
91
92 ctx->config = config;
93
94 ret = am_hal_ble_config(data->BLEhandle, &data->ble_cfg);
95
96 return ret;
97 }
98
spi_ambiq_xfer(const struct device * dev,const struct spi_config * config)99 static int spi_ambiq_xfer(const struct device *dev, const struct spi_config *config)
100 {
101 struct spi_ambiq_data *data = dev->data;
102 struct spi_context *ctx = &data->ctx;
103 int ret = 0;
104
105 am_hal_ble_transfer_t trans = {0};
106
107 if (ctx->tx_len) {
108 trans.ui8Command = AM_HAL_BLE_WRITE;
109 trans.pui32Data = (uint32_t *)ctx->tx_buf;
110 trans.ui16Length = ctx->tx_len;
111 trans.bContinue = false;
112 } else {
113 trans.ui8Command = AM_HAL_BLE_READ;
114 trans.pui32Data = (uint32_t *)ctx->rx_buf;
115 trans.ui16Length = ctx->rx_len;
116 trans.bContinue = false;
117 }
118
119 ret = am_hal_ble_blocking_transfer(data->BLEhandle, &trans);
120 spi_context_complete(ctx, dev, 0);
121
122 return ret;
123 }
124
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)125 static int spi_ambiq_transceive(const struct device *dev, const struct spi_config *config,
126 const struct spi_buf_set *tx_bufs,
127 const struct spi_buf_set *rx_bufs)
128 {
129 struct spi_ambiq_data *data = dev->data;
130 int ret;
131
132 ret = spi_config(dev, config);
133
134 if (ret) {
135 return ret;
136 }
137
138 if (!tx_bufs && !rx_bufs) {
139 return 0;
140 }
141
142 spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, 1);
143
144 ret = spi_ambiq_xfer(dev, config);
145
146 return ret;
147 }
148
spi_ambiq_release(const struct device * dev,const struct spi_config * config)149 static int spi_ambiq_release(const struct device *dev, const struct spi_config *config)
150 {
151 struct spi_ambiq_data *data = dev->data;
152
153 if (!sys_read32(SPI_STAT(dev))) {
154 return -EBUSY;
155 }
156
157 spi_context_unlock_unconditionally(&data->ctx);
158
159 return 0;
160 }
161
162 static DEVICE_API(spi, spi_ambiq_driver_api) = {
163 .transceive = spi_ambiq_transceive,
164 #ifdef CONFIG_SPI_RTIO
165 .iodev_submit = spi_rtio_iodev_default_submit,
166 #endif
167 .release = spi_ambiq_release,
168 };
169
spi_ambiq_init(const struct device * dev)170 static int spi_ambiq_init(const struct device *dev)
171 {
172 struct spi_ambiq_data *data = dev->data;
173 const struct spi_ambiq_config *cfg = dev->config;
174 int ret;
175
176 #if defined(CONFIG_SPI_AMBIQ_BLEIF_TIMING_TRACE)
177 ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
178 if (ret) {
179 return ret;
180 }
181 #endif /* CONFIG_SPI_AMBIQ_BLEIF_TIMING_TRACE */
182
183 ret = am_hal_ble_initialize((cfg->base - BLEIF_BASE) / cfg->size, &data->BLEhandle);
184 if (ret) {
185 return ret;
186 }
187
188 ret = am_hal_ble_power_control(data->BLEhandle, AM_HAL_BLE_POWER_ACTIVE);
189 if (ret) {
190 return ret;
191 }
192
193 ret = cfg->pwr_func();
194
195 return ret;
196 }
197
198 #define AMBIQ_SPI_BLEIF_INIT(n) \
199 PINCTRL_DT_INST_DEFINE(n); \
200 static int pwr_on_ambiq_spi_##n(void) \
201 { \
202 uint32_t addr = DT_REG_ADDR(DT_INST_PHANDLE(n, ambiq_pwrcfg)) + \
203 DT_INST_PHA(n, ambiq_pwrcfg, offset); \
204 sys_write32((sys_read32(addr) | DT_INST_PHA(n, ambiq_pwrcfg, mask)), addr); \
205 k_busy_wait(PWRCTRL_MAX_WAIT_US); \
206 return 0; \
207 } \
208 static struct spi_ambiq_data spi_ambiq_data##n = { \
209 SPI_CONTEXT_INIT_LOCK(spi_ambiq_data##n, ctx), \
210 SPI_CONTEXT_INIT_SYNC(spi_ambiq_data##n, ctx)}; \
211 static const struct spi_ambiq_config spi_ambiq_config##n = { \
212 .base = DT_INST_REG_ADDR(n), \
213 .size = DT_INST_REG_SIZE(n), \
214 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
215 .pwr_func = pwr_on_ambiq_spi_##n}; \
216 SPI_DEVICE_DT_INST_DEFINE(n, spi_ambiq_init, NULL, &spi_ambiq_data##n, \
217 &spi_ambiq_config##n, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \
218 &spi_ambiq_driver_api);
219
220 DT_INST_FOREACH_STATUS_OKAY(AMBIQ_SPI_BLEIF_INIT)
221