1 /*
2 * Copyright 2023 Cypress Semiconductor Corporation (an Infineon company) or
3 * an affiliate of Cypress Semiconductor Corporation
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #define DT_DRV_COMPAT infineon_cat1_spi
9
10 #define LOG_LEVEL CONFIG_SPI_LOG_LEVEL
11 #include <zephyr/logging/log.h>
12 LOG_MODULE_REGISTER(cat1_spi);
13
14 #include "spi_context.h"
15
16 #include <zephyr/drivers/pinctrl.h>
17 #include <zephyr/drivers/spi.h>
18 #include <zephyr/drivers/spi/rtio.h>
19 #include <zephyr/kernel.h>
20
21 #include <cyhal_scb_common.h>
22 #include <cyhal_spi.h>
23
24 #define IFX_CAT1_SPI_LOCK_TMOUT_MS (30 * 1000)
25 #define IFX_CAT1_SPI_DEFAULT_OVERSAMPLE (4)
26 #define IFX_CAT1_SPI_MIN_DATA_WIDTH (8)
27 #define IFX_CAT1_SPI_MAX_DATA_WIDTH (32)
28
29 /* Device config structure */
30 struct ifx_cat1_spi_config {
31 CySCB_Type *reg_addr;
32 const struct pinctrl_dev_config *pcfg;
33 cy_stc_scb_spi_config_t scb_spi_config;
34 uint8_t irq_priority;
35 };
36
37 /* Data structure */
38 struct ifx_cat1_spi_data {
39 struct spi_context ctx;
40 cyhal_spi_t obj; /* SPI CYHAL object */
41 cyhal_resource_inst_t hw_resource;
42 uint8_t dfs_value;
43 size_t chunk_len;
44 };
45
get_hw_block_num(CySCB_Type * reg_addr)46 static int32_t get_hw_block_num(CySCB_Type *reg_addr)
47 {
48 extern const uint8_t _CYHAL_SCB_BASE_ADDRESS_INDEX[_SCB_ARRAY_SIZE];
49 extern CySCB_Type *const _CYHAL_SCB_BASE_ADDRESSES[_SCB_ARRAY_SIZE];
50
51 uint32_t i;
52
53 for (i = 0u; i < _SCB_ARRAY_SIZE; i++) {
54 if (_CYHAL_SCB_BASE_ADDRESSES[i] == reg_addr) {
55 return _CYHAL_SCB_BASE_ADDRESS_INDEX[i];
56 }
57 }
58
59 return -ENOMEM;
60 }
61
get_dfs_value(struct spi_context * ctx)62 static uint8_t get_dfs_value(struct spi_context *ctx)
63 {
64 switch (SPI_WORD_SIZE_GET(ctx->config->operation)) {
65 case 8:
66 return 1;
67 case 16:
68 return 2;
69 case 32:
70 return 4;
71 default:
72 return 1;
73 }
74 }
75
transfer_chunk(const struct device * dev)76 static void transfer_chunk(const struct device *dev)
77 {
78 struct ifx_cat1_spi_data *const data = dev->data;
79 struct spi_context *ctx = &data->ctx;
80 int ret = 0;
81 size_t chunk_len = spi_context_max_continuous_chunk(ctx);
82
83 if (chunk_len == 0) {
84 goto exit;
85 }
86
87 data->chunk_len = chunk_len;
88
89 cy_rslt_t result = cyhal_spi_transfer_async(
90 &data->obj, ctx->tx_buf, spi_context_tx_buf_on(ctx) ? chunk_len : 0, ctx->rx_buf,
91 spi_context_rx_buf_on(ctx) ? chunk_len : 0);
92 if (result == CY_RSLT_SUCCESS) {
93 return;
94 }
95
96 ret = -EIO;
97
98 exit:
99 spi_context_cs_control(ctx, false);
100 spi_context_complete(ctx, dev, ret);
101 }
102
spi_interrupt_callback(void * arg,cyhal_spi_event_t event)103 static void spi_interrupt_callback(void *arg, cyhal_spi_event_t event)
104 {
105 const struct device *dev = (const struct device *)arg;
106 struct ifx_cat1_spi_data *const data = dev->data;
107 struct spi_context *ctx = &data->ctx;
108
109 if (event & CYHAL_SPI_IRQ_ERROR) {
110 #if defined(CONFIG_SPI_ASYNC)
111 cyhal_spi_abort_async(&data->obj);
112 #endif
113 spi_context_cs_control(ctx, false);
114 spi_context_complete(ctx, dev, -EIO);
115 }
116
117 if (event & CYHAL_SPI_IRQ_DONE) {
118 spi_context_update_tx(ctx, data->dfs_value, data->chunk_len);
119 spi_context_update_rx(ctx, data->dfs_value, data->chunk_len);
120
121 transfer_chunk(dev);
122 }
123 }
124
spi_config(const struct device * dev,const struct spi_config * spi_cfg)125 int spi_config(const struct device *dev, const struct spi_config *spi_cfg)
126 {
127 cy_rslt_t result;
128 struct ifx_cat1_spi_data *const data = dev->data;
129 const struct ifx_cat1_spi_config *const config = dev->config;
130 cy_stc_scb_spi_config_t scb_spi_config = config->scb_spi_config;
131 struct spi_context *ctx = &data->ctx;
132 bool spi_mode_cpol = false;
133 bool spi_mode_cpha = false;
134
135 /* check if configuration was changed from previous run, if so skip setup again */
136 if (spi_context_configured(ctx, spi_cfg)) {
137 /* Already configured. No need to do it again. */
138 return 0;
139 }
140
141 if (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_LOOP) {
142 return -ENOTSUP;
143 }
144
145 if (SPI_WORD_SIZE_GET(spi_cfg->operation) > IFX_CAT1_SPI_MAX_DATA_WIDTH) {
146 LOG_ERR("Word size %d is greater than %d", SPI_WORD_SIZE_GET(spi_cfg->operation),
147 IFX_CAT1_SPI_MAX_DATA_WIDTH);
148 return -EINVAL;
149 }
150
151 if (SPI_WORD_SIZE_GET(spi_cfg->operation) < IFX_CAT1_SPI_MIN_DATA_WIDTH) {
152 LOG_ERR("Word size %d is less than %d", SPI_WORD_SIZE_GET(spi_cfg->operation),
153 IFX_CAT1_SPI_MIN_DATA_WIDTH);
154 return -EINVAL;
155 }
156
157 if (SPI_OP_MODE_GET(spi_cfg->operation) == SPI_OP_MODE_SLAVE) {
158 scb_spi_config.spiMode = CY_SCB_SPI_SLAVE;
159 scb_spi_config.oversample = 0;
160 scb_spi_config.enableMisoLateSample = false;
161 } else {
162 scb_spi_config.spiMode = CY_SCB_SPI_MASTER;
163 }
164
165 if (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPOL) {
166 spi_mode_cpol = true;
167 }
168
169 if (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPHA) {
170 spi_mode_cpha = true;
171 }
172
173 if (SPI_WORD_SIZE_GET(spi_cfg->operation)) {
174 scb_spi_config.txDataWidth = SPI_WORD_SIZE_GET(spi_cfg->operation);
175 scb_spi_config.rxDataWidth = SPI_WORD_SIZE_GET(spi_cfg->operation);
176 }
177
178 if (spi_mode_cpha) {
179 scb_spi_config.sclkMode =
180 spi_mode_cpol ? CY_SCB_SPI_CPHA1_CPOL1 : CY_SCB_SPI_CPHA1_CPOL0;
181 } else {
182 scb_spi_config.sclkMode =
183 spi_mode_cpol ? CY_SCB_SPI_CPHA0_CPOL1 : CY_SCB_SPI_CPHA0_CPOL0;
184 }
185
186 scb_spi_config.enableMsbFirst = (spi_cfg->operation & SPI_TRANSFER_LSB) ? false : true;
187
188 /* Force free resource */
189 if (data->obj.base != NULL) {
190 cyhal_spi_free(&data->obj);
191 }
192
193 /* Initialize the SPI peripheral */
194 cyhal_spi_configurator_t spi_init_cfg = {.resource = &data->hw_resource,
195 .config = &scb_spi_config,
196 .gpios = {NC, {NC, NC, NC, NC}, NC, NC}};
197
198 result = cyhal_spi_init_cfg(&data->obj, &spi_init_cfg);
199 if (result != CY_RSLT_SUCCESS) {
200 return -ENOTSUP;
201 }
202
203 /* Assigns a programmable divider to a selected IP block */
204 en_clk_dst_t clk_idx = _cyhal_scb_get_clock_index(spi_init_cfg.resource->block_num);
205
206 result = _cyhal_utils_peri_pclk_assign_divider(clk_idx, &data->obj.clock);
207 if (result != CY_RSLT_SUCCESS) {
208 return -ENOTSUP;
209 }
210
211 /* Configure Slave select polarity */
212 if (SPI_OP_MODE_GET(spi_cfg->operation) == SPI_OP_MODE_SLAVE) {
213 Cy_SCB_SPI_SetActiveSlaveSelectPolarity(data->obj.base, CY_SCB_SPI_SLAVE_SELECT0,
214 scb_spi_config.ssPolarity);
215 }
216
217 /* Set the data rate */
218 result = cyhal_spi_set_frequency(&data->obj, spi_cfg->frequency);
219 if (result != CY_RSLT_SUCCESS) {
220 return -EIO;
221 }
222
223 /* Write 0 when NULL buffer is provided for Tx/Rx */
224 data->obj.write_fill = 0;
225
226 /* Register common SPI callback */
227 cyhal_spi_register_callback(&data->obj, spi_interrupt_callback, (void *)dev);
228 cyhal_spi_enable_event(&data->obj, CYHAL_SPI_IRQ_DONE, config->irq_priority, true);
229
230 /* Store spi config in context */
231 ctx->config = spi_cfg;
232
233 data->dfs_value = get_dfs_value(ctx);
234
235 return 0;
236 }
237
transceive(const struct device * dev,const struct spi_config * spi_cfg,const struct spi_buf_set * tx_bufs,const struct spi_buf_set * rx_bufs,bool asynchronous,spi_callback_t cb,void * userdata)238 static int transceive(const struct device *dev, const struct spi_config *spi_cfg,
239 const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
240 bool asynchronous, spi_callback_t cb, void *userdata)
241 {
242 int result;
243 struct ifx_cat1_spi_data *const data = dev->data;
244 struct spi_context *ctx = &data->ctx;
245
246 spi_context_lock(ctx, asynchronous, cb, userdata, spi_cfg);
247
248 result = spi_config(dev, spi_cfg);
249 if (result) {
250 LOG_ERR("Error in SPI Configuration (result: 0x%x)", result);
251 return result;
252 }
253
254 spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, data->dfs_value);
255
256 spi_context_cs_control(ctx, true);
257
258 transfer_chunk(dev);
259
260 result = spi_context_wait_for_completion(&data->ctx);
261
262 spi_context_release(ctx, result);
263
264 return result;
265 }
266
ifx_cat1_spi_transceive_sync(const struct device * dev,const struct spi_config * spi_cfg,const struct spi_buf_set * tx_bufs,const struct spi_buf_set * rx_bufs)267 static int ifx_cat1_spi_transceive_sync(const struct device *dev, const struct spi_config *spi_cfg,
268 const struct spi_buf_set *tx_bufs,
269 const struct spi_buf_set *rx_bufs)
270 {
271 return transceive(dev, spi_cfg, tx_bufs, rx_bufs, false, NULL, NULL);
272 }
273
274 #if defined(CONFIG_SPI_ASYNC)
ifx_cat1_spi_transceive_async(const struct device * dev,const struct spi_config * spi_cfg,const struct spi_buf_set * tx_bufs,const struct spi_buf_set * rx_bufs,spi_callback_t cb,void * userdata)275 static int ifx_cat1_spi_transceive_async(const struct device *dev, const struct spi_config *spi_cfg,
276 const struct spi_buf_set *tx_bufs,
277 const struct spi_buf_set *rx_bufs, spi_callback_t cb,
278 void *userdata)
279 {
280 return transceive(dev, spi_cfg, tx_bufs, rx_bufs, true, cb, userdata);
281 }
282 #endif
283
ifx_cat1_spi_release(const struct device * dev,const struct spi_config * spi_cfg)284 static int ifx_cat1_spi_release(const struct device *dev, const struct spi_config *spi_cfg)
285 {
286 struct ifx_cat1_spi_data *const data = dev->data;
287
288 cyhal_spi_free(&data->obj);
289
290 return 0;
291 }
292
293 static DEVICE_API(spi, ifx_cat1_spi_api) = {
294 .transceive = ifx_cat1_spi_transceive_sync,
295 #if defined(CONFIG_SPI_ASYNC)
296 .transceive_async = ifx_cat1_spi_transceive_async,
297 #endif
298 #ifdef CONFIG_SPI_RTIO
299 .iodev_submit = spi_rtio_iodev_default_submit,
300 #endif
301 .release = ifx_cat1_spi_release,
302 };
303
ifx_cat1_spi_init(const struct device * dev)304 static int ifx_cat1_spi_init(const struct device *dev)
305 {
306 struct ifx_cat1_spi_data *const data = dev->data;
307 const struct ifx_cat1_spi_config *const config = dev->config;
308 int ret;
309
310 /* Dedicate SCB HW resource */
311 data->hw_resource.type = CYHAL_RSC_SCB;
312 data->hw_resource.block_num = get_hw_block_num(config->reg_addr);
313
314 /* Configure dt provided device signals when available */
315 ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
316 if (ret < 0) {
317 return ret;
318 }
319
320 /* Configure slave select (master) */
321 spi_context_cs_configure_all(&data->ctx);
322
323 spi_context_unlock_unconditionally(&data->ctx);
324
325 return 0;
326 }
327
328 #define IFX_CAT1_SPI_INIT(n) \
329 PINCTRL_DT_INST_DEFINE(n); \
330 static struct ifx_cat1_spi_data spi_cat1_data_##n = { \
331 SPI_CONTEXT_INIT_LOCK(spi_cat1_data_##n, ctx), \
332 SPI_CONTEXT_INIT_SYNC(spi_cat1_data_##n, ctx), \
333 SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(n), ctx)}; \
334 static struct ifx_cat1_spi_config spi_cat1_config_##n = { \
335 .reg_addr = (CySCB_Type *)DT_INST_REG_ADDR(n), \
336 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
337 .scb_spi_config = \
338 { \
339 .spiMode = CY_SCB_SPI_MASTER, /* overwrite by cfg */ \
340 .sclkMode = CY_SCB_SPI_CPHA0_CPOL0, /* overwrite by cfg */ \
341 .rxDataWidth = 8, /* overwrite by cfg */ \
342 .txDataWidth = 8, /* overwrite by cfg */ \
343 .enableMsbFirst = true, /* overwrite by cfg */ \
344 .subMode = CY_SCB_SPI_MOTOROLA, \
345 .oversample = IFX_CAT1_SPI_DEFAULT_OVERSAMPLE, \
346 .enableMisoLateSample = true, \
347 .ssPolarity = CY_SCB_SPI_ACTIVE_LOW, \
348 }, \
349 .irq_priority = DT_INST_IRQ(n, priority), \
350 }; \
351 SPI_DEVICE_DT_INST_DEFINE(n, ifx_cat1_spi_init, NULL, &spi_cat1_data_##n, \
352 &spi_cat1_config_##n, POST_KERNEL, \
353 CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &ifx_cat1_spi_api);
354
355 DT_INST_FOREACH_STATUS_OKAY(IFX_CAT1_SPI_INIT)
356