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
118 return disk_access_register(data->disk_info);
119 }
120
121 #define DISK_ACCESS_MMC_INIT(n) \
122 static const struct mmc_config mmc_config_##n = { \
123 .host_controller = DEVICE_DT_GET(DT_INST_PARENT(n)), \
124 .bus_width = DT_INST_PROP(n, bus_width), \
125 }; \
126 \
127 static struct disk_info mmc_disk_##n = { \
128 .name = DT_INST_PROP(n, disk_name), \
129 .ops = &mmc_disk_ops, \
130 .dev = DEVICE_DT_INST_GET(n), \
131 }; \
132 \
133 static struct mmc_data mmc_data_##n = { \
134 .disk_info = &mmc_disk_##n, \
135 }; \
136 \
137 DEVICE_DT_INST_DEFINE(n, \
138 &disk_mmc_init, \
139 NULL, \
140 &mmc_data_##n, \
141 &mmc_config_##n, \
142 POST_KERNEL, \
143 CONFIG_SD_INIT_PRIORITY, \
144 NULL);
145
146 DT_INST_FOREACH_STATUS_OKAY(DISK_ACCESS_MMC_INIT)
147