// SPDX-License-Identifier: GPL-2.0-only #include #include #include #include "atmel-mci-regs.h" #define SECTOR_SIZE 512 #define SUPPORT_MAX_BLOCKS 16U struct atmel_mci_priv { struct atmel_mci host; bool highcapacity_card; }; static struct atmel_mci_priv atmci_sdcard; static int atmel_mci_pbl_stop_transmission(struct atmel_mci_priv *priv) { struct mci_cmd cmd = { .cmdidx = MMC_CMD_STOP_TRANSMISSION, .resp_type = MMC_RSP_R1b, }; return atmci_common_request(&priv->host, &cmd, NULL); } static int at91_mci_sd_cmd_read_multiple_block(struct atmel_mci_priv *priv, void *buf, unsigned int start, unsigned int block_count) { u16 block_len = SECTOR_SIZE; struct mci_data data; struct mci_cmd cmd = { .cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK, .resp_type = MMC_RSP_R1, .cmdarg = start, }; if (!priv->highcapacity_card) cmd.cmdarg *= block_len; data.dest = buf; data.flags = MMC_DATA_READ; data.blocksize = block_len; data.blocks = block_count; return atmci_common_request(&priv->host, &cmd, &data); } static int at91_mci_bio_read(struct pbl_bio *bio, off_t start, void *buf, unsigned int nblocks) { struct atmel_mci_priv *priv = bio->priv; unsigned int blocks_done = 0; unsigned int blocks; unsigned int block_len = SECTOR_SIZE; unsigned int blocks_read; int ret; while (blocks_done < nblocks) { blocks = min(nblocks - blocks_done, SUPPORT_MAX_BLOCKS); blocks_read = at91_mci_sd_cmd_read_multiple_block(priv, buf, start + blocks_done, blocks); ret = atmel_mci_pbl_stop_transmission(priv); if (ret) return ret; blocks_done += blocks_read; if (blocks_read != blocks) break; buf += blocks * block_len; } return blocks_done; } int at91_mci_bio_init(struct pbl_bio *bio, void __iomem *base, unsigned int clock, unsigned int slot) { struct atmel_mci_priv *priv = &atmci_sdcard; struct atmel_mci *host = &priv->host; struct mci_ios ios = { .bus_width = MMC_BUS_WIDTH_4, .clock = 25000000 }; /* PBL will get MCI controller in disabled state. We need to reconfigure * it. */ bio->priv = priv; bio->read = at91_mci_bio_read; host->regs = base; atmci_get_cap(host); host->bus_hz = clock; host->slot_b = slot; if (host->slot_b) host->sdc_reg = ATMCI_SDCSEL_SLOT_B; else host->sdc_reg = ATMCI_SDCSEL_SLOT_A; atmci_writel(host, ATMCI_CR, ATMCI_CR_PWSDIS); atmci_writel(host, ATMCI_DTOR, 0x7f); atmci_common_set_ios(host, &ios); priv->highcapacity_card = 1; return 0; } void at91_mci_bio_set_highcapacity(bool highcapacity_card) { atmci_sdcard.highcapacity_card = highcapacity_card; }