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