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