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