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