summaryrefslogtreecommitdiffstats
path: root/sys/dev/mmc/mmcsd.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/mmc/mmcsd.c')
-rw-r--r--sys/dev/mmc/mmcsd.c210
1 files changed, 155 insertions, 55 deletions
diff --git a/sys/dev/mmc/mmcsd.c b/sys/dev/mmc/mmcsd.c
index d0ea6a8..44ba5d2 100644
--- a/sys/dev/mmc/mmcsd.c
+++ b/sys/dev/mmc/mmcsd.c
@@ -133,10 +133,11 @@ mmcsd_attach(device_t dev)
// d->d_dump = mmcsd_dump; Need polling mmc layer
d->d_name = "mmcsd";
d->d_drv1 = sc;
- d->d_maxsize = MAXPHYS; /* Maybe ask bridge? */
+ d->d_maxsize = 4*1024*1024; /* Maximum defined SD card AU size. */
d->d_sectorsize = mmc_get_sector_size(dev);
d->d_mediasize = mmc_get_media_size(dev) * d->d_sectorsize;
d->d_unit = device_get_unit(dev);
+ d->d_flags = DISKFLAG_CANDELETE;
/*
* Display in most natural units. There's no cards < 1MB.
* The SD standard goes to 2GiB, but the data format supports
@@ -216,6 +217,151 @@ mmcsd_strategy(struct bio *bp)
MMCSD_UNLOCK(sc);
}
+static daddr_t
+mmcsd_rw(struct mmcsd_softc *sc, struct bio *bp)
+{
+ daddr_t block, end;
+ struct mmc_command cmd;
+ struct mmc_command stop;
+ struct mmc_request req;
+ struct mmc_data data;
+ device_t dev = sc->dev;
+ int sz = sc->disk->d_sectorsize;
+
+ block = bp->bio_pblkno;
+ end = bp->bio_pblkno + (bp->bio_bcount / sz);
+ while (block < end) {
+ char *vaddr = bp->bio_data +
+ (block - bp->bio_pblkno) * sz;
+ int numblocks;
+#ifdef MULTI_BLOCK
+ numblocks = end - block;
+#else
+ numblocks = 1;
+#endif
+ memset(&req, 0, sizeof(req));
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&stop, 0, sizeof(stop));
+ req.cmd = &cmd;
+ cmd.data = &data;
+ if (bp->bio_cmd == BIO_READ) {
+ if (numblocks > 1)
+ cmd.opcode = MMC_READ_MULTIPLE_BLOCK;
+ else
+ cmd.opcode = MMC_READ_SINGLE_BLOCK;
+ } else {
+ if (numblocks > 1)
+ cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK;
+ else
+ cmd.opcode = MMC_WRITE_BLOCK;
+ }
+ cmd.arg = block;
+ if (!mmc_get_high_cap(dev))
+ cmd.arg <<= 9;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+ data.data = vaddr;
+ data.mrq = &req;
+ if (bp->bio_cmd == BIO_READ)
+ data.flags = MMC_DATA_READ;
+ else
+ data.flags = MMC_DATA_WRITE;
+ data.len = numblocks * sz;
+ if (numblocks > 1) {
+ data.flags |= MMC_DATA_MULTI;
+ stop.opcode = MMC_STOP_TRANSMISSION;
+ stop.arg = 0;
+ stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
+ req.stop = &stop;
+ }
+// printf("Len %d %lld-%lld flags %#x sz %d\n",
+// (int)data.len, (long long)block, (long long)end, data.flags, sz);
+ MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
+ &req);
+ if (req.cmd->error != MMC_ERR_NONE)
+ break;
+ block += numblocks;
+ }
+ return (block);
+}
+
+static daddr_t
+mmcsd_delete(struct mmcsd_softc *sc, struct bio *bp)
+{
+ daddr_t block, end, start, stop;
+ struct mmc_command cmd;
+ struct mmc_request req;
+ device_t dev = sc->dev;
+ int sz = sc->disk->d_sectorsize;
+ int erase_sector;
+
+ block = bp->bio_pblkno;
+ end = bp->bio_pblkno + (bp->bio_bcount / sz);
+
+ /* Safe round to the erase sector boundaries. */
+ erase_sector = mmc_get_erase_sector(dev);
+ start = block + erase_sector - 1; /* Round up. */
+ start -= start % erase_sector;
+ stop = end; /* Round down. */
+ stop -= end % erase_sector;
+
+ /* We can't erase areas smaller then sector. */
+ if (start >= stop)
+ return (end);
+
+ /* Set erase start position. */
+ memset(&req, 0, sizeof(req));
+ memset(&cmd, 0, sizeof(cmd));
+ req.cmd = &cmd;
+ if (mmc_get_card_type(dev) == mode_sd)
+ cmd.opcode = SD_ERASE_WR_BLK_START;
+ else
+ cmd.opcode = MMC_ERASE_GROUP_START;
+ cmd.arg = start;
+ if (!mmc_get_high_cap(dev))
+ cmd.arg <<= 9;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
+ &req);
+ if (req.cmd->error != MMC_ERR_NONE) {
+ printf("erase err1: %d\n", req.cmd->error);
+ return (block);
+ }
+ /* Set erase stop position. */
+ memset(&req, 0, sizeof(req));
+ memset(&cmd, 0, sizeof(cmd));
+ req.cmd = &cmd;
+ if (mmc_get_card_type(dev) == mode_sd)
+ cmd.opcode = SD_ERASE_WR_BLK_END;
+ else
+ cmd.opcode = MMC_ERASE_GROUP_END;
+ cmd.arg = stop;
+ if (!mmc_get_high_cap(dev))
+ cmd.arg <<= 9;
+ cmd.arg--;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
+ &req);
+ if (req.cmd->error != MMC_ERR_NONE) {
+ printf("erase err2: %d\n", req.cmd->error);
+ return (block);
+ }
+ /* Erase range. */
+ memset(&req, 0, sizeof(req));
+ memset(&cmd, 0, sizeof(cmd));
+ req.cmd = &cmd;
+ cmd.opcode = MMC_ERASE;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+ MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
+ &req);
+ if (req.cmd->error != MMC_ERR_NONE) {
+ printf("erase err3 %d\n", req.cmd->error);
+ return (block);
+ }
+
+ return (end);
+}
+
static void
mmcsd_task(void *arg)
{
@@ -223,10 +369,6 @@ mmcsd_task(void *arg)
struct bio *bp;
int sz;
daddr_t block, end;
- struct mmc_command cmd;
- struct mmc_command stop;
- struct mmc_request req;
- struct mmc_data data;
device_t dev;
dev = sc->dev;
@@ -242,7 +384,6 @@ mmcsd_task(void *arg)
MMCSD_UNLOCK(sc);
if (!sc->running)
break;
-// printf("mmc_task: request %p for block %ju\n", bp, bp->bio_pblkno);
if (bp->bio_cmd != BIO_READ && mmc_get_read_only(dev)) {
bp->bio_error = EROFS;
bp->bio_resid = bp->bio_bcount;
@@ -252,56 +393,15 @@ mmcsd_task(void *arg)
}
MMCBUS_ACQUIRE_BUS(device_get_parent(dev), dev);
sz = sc->disk->d_sectorsize;
+ block = bp->bio_pblkno;
end = bp->bio_pblkno + (bp->bio_bcount / sz);
- for (block = bp->bio_pblkno; block < end;) {
- char *vaddr = bp->bio_data + (block - bp->bio_pblkno) * sz;
- int numblocks;
-#ifdef MULTI_BLOCK
- numblocks = end - block;
-#else
- numblocks = 1;
-#endif
- memset(&req, 0, sizeof(req));
- memset(&cmd, 0, sizeof(cmd));
- memset(&stop, 0, sizeof(stop));
- req.cmd = &cmd;
- cmd.data = &data;
- if (bp->bio_cmd == BIO_READ) {
- if (numblocks > 1)
- cmd.opcode = MMC_READ_MULTIPLE_BLOCK;
- else
- cmd.opcode = MMC_READ_SINGLE_BLOCK;
- } else {
- if (numblocks > 1)
- cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK;
- else
- cmd.opcode = MMC_WRITE_BLOCK;
- }
- cmd.arg = block;
- if (!mmc_get_high_cap(dev))
- cmd.arg <<= 9;
- cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
- data.data = vaddr;
- data.mrq = &req;
- if (bp->bio_cmd == BIO_READ)
- data.flags = MMC_DATA_READ;
- else
- data.flags = MMC_DATA_WRITE;
- data.len = numblocks * sz;
- if (numblocks > 1) {
- data.flags |= MMC_DATA_MULTI;
- stop.opcode = MMC_STOP_TRANSMISSION;
- stop.arg = 0;
- stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
- req.stop = &stop;
- }
-// printf("Len %d %lld-%lld flags %#x sz %d\n",
-// (int)data.len, (long long)block, (long long)end, data.flags, sz);
- MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
- &req);
- if (req.cmd->error != MMC_ERR_NONE)
- break;
- block += numblocks;
+ if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) {
+ block = mmcsd_rw(sc, bp);
+ } else if (bp->bio_cmd == BIO_DELETE) {
+ block = mmcsd_delete(sc, bp);
+ } else {
+ /* UNSUPPORTED COMMAND */
+ block = bp->bio_pblkno;
}
MMCBUS_RELEASE_BUS(device_get_parent(dev), dev);
if (block < end) {
OpenPOWER on IntegriCloud