1 /*
2 * Copyright (c) 2019 Brett Witherspoon
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT ti_cc13xx_cc26xx_spi
8
9 #define LOG_LEVEL CONFIG_SPI_LOG_LEVEL
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_REGISTER(spi_cc13xx_cc26xx);
12
13 #include <zephyr/drivers/spi.h>
14 #include <zephyr/drivers/spi/rtio.h>
15 #include <zephyr/drivers/pinctrl.h>
16 #include <zephyr/pm/device.h>
17 #include <zephyr/pm/policy.h>
18
19 #include <driverlib/prcm.h>
20 #include <driverlib/ssi.h>
21
22 #include <ti/drivers/Power.h>
23 #include <ti/drivers/power/PowerCC26X2.h>
24
25 #include "spi_context.h"
26
27 struct spi_cc13xx_cc26xx_config {
28 uint32_t base;
29 const struct pinctrl_dev_config *pcfg;
30 };
31
32 struct spi_cc13xx_cc26xx_data {
33 struct spi_context ctx;
34 };
35
36 #define CPU_FREQ DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency)
37
spi_cc13xx_cc26xx_configure(const struct device * dev,const struct spi_config * config)38 static int spi_cc13xx_cc26xx_configure(const struct device *dev,
39 const struct spi_config *config)
40 {
41 const struct spi_cc13xx_cc26xx_config *cfg = dev->config;
42 struct spi_cc13xx_cc26xx_data *data = dev->data;
43 struct spi_context *ctx = &data->ctx;
44 uint32_t prot;
45 int ret;
46
47 if (spi_context_configured(ctx, config)) {
48 return 0;
49 }
50
51 if (config->operation & SPI_HALF_DUPLEX) {
52 LOG_ERR("Half-duplex not supported");
53 return -ENOTSUP;
54 }
55
56 /* Slave mode has not been implemented */
57 if (SPI_OP_MODE_GET(config->operation) != SPI_OP_MODE_MASTER) {
58 LOG_ERR("Slave mode is not supported");
59 return -ENOTSUP;
60 }
61
62 /* Word sizes other than 8 bits has not been implemented */
63 if (SPI_WORD_SIZE_GET(config->operation) != 8) {
64 LOG_ERR("Word sizes other than 8 bits are not supported");
65 return -ENOTSUP;
66 }
67
68 if (config->operation & SPI_TRANSFER_LSB) {
69 LOG_ERR("Transfer LSB first mode is not supported");
70 return -EINVAL;
71 }
72
73 if (IS_ENABLED(CONFIG_SPI_EXTENDED_MODES) &&
74 (config->operation & SPI_LINES_MASK) != SPI_LINES_SINGLE) {
75 LOG_ERR("Multiple lines are not supported");
76 return -EINVAL;
77 }
78
79 if (config->operation & SPI_CS_ACTIVE_HIGH && !spi_cs_is_gpio(config)) {
80 LOG_ERR("Active high CS requires emulation through a GPIO line.");
81 return -EINVAL;
82 }
83
84 if (config->frequency < 2000000) {
85 LOG_ERR("Frequencies lower than 2 MHz are not supported");
86 return -EINVAL;
87 }
88
89 if (2 * config->frequency > CPU_FREQ) {
90 LOG_ERR("Frequency greater than supported in master mode");
91 return -EINVAL;
92 }
93
94 if (SPI_MODE_GET(config->operation) & SPI_MODE_CPOL) {
95 if (SPI_MODE_GET(config->operation) & SPI_MODE_CPHA) {
96 prot = SSI_FRF_MOTO_MODE_3;
97 } else {
98 prot = SSI_FRF_MOTO_MODE_2;
99 }
100 } else {
101 if (SPI_MODE_GET(config->operation) & SPI_MODE_CPHA) {
102 prot = SSI_FRF_MOTO_MODE_1;
103 } else {
104 prot = SSI_FRF_MOTO_MODE_0;
105 }
106 }
107
108 ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
109 if (ret < 0) {
110 LOG_ERR("applying SPI pinctrl state failed");
111 return ret;
112 }
113
114 ctx->config = config;
115
116 /* Disable SSI before making configuration changes */
117 SSIDisable(cfg->base);
118
119 /* Configure SSI */
120 SSIConfigSetExpClk(cfg->base, CPU_FREQ, prot,
121 SSI_MODE_MASTER, config->frequency, 8);
122
123 if (SPI_MODE_GET(config->operation) & SPI_MODE_LOOP) {
124 sys_set_bit(cfg->base + SSI_O_CR1, 0);
125 }
126
127 /* Re-enable SSI after making configuration changes */
128 SSIEnable(cfg->base);
129
130 return 0;
131 }
132
spi_cc13xx_cc26xx_transceive(const struct device * dev,const struct spi_config * config,const struct spi_buf_set * tx_bufs,const struct spi_buf_set * rx_bufs)133 static int spi_cc13xx_cc26xx_transceive(const struct device *dev,
134 const struct spi_config *config,
135 const struct spi_buf_set *tx_bufs,
136 const struct spi_buf_set *rx_bufs)
137 {
138 const struct spi_cc13xx_cc26xx_config *cfg = dev->config;
139 struct spi_cc13xx_cc26xx_data *data = dev->data;
140 struct spi_context *ctx = &data->ctx;
141 uint32_t txd, rxd;
142 int err;
143
144 spi_context_lock(ctx, false, NULL, NULL, config);
145 pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
146
147 err = spi_cc13xx_cc26xx_configure(dev, config);
148 if (err) {
149 goto done;
150 }
151
152 spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1);
153
154 spi_context_cs_control(ctx, true);
155
156 do {
157 if (spi_context_tx_buf_on(ctx)) {
158 txd = *ctx->tx_buf;
159 } else {
160 txd = 0U;
161 }
162
163 SSIDataPut(cfg->base, txd);
164
165 spi_context_update_tx(ctx, 1, 1);
166
167 SSIDataGet(cfg->base, &rxd);
168
169 if (spi_context_rx_buf_on(ctx)) {
170 *ctx->rx_buf = rxd;
171 }
172
173 spi_context_update_rx(ctx, 1, 1);
174 } while (spi_context_tx_on(ctx) || spi_context_rx_on(ctx));
175
176 spi_context_cs_control(ctx, false);
177
178 done:
179 pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
180 spi_context_release(ctx, err);
181 return err;
182 }
183
spi_cc13xx_cc26xx_release(const struct device * dev,const struct spi_config * config)184 static int spi_cc13xx_cc26xx_release(const struct device *dev,
185 const struct spi_config *config)
186 {
187 const struct spi_cc13xx_cc26xx_config *cfg = dev->config;
188 struct spi_cc13xx_cc26xx_data *data = dev->data;
189 struct spi_context *ctx = &data->ctx;
190
191 if (!spi_context_configured(ctx, config)) {
192 return -EINVAL;
193 }
194
195 if (SSIBusy(cfg->base)) {
196 return -EBUSY;
197 }
198
199 spi_context_unlock_unconditionally(ctx);
200
201 return 0;
202 }
203
204 #ifdef CONFIG_PM_DEVICE
spi_cc13xx_cc26xx_pm_action(const struct device * dev,enum pm_device_action action)205 static int spi_cc13xx_cc26xx_pm_action(const struct device *dev,
206 enum pm_device_action action)
207 {
208 const struct spi_cc13xx_cc26xx_config *config = dev->config;
209
210 switch (action) {
211 case PM_DEVICE_ACTION_RESUME:
212 if (config->base == DT_INST_REG_ADDR(0)) {
213 Power_setDependency(PowerCC26XX_PERIPH_SSI0);
214 } else {
215 Power_setDependency(PowerCC26XX_PERIPH_SSI1);
216 }
217 break;
218 case PM_DEVICE_ACTION_SUSPEND:
219 SSIDisable(config->base);
220 /*
221 * Release power dependency
222 */
223 if (config->base == DT_INST_REG_ADDR(0)) {
224 Power_releaseDependency(PowerCC26XX_PERIPH_SSI0);
225 } else {
226 Power_releaseDependency(PowerCC26XX_PERIPH_SSI1);
227 }
228 break;
229 default:
230 return -ENOTSUP;
231 }
232
233 return 0;
234 }
235 #endif /* CONFIG_PM_DEVICE */
236
237
238 static DEVICE_API(spi, spi_cc13xx_cc26xx_driver_api) = {
239 .transceive = spi_cc13xx_cc26xx_transceive,
240 .release = spi_cc13xx_cc26xx_release,
241 #ifdef CONFIG_SPI_RTIO
242 .iodev_submit = spi_rtio_iodev_default_submit,
243 #endif
244 };
245
246 #ifdef CONFIG_PM
247 #define SPI_CC13XX_CC26XX_POWER_SPI(n) \
248 do { \
249 /* Set Power dependencies & constraints */ \
250 if (DT_INST_REG_ADDR(n) == 0x40000000) { \
251 Power_setDependency(PowerCC26XX_PERIPH_SSI0); \
252 } else { \
253 Power_setDependency(PowerCC26XX_PERIPH_SSI1); \
254 } \
255 } while (false)
256 #else
257 #define SPI_CC13XX_CC26XX_POWER_SPI(n) \
258 do { \
259 uint32_t domain, periph; \
260 \
261 /* Enable UART power domain */ \
262 if (DT_INST_REG_ADDR(n) == 0x40000000) { \
263 domain = PRCM_DOMAIN_SERIAL; \
264 periph = PRCM_PERIPH_SSI0; \
265 } else { \
266 domain = PRCM_DOMAIN_PERIPH; \
267 periph = PRCM_PERIPH_SSI1; \
268 } \
269 /* Enable SSI##n power domain */ \
270 PRCMPowerDomainOn(domain); \
271 \
272 /* Enable SSI##n peripherals */ \
273 PRCMPeripheralRunEnable(periph); \
274 PRCMPeripheralSleepEnable(periph); \
275 PRCMPeripheralDeepSleepEnable(periph); \
276 \
277 /* Load PRCM settings */ \
278 PRCMLoadSet(); \
279 while (!PRCMLoadGet()) { \
280 continue; \
281 } \
282 \
283 /* SSI should not be accessed until power domain is on. */\
284 while (PRCMPowerDomainsAllOn(domain) != \
285 PRCM_DOMAIN_POWER_ON) { \
286 continue; \
287 } \
288 } while (false)
289 #endif
290
291 #define SPI_CC13XX_CC26XX_DEVICE_INIT(n) \
292 PM_DEVICE_DT_INST_DEFINE(n, spi_cc13xx_cc26xx_pm_action); \
293 \
294 SPI_DEVICE_DT_INST_DEFINE(n, \
295 spi_cc13xx_cc26xx_init_##n, \
296 PM_DEVICE_DT_INST_GET(n), \
297 &spi_cc13xx_cc26xx_data_##n, &spi_cc13xx_cc26xx_config_##n, \
298 POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \
299 &spi_cc13xx_cc26xx_driver_api)
300
301 #define SPI_CC13XX_CC26XX_INIT_FUNC(n) \
302 static int spi_cc13xx_cc26xx_init_##n(const struct device *dev) \
303 { \
304 struct spi_cc13xx_cc26xx_data *data = dev->data; \
305 int err; \
306 SPI_CC13XX_CC26XX_POWER_SPI(n); \
307 \
308 err = spi_context_cs_configure_all(&data->ctx); \
309 if (err < 0) { \
310 return err; \
311 } \
312 \
313 spi_context_unlock_unconditionally(&data->ctx); \
314 \
315 return 0; \
316 }
317
318 #define SPI_CC13XX_CC26XX_INIT(n) \
319 PINCTRL_DT_INST_DEFINE(n); \
320 SPI_CC13XX_CC26XX_INIT_FUNC(n) \
321 \
322 static const struct spi_cc13xx_cc26xx_config \
323 spi_cc13xx_cc26xx_config_##n = { \
324 .base = DT_INST_REG_ADDR(n), \
325 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n) \
326 }; \
327 \
328 static struct spi_cc13xx_cc26xx_data \
329 spi_cc13xx_cc26xx_data_##n = { \
330 SPI_CONTEXT_INIT_LOCK(spi_cc13xx_cc26xx_data_##n, ctx), \
331 SPI_CONTEXT_INIT_SYNC(spi_cc13xx_cc26xx_data_##n, ctx), \
332 SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(n), ctx) \
333 }; \
334 \
335 SPI_CC13XX_CC26XX_DEVICE_INIT(n);
336
337 DT_INST_FOREACH_STATUS_OKAY(SPI_CC13XX_CC26XX_INIT)
338