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