1 /*
2  * Copyright 2022 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /*
8  * SDMMC disk driver using zephyr SD subsystem
9  */
10 #define DT_DRV_COMPAT zephyr_sdmmc_disk
11 
12 #include <zephyr/sd/sdmmc.h>
13 #include <zephyr/drivers/disk.h>
14 
15 
16 enum sd_status {
17 	SD_UNINIT,
18 	SD_ERROR,
19 	SD_OK,
20 };
21 
22 struct sdmmc_config {
23 	const struct device *host_controller;
24 };
25 
26 struct sdmmc_data {
27 	struct sd_card card;
28 	enum sd_status status;
29 	struct disk_info *disk_info;
30 };
31 
32 
disk_sdmmc_access_init(struct disk_info * disk)33 static int disk_sdmmc_access_init(struct disk_info *disk)
34 {
35 	const struct device *dev = disk->dev;
36 	const struct sdmmc_config *cfg = dev->config;
37 	struct sdmmc_data *data = dev->data;
38 	int ret;
39 
40 	if (!sd_is_card_present(cfg->host_controller)) {
41 		return DISK_STATUS_NOMEDIA;
42 	}
43 
44 	ret = sd_init(cfg->host_controller, &data->card);
45 	if (ret) {
46 		data->status = SD_ERROR;
47 		return ret;
48 	}
49 	data->status = SD_OK;
50 	return 0;
51 }
52 
disk_sdmmc_access_status(struct disk_info * disk)53 static int disk_sdmmc_access_status(struct disk_info *disk)
54 {
55 	const struct device *dev = disk->dev;
56 	const struct sdmmc_config *cfg = dev->config;
57 	struct sdmmc_data *data = dev->data;
58 
59 	if (!sd_is_card_present(cfg->host_controller)) {
60 		return DISK_STATUS_NOMEDIA;
61 	}
62 	if (data->status == SD_OK) {
63 		return DISK_STATUS_OK;
64 	} else {
65 		return DISK_STATUS_UNINIT;
66 	}
67 }
68 
disk_sdmmc_access_read(struct disk_info * disk,uint8_t * buf,uint32_t sector,uint32_t count)69 static int disk_sdmmc_access_read(struct disk_info *disk, uint8_t *buf,
70 				 uint32_t sector, uint32_t count)
71 {
72 	const struct device *dev = disk->dev;
73 	struct sdmmc_data *data = dev->data;
74 
75 	return sdmmc_read_blocks(&data->card, buf, sector, count);
76 }
77 
disk_sdmmc_access_write(struct disk_info * disk,const uint8_t * buf,uint32_t sector,uint32_t count)78 static int disk_sdmmc_access_write(struct disk_info *disk, const uint8_t *buf,
79 				 uint32_t sector, uint32_t count)
80 {
81 	const struct device *dev = disk->dev;
82 	struct sdmmc_data *data = dev->data;
83 
84 	return sdmmc_write_blocks(&data->card, buf, sector, count);
85 }
86 
disk_sdmmc_access_ioctl(struct disk_info * disk,uint8_t cmd,void * buf)87 static int disk_sdmmc_access_ioctl(struct disk_info *disk, uint8_t cmd, void *buf)
88 {
89 	const struct device *dev = disk->dev;
90 	struct sdmmc_data *data = dev->data;
91 
92 	switch (cmd) {
93 	case DISK_IOCTL_CTRL_INIT:
94 		return disk_sdmmc_access_init(disk);
95 	case DISK_IOCTL_CTRL_DEINIT:
96 		/* Card will be uninitialized after DEINIT */
97 		data->status = SD_UNINIT;
98 		return sdmmc_ioctl(&data->card, DISK_IOCTL_CTRL_DEINIT, NULL);
99 	default:
100 		return sdmmc_ioctl(&data->card, cmd, buf);
101 	}
102 
103 	return 0;
104 }
105 
106 static const struct disk_operations sdmmc_disk_ops = {
107 	.init = disk_sdmmc_access_init,
108 	.status = disk_sdmmc_access_status,
109 	.read = disk_sdmmc_access_read,
110 	.write = disk_sdmmc_access_write,
111 	.ioctl = disk_sdmmc_access_ioctl,
112 };
113 
disk_sdmmc_init(const struct device * dev)114 static int disk_sdmmc_init(const struct device *dev)
115 {
116 	struct sdmmc_data *data = dev->data;
117 
118 	data->status = SD_UNINIT;
119 
120 	return disk_access_register(data->disk_info);
121 }
122 
123 #define DISK_ACCESS_SDMMC_INIT(n)						\
124 	static const struct sdmmc_config sdmmc_config_##n = {			\
125 		.host_controller = DEVICE_DT_GET(DT_INST_PARENT(n)),		\
126 	};									\
127 										\
128 	static struct disk_info sdmmc_disk_##n = {                              \
129 		.name = DT_INST_PROP(n, disk_name),                             \
130 		.ops = &sdmmc_disk_ops,                                         \
131 		.dev = DEVICE_DT_INST_GET(n),                                   \
132 	};									\
133 										\
134 	static struct sdmmc_data sdmmc_data_##n = {				\
135 		.disk_info = &sdmmc_disk_##n,					\
136 	};									\
137 										\
138 	DEVICE_DT_INST_DEFINE(n,						\
139 			&disk_sdmmc_init,					\
140 			NULL,							\
141 			&sdmmc_data_##n,					\
142 			&sdmmc_config_##n,					\
143 			POST_KERNEL,						\
144 			CONFIG_SD_INIT_PRIORITY,				\
145 			NULL);
146 
147 DT_INST_FOREACH_STATUS_OKAY(DISK_ACCESS_SDMMC_INIT)
148