diff options
Diffstat (limited to 'drivers/mci/atmel_mci_pbl.c')
-rw-r--r-- | drivers/mci/atmel_mci_pbl.c | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/drivers/mci/atmel_mci_pbl.c b/drivers/mci/atmel_mci_pbl.c new file mode 100644 index 0000000000..767d6f3ce2 --- /dev/null +++ b/drivers/mci/atmel_mci_pbl.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <common.h> +#include <mach/xload.h> +#include <mci.h> + +#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_DTOR, 0x7f); + + atmci_common_set_ios(host, &ios); + + priv->highcapacity_card = 1; + + return 0; +} |