1 /*
2  * Copyright 2022 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /*
8  * MMC disk driver using zephyr SD subsystem
9  */
10 #define DT_DRV_COMPAT zephyr_mmc_disk
11 
12 #include <zephyr/sd/mmc.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 mmc_config {
23 	const struct device *host_controller;
24 	uint8_t bus_width;
25 };
26 
27 struct mmc_data {
28 	struct sd_card card;
29 	enum sd_status status;
30 	struct disk_info *disk_info;
31 };
32 
33 
disk_mmc_access_init(struct disk_info * disk)34 static int disk_mmc_access_init(struct disk_info *disk)
35 {
36 	const struct device *dev = disk->dev;
37 	const struct mmc_config *cfg = dev->config;
38 	struct mmc_data *data = dev->data;
39 	int ret;
40 
41 	ret = sd_init(cfg->host_controller, &data->card);
42 	if (ret) {
43 		data->status = SD_ERROR;
44 		return ret;
45 	}
46 	data->status = SD_OK;
47 	return 0;
48 }
49 
disk_mmc_access_status(struct disk_info * disk)50 static int disk_mmc_access_status(struct disk_info *disk)
51 {
52 	const struct device *dev = disk->dev;
53 	struct mmc_data *data = dev->data;
54 
55 	if (data->status == SD_OK) {
56 		return DISK_STATUS_OK;
57 	} else {
58 		return DISK_STATUS_UNINIT;
59 	}
60 }
61 
disk_mmc_access_read(struct disk_info * disk,uint8_t * buf,uint32_t sector,uint32_t count)62 static int disk_mmc_access_read(struct disk_info *disk, uint8_t *buf,
63 				 uint32_t sector, uint32_t count)
64 {
65 	const struct device *dev = disk->dev;
66 	struct mmc_data *data = dev->data;
67 
68 	return mmc_read_blocks(&data->card, buf, sector, count);
69 }
70 
disk_mmc_access_write(struct disk_info * disk,const uint8_t * buf,uint32_t sector,uint32_t count)71 static int disk_mmc_access_write(struct disk_info *disk, const uint8_t *buf,
72 				 uint32_t sector, uint32_t count)
73 {
74 	const struct device *dev = disk->dev;
75 	struct mmc_data *data = dev->data;
76 
77 	return mmc_write_blocks(&data->card, buf, sector, count);
78 }
79 
disk_mmc_access_ioctl(struct disk_info * disk,uint8_t cmd,void * buf)80 static int disk_mmc_access_ioctl(struct disk_info *disk, uint8_t cmd, void *buf)
81 {
82 	const struct device *dev = disk->dev;
83 	struct mmc_data *data = dev->data;
84 
85 	switch (cmd) {
86 	case DISK_IOCTL_CTRL_INIT:
87 		return disk_mmc_access_init(disk);
88 	case DISK_IOCTL_CTRL_DEINIT:
89 		mmc_ioctl(&data->card, DISK_IOCTL_CTRL_SYNC, NULL);
90 		/* sd_init() will toggle power to MMC, so we can just mark
91 		 * disk as uninitialized
92 		 */
93 		data->status = SD_UNINIT;
94 		return 0;
95 	default:
96 		return mmc_ioctl(&data->card, cmd, buf);
97 	}
98 
99 	return 0;
100 }
101 
102 static const struct disk_operations mmc_disk_ops = {
103 	.init = disk_mmc_access_init,
104 	.status = disk_mmc_access_status,
105 	.read = disk_mmc_access_read,
106 	.write = disk_mmc_access_write,
107 	.ioctl = disk_mmc_access_ioctl,
108 };
109 
disk_mmc_init(const struct device * dev)110 static int disk_mmc_init(const struct device *dev)
111 {
112 	struct mmc_data *data = dev->data;
113 	const struct mmc_config *config = dev->config;
114 
115 	data->status = SD_UNINIT;
116 	data->card.bus_width = config->bus_width;
117 	data->card.type = CARD_MMC;
118 
119 	return disk_access_register(data->disk_info);
120 }
121 
122 #define DISK_ACCESS_MMC_INIT(n)						\
123 	static const struct mmc_config mmc_config_##n = {			\
124 		.host_controller = DEVICE_DT_GET(DT_INST_PARENT(n)),		\
125 		.bus_width = DT_INST_PROP(n, bus_width),			\
126 	};									\
127 										\
128 	static struct disk_info mmc_disk_##n = {				\
129 		.name = DT_INST_PROP(n, disk_name),				\
130 		.ops = &mmc_disk_ops,						\
131 		.dev = DEVICE_DT_INST_GET(n),					\
132 	};									\
133 										\
134 	static struct mmc_data mmc_data_##n = {				\
135 		.disk_info = &mmc_disk_##n,					\
136 	};									\
137 										\
138 	DEVICE_DT_INST_DEFINE(n,						\
139 			&disk_mmc_init,					\
140 			NULL,							\
141 			&mmc_data_##n,					\
142 			&mmc_config_##n,					\
143 			POST_KERNEL,						\
144 			CONFIG_SD_INIT_PRIORITY,				\
145 			NULL);
146 
147 DT_INST_FOREACH_STATUS_OKAY(DISK_ACCESS_MMC_INIT)
148