1 /*
2  * Copyright (c) 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 
9 /**
10  * @brief SDIO driver for Infineon CAT1 MCU family.
11  *
12  * This driver support only SDIO protocol of the SD interface for general
13  * I/O functions.
14  *
15  * Refer to the SD Specifications Part 1 SDIO Specifications Version 4.10 for more
16  * information on the SDIO protocol and specifications.
17  *
18  * Features
19  * - Supports 4-bit interface
20  * - Supports Ultra High Speed (UHS-I) mode
21  * - Supports Default Speed (DS), High Speed (HS), SDR12, SDR25 and SDR50 speed modes
22  * - Supports SDIO card interrupts in both 1-bit SD and 4-bit SD modes
23  * - Supports Standard capacity (SDSC), High capacity (SDHC) and
24  *   Extended capacity (SDXC) memory
25  *
26  * Note (limitations):
27  * - current version of ifx_cat1_sdio supports only following set of commands:
28  *   > GO_IDLE_STATE      (CMD0)
29  *   > SEND_RELATIVE_ADDR (CMD3)
30  *   > IO_SEND_OP_COND    (CMD5)
31  *   > SELECT_CARD        (CMD7)
32  *   > VOLTAGE_SWITCH     (CMD11)
33  *   > GO_INACTIVE_STATE  (CMD15)
34  *   > IO_RW_DIRECT       (CMD52)
35  *   > IO_RW_EXTENDED     (CMD53)
36  */
37 
38 #define DT_DRV_COMPAT infineon_cat1_sdhc_sdio
39 
40 #include <zephyr/kernel.h>
41 #include <zephyr/devicetree.h>
42 #include <zephyr/drivers/sdhc.h>
43 #include <zephyr/sd/sd_spec.h>
44 #include <zephyr/drivers/clock_control.h>
45 #include <zephyr/drivers/gpio.h>
46 #include <zephyr/logging/log.h>
47 #include <soc.h>
48 #include <zephyr/drivers/pinctrl.h>
49 
50 #include <cyhal_sdhc.h>
51 #include <cyhal_sdio.h>
52 #include <cyhal_gpio.h>
53 
54 LOG_MODULE_REGISTER(ifx_cat1_sdio, CONFIG_SDHC_LOG_LEVEL);
55 
56 #include <zephyr/irq.h>
57 
58 #define IFX_CAT1_SDIO_F_MIN (SDMMC_CLOCK_400KHZ)
59 #define IFX_CAT1_SDIO_F_MAX (SD_CLOCK_50MHZ)
60 
61 struct ifx_cat1_sdio_config {
62 	const struct pinctrl_dev_config *pincfg;
63 	SDHC_Type *reg_addr;
64 	uint8_t irq_priority;
65 };
66 
67 struct ifx_cat1_sdio_data {
68 	cyhal_sdio_t sdio_obj;
69 	cyhal_resource_inst_t hw_resource;
70 	cyhal_sdio_configurator_t cyhal_sdio_config;
71 	enum sdhc_clock_speed clock_speed;
72 	enum sdhc_bus_width bus_width;
73 
74 	void *sdio_cb_user_data;
75 	sdhc_interrupt_cb_t sdio_cb;
76 };
77 
78 static uint32_t sdio_rca;
79 static const cy_stc_sd_host_init_config_t host_config = {false, CY_SD_HOST_DMA_ADMA2, false};
80 static cy_en_sd_host_card_capacity_t sd_host_card_capacity = CY_SD_HOST_SDSC;
81 static cy_en_sd_host_card_type_t sd_host_card_type = CY_SD_HOST_NOT_EMMC;
82 static cy_stc_sd_host_sd_card_config_t sd_host_sd_card_config = {
83 	.lowVoltageSignaling = false,
84 	.busWidth = CY_SD_HOST_BUS_WIDTH_4_BIT,
85 	.cardType = &sd_host_card_type,
86 	.rca = &sdio_rca,
87 	.cardCapacity = &sd_host_card_capacity,
88 };
89 
90 /* List of available SDHC instances */
91 static SDHC_Type *const IFX_CAT1_SDHC_BASE_ADDRESSES[CY_IP_MXSDHC_INSTANCES] = {
92 #ifdef SDHC0
93 	SDHC0,
94 #endif /* ifdef SDHC0 */
95 
96 #ifdef SDHC1
97 	SDHC1,
98 #endif /* ifdef SDHC1 */
99 };
100 
_get_hw_block_num(SDHC_Type * reg_addr)101 static int32_t _get_hw_block_num(SDHC_Type *reg_addr)
102 {
103 	uint32_t i;
104 
105 	for (i = 0u; i < CY_IP_MXSDHC_INSTANCES; i++) {
106 		if (IFX_CAT1_SDHC_BASE_ADDRESSES[i] == reg_addr) {
107 			return i;
108 		}
109 	}
110 
111 	return -EINVAL;
112 }
113 
ifx_cat1_sdio_reset(const struct device * dev)114 static int ifx_cat1_sdio_reset(const struct device *dev)
115 {
116 	struct ifx_cat1_sdio_data *dev_data = dev->data;
117 
118 	cyhal_sdhc_software_reset((cyhal_sdhc_t *)&dev_data->sdio_obj);
119 
120 	return 0;
121 }
122 
ifx_cat1_sdio_set_io(const struct device * dev,struct sdhc_io * ios)123 static int ifx_cat1_sdio_set_io(const struct device *dev, struct sdhc_io *ios)
124 {
125 	cy_rslt_t ret;
126 	struct ifx_cat1_sdio_data *dev_data = dev->data;
127 	cyhal_sdio_cfg_t config = {.frequencyhal_hz = ios->clock};
128 
129 	/* NOTE: Set bus width, set card power, set host signal voltage,
130 	 * set I/O timing does not support in current version of driver
131 	 */
132 
133 	/* Set host clock */
134 	if ((dev_data->clock_speed != ios->clock) && (ios->clock != 0)) {
135 
136 		if ((ios->clock > IFX_CAT1_SDIO_F_MAX) || (ios->clock < IFX_CAT1_SDIO_F_MIN)) {
137 			return -EINVAL;
138 		}
139 
140 		ret = cyhal_sdio_configure(&dev_data->sdio_obj, &config);
141 		if (ret != CY_RSLT_SUCCESS) {
142 			return -ENOTSUP;
143 		}
144 
145 		dev_data->clock_speed = ios->clock;
146 	}
147 
148 	return 0;
149 }
150 
ifx_cat1_sdio_card_busy(const struct device * dev)151 static int ifx_cat1_sdio_card_busy(const struct device *dev)
152 {
153 	struct ifx_cat1_sdio_data *dev_data = dev->data;
154 
155 	return cyhal_sdio_is_busy(&dev_data->sdio_obj) ? 1 : 0;
156 }
157 
ifx_cat1_sdio_request(const struct device * dev,struct sdhc_command * cmd,struct sdhc_data * data)158 static int ifx_cat1_sdio_request(const struct device *dev, struct sdhc_command *cmd,
159 				 struct sdhc_data *data)
160 {
161 	struct ifx_cat1_sdio_data *dev_data = dev->data;
162 	int ret;
163 
164 	switch (cmd->opcode) {
165 	case CYHAL_SDIO_CMD_GO_IDLE_STATE:
166 	case CYHAL_SDIO_CMD_SEND_RELATIVE_ADDR:
167 	case CYHAL_SDIO_CMD_IO_SEND_OP_COND:
168 	case CYHAL_SDIO_CMD_SELECT_CARD:
169 	case CYHAL_SDIO_CMD_VOLTAGE_SWITCH:
170 	case CYHAL_SDIO_CMD_GO_INACTIVE_STATE:
171 	case CYHAL_SDIO_CMD_IO_RW_DIRECT:
172 		ret = cyhal_sdio_send_cmd(&dev_data->sdio_obj, CYHAL_SDIO_XFER_TYPE_READ,
173 					  cmd->opcode, cmd->arg, cmd->response);
174 		if (ret != CY_RSLT_SUCCESS) {
175 			LOG_ERR("cyhal_sdio_send_cmd failed ret = %d \r\n", ret);
176 		}
177 		break;
178 
179 	case CYHAL_SDIO_CMD_IO_RW_EXTENDED:
180 		cyhal_sdio_transfer_type_t direction;
181 
182 		direction = (cmd->arg & BIT(SDIO_CMD_ARG_RW_SHIFT)) ? CYHAL_SDIO_XFER_TYPE_WRITE
183 								    : CYHAL_SDIO_XFER_TYPE_READ;
184 
185 		ret = cyhal_sdio_bulk_transfer(&dev_data->sdio_obj, direction, cmd->arg, data->data,
186 					       data->blocks * data->block_size, cmd->response);
187 
188 		if (ret != CY_RSLT_SUCCESS) {
189 			LOG_ERR("cyhal_sdio_bulk_transfer failed ret = %d \r\n", ret);
190 		}
191 		break;
192 
193 	default:
194 		ret = -ENOTSUP;
195 	}
196 
197 	return ret;
198 }
199 
ifx_cat1_sdio_get_card_present(const struct device * dev)200 static int ifx_cat1_sdio_get_card_present(const struct device *dev)
201 {
202 	return 1;
203 }
204 
ifx_cat1_sdio_get_host_props(const struct device * dev,struct sdhc_host_props * props)205 static int ifx_cat1_sdio_get_host_props(const struct device *dev, struct sdhc_host_props *props)
206 {
207 	memset(props, 0, sizeof(*props));
208 	props->f_max = IFX_CAT1_SDIO_F_MAX;
209 	props->f_min = IFX_CAT1_SDIO_F_MIN;
210 	props->host_caps.bus_4_bit_support = true;
211 	props->host_caps.high_spd_support = true;
212 	props->host_caps.sdr50_support = true;
213 	props->host_caps.sdio_async_interrupt_support = true;
214 	props->host_caps.vol_330_support = true;
215 
216 	return 0;
217 }
218 
ifx_cat1_sdio_enable_interrupt(const struct device * dev,sdhc_interrupt_cb_t callback,int sources,void * user_data)219 static int ifx_cat1_sdio_enable_interrupt(const struct device *dev, sdhc_interrupt_cb_t callback,
220 					  int sources, void *user_data)
221 {
222 	struct ifx_cat1_sdio_data *data = dev->data;
223 	const struct ifx_cat1_sdio_config *cfg = dev->config;
224 
225 	if (sources != SDHC_INT_SDIO) {
226 		return -ENOTSUP;
227 	}
228 
229 	if (callback == NULL) {
230 		return -EINVAL;
231 	}
232 
233 	/* Record SDIO callback parameters */
234 	data->sdio_cb = callback;
235 	data->sdio_cb_user_data = user_data;
236 
237 	/* Enable CARD INTERRUPT event */
238 	cyhal_sdio_enable_event(&data->sdio_obj, CYHAL_SDIO_CARD_INTERRUPT,
239 				cfg->irq_priority, true);
240 
241 	return 0;
242 }
243 
ifx_cat1_sdio_disable_interrupt(const struct device * dev,int sources)244 static int ifx_cat1_sdio_disable_interrupt(const struct device *dev, int sources)
245 {
246 	struct ifx_cat1_sdio_data *data = dev->data;
247 	const struct ifx_cat1_sdio_config *cfg = dev->config;
248 
249 	if (sources != SDHC_INT_SDIO) {
250 		return -ENOTSUP;
251 	}
252 
253 	data->sdio_cb = NULL;
254 	data->sdio_cb_user_data = NULL;
255 
256 	/* Disable CARD INTERRUPT event */
257 	cyhal_sdio_enable_event(&data->sdio_obj, CYHAL_SDIO_CARD_INTERRUPT,
258 				cfg->irq_priority, false);
259 
260 	return 0;
261 }
262 
ifx_cat1_sdio_event_callback(void * callback_arg,cyhal_sdio_event_t event)263 static void ifx_cat1_sdio_event_callback(void *callback_arg, cyhal_sdio_event_t event)
264 {
265 	const struct device *dev = callback_arg;
266 	struct ifx_cat1_sdio_data *data = dev->data;
267 
268 	if ((event == CYHAL_SDIO_CARD_INTERRUPT) && (data->sdio_cb != NULL)) {
269 		data->sdio_cb(dev, SDHC_INT_SDIO, data->sdio_cb_user_data);
270 	}
271 }
272 
ifx_cat1_sdio_init(const struct device * dev)273 static int ifx_cat1_sdio_init(const struct device *dev)
274 {
275 	cy_rslt_t ret;
276 	struct ifx_cat1_sdio_data *data = dev->data;
277 	const struct ifx_cat1_sdio_config *config = dev->config;
278 
279 	/* Configure dt provided device signals when available */
280 	ret = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
281 	if (ret) {
282 		return ret;
283 	}
284 
285 	/* Dedicate SDIO HW resource */
286 	data->hw_resource.type = CYHAL_RSC_SDIODEV;
287 	data->hw_resource.block_num = _get_hw_block_num(config->reg_addr);
288 
289 	/* Initialize the SDIO peripheral */
290 	data->cyhal_sdio_config.resource = &data->hw_resource;
291 	data->cyhal_sdio_config.host_config = &host_config,
292 	data->cyhal_sdio_config.card_config = &sd_host_sd_card_config,
293 
294 	ret = cyhal_sdio_init_cfg(&data->sdio_obj, &data->cyhal_sdio_config);
295 	if (ret != CY_RSLT_SUCCESS) {
296 		LOG_ERR("cyhal_sdio_init_cfg failed ret = %d \r\n", ret);
297 		return ret;
298 	}
299 
300 	/* Register callback for SDIO events */
301 	cyhal_sdio_register_callback(&data->sdio_obj, ifx_cat1_sdio_event_callback, (void *)dev);
302 
303 	return 0;
304 }
305 
306 static DEVICE_API(sdhc, ifx_cat1_sdio_api) = {
307 	.reset = ifx_cat1_sdio_reset,
308 	.request = ifx_cat1_sdio_request,
309 	.set_io = ifx_cat1_sdio_set_io,
310 	.get_card_present = ifx_cat1_sdio_get_card_present,
311 	.card_busy = ifx_cat1_sdio_card_busy,
312 	.get_host_props = ifx_cat1_sdio_get_host_props,
313 	.enable_interrupt = ifx_cat1_sdio_enable_interrupt,
314 	.disable_interrupt = ifx_cat1_sdio_disable_interrupt,
315 };
316 
317 #define IFX_CAT1_SDHC_INIT(n)                                                                      \
318                                                                                                    \
319 	PINCTRL_DT_INST_DEFINE(n);                                                                 \
320                                                                                                    \
321 	static const struct ifx_cat1_sdio_config ifx_cat1_sdio_##n##_config = {                    \
322 		.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n),                                       \
323 		.reg_addr = (SDHC_Type *)DT_INST_REG_ADDR(n),                                      \
324 		.irq_priority = DT_INST_IRQ(n, priority)};                                         \
325                                                                                                    \
326 	static struct ifx_cat1_sdio_data ifx_cat1_sdio_##n##_data;                                 \
327                                                                                                    \
328 	DEVICE_DT_INST_DEFINE(n, &ifx_cat1_sdio_init, NULL, &ifx_cat1_sdio_##n##_data,             \
329 			      &ifx_cat1_sdio_##n##_config, POST_KERNEL, CONFIG_SDHC_INIT_PRIORITY, \
330 			      &ifx_cat1_sdio_api);
331 
332 DT_INST_FOREACH_STATUS_OKAY(IFX_CAT1_SDHC_INIT)
333