Hi, On Fri, May 21, 2010 at 04:47:40PM +0300, Adrian Hunter wrote: > >From f3baf566eb33a22bf12a48e4cdc7c99611bde934 Mon Sep 17 00:00:00 2001 > From: Adrian Hunter <adrian.hunter@xxxxxxxxx> > Date: Wed, 5 May 2010 14:07:55 +0300 > Subject: [PATCH] mmc: add an ioctl for erasing > > As SD and MMC cards have a NAND core, they can support an erase > operation that is typically 10x to 100x faster than writing. > > In addition, eMMCv4.4 also offers: > o Secure Erase > o Trim > o Secure Trim As already noted by others, getting SD/MMC card trim properly implemented to support block discard requests would be a good thing and I'm willing to help with getting that implemented. Thanks! -- Regards, George > > The "secure" variants also ensure that any copies of the data > (for example remnants of garbage collection) are also erased. > > "Trim" is the same as "erase" except that individual sectors > can be erased instead of whole Erase Groups. > > The Erase Operation and its variants are not supported by > default and drivers must set MMC_CAP_ERASE. This is because > the operation can take a long time and drivers that rely on > polling the status may perform very badly. Also drivers > may need changes to support the very long erase timeouts. > > Signed-off-by: Adrian Hunter <adrian.hunter@xxxxxxxxx> > --- > Documentation/ioctl/ioctl-number.txt | 1 + > drivers/mmc/card/block.c | 159 +++++++++++++++++ > drivers/mmc/core/core.c | 323 ++++++++++++++++++++++++++++++++++ > drivers/mmc/core/core.h | 2 + > drivers/mmc/core/mmc.c | 43 +++++- > drivers/mmc/core/sd.c | 80 +++++++++ > drivers/mmc/core/sd_ops.c | 48 +++++ > drivers/mmc/core/sd_ops.h | 1 + > include/linux/Kbuild | 1 + > include/linux/mmc/Kbuild | 1 + > include/linux/mmc/card.h | 19 ++ > include/linux/mmc/core.h | 17 ++ > include/linux/mmc/host.h | 1 + > include/linux/mmc/mmc.h | 24 ++- > include/linux/mmc/mmc_ioctl.h | 63 +++++++ > include/linux/mmc/sd.h | 5 + > 16 files changed, 781 insertions(+), 7 deletions(-) > create mode 100644 include/linux/mmc/Kbuild > create mode 100644 include/linux/mmc/mmc_ioctl.h > > diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt > index dd5806f..10ae568 100644 > --- a/Documentation/ioctl/ioctl-number.txt > +++ b/Documentation/ioctl/ioctl-number.txt > @@ -308,6 +308,7 @@ Code Seq#(hex) Include File Comments > 0xB0 all RATIO devices in development: > <mailto:vgo@xxxxxxxx> > 0xB1 00-1F PPPoX <mailto:mostrows@xxxxxxxxxxxxxxxxx> > +0xB3 00-1F linux/mmc/mmc_ioctl.h > 0xC0 00-0F linux/usb/iowarrior.h > 0xCB 00-1F CBM serial IEC bus in development: > <mailto:michael.klein@xxxxxxxxxxxxxxxxxxxx> > diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c > index cb9fbc8..6a386af 100644 > --- a/drivers/mmc/card/block.c > +++ b/drivers/mmc/card/block.c > @@ -31,11 +31,13 @@ > #include <linux/mutex.h> > #include <linux/scatterlist.h> > #include <linux/string_helpers.h> > +#include <linux/delay.h> > > #include <linux/mmc/card.h> > #include <linux/mmc/host.h> > #include <linux/mmc/mmc.h> > #include <linux/mmc/sd.h> > +#include <linux/mmc/mmc_ioctl.h> > > #include <asm/system.h> > #include <asm/uaccess.h> > @@ -138,9 +140,166 @@ mmc_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo) > return 0; > } > > +static int check_eod(struct block_device *bdev, unsigned int from, > + unsigned int nr) > +{ > + unsigned int maxsector; > + > + if (!nr) > + return 0; > + > + maxsector = bdev->bd_inode->i_size >> 9; > + if (maxsector && (maxsector < nr || maxsector - nr < from)) > + return 1; > + > + return 0; > +} > + > +static int mmc_blk_erase(struct mmc_blk_data *md, unsigned int from, > + unsigned int nr, unsigned int flag) > +{ > + struct mmc_card *card = md->queue.card; > + struct request_queue *q = md->queue.queue; > + unsigned int n, arg; > + int i, ok, err; > + > + switch (flag) { > + case MMC_ERASE_FLAG: > + arg = MMC_ERASE_ARG; > + break; > + case MMC_SECURE_ERASE_FLAG: > + arg = MMC_SECURE_ERASE_ARG; > + break; > + case MMC_TRIM_FLAG: > + arg = MMC_TRIM_ARG; > + break; > + case MMC_SECURE_TRIM1_FLAG: > + arg = MMC_SECURE_TRIM1_ARG; > + break; > + case MMC_SECURE_TRIM2_FLAG: > + arg = MMC_SECURE_TRIM2_ARG; > + break; > + default: > + return -EINVAL; > + } > + > + mmc_claim_host(card->host); > + n = card->max_erase - (from % card->max_erase); > + do { > + /* > + * Do not allow the MMCERASE ioctl to have priority over > + * scheduled I/O. > + */ > + for (i = 0; i < 40; i++) { > + spin_lock_irq(q->queue_lock); > + ok = elv_queue_empty(q); > + spin_unlock_irq(q->queue_lock); > + if (ok) > + break; > + mmc_release_host(card->host); > + msleep(50); > + mmc_claim_host(card->host); > + } > + if (n > nr) > + n = nr; > + err = mmc_erase(card, from, n, arg); > + if (err) > + break; > + from += n; > + nr -= n; > + n = card->max_erase; > + } while (nr); > + mmc_release_host(card->host); > + > + return err; > +} > + > +static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode, > + unsigned cmd, unsigned long arg) > +{ > + struct mmc_blk_data *md = bdev->bd_disk->private_data; > + > + switch (cmd) { > + case MMCERASEINFO: { > + struct mmc_blk_erase_info info; > + struct mmc_card *card = md->queue.card; > + unsigned int flags = 0; > + > + if (!arg) > + return -EINVAL; > + > + mmc_claim_host(card->host); > + info.erase_size = card->erase_size; > + if (mmc_can_erase(card)) { > + flags |= MMC_ERASE_FLAG; > + if (mmc_can_secure_erase_trim(card)) > + flags |= MMC_SECURE_ERASE_FLAG; > + if (mmc_can_trim(card)) { > + flags |= MMC_TRIM_FLAG; > + if (mmc_can_secure_erase_trim(card)) { > + flags |= MMC_SECURE_TRIM1_FLAG; > + flags |= MMC_SECURE_TRIM2_FLAG; > + } > + } > + } > + info.flags = flags; > + info.erased_byte = card->erased_byte; > + mmc_release_host(card->host); > + > + if (copy_to_user((struct mmc_blk_erase_info __user *)arg, &info, > + sizeof(struct mmc_blk_erase_info))) > + return -EFAULT; > + > + return 0; > + } > + case MMCERASE: { > + struct mmc_blk_erase_args args; > + > + if (!(mode & FMODE_WRITE)) > + return -EBADF; > + > + if (copy_from_user(&args, (void __user *)arg, > + sizeof(struct mmc_blk_erase_args))) > + return -EFAULT; > + > + if (check_eod(bdev, args.from, args.nr)) > + return -EINVAL; > + > + if (bdev != bdev->bd_contains) > + args.from += bdev->bd_part->start_sect; > + > + return mmc_blk_erase(md, args.from, args.nr, args.flag); > + } > + } > + return -ENOTTY; > +} > + > +#ifdef CONFIG_COMPAT > + > +static int mmc_blk_compat_ioctl(struct block_device *bdev, fmode_t mode, > + unsigned cmd, unsigned long arg) > +{ > + switch (cmd) { > + case MMCERASEINFO: > + case MMCERASE: > + break; > + default: > + return -ENOIOCTLCMD; > + } > + return mmc_blk_ioctl(bdev, mode, cmd, arg); > +} > + > +#else > + > +#define mmc_blk_compat_ioctl NULL > + > +#endif > + > static const struct block_device_operations mmc_bdops = { > .open = mmc_blk_open, > .release = mmc_blk_release, > + .ioctl = mmc_blk_ioctl, > + .compat_ioctl = mmc_blk_compat_ioctl, > .getgeo = mmc_blk_getgeo, > .owner = THIS_MODULE, > }; > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c > index 3168ebd..f32cc2d 100644 > --- a/drivers/mmc/core/core.c > +++ b/drivers/mmc/core/core.c > @@ -1050,6 +1050,329 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay) > > EXPORT_SYMBOL(mmc_detect_change); > > +void mmc_init_erase(struct mmc_card *card) > +{ > + unsigned int sz; > + > + if (is_power_of_2(card->erase_size) && card->erase_size > 512) > + card->erase_shift = ffs(card->erase_size) - 10; > + else > + card->erase_shift = 0; > + > + /* > + * It is possible to erase an arbitrarily large area of an SD or MMC > + * card. That is not desirable because it can take a long time > + * (minutes) potentially delaying more important I/O, and also the > + * timeout calculations become increasingly hugely over-estimated. > + * Consequently, 'max_erase' is defined as a guide to upper layers > + * (i.e. the MMC Block driver) to limit erases to that size and > + * alignment. > + * > + * For SD cards that define Allocation Unit size, limit erases to one > + * Allocation Unit at a time. For MMC cards that define High Capacity > + * Erase Size, whether it is switched on or not, limit to that size. > + * Otherwise just have a stab at a good value. For modern cards it > + * will end up being 4MiB. Note that if the value is too small, it > + * can end up taking longer to erase. > + */ > + if (mmc_card_sd(card) && card->ssr.au) { > + card->max_erase = card->ssr.au; > + card->erase_shift = ffs(card->ssr.au) - 1; > + } else if (card->ext_csd.hc_erase_size) { > + card->max_erase = card->ext_csd.hc_erase_size >> 9; > + } else { > + sz = (card->csd.capacity << (card->csd.read_blkbits - 9)) >> 11; > + if (sz < 128) > + card->max_erase = 512 * 1024; > + else if (sz < 512) > + card->max_erase = 1024 * 1024; > + else if (sz < 1024) > + card->max_erase = 2 * 1024 * 1024; > + else > + card->max_erase = 4 * 1024 * 1024; > + if (card->max_erase < card->erase_size) > + card->max_erase = card->erase_size; > + else { > + sz = card->max_erase % card->erase_size; > + if (sz) > + card->max_erase += card->erase_size - sz; > + } > + card->max_erase >>= 9; > + } > +} > + > +static void mmc_set_mmc_erase_timeout(struct mmc_card *card, > + struct mmc_command *cmd, > + unsigned int arg, unsigned int qty) > +{ > + unsigned int erase_timeout; > + > + if (card->ext_csd.erase_group_def & 1) { > + /* High Capacity Erase Group Size uses HC timeouts */ > + if (arg == MMC_TRIM_ARG) > + erase_timeout = card->ext_csd.trim_timeout; > + else > + erase_timeout = card->ext_csd.hc_erase_timeout; > + } else { > + /* CSD Erase Group Size uses write timeout */ > + unsigned int mult = (10 << card->csd.r2w_factor); > + unsigned int timeout_clks = card->csd.tacc_clks * mult; > + unsigned int timeout_us; > + > + /* Avoid overflow: e.g. tacc_ns=80000000 mult=1280 */ > + if (card->csd.tacc_ns < 1000000) > + timeout_us = (card->csd.tacc_ns * mult) / 1000; > + else > + timeout_us = (card->csd.tacc_ns / 1000) * mult; > + > + /* > + * ios.clock is only a target. The real clock rate might be > + * less but not that much less, so fudge it by multiplying by 2. > + */ > + timeout_clks <<= 1; > + timeout_us += (timeout_clks * 1000) / > + (card->host->ios.clock / 1000); > + > + erase_timeout = timeout_us / 1000; > + > + /* > + * Theoretically, the calculation could underflow so round up > + * to 1ms in that case. > + */ > + if (!erase_timeout) > + erase_timeout = 1; > + } > + > + /* Multiplier for secure operations */ > + if (arg & MMC_SECURE_ARGS) { > + if (arg == MMC_SECURE_ERASE_ARG) > + erase_timeout *= card->ext_csd.sec_erase_mult; > + else > + erase_timeout *= card->ext_csd.sec_trim_mult; > + } > + > + erase_timeout *= qty; > + > + /* > + * Ensure at least a 1 second timeout for SPI as per > + * 'mmc_set_data_timeout()' > + */ > + if (mmc_host_is_spi(card->host) && erase_timeout < 1000) > + erase_timeout = 1000; > + > + cmd->erase_timeout = erase_timeout; > +} > + > +static void mmc_set_sd_erase_timeout(struct mmc_card *card, > + struct mmc_command *cmd, unsigned int arg, > + unsigned int qty) > +{ > + if (card->ssr.erase_timeout) { > + /* Erase timeout specified in SD Status Register (SSR) */ > + cmd->erase_timeout = card->ssr.erase_timeout * qty + > + card->ssr.erase_offset; > + } else { > + /* > + * Erase timeout not specified in SD Status Register (SSR) so > + * use 250ms per write block. > + */ > + cmd->erase_timeout = 250 * qty; > + } > + > + /* Must not be less than 1 second */ > + if (cmd->erase_timeout < 1000) > + cmd->erase_timeout = 1000; > +} > + > +static void mmc_set_erase_timeout(struct mmc_card *card, > + struct mmc_command *cmd, unsigned int arg, > + unsigned int qty) > +{ > + if (mmc_card_sd(card)) > + mmc_set_sd_erase_timeout(card, cmd, arg, qty); > + else > + mmc_set_mmc_erase_timeout(card, cmd, arg, qty); > +} > + > +static int mmc_do_erase(struct mmc_card *card, unsigned int from, > + unsigned int to, unsigned int arg) > +{ > + struct mmc_command cmd; > + unsigned int qty = 0; > + int err; > + > + /* > + * qty is used to calculate the erase timeout which depends on how many > + * erase groups (or allocation units in SD terminology) are affected. > + * We count erasing part of an erase group as one erase group. > + * For SD, the allocation units are always a power of 2. For MMC, the > + * erase group size is almost certainly also power of 2, but it does not > + * seem to insist on that in the JEDEC standard, so we fall back to > + * division in that case. SD may not specify an allocation unit size, > + * in which case the timeout is based on the number of write blocks. > + * > + * Note that the timeout for secure trim 2 will only be correct if the > + * number of erase groups specified is the same as the total of all > + * preceding secure trim 1 commands. Since the power may have been > + * lost since the secure trim 1 commands occurred, it is generally > + * impossible to calculate the secure trim 2 timeout correctly. > + */ > + if (card->erase_shift) > + qty += ((to >> card->erase_shift) - > + (from >> card->erase_shift)) + 1; > + else if (mmc_card_sd(card)) > + qty += to - from + 1; > + else > + qty += ((to / (card->erase_size >> 9)) - > + (from / (card->erase_size >> 9))) + 1; > + > + if (!mmc_card_blockaddr(card)) { > + from <<= 9; > + to <<= 9; > + } > + > + memset(&cmd, 0, sizeof(struct mmc_command)); > + if (mmc_card_sd(card)) > + cmd.opcode = SD_ERASE_WR_BLK_START; > + else > + cmd.opcode = MMC_ERASE_GROUP_START; > + cmd.arg = from; > + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; > + err = mmc_wait_for_cmd(card->host, &cmd, 0); > + if (err) { > + printk(KERN_ERR "mmc_erase: group start error %d, " > + "status %#x\n", err, cmd.resp[0]); > + err = -EINVAL; > + goto out; > + } > + > + memset(&cmd, 0, sizeof(struct mmc_command)); > + if (mmc_card_sd(card)) > + cmd.opcode = SD_ERASE_WR_BLK_END; > + else > + cmd.opcode = MMC_ERASE_GROUP_END; > + cmd.arg = to; > + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; > + err = mmc_wait_for_cmd(card->host, &cmd, 0); > + if (err) { > + printk(KERN_ERR "mmc_erase: group end error %d, status %#x\n", > + err, cmd.resp[0]); > + err = -EINVAL; > + goto out; > + } > + > + memset(&cmd, 0, sizeof(struct mmc_command)); > + cmd.opcode = MMC_ERASE; > + cmd.arg = arg; > + cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; > + mmc_set_erase_timeout(card, &cmd, arg, qty); > + err = mmc_wait_for_cmd(card->host, &cmd, 0); > + if (err) { > + printk(KERN_ERR "mmc_erase: erase error %d, status %#x\n", > + err, cmd.resp[0]); > + err = -EIO; > + goto out; > + } > + > + if (mmc_host_is_spi(card->host)) > + goto out; > + > + do { > + memset(&cmd, 0, sizeof(struct mmc_command)); > + cmd.opcode = MMC_SEND_STATUS; > + cmd.arg = card->rca << 16; > + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; > + /* Do not retry else we can't see errors */ > + err = mmc_wait_for_cmd(card->host, &cmd, 0); > + if (err || (cmd.resp[0] & 0xFDF92000)) { > + printk(KERN_ERR "error %d requesting status %#x\n", > + err, cmd.resp[0]); > + err = -EIO; > + goto out; > + } > + } while (!(cmd.resp[0] & R1_READY_FOR_DATA) || > + R1_CURRENT_STATE(cmd.resp[0]) == 7); > +out: > + return err; > +} > + > +/** > + * mmc_erase - erase sectors. > + * @card: card to erase > + * @from: first sector to erase > + * @nr: number of sectors to erase > + * @arg: erase command argument (SD supports only %MMC_ERASE_ARG) > + * > + * Caller must claim host before calling this function. > + */ > +int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr, > + unsigned int arg) > +{ > + unsigned int erase_sects = card->erase_size >> 9, to = from + nr; > + > + if (!(card->host->caps & MMC_CAP_ERASE) || > + !(card->csd.cmdclass & CCC_ERASE)) > + return -EOPNOTSUPP; > + > + if ((card->erase_size & 511) || erase_sects == 0) > + return -EOPNOTSUPP; > + > + if (mmc_card_sd(card) && arg != MMC_ERASE_ARG) > + return -EOPNOTSUPP; > + > + if ((arg & MMC_SECURE_ARGS) && > + !(card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN)) > + return -EOPNOTSUPP; > + > + if ((arg & MMC_TRIM_ARGS) && > + !(card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN)) > + return -EOPNOTSUPP; > + > + if (arg == MMC_ERASE_ARG || arg == MMC_SECURE_ERASE_ARG) { > + if (erase_sects != 1 && > + (from % erase_sects != 0 || nr % erase_sects != 0)) > + return -EINVAL; > + } > + > + if (nr == 0) > + return 0; > + > + if (to <= from) > + return -EINVAL; > + > + /* 'from' and 'to' are inclusive */ > + to -= 1; > + > + return mmc_do_erase(card, from, to, arg); > +} > +EXPORT_SYMBOL(mmc_erase); > + > +int mmc_can_erase(struct mmc_card *card) > +{ > + if ((card->host->caps & MMC_CAP_ERASE) && > + (card->csd.cmdclass & CCC_ERASE) && > + card->erase_size && > + !(card->erase_size & 511)) > + return 1; > + return 0; > +} > +EXPORT_SYMBOL(mmc_can_erase); > + > +int mmc_can_trim(struct mmc_card *card) > +{ > + if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN) > + return 1; > + return 0; > +} > +EXPORT_SYMBOL(mmc_can_trim); > + > +int mmc_can_secure_erase_trim(struct mmc_card *card) > +{ > + if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN) > + return 1; > + return 0; > +} > +EXPORT_SYMBOL(mmc_can_secure_erase_trim); > > void mmc_rescan(struct work_struct *work) > { > diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h > index a811c52..9d9eef5 100644 > --- a/drivers/mmc/core/core.h > +++ b/drivers/mmc/core/core.h > @@ -29,6 +29,8 @@ struct mmc_bus_ops { > void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); > void mmc_detach_bus(struct mmc_host *host); > > +void mmc_init_erase(struct mmc_card *card); > + > void mmc_set_chip_select(struct mmc_host *host, int mode); > void mmc_set_clock(struct mmc_host *host, unsigned int hz); > void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode); > diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c > index 89f7a25..8eddc97 100644 > --- a/drivers/mmc/core/mmc.c > +++ b/drivers/mmc/core/mmc.c > @@ -108,13 +108,23 @@ static int mmc_decode_cid(struct mmc_card *card) > return 0; > } > > +static void mmc_set_erase_size(struct mmc_card *card) > +{ > + if (card->ext_csd.erase_group_def & 1) > + card->erase_size = card->ext_csd.hc_erase_size; > + else > + card->erase_size = card->csd.erase_size; > + > + mmc_init_erase(card); > +} > + > /* > * Given a 128-bit response, decode to our card CSD structure. > */ > static int mmc_decode_csd(struct mmc_card *card) > { > struct mmc_csd *csd = &card->csd; > - unsigned int e, m, csd_struct; > + unsigned int e, m, a, b, csd_struct; > u32 *resp = card->raw_csd; > > /* > @@ -151,6 +161,11 @@ static int mmc_decode_csd(struct mmc_card *card) > csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); > csd->write_partial = UNSTUFF_BITS(resp, 21, 1); > > + a = UNSTUFF_BITS(resp, 42, 5); > + b = UNSTUFF_BITS(resp, 37, 5); > + csd->erase_size = (a + 1) * (b + 1); > + csd->erase_size <<= csd->write_blkbits; > + > return 0; > } > > @@ -247,8 +262,30 @@ static int mmc_read_ext_csd(struct mmc_card *card) > if (sa_shift > 0 && sa_shift <= 0x17) > card->ext_csd.sa_timeout = > 1 << ext_csd[EXT_CSD_S_A_TIMEOUT]; > + card->ext_csd.erase_group_def = > + ext_csd[EXT_CSD_ERASE_GROUP_DEF]; > + card->ext_csd.hc_erase_timeout = 300 * > + ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]; > + card->ext_csd.hc_erase_size = > + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] << 19; > + } > + > + if (card->ext_csd.rev >= 4) { > + card->ext_csd.sec_trim_mult = > + ext_csd[EXT_CSD_SEC_TRIM_MULT]; > + card->ext_csd.sec_erase_mult = > + ext_csd[EXT_CSD_SEC_ERASE_MULT]; > + card->ext_csd.sec_feature_support = > + ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]; > + card->ext_csd.trim_timeout = 300 * > + ext_csd[EXT_CSD_TRIM_MULT]; > } > > + if (ext_csd[EXT_CSD_ERASED_MEM_CONT]) > + card->erased_byte = 0xFF; > + else > + card->erased_byte = 0x0; > + > out: > kfree(ext_csd); > > @@ -260,6 +297,7 @@ MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], > MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], > card->raw_csd[2], card->raw_csd[3]); > MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); > +MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size); > MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev); > MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev); > MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid); > @@ -271,6 +309,7 @@ static struct attribute *mmc_std_attrs[] = { > &dev_attr_cid.attr, > &dev_attr_csd.attr, > &dev_attr_date.attr, > + &dev_attr_erase_size.attr, > &dev_attr_fwrev.attr, > &dev_attr_hwrev.attr, > &dev_attr_manfid.attr, > @@ -407,6 +446,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, > err = mmc_read_ext_csd(card); > if (err) > goto free_card; > + /* Erase size depends on CSD and Extended CSD */ > + mmc_set_erase_size(card); > } > > /* > diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c > index 5eac21d..6054f62 100644 > --- a/drivers/mmc/core/sd.c > +++ b/drivers/mmc/core/sd.c > @@ -119,6 +119,13 @@ static int mmc_decode_csd(struct mmc_card *card) > csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); > csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); > csd->write_partial = UNSTUFF_BITS(resp, 21, 1); > + > + if (UNSTUFF_BITS(resp, 46, 1)) > + csd->erase_size = 512; > + else { > + csd->erase_size = UNSTUFF_BITS(resp, 39, 7) + 1; > + csd->erase_size <<= csd->write_blkbits; > + } > break; > case 1: > /* > @@ -147,6 +154,7 @@ static int mmc_decode_csd(struct mmc_card *card) > csd->r2w_factor = 4; /* Unused */ > csd->write_blkbits = 9; > csd->write_partial = 0; > + csd->erase_size = 512; > break; > default: > printk(KERN_ERR "%s: unrecognised CSD structure version %d\n", > @@ -154,6 +162,8 @@ static int mmc_decode_csd(struct mmc_card *card) > return -EINVAL; > } > > + card->erase_size = csd->erase_size; > + > return 0; > } > > @@ -179,10 +189,68 @@ static int mmc_decode_scr(struct mmc_card *card) > scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4); > scr->bus_widths = UNSTUFF_BITS(resp, 48, 4); > > + if (UNSTUFF_BITS(resp, 55, 1)) > + card->erased_byte = 0xFF; > + else > + card->erased_byte = 0x0; > + > return 0; > } > > /* > + * Fetch and process SD Status register. > + */ > +static int mmc_read_ssr(struct mmc_card *card) > +{ > + unsigned int au, es, et, eo; > + int err, i; > + u32 *ssr; > + > + if (!(card->csd.cmdclass & CCC_APP_SPEC)) { > + printk(KERN_WARNING "%s: card lacks mandatory SD Status " > + "function.\n", mmc_hostname(card->host)); > + return 0; > + } > + > + ssr = kmalloc(64, GFP_KERNEL); > + if (!ssr) > + return -ENOMEM; > + > + err = mmc_app_sd_status(card, ssr); > + if (err) { > + printk(KERN_WARNING "%s: problem reading SD Status " > + "register.\n", mmc_hostname(card->host)); > + err = 0; > + goto out; > + } > + > + for (i = 0; i < 16; i++) > + ssr[i] = be32_to_cpu(ssr[i]); > + > + /* > + * UNSTUFF_BITS only works with four u32s so we have to offset the > + * bitfield positions accordingly. > + */ > + au = UNSTUFF_BITS(ssr, 428 - 384, 4); > + if (au > 0 || au <= 9) { > + card->ssr.au = 1 << (au + 4); > + es = UNSTUFF_BITS(ssr, 408 - 384, 16); > + et = UNSTUFF_BITS(ssr, 402 - 384, 6); > + eo = UNSTUFF_BITS(ssr, 400 - 384, 2); > + if (es && et) { > + card->ssr.erase_timeout = (et * 1000) / es; > + card->ssr.erase_offset = eo * 1000; > + } > + } else { > + printk(KERN_WARNING "%s: SD Status: Invalid Allocation Unit " > + "size.\n", mmc_hostname(card->host)); > + } > +out: > + kfree(ssr); > + return err; > +} > + > +/* > * Fetches and decodes switch information > */ > static int mmc_read_switch(struct mmc_card *card) > @@ -289,6 +357,7 @@ MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], > card->raw_csd[2], card->raw_csd[3]); > MMC_DEV_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]); > MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); > +MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size); > MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev); > MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev); > MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid); > @@ -302,6 +371,7 @@ static struct attribute *sd_std_attrs[] = { > &dev_attr_csd.attr, > &dev_attr_scr.attr, > &dev_attr_date.attr, > + &dev_attr_erase_size.attr, > &dev_attr_fwrev.attr, > &dev_attr_hwrev.attr, > &dev_attr_manfid.attr, > @@ -442,6 +512,16 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, > goto free_card; > > /* > + * Fetch and process SD Status register. > + */ > + err = mmc_read_ssr(card); > + if (err) > + goto free_card; > + > + /* Erase init depends on CSD and SSR */ > + mmc_init_erase(card); > + > + /* > * Fetch switch information from card. > */ > err = mmc_read_switch(card); > diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c > index 0d96080..7ca5774 100644 > --- a/drivers/mmc/core/sd_ops.c > +++ b/drivers/mmc/core/sd_ops.c > @@ -348,3 +348,51 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, > return 0; > } > > +int mmc_app_sd_status(struct mmc_card *card, void *ssr) > +{ > + int err; > + struct mmc_request mrq; > + struct mmc_command cmd; > + struct mmc_data data; > + struct scatterlist sg; > + > + BUG_ON(!card); > + BUG_ON(!card->host); > + BUG_ON(!ssr); > + > + /* NOTE: caller guarantees ssr is heap-allocated */ > + > + err = mmc_app_cmd(card->host, card); > + if (err) > + return err; > + > + memset(&mrq, 0, sizeof(struct mmc_request)); > + memset(&cmd, 0, sizeof(struct mmc_command)); > + memset(&data, 0, sizeof(struct mmc_data)); > + > + mrq.cmd = &cmd; > + mrq.data = &data; > + > + cmd.opcode = SD_APP_SD_STATUS; > + cmd.arg = 0; > + cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_ADTC; > + > + data.blksz = 64; > + data.blocks = 1; > + data.flags = MMC_DATA_READ; > + data.sg = &sg; > + data.sg_len = 1; > + > + sg_init_one(&sg, ssr, 64); > + > + mmc_set_data_timeout(&data, card); > + > + mmc_wait_for_req(card->host, &mrq); > + > + if (cmd.error) > + return cmd.error; > + if (data.error) > + return data.error; > + > + return 0; > +} > diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h > index 9742d8a..ffc2305 100644 > --- a/drivers/mmc/core/sd_ops.h > +++ b/drivers/mmc/core/sd_ops.h > @@ -19,6 +19,7 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca); > int mmc_app_send_scr(struct mmc_card *card, u32 *scr); > int mmc_sd_switch(struct mmc_card *card, int mode, int group, > u8 value, u8 *resp); > +int mmc_app_sd_status(struct mmc_card *card, void *ssr); > > #endif > > diff --git a/include/linux/Kbuild b/include/linux/Kbuild > index 2fc8e14..cca6850 100644 > --- a/include/linux/Kbuild > +++ b/include/linux/Kbuild > @@ -3,6 +3,7 @@ header-y += can/ > header-y += dvb/ > header-y += hdlc/ > header-y += isdn/ > +header-y += mmc/ > header-y += nfsd/ > header-y += raid/ > header-y += spi/ > diff --git a/include/linux/mmc/Kbuild b/include/linux/mmc/Kbuild > new file mode 100644 > index 0000000..547136d > --- /dev/null > +++ b/include/linux/mmc/Kbuild > @@ -0,0 +1 @@ > +header-y += mmc_ioctl.h > diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h > index d02d2c6..bbbcc95 100644 > --- a/include/linux/mmc/card.h > +++ b/include/linux/mmc/card.h > @@ -30,6 +30,7 @@ struct mmc_csd { > unsigned int tacc_ns; > unsigned int r2w_factor; > unsigned int max_dtr; > + unsigned int erase_size; > unsigned int read_blkbits; > unsigned int write_blkbits; > unsigned int capacity; > @@ -41,9 +42,16 @@ struct mmc_csd { > > struct mmc_ext_csd { > u8 rev; > + u8 erase_group_def; > + u8 sec_feature_support; > unsigned int sa_timeout; /* Units: 100ns */ > unsigned int hs_max_dtr; > unsigned int sectors; > + unsigned int hc_erase_size; /* In bytes */ > + unsigned int hc_erase_timeout; /* In milliseconds */ > + unsigned int sec_trim_mult; /* Secure trim multiplier */ > + unsigned int sec_erase_mult; /* Secure erase multiplier */ > + unsigned int trim_timeout; /* In milliseconds */ > }; > > struct sd_scr { > @@ -53,6 +61,12 @@ struct sd_scr { > #define SD_SCR_BUS_WIDTH_4 (1<<2) > }; > > +struct sd_ssr { > + unsigned int au; /* In sectors */ > + unsigned int erase_timeout; /* In milliseconds */ > + unsigned int erase_offset; /* In milliseconds */ > +}; > + > struct sd_switch_caps { > unsigned int hs_max_dtr; > }; > @@ -101,6 +115,10 @@ struct mmc_card { > #define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */ > #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */ > /* for byte mode */ > + unsigned int erase_size; /* erase size in bytes */ > + unsigned int erase_shift; /* if erase unit is power 2 */ > + unsigned int max_erase; /* in sectors */ > + u8 erased_byte; /* value of erased bytes */ > > u32 raw_cid[4]; /* raw card CID */ > u32 raw_csd[4]; /* raw card CSD */ > @@ -109,6 +127,7 @@ struct mmc_card { > struct mmc_csd csd; /* card specific */ > struct mmc_ext_csd ext_csd; /* mmc v4 extended card specific */ > struct sd_scr scr; /* extra SD information */ > + struct sd_ssr ssr; /* yet more SD information */ > struct sd_switch_caps sw_caps; /* switch (CMD6) caps */ > > unsigned int sdio_funcs; /* number of SDIO functions */ > diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h > index e4898e9..af3dded 100644 > --- a/include/linux/mmc/core.h > +++ b/include/linux/mmc/core.h > @@ -92,6 +92,8 @@ struct mmc_command { > * actively failing requests > */ > > + unsigned int erase_timeout; /* in milliseconds */ > + > struct mmc_data *data; /* data segment associated with cmd */ > struct mmc_request *mrq; /* associated request */ > }; > @@ -134,6 +136,21 @@ extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); > extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, > struct mmc_command *, int); > > +#define MMC_ERASE_ARG 0x00000000 > +#define MMC_SECURE_ERASE_ARG 0x80000000 > +#define MMC_TRIM_ARG 0x00000001 > +#define MMC_SECURE_TRIM1_ARG 0x80000001 > +#define MMC_SECURE_TRIM2_ARG 0x80008000 > + > +#define MMC_SECURE_ARGS 0x80000000 > +#define MMC_TRIM_ARGS 0x00008001 > + > +extern int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr, > + unsigned int arg); > +extern int mmc_can_erase(struct mmc_card *card); > +extern int mmc_can_trim(struct mmc_card *card); > +extern int mmc_can_secure_erase_trim(struct mmc_card *card); > + > extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *); > extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int); > > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h > index 43eaf5c..270f281 100644 > --- a/include/linux/mmc/host.h > +++ b/include/linux/mmc/host.h > @@ -152,6 +152,7 @@ struct mmc_host { > #define MMC_CAP_DISABLE (1 << 7) /* Can the host be disabled */ > #define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */ > #define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */ > +#define MMC_CAP_ERASE (1 << 10) /* Allow erase/trim commands */ > > mmc_pm_flag_t pm_caps; /* supported pm features */ > > diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h > index 8a49cbf..82cdf8b 100644 > --- a/include/linux/mmc/mmc.h > +++ b/include/linux/mmc/mmc.h > @@ -251,12 +251,20 @@ struct _mmc_csd { > * EXT_CSD fields > */ > > -#define EXT_CSD_BUS_WIDTH 183 /* R/W */ > -#define EXT_CSD_HS_TIMING 185 /* R/W */ > -#define EXT_CSD_CARD_TYPE 196 /* RO */ > -#define EXT_CSD_REV 192 /* RO */ > -#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ > -#define EXT_CSD_S_A_TIMEOUT 217 > +#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */ > +#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */ > +#define EXT_CSD_BUS_WIDTH 183 /* R/W */ > +#define EXT_CSD_HS_TIMING 185 /* R/W */ > +#define EXT_CSD_CARD_TYPE 196 /* RO */ > +#define EXT_CSD_REV 192 /* RO */ > +#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ > +#define EXT_CSD_S_A_TIMEOUT 217 /* RO */ > +#define EXT_CSD_ERASE_TIMEOUT_MULT 223 /* RO */ > +#define EXT_CSD_HC_ERASE_GRP_SIZE 224 /* RO */ > +#define EXT_CSD_SEC_TRIM_MULT 229 /* RO */ > +#define EXT_CSD_SEC_ERASE_MULT 230 /* RO */ > +#define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */ > +#define EXT_CSD_TRIM_MULT 232 /* RO */ > > /* > * EXT_CSD field definitions > @@ -274,6 +282,10 @@ struct _mmc_csd { > #define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */ > #define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */ > > +#define EXT_CSD_SEC_ER_EN BIT(0) > +#define EXT_CSD_SEC_BD_BLK_EN BIT(2) > +#define EXT_CSD_SEC_GB_CL_EN BIT(4) > + > /* > * MMC_SWITCH access modes > */ > diff --git a/include/linux/mmc/mmc_ioctl.h b/include/linux/mmc/mmc_ioctl.h > new file mode 100644 > index 0000000..f9737dd > --- /dev/null > +++ b/include/linux/mmc/mmc_ioctl.h > @@ -0,0 +1,63 @@ > +/* > + * linux/include/linux/mmc/mmc_ioctl.h > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + * > + */ > + > +#ifndef LINUX_MMC_MMC_IOCTL_H > +#define LINUX_MMC_MMC_IOCTL_H > + > +#include <linux/types.h> > +#include <linux/ioctl.h> > + > +/* Same as MMC block device major number */ > +#define MMC_IOCTL_CODE 0xB3 > + > +#define MMCERASEINFO _IOR(MMC_IOCTL_CODE, 1, struct mmc_blk_erase_info) > +#define MMCERASE _IOW(MMC_IOCTL_CODE, 2, struct mmc_blk_erase_args) > + > +/* Erase flags */ > +#define MMC_ERASE_FLAG 0x01 > +#define MMC_SECURE_ERASE_FLAG 0x02 > +#define MMC_TRIM_FLAG 0x04 > +#define MMC_SECURE_TRIM1_FLAG 0x08 > +#define MMC_SECURE_TRIM2_FLAG 0x10 > + > +/** > + * struct mmc_blk_erase_info - erase information. > + * @erase_size: minimum unit of erasure in bytes > + * @flags: flags indicating which kinds of erase are permitted > + * @erased_byte: value of bytes that have been erased (0x00 or 0xFF) > + * @reserved: reserved > + */ > +struct mmc_blk_erase_info { > + __u32 erase_size; > + __u32 flags; > + __u8 erased_byte; > + __u8 reserved[23]; > +} __attribute__ ((packed)); > + > +/** > + * struct mmc_blk_erase_args - erase SD or MMC sectors. > + * @from: start sector number > + * @nr: number of sectors to erase > + * @flag: flag indicating which kind of erase to do > + * @reserved: reserved > + * > + * @flag must be one of the flags above. For %MMC_ERASE_FLAG and > + * %MMC_SECURE_ERASE_FLAG, @from and @nr must be multiples of the erase_size > + * returned by the %MMCERASEINFO ioctl. %MMC_ERASE_FLAG may be used with > + * MMC or SD cards, whereas the other flags are only valid for MMCv4.4 or > + * later. > + */ > +struct mmc_blk_erase_args { > + __u32 from; > + __u32 nr; > + __u32 flag; > + __u8 reserved[20]; > +} __attribute__ ((packed)); > + > +#endif /* LINUX_MMC_MMC_IOCTL_H */ > diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h > index f310062..3fd85e0 100644 > --- a/include/linux/mmc/sd.h > +++ b/include/linux/mmc/sd.h > @@ -21,8 +21,13 @@ > /* class 10 */ > #define SD_SWITCH 6 /* adtc [31:0] See below R1 */ > > + /* class 5 */ > +#define SD_ERASE_WR_BLK_START 32 /* ac [31:0] data addr R1 */ > +#define SD_ERASE_WR_BLK_END 33 /* ac [31:0] data addr R1 */ > + > /* Application commands */ > #define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */ > +#define SD_APP_SD_STATUS 13 /* adtc R1 */ > #define SD_APP_SEND_NUM_WR_BLKS 22 /* adtc R1 */ > #define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */ > #define SD_APP_SEND_SCR 51 /* adtc R1 */ > -- > 1.6.3.3 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-mmc" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html