| /* |
| * Copyright 2022 NXP |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /* |
| * SDMMC disk driver using zephyr SD subsystem |
| */ |
| #define DT_DRV_COMPAT zephyr_sdmmc_disk |
| |
| #include <zephyr/sd/sdmmc.h> |
| #include <zephyr/drivers/disk.h> |
| |
| |
| enum sd_status { |
| SD_UNINIT, |
| SD_ERROR, |
| SD_OK, |
| }; |
| |
| struct sdmmc_config { |
| const struct device *host_controller; |
| }; |
| |
| struct sdmmc_data { |
| struct sd_card card; |
| enum sd_status status; |
| char *name; |
| }; |
| |
| |
| static int disk_sdmmc_access_init(struct disk_info *disk) |
| { |
| const struct device *dev = disk->dev; |
| const struct sdmmc_config *cfg = dev->config; |
| struct sdmmc_data *data = dev->data; |
| int ret; |
| |
| if (!sd_is_card_present(cfg->host_controller)) { |
| return DISK_STATUS_NOMEDIA; |
| } |
| |
| ret = sd_init(cfg->host_controller, &data->card); |
| if (ret) { |
| data->status = SD_ERROR; |
| return ret; |
| } |
| data->status = SD_OK; |
| return 0; |
| } |
| |
| static int disk_sdmmc_access_status(struct disk_info *disk) |
| { |
| const struct device *dev = disk->dev; |
| const struct sdmmc_config *cfg = dev->config; |
| struct sdmmc_data *data = dev->data; |
| |
| if (!sd_is_card_present(cfg->host_controller)) { |
| return DISK_STATUS_NOMEDIA; |
| } |
| if (data->status == SD_OK) { |
| return DISK_STATUS_OK; |
| } else { |
| return DISK_STATUS_UNINIT; |
| } |
| } |
| |
| static int disk_sdmmc_access_read(struct disk_info *disk, uint8_t *buf, |
| uint32_t sector, uint32_t count) |
| { |
| const struct device *dev = disk->dev; |
| struct sdmmc_data *data = dev->data; |
| |
| return sdmmc_read_blocks(&data->card, buf, sector, count); |
| } |
| |
| static int disk_sdmmc_access_write(struct disk_info *disk, const uint8_t *buf, |
| uint32_t sector, uint32_t count) |
| { |
| const struct device *dev = disk->dev; |
| struct sdmmc_data *data = dev->data; |
| |
| return sdmmc_write_blocks(&data->card, buf, sector, count); |
| } |
| |
| static int disk_sdmmc_access_ioctl(struct disk_info *disk, uint8_t cmd, void *buf) |
| { |
| const struct device *dev = disk->dev; |
| struct sdmmc_data *data = dev->data; |
| |
| switch (cmd) { |
| case DISK_IOCTL_CTRL_INIT: |
| return disk_sdmmc_access_init(disk); |
| case DISK_IOCTL_CTRL_DEINIT: |
| sdmmc_ioctl(&data->card, DISK_IOCTL_CTRL_SYNC, NULL); |
| /* sd_init() will toggle power to SDMMC, so we can just mark |
| * disk as uninitialized |
| */ |
| data->status = SD_UNINIT; |
| return 0; |
| default: |
| return sdmmc_ioctl(&data->card, cmd, buf); |
| } |
| |
| return 0; |
| } |
| |
| static const struct disk_operations sdmmc_disk_ops = { |
| .init = disk_sdmmc_access_init, |
| .status = disk_sdmmc_access_status, |
| .read = disk_sdmmc_access_read, |
| .write = disk_sdmmc_access_write, |
| .ioctl = disk_sdmmc_access_ioctl, |
| }; |
| |
| static struct disk_info sdmmc_disk = { |
| .ops = &sdmmc_disk_ops, |
| }; |
| |
| static int disk_sdmmc_init(const struct device *dev) |
| { |
| struct sdmmc_data *data = dev->data; |
| |
| data->status = SD_UNINIT; |
| sdmmc_disk.dev = dev; |
| sdmmc_disk.name = data->name; |
| |
| return disk_access_register(&sdmmc_disk); |
| } |
| |
| #define DISK_ACCESS_SDMMC_INIT(n) \ |
| static const struct sdmmc_config sdmmc_config_##n = { \ |
| .host_controller = DEVICE_DT_GET(DT_INST_PARENT(n)), \ |
| }; \ |
| \ |
| static struct sdmmc_data sdmmc_data_##n = { \ |
| .name = CONFIG_SDMMC_VOLUME_NAME, \ |
| }; \ |
| \ |
| DEVICE_DT_INST_DEFINE(n, \ |
| &disk_sdmmc_init, \ |
| NULL, \ |
| &sdmmc_data_##n, \ |
| &sdmmc_config_##n, \ |
| POST_KERNEL, \ |
| CONFIG_SD_INIT_PRIORITY, \ |
| NULL); |
| |
| DT_INST_FOREACH_STATUS_OKAY(DISK_ACCESS_SDMMC_INIT) |