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