diff options
author | Ahmad Fatoum <a.fatoum@pengutronix.de> | 2020-07-01 11:11:11 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2020-07-11 06:14:04 +0200 |
commit | 76b717212c8d907508ce2737ecb155a71aa789db (patch) | |
tree | 1669ba7264e374383d28ac05cf6a3083641e21d1 /drivers | |
parent | 0e2aafed3817f76f9d9b33ead8d11d6151813038 (diff) | |
download | barebox-76b717212c8d907508ce2737ecb155a71aa789db.tar.gz barebox-76b717212c8d907508ce2737ecb155a71aa789db.tar.xz |
mci: sdhci: atmel: extend driver for PBL usage
The BootROM resets both the SD/MMC host controller and the pin controller,
but the card itself remains in transfer mode. If we redo host-side
setup, we can directly read new blocks off the card.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mci/Kconfig | 4 | ||||
-rw-r--r-- | drivers/mci/Makefile | 1 | ||||
-rw-r--r-- | drivers/mci/atmel-sdhci-common.c | 3 | ||||
-rw-r--r-- | drivers/mci/atmel-sdhci-pbl.c | 128 |
4 files changed, 136 insertions, 0 deletions
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig index 6ae1e81252..3026b25cad 100644 --- a/drivers/mci/Kconfig +++ b/drivers/mci/Kconfig @@ -187,3 +187,7 @@ endif config MCI_IMX_ESDHC_PBL bool select MCI_SDHCI + +config MCI_ATMEL_SDHCI_PBL + bool + select MCI_SDHCI diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile index 177483dcfb..4a53633674 100644 --- a/drivers/mci/Makefile +++ b/drivers/mci/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_MCI_ATMEL_SDHCI) += atmel-sdhci.o atmel-sdhci-common.o obj-$(CONFIG_MCI_BCM283X) += mci-bcm2835.o obj-$(CONFIG_MCI_BCM283X_SDHOST) += bcm2835-sdhost.o obj-$(CONFIG_MCI_DOVE) += dove-sdhci.o +pbl-$(CONFIG_MCI_ATMEL_SDHCI_PBL) += atmel-sdhci-pbl.o atmel-sdhci-common.o obj-$(CONFIG_MCI_IMX) += imx.o obj-$(CONFIG_MCI_IMX_ESDHC) += imx-esdhc.o imx-esdhc-common.o pbl-$(CONFIG_MCI_IMX_ESDHC_PBL) += imx-esdhc-pbl.o imx-esdhc-common.o diff --git a/drivers/mci/atmel-sdhci-common.c b/drivers/mci/atmel-sdhci-common.c index 1884f38363..a83610c3d0 100644 --- a/drivers/mci/atmel-sdhci-common.c +++ b/drivers/mci/atmel-sdhci-common.c @@ -12,7 +12,10 @@ #include <common.h> #include <mci.h> +#include <mach/early_udelay.h> + #ifdef __PBL__ +#define udelay early_udelay #undef dev_err #define dev_err(d, ...) pr_err(__VA_ARGS__) #undef dev_warn diff --git a/drivers/mci/atmel-sdhci-pbl.c b/drivers/mci/atmel-sdhci-pbl.c new file mode 100644 index 0000000000..626e4008fe --- /dev/null +++ b/drivers/mci/atmel-sdhci-pbl.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: BSD-1-Clause +/* + * Copyright (c) 2015, Atmel Corporation + * Copyright (c) 2019, Ahmad Fatoum, Pengutronix + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + */ + +#include <common.h> +#include <pbl.h> +#include <mci.h> +#include <debug_ll.h> +#include <mach/xload.h> +#include "atmel-sdhci.h" + +#include <mach/early_udelay.h> + +#ifdef __PBL__ +#define udelay early_udelay +#endif + +#define SECTOR_SIZE 512 +#define SUPPORT_MAX_BLOCKS 16U + +struct at91_sdhci_priv { + struct at91_sdhci host; + bool highcapacity_card; +}; + +static int sd_cmd_stop_transmission(struct at91_sdhci_priv *priv) +{ + struct mci_cmd cmd = { + .cmdidx = MMC_CMD_STOP_TRANSMISSION, + .resp_type = MMC_RSP_R1b, + }; + + return at91_sdhci_send_command(&priv->host, &cmd, NULL); +} + +static int sd_cmd_read_multiple_block(struct at91_sdhci_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 at91_sdhci_send_command(&priv->host, &cmd, &data); +} + +static int at91_sdhci_bio_read(struct pbl_bio *bio, off_t start, + void *buf, unsigned int nblocks) +{ + struct at91_sdhci_priv *priv = bio->priv; + unsigned int blocks_done = 0; + unsigned int blocks; + unsigned int block_len = SECTOR_SIZE; + unsigned int blocks_read; + int ret; + + /* + * Refer to the at91sam9g20 datasheet: + * Figure 35-10. Read Function Flow Diagram + */ + + while (blocks_done < nblocks) { + blocks = min(nblocks - blocks_done, SUPPORT_MAX_BLOCKS); + + blocks_read = sd_cmd_read_multiple_block(priv, buf, + start + blocks_done, + blocks); + + ret = sd_cmd_stop_transmission(priv); + if (ret) + return ret; + + blocks_done += blocks_read; + + if (blocks_read != blocks) + break; + + buf += blocks * block_len; + } + + return blocks_done; +} + +static struct at91_sdhci_priv atmel_sdcard; + +int at91_sdhci_bio_init(struct pbl_bio *bio, void __iomem *base) +{ + struct at91_sdhci_priv *priv = &atmel_sdcard; + struct at91_sdhci *host = &priv->host; + struct mci_ios ios = { .bus_width = MMC_BUS_WIDTH_1, .clock = 25000000 }; + int ret; + + bio->priv = priv; + bio->read = at91_sdhci_bio_read; + + at91_sdhci_mmio_init(host, base); + + sdhci_reset(&host->sdhci, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + + ret = at91_sdhci_init(host, 240000000, true, true); + if (ret) + return ret; + + ret = at91_sdhci_set_ios(host, &ios); + + // FIXME can we determine this without leaving SD transfer mode? + priv->highcapacity_card = 1; + + return 0; +} |