11.1Sjmcneill/* $NetBSD: sdmmc.c,v 1.1 2025/11/16 20:11:47 jmcneill Exp $ */ 21.1Sjmcneill 31.1Sjmcneill/*- 41.1Sjmcneill * Copyright (c) 2025 Jared McNeill <jmcneill@invisible.ca> 51.1Sjmcneill * All rights reserved. 61.1Sjmcneill * 71.1Sjmcneill * Redistribution and use in source and binary forms, with or without 81.1Sjmcneill * modification, are permitted provided that the following conditions 91.1Sjmcneill * are met: 101.1Sjmcneill * 1. Redistributions of source code must retain the above copyright 111.1Sjmcneill * notice, this list of conditions and the following disclaimer. 121.1Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright 131.1Sjmcneill * notice, this list of conditions and the following disclaimer in the 141.1Sjmcneill * documentation and/or other materials provided with the distribution. 151.1Sjmcneill * 161.1Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 171.1Sjmcneill * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 181.1Sjmcneill * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 191.1Sjmcneill * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 201.1Sjmcneill * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 211.1Sjmcneill * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 221.1Sjmcneill * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 231.1Sjmcneill * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 241.1Sjmcneill * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 251.1Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 261.1Sjmcneill * SUCH DAMAGE. 271.1Sjmcneill */ 281.1Sjmcneill 291.1Sjmcneill#include <lib/libsa/stand.h> 301.1Sjmcneill#include <lib/libkern/libkern.h> 311.1Sjmcneill 321.1Sjmcneill#include <sys/bootblock.h> 331.1Sjmcneill 341.1Sjmcneill#define FSTYPENAMES 351.1Sjmcneill#include <sys/disklabel.h> 361.1Sjmcneill 371.1Sjmcneill#include "sdmmc.h" 381.1Sjmcneill#include "miniipc.h" 391.1Sjmcneill#include "gpio.h" 401.1Sjmcneill 411.1Sjmcneill#if LABELSECTOR != 1 421.1Sjmcneill#error bad LABELSECTOR 431.1Sjmcneill#endif 441.1Sjmcneill 451.1Sjmcneill#define DEFAULT_DEVICE "ld0" 461.1Sjmcneill#define DEFAULT_PART 0 471.1Sjmcneill 481.1Sjmcneill#define SDMMC_STATE_NEW_CARD 2 491.1Sjmcneill 501.1Sjmcneillstatic uint8_t sdmmc_buf[SDMMC_BLOCK_SIZE]; 511.1Sjmcneillstatic struct disklabel sdmmc_disklabel; 521.1Sjmcneill 531.1Sjmcneillstatic void 541.1Sjmcneillsdmmc_print_label(void) 551.1Sjmcneill{ 561.1Sjmcneill#ifdef SDMMC_DEBUG 571.1Sjmcneill struct disklabel *d = &sdmmc_disklabel; 581.1Sjmcneill int part; 591.1Sjmcneill 601.1Sjmcneill for (part = 0; part < le16toh(d->d_npartitions); part++) { 611.1Sjmcneill struct partition *p = &d->d_partitions[part]; 621.1Sjmcneill 631.1Sjmcneill printf("partition %s%c ", DEFAULT_DEVICE, part + 'a'); 641.1Sjmcneill 651.1Sjmcneill if (p->p_fstype < __arraycount(fstypenames)) { 661.1Sjmcneill printf("(%s)", fstypenames[p->p_fstype]); 671.1Sjmcneill } else { 681.1Sjmcneill printf("(type %u)", p->p_fstype); 691.1Sjmcneill } 701.1Sjmcneill 711.1Sjmcneill printf(" - %u %u\n", le32toh(p->p_offset), le32toh(p->p_size)); 721.1Sjmcneill } 731.1Sjmcneill#endif 741.1Sjmcneill} 751.1Sjmcneill 761.1Sjmcneillstatic int 771.1Sjmcneillsdmmc_read_label(daddr_t dblk, size_t size) 781.1Sjmcneill{ 791.1Sjmcneill struct disklabel d; 801.1Sjmcneill int error; 811.1Sjmcneill 821.1Sjmcneill error = miniipc_sdmmc_read(dblk + LABELSECTOR, 1, sdmmc_buf); 831.1Sjmcneill if (error != 0) { 841.1Sjmcneill printf("SDMMC: failed to read disklabel: %d\n", error); 851.1Sjmcneill return error; 861.1Sjmcneill } 871.1Sjmcneill memcpy(&d, sdmmc_buf, sizeof(d)); 881.1Sjmcneill 891.1Sjmcneill if (le32toh(d.d_magic) != DISKMAGIC || 901.1Sjmcneill le32toh(d.d_magic2) != DISKMAGIC) { 911.1Sjmcneill#ifdef SDMMC_DEBUG 921.1Sjmcneill printf("SDMMC: bad diskmagic 0x%x 0x%x\n", le32toh(d.d_magic), 931.1Sjmcneill le32toh(d.d_magic2)); 941.1Sjmcneill#endif 951.1Sjmcneill return EINVAL; 961.1Sjmcneill } 971.1Sjmcneill if (le16toh(d.d_npartitions) > MAXPARTITIONS) { 981.1Sjmcneill printf("SDMMC: bad npartitions %u\n", le16toh(d.d_npartitions)); 991.1Sjmcneill return EINVAL; 1001.1Sjmcneill } 1011.1Sjmcneill 1021.1Sjmcneill memcpy(&sdmmc_disklabel, sdmmc_buf, sizeof(sdmmc_disklabel)); 1031.1Sjmcneill return 0; 1041.1Sjmcneill} 1051.1Sjmcneill 1061.1Sjmcneillint 1071.1Sjmcneillsdmmc_init(void) 1081.1Sjmcneill{ 1091.1Sjmcneill struct mbr_sector mbr; 1101.1Sjmcneill struct mbr_partition *mbr_part; 1111.1Sjmcneill int error, n; 1121.1Sjmcneill bool has_label = false; 1131.1Sjmcneill uint32_t state, ack; 1141.1Sjmcneill 1151.1Sjmcneill miniipc_sdmmc_state(&state); 1161.1Sjmcneill if (state == SDMMC_STATE_NEW_CARD) { 1171.1Sjmcneill miniipc_sdmmc_ack(&ack); 1181.1Sjmcneill } 1191.1Sjmcneill 1201.1Sjmcneill error = miniipc_sdmmc_read(0, 1, sdmmc_buf); 1211.1Sjmcneill if (error != 0) { 1221.1Sjmcneill printf("SDMMC: failed to read MBR: %d\n", error); 1231.1Sjmcneill return error; 1241.1Sjmcneill } 1251.1Sjmcneill memcpy(&mbr, sdmmc_buf, sizeof(mbr)); 1261.1Sjmcneill if (le16toh(mbr.mbr_magic) != MBR_MAGIC) { 1271.1Sjmcneill printf("SDMMC: bad MBR magic: 0x%x\n", le32toh(mbr.mbr_magic)); 1281.1Sjmcneill return ENXIO; 1291.1Sjmcneill } 1301.1Sjmcneill 1311.1Sjmcneill for (n = 0; n < MBR_PART_COUNT; n++) { 1321.1Sjmcneill uint32_t start, size; 1331.1Sjmcneill 1341.1Sjmcneill mbr_part = &mbr.mbr_parts[n]; 1351.1Sjmcneill size = le32toh(mbr_part->mbrp_size); 1361.1Sjmcneill if (le32toh(mbr_part->mbrp_size) == 0) { 1371.1Sjmcneill continue; 1381.1Sjmcneill } 1391.1Sjmcneill start = le32toh(mbr_part->mbrp_start); 1401.1Sjmcneill#ifdef SDMMC_DEBUG 1411.1Sjmcneill printf("MBR part %u type 0x%x start %u size %u\n", 1421.1Sjmcneill n, mbr_part->mbrp_type, start, size); 1431.1Sjmcneill#endif 1441.1Sjmcneill 1451.1Sjmcneill if (mbr_part->mbrp_type == MBR_PTYPE_NETBSD && !has_label) { 1461.1Sjmcneill error = sdmmc_read_label(start, size); 1471.1Sjmcneill if (error != 0) { 1481.1Sjmcneill struct partition *p = 1491.1Sjmcneill &sdmmc_disklabel.d_partitions[0]; 1501.1Sjmcneill 1511.1Sjmcneill /* No label on disk, fake one. */ 1521.1Sjmcneill p->p_fstype = FS_BSDFFS; 1531.1Sjmcneill p->p_size = htole32(size); 1541.1Sjmcneill p->p_offset = htole32(start); 1551.1Sjmcneill sdmmc_disklabel.d_npartitions = htole16(1); 1561.1Sjmcneill } 1571.1Sjmcneill has_label = true; 1581.1Sjmcneill } 1591.1Sjmcneill } 1601.1Sjmcneill 1611.1Sjmcneill sdmmc_print_label(); 1621.1Sjmcneill 1631.1Sjmcneill return 0; 1641.1Sjmcneill} 1651.1Sjmcneill 1661.1Sjmcneillstatic int 1671.1Sjmcneillsdmmc_parse(const char *fname, int *part, char **pfile) 1681.1Sjmcneill{ 1691.1Sjmcneill const char *full_path; 1701.1Sjmcneill char pathbuf[PATH_MAX]; 1711.1Sjmcneill 1721.1Sjmcneill if (strchr(fname, ':') == NULL) { 1731.1Sjmcneill snprintf(pathbuf, sizeof(pathbuf), "%s%c:%s", 1741.1Sjmcneill DEFAULT_DEVICE, DEFAULT_PART + 'a', fname); 1751.1Sjmcneill full_path = pathbuf; 1761.1Sjmcneill *pfile = __UNCONST(fname); 1771.1Sjmcneill } else { 1781.1Sjmcneill full_path = fname; 1791.1Sjmcneill *pfile = strchr(fname, ':') + 1; 1801.1Sjmcneill } 1811.1Sjmcneill if (*pfile[0] == '\0') { 1821.1Sjmcneill *pfile = __UNCONST("/"); 1831.1Sjmcneill } 1841.1Sjmcneill 1851.1Sjmcneill if (strncmp(full_path, DEFAULT_DEVICE, 3) != 0) { 1861.1Sjmcneill return EINVAL; 1871.1Sjmcneill } 1881.1Sjmcneill if (full_path[3] < 'a' || full_path[3] >= 'a' + MAXPARTITIONS || 1891.1Sjmcneill full_path[4] != ':') { 1901.1Sjmcneill return EINVAL; 1911.1Sjmcneill } 1921.1Sjmcneill *part = full_path[3] - 'a'; 1931.1Sjmcneill 1941.1Sjmcneill return 0; 1951.1Sjmcneill} 1961.1Sjmcneill 1971.1Sjmcneillint 1981.1Sjmcneillsdmmc_open(struct open_file *f, ...) 1991.1Sjmcneill{ 2001.1Sjmcneill int error, n, part; 2011.1Sjmcneill const char *fname; 2021.1Sjmcneill va_list ap; 2031.1Sjmcneill char **file; 2041.1Sjmcneill char *path; 2051.1Sjmcneill 2061.1Sjmcneill va_start(ap, f); 2071.1Sjmcneill fname = va_arg(ap, const char *); 2081.1Sjmcneill file = va_arg(ap, char **); 2091.1Sjmcneill va_end(ap); 2101.1Sjmcneill 2111.1Sjmcneill error = sdmmc_parse(fname, &part, &path); 2121.1Sjmcneill if (error != 0) { 2131.1Sjmcneill return error; 2141.1Sjmcneill } 2151.1Sjmcneill 2161.1Sjmcneill for (n = 0; n < ndevs; n++) { 2171.1Sjmcneill if (strcmp(DEV_NAME(&devsw[n]), "sdmmc") == 0) { 2181.1Sjmcneill f->f_dev = &devsw[n]; 2191.1Sjmcneill break; 2201.1Sjmcneill } 2211.1Sjmcneill } 2221.1Sjmcneill if (n == ndevs) { 2231.1Sjmcneill return ENXIO; 2241.1Sjmcneill } 2251.1Sjmcneill 2261.1Sjmcneill f->f_devdata = &sdmmc_disklabel.d_partitions[part]; 2271.1Sjmcneill *file = path; 2281.1Sjmcneill 2291.1Sjmcneill return 0; 2301.1Sjmcneill} 2311.1Sjmcneill 2321.1Sjmcneillint 2331.1Sjmcneillsdmmc_close(struct open_file *f) 2341.1Sjmcneill{ 2351.1Sjmcneill return 0; 2361.1Sjmcneill} 2371.1Sjmcneill 2381.1Sjmcneillint 2391.1Sjmcneillsdmmc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, void *buf, 2401.1Sjmcneill size_t *rsize) 2411.1Sjmcneill{ 2421.1Sjmcneill struct partition *p = devdata; 2431.1Sjmcneill int error; 2441.1Sjmcneill 2451.1Sjmcneill if (rw != F_READ) { 2461.1Sjmcneill return EROFS; 2471.1Sjmcneill } 2481.1Sjmcneill if ((size % SDMMC_BLOCK_SIZE) != 0) { 2491.1Sjmcneill printf("I/O must be multiple of SDMMC_BLOCK_SIZE\n"); 2501.1Sjmcneill return EIO; 2511.1Sjmcneill } 2521.1Sjmcneill 2531.1Sjmcneill gpio_clear(GPIO_SLOT_LED); 2541.1Sjmcneill error = miniipc_sdmmc_read(le32toh(p->p_offset) + dblk, 2551.1Sjmcneill size / SDMMC_BLOCK_SIZE, buf); 2561.1Sjmcneill gpio_set(GPIO_SLOT_LED); 2571.1Sjmcneill if (error == 0) { 2581.1Sjmcneill *rsize = size; 2591.1Sjmcneill } 2601.1Sjmcneill 2611.1Sjmcneill return error; 2621.1Sjmcneill} 263