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