This commit imports the at91bootstrap v3.9.1-rc1 state of the Atmel SDHCI variant driver. Second stage use is not implemented and first stage use in general is not possible, because SDRAM set up is not yet implemented. However, it can already be used to show case the use of FAT from the barebox PBL used in a follow up commit. Signed-off-by: Ahmad Fatoum <ahmad@xxxxxx> --- drivers/mci/Kconfig | 4 + drivers/mci/Makefile | 1 + drivers/mci/atmel-sdhci-common.c | 279 ++++++++++++++++++++ drivers/mci/atmel-sdhci-pbl.c | 440 +++++++++++++++++++++++++++++++ drivers/mci/atmel-sdhci.h | 38 +++ drivers/mci/sdhci.h | 17 ++ 6 files changed, 779 insertions(+) create mode 100644 drivers/mci/atmel-sdhci-common.c create mode 100644 drivers/mci/atmel-sdhci-pbl.c create mode 100644 drivers/mci/atmel-sdhci.h diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig index 80b3a26002b4..7485be5dcb6e 100644 --- a/drivers/mci/Kconfig +++ b/drivers/mci/Kconfig @@ -177,3 +177,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 54eb65978e5d..40ed772d3ff7 100644 --- a/drivers/mci/Makefile +++ b/drivers/mci/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_MCI_ATMEL) += atmel_mci.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 new file mode 100644 index 000000000000..499627e2e396 --- /dev/null +++ b/drivers/mci/atmel-sdhci-common.c @@ -0,0 +1,279 @@ +// 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. + */ + +#define pr_fmt(fmt) "atmel-sdhci: " fmt + +#include <common.h> +#include <io.h> +#include <mci.h> +#include <debug_ll.h> +#include "atmel-sdhci.h" + +#define SDHCI_CARD_INSERTED BIT(16) + +static u32 at91_sdhci_read32(struct sdhci *sdhci, int reg) +{ + struct at91_sdhci *priv = container_of(sdhci, struct at91_sdhci, sdhci); + return readl(priv->base + reg); +} + +static void at91_sdhci_write32(struct sdhci *sdhci, int reg, u32 value) +{ + struct at91_sdhci *priv = container_of(sdhci, struct at91_sdhci, sdhci); + writel(value, priv->base + reg); +} + +static u16 at91_sdhci_read16(struct sdhci *sdhci, int reg) +{ + struct at91_sdhci *priv = container_of(sdhci, struct at91_sdhci, sdhci); + return readw(priv->base + reg); +} + +static void at91_sdhci_write16(struct sdhci *sdhci, int reg, u16 value) +{ + struct at91_sdhci *priv = container_of(sdhci, struct at91_sdhci, sdhci); + writew(value, priv->base + reg); +} + +static u8 at91_sdhci_read8(struct sdhci *sdhci, int reg) +{ + struct at91_sdhci *priv = container_of(sdhci, struct at91_sdhci, sdhci); + return readb(priv->base + reg); +} + +static void at91_sdhci_write8(struct sdhci *sdhci, int reg, u8 value) +{ + struct at91_sdhci *priv = container_of(sdhci, struct at91_sdhci, sdhci); + writeb(value, priv->base + reg); +} + +static void at91_sdhci_software_reset_cmd(struct at91_sdhci *host) +{ + sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET, SDHCI_RESET_CMD); + + while (sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET) & SDHCI_RESET_CMD) + ; +} + +void at91_sdhci_host_capability(struct at91_sdhci *host, + unsigned int *voltages) +{ + u16 caps; + + caps = sdhci_read16(&host->sdhci, SDHCI_CAPABILITIES_1); + + if (caps & SDHCI_HOSTCAP_VOLTAGE_330) + *voltages |= MMC_VDD_32_33 | MMC_VDD_33_34; + if (caps & SDHCI_HOSTCAP_VOLTAGE_300) + *voltages |= MMC_VDD_29_30 | MMC_VDD_30_31; + if (caps & SDHCI_HOSTCAP_VOLTAGE_180) + *voltages |= MMC_VDD_165_195; +} + +bool at91_sdhci_is_card_inserted(struct at91_sdhci *host) +{ + /* + * Debouncing of the card detect pin is up to 13ms on sama5d2 rev B + * and later. + * Try to be safe and wait for up to 50ms (50000µs). Let assume + * the PCK (processor clock) frequency is 500MHz, hence 500 cycles/µs. + * 500 * 50000 = 25000000 cycles. + */ + unsigned int timeout = 25000000; + bool is_inserted; + u32 status_mask; + + /* Enable (unmask) the Interrupt Status 'card inserted' bit */ + status_mask = sdhci_read32(&host->sdhci, SDHCI_INT_ENABLE); + status_mask |= SDHCI_INT_CARD_INSERT; + sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE, status_mask); + + is_inserted = !!(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_CARD_INSERTED); + if (is_inserted) + goto exit; + + while (!(sdhci_read32(&host->sdhci, SDHCI_INT_STATUS) & SDHCI_INT_CARD_INSERT) + && timeout--) + ; + + is_inserted = !!(sdhci_read32(&host->sdhci, SDHCI_INT_STATUS) & + SDHCI_INT_CARD_INSERT); + +exit: + status_mask &= ~SDHCI_INT_CARD_INSERT; + sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE, status_mask); + + status_mask = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS); + status_mask |= SDHCI_INT_CARD_INSERT; + sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, status_mask); + + return is_inserted; +} + +static void at91_sdhci_set_cmd_xfer_mode(struct sdhci *host, struct mci_cmd *cmd, + struct mci_data *data, bool dma, u32 *command, + u32 *xfer) +{ + *xfer = 0; + + *command = SDHCI_CMD_INDEX(cmd->cmdidx); + + if ((cmd->resp_type == SD_RESP_TYPE_R1) + || (cmd->resp_type == SD_RESP_TYPE_R5) + || (cmd->resp_type == SD_RESP_TYPE_R6) + || (cmd->resp_type == SD_RESP_TYPE_R7)) + *command |= SDHCI_RESP_TYPE_48 + | SDHCI_CMD_CRC_CHECK_EN + | SDHCI_CMD_INDEX_CHECK_EN; + else if (cmd->resp_type == SD_RESP_TYPE_R1B) + *command |= SDHCI_RESP_TYPE_48_BUSY + | SDHCI_CMD_CRC_CHECK_EN + | SDHCI_CMD_INDEX_CHECK_EN; + else if (cmd->resp_type == SD_RESP_TYPE_R2) + *command |= SDHCI_RESP_TYPE_136 + | SDHCI_CMD_CRC_CHECK_EN; + else if ((cmd->resp_type == SD_RESP_TYPE_R3) + || (cmd->resp_type == SD_RESP_TYPE_R4)) + *command |= SDHCI_RESP_TYPE_48; + else + *command |= SDHCI_RESP_NONE; + + if (data) { + *command |= SDHCI_DATA_PRESENT; + + *xfer |= SDHCI_BLOCK_COUNT_EN; + + if (data->blocks > 1) + *xfer |= SDHCI_MULTIPLE_BLOCKS; + + if (data->flags & MMC_DATA_READ) + *xfer |= SDHCI_DATA_TO_HOST; + + if (dma) + *xfer |= SDHCI_DMA_EN; + } +} + +int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *sd_cmd, + struct mci_data *data) +{ + unsigned int normal_status, normal_status_mask; + unsigned int command, xfer; + unsigned int timeout; + + timeout = 100000; + while ((--timeout) && + (sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & + (SDHCI_CMD_INHIBIT_CMD | SDHCI_CMD_INHIBIT_DATA))) + ; + + if (!timeout) + pr_warn("SDHC: Timeout waiting for CMD and DAT Inhibit bits\n"); + + normal_status_mask = SDHCI_INT_CMD_COMPLETE; + + at91_sdhci_set_cmd_xfer_mode(&host->sdhci, sd_cmd, + data, false, &command, + &xfer); + + if (sd_cmd->resp_type == SD_RESP_TYPE_R1B) + normal_status_mask |= SDHCI_INT_XFER_COMPLETE; + + if (data) { + sdhci_write8(&host->sdhci, SDHCI_TIMEOUT_CONTROL, 0xe); + sdhci_write16(&host->sdhci, SDHCI_BLOCK_SIZE, data->blocksize); + if (data->blocks > 1) + sdhci_write16(&host->sdhci, SDHCI_BLOCK_COUNT, data->blocks); + + sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer); + } + + sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, sd_cmd->cmdarg); + + sdhci_write16(&host->sdhci, SDHCI_COMMAND, command); + + timeout = 100000; + do { + normal_status = sdhci_read16(&host->sdhci, SDHCI_INT_NORMAL_STATUS); + } while ((--timeout) && + ((normal_status & normal_status_mask) != normal_status_mask)); + + if (!timeout) + pr_debug("SDHC: Timeout waiting for command complete\n"); + + /* clear the status, except for read and write ready. + * those will be cleared by the read/write data routine, which + * bases itself on the fact that the hardware is ready to receive data + * or has data ready to be read + */ + sdhci_write16(&host->sdhci, SDHCI_INT_NORMAL_STATUS, + normal_status & ~(SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_AVAIL)); + + if ((normal_status & normal_status_mask) == normal_status_mask) { + sdhci_read_response(&host->sdhci, sd_cmd); + + /* if we have data but not using block transfer, we use PIO mode */ + if (data) + sdhci_transfer_data(&host->sdhci, data); + + return 0; + } + + normal_status = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS); + + at91_sdhci_software_reset_cmd(host); + + sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, normal_status); + + if (normal_status & SDHCI_INT_TIMEOUT) + return -ETIMEDOUT; + + return -EIO; +} + +#define DEFAULT_SD_BLOCK_LEN 512 + +int atmel_sdhci_init(struct at91_sdhci *host) +{ + unsigned int status_mask; + + status_mask = SDHCI_INT_CMD_COMPLETE + | SDHCI_INT_XFER_COMPLETE + | SDHCI_INT_SPACE_AVAIL + | SDHCI_INT_DATA_AVAIL; + + status_mask |= SDHCI_INT_TIMEOUT + | SDHCI_INT_CRC + | SDHCI_INT_END_BIT + | SDHCI_INT_INDEX + | SDHCI_INT_DATA_TIMEOUT + | SDHCI_INT_DATA_CRC + | SDHCI_INT_DATA_END_BIT; + + sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE, status_mask); + + sdhci_write32(&host->sdhci, SDHCI_SIGNAL_ENABLE, 0); + + sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, + sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL) + & ~(SDHCI_DATA_WIDTH_8BIT | SDHCI_DATA_WIDTH_4BIT)); + + return 0; +} + +void atmel_sdhci_prep(struct at91_sdhci *host, void __iomem *base) +{ + host->base = base; + host->sdhci.read8 = at91_sdhci_read8; + host->sdhci.read16 = at91_sdhci_read16; + host->sdhci.read32 = at91_sdhci_read32; + host->sdhci.write8 = at91_sdhci_write8; + host->sdhci.write16 = at91_sdhci_write16; + host->sdhci.write32 = at91_sdhci_write32; +} diff --git a/drivers/mci/atmel-sdhci-pbl.c b/drivers/mci/atmel-sdhci-pbl.c new file mode 100644 index 000000000000..52d15a7c0c76 --- /dev/null +++ b/drivers/mci/atmel-sdhci-pbl.c @@ -0,0 +1,440 @@ +// 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" + +#define DEFAULT_SD_BLOCK_LEN 512 +#define SUPPORT_MAX_BLOCKS 16U + +#define OCR_VOLTAGE_27_36_MASK 0xff8000 +#define CHECK_PATTERN 0xaa + +struct at91_sdhci_priv { + struct at91_sdhci host; + + unsigned int voltages; + u32 rca; /* Relative card address */ + u32 ocr; /* Operation condition register */ + bool no_sd; + bool highcapacity_card; +}; + +/* + * Use time in us as a busy counter timeout value + */ +static inline void early_udelay(unsigned us) +{ + volatile unsigned i; + + for (i = 0; i < us * 4; i++) + ; +} + +static int sd_cmd_send_status(struct at91_sdhci_priv *priv, unsigned int retries) +{ + unsigned int i; + int ret; + struct mci_cmd cmd = { + .cmdidx = MMC_CMD_SEND_STATUS, + .resp_type = SD_RESP_TYPE_R1, + .cmdarg = priv->rca << 16, + }; + + for (i = 0; i < retries; i++) { + ret = at91_sdhci_send_command(&priv->host, &cmd, NULL); + if (ret) + return ret; + + if ((cmd.response[0] >> 8) & 0x01) + break; + + early_udelay(1000); + }; + + if (i == retries) { + pr_warn("Timeout, wait for card ready\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int sd_cmd_stop_transmission(struct at91_sdhci_priv *priv) +{ + unsigned int retries = 1000; + int ret; + struct mci_cmd cmd = { + .cmdidx = MMC_CMD_STOP_TRANSMISSION, + .resp_type = SD_RESP_TYPE_R1B, + }; + + ret = at91_sdhci_send_command(&priv->host, &cmd, NULL); + if (ret) + return ret; + + return sd_cmd_send_status(priv, retries); +} + +static int sd_cmd_read_multiple_block(struct at91_sdhci_priv *priv, + void *buf, + unsigned int start, + unsigned int block_count) +{ + u16 block_len = DEFAULT_SD_BLOCK_LEN; + struct mci_data data; + struct mci_cmd cmd = { + .cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK, + .resp_type = SD_RESP_TYPE_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 atmel_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 = DEFAULT_SD_BLOCK_LEN; + 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 int sd_cmd_go_idle_state(struct at91_sdhci_priv *priv) +{ + struct mci_cmd cmd = { + .cmdidx = MMC_CMD_GO_IDLE_STATE, + .resp_type = MMC_RSP_NONE, + }; + + return at91_sdhci_send_command(&priv->host, &cmd, NULL); +} + +static int sd_cmd_send_if_cond(struct at91_sdhci_priv *priv) +{ + int ret; + struct mci_cmd cmd = { + .cmdidx = MMC_CMD_SEND_EXT_CSD, + .resp_type = SD_RESP_TYPE_R1, + .cmdarg = CHECK_PATTERN, + }; + + if (priv->voltages & OCR_VOLTAGE_27_36_MASK) + cmd.cmdarg |= 0x01 << 8; + + ret = at91_sdhci_send_command(&priv->host, &cmd, NULL); + if (ret) + return ret; + + if (((cmd.response[0] & CHECK_PATTERN) != CHECK_PATTERN) + || (((cmd.response[0] >> 8) & 0x0f) != 0x01)) + return -EIO; + + return 0; +} + +static int sd_cmd_send_app_cmd(struct at91_sdhci_priv *priv) +{ + struct mci_cmd cmd = { + .cmdidx = MMC_CMD_APP_CMD, + .resp_type = SD_RESP_TYPE_R1, + .cmdarg = priv->rca << 16, + }; + + return at91_sdhci_send_command(&priv->host, &cmd, NULL); +} + +static int sd_cmd_app_sd_send_op_cmd(struct at91_sdhci_priv *priv, + unsigned int capacity_support, + unsigned int *reponse) +{ + int ret; + struct mci_cmd cmd = { + .cmdidx = SD_CMD_APP_SEND_OP_COND, + .resp_type = SD_RESP_TYPE_R3, + .cmdarg = priv->voltages & OCR_VOLTAGE_27_36_MASK, + }; + + if (capacity_support) + cmd.cmdarg |= OCR_HCS; + + ret = at91_sdhci_send_command(&priv->host, &cmd, NULL); + if (ret) + return ret; + + *reponse = cmd.response[0]; + + return 0; +} + +static int sd_check_operational_condition(struct at91_sdhci_priv *priv, + unsigned int capacity_support) +{ + unsigned int response = 0; + unsigned int retries = 1000; + unsigned int i; + int ret; + + /* + * The host repeatedly issues ACMD41 for at least 1 second + * or until the busy bit are set to 1. + */ + for (i = 0; i < retries; i++) { + ret = sd_cmd_send_app_cmd(priv); + if (ret) + return ret; + + ret = sd_cmd_app_sd_send_op_cmd(priv, + capacity_support, &response); + if (ret) + return ret; + + if (response & OCR_BUSY) + break; + + early_udelay(1000); + }; + + if (i == retries) + return -EIO; + + priv->ocr = response; + + return 0; +} + +static int sd_cmd_all_send_cid(struct at91_sdhci_priv *priv) +{ + struct mci_cmd cmd = { + .cmdidx = MMC_CMD_ALL_SEND_CID, + .resp_type = SD_RESP_TYPE_R2, + }; + + return at91_sdhci_send_command(&priv->host, &cmd, NULL); +} + +static int sd_cmd_send_relative_addr(struct at91_sdhci_priv *priv) +{ + int ret; + struct mci_cmd cmd = { + .cmdidx = SD_CMD_SEND_RELATIVE_ADDR, + .resp_type = SD_RESP_TYPE_R6, + }; + + priv->rca = 1; + cmd.cmdarg = priv->rca << 16, + + ret = at91_sdhci_send_command(&priv->host, &cmd, NULL); + if (ret) + return ret; + + if (!priv->no_sd) + priv->rca = (cmd.response[0] >> 16) & 0xffff; + + return 0; +} + +static int sd_cmd_select_card(struct at91_sdhci_priv *priv) +{ + struct mci_cmd cmd = { + .cmdidx = MMC_CMD_SELECT_CARD, + .resp_type = SD_RESP_TYPE_R1, + .cmdarg = priv->rca << 16, + }; + + return at91_sdhci_send_command(&priv->host, &cmd, NULL); +} + +#define OCR_VOLTAGE_WIN_27_36 0x00FF8000 +#define OCR_ACCESS_MODE 0x60000000 + +#define OCR_ACCESS_MODE_BYTE (0x00 << 30) +#define OCR_ACCESS_MODE_SECTOR (0x01 << 30) + +static int mmc_cmd_send_op_cond(struct at91_sdhci_priv *priv, + unsigned int *ocr) +{ + struct mci_cmd cmd = { + .cmdidx = MMC_CMD_SEND_OP_COND, + .resp_type = SD_RESP_TYPE_R3, + .cmdarg = *ocr & (OCR_VOLTAGE_WIN_27_36 | OCR_ACCESS_MODE), + }; + int ret; + + ret = at91_sdhci_send_command(&priv->host, &cmd, NULL); + if (ret) + return ret; + + *ocr = cmd.response[0]; + + return 0; +} + +static int mmc_verify_operating_condition(struct at91_sdhci_priv *priv) +{ + unsigned int ocr = 0; + unsigned int retries = 1000; + unsigned int i; + int ret; + + /* Query the card and determine the voltage type of the card */ + ret = mmc_cmd_send_op_cond(priv, &ocr); + if (ret) + return ret; + + ocr |= OCR_ACCESS_MODE_SECTOR; + + for (i = 0; i < retries; i++) { + ret = mmc_cmd_send_op_cond(priv, &ocr); + if (ret) + return ret; + + if (ocr & (0x01U << 31)) + break; + + early_udelay(1000); + }; + + if (i == retries) + return -EIO; + + pr_debug("mmc_verify_operating_condition success OCR = %x\n", ocr); + return 0; +} + +/* + * Refer to Physical Layer Specification Version 3.1 + * Figure 4-1: SD Memory Card State Diagram (card identification mode) + * Figure 4-2: Card Initialization and Indentification Flow (SD mode) + */ +static int sdcard_identification(struct at91_sdhci_priv *priv) +{ + int ret; + + early_udelay(3000); + + ret = sd_cmd_go_idle_state(priv); + if (ret) + return ret; + + early_udelay(2000); + + ret = mmc_verify_operating_condition(priv); + if (ret == 0) { + priv->no_sd = true; + } else if (ret == -ETIMEDOUT) { + ret = sd_cmd_send_if_cond(priv); + if (ret == 0) /* Ver 2.00 or later SD Memory Card */ + ret = sd_check_operational_condition(priv, 1); + else if (ret == -ETIMEDOUT) + ret = sd_check_operational_condition(priv, 0); + } + + if (ret) { + pr_warn("Unusable Card\n"); + return ret; + } + + priv->highcapacity_card = priv->ocr & OCR_HCS ? 1 : 0; + + /* + * Card that is unidentified (which is in Ready State) + * sends its CID number + */ + ret = sd_cmd_all_send_cid(priv); + if (ret) { + pr_warn("sd_cmd_all_send_cid failed\n"); + return ret; + } + + /* Asks the card to publish a new relative card address (RCA) */ + ret = sd_cmd_send_relative_addr(priv); + if (ret) { + pr_warn("sd_cmd_send_relative_addr failed\n"); + return ret; + } + + pr_debug("sdcard_identification success\n"); + return 0; +} + + +static struct at91_sdhci_priv atmel_sdcard; + +int atmel_sdhci_bio_init(struct pbl_bio *bio, void __iomem *base) +{ + struct at91_sdhci_priv *priv = &atmel_sdcard; + struct at91_sdhci *host = &priv->host; + int ret; + + bio->priv = priv; + bio->read = atmel_sdhci_bio_read; + + atmel_sdhci_prep(host, base); + + at91_sdhci_host_capability(host, &priv->voltages); + + if (!at91_sdhci_is_card_inserted(host)) { + pr_err("SDHC: No Card Inserted\n"); + return -ENODEV; + } + + ret = atmel_sdhci_init(host); + if (ret) + return ret; + + /* Card Indentification Mode */ + ret = sdcard_identification(priv); + if (ret) + return ret; + + return sd_cmd_select_card(priv); +} diff --git a/drivers/mci/atmel-sdhci.h b/drivers/mci/atmel-sdhci.h new file mode 100644 index 000000000000..fb4c0e346e7d --- /dev/null +++ b/drivers/mci/atmel-sdhci.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2020 Ahmad Fatoum, Pengutronix + +#ifndef ATMEL_SDHCI_H_ +#define ATMEL_SDHCI_H_ + +#include <linux/types.h> +#include <mci.h> + +#include "sdhci.h" + +struct at91_sdhci { + struct sdhci sdhci; + void __iomem *base; +}; + +/* + * Response Types + */ +#define SD_RESP_TYPE_NO_RESP 0x00 +#define SD_RESP_TYPE_R1 0x10 +#define SD_RESP_TYPE_R1B 0x11 +#define SD_RESP_TYPE_R2 0x20 +#define SD_RESP_TYPE_R3 0x30 +#define SD_RESP_TYPE_R4 0x40 +#define SD_RESP_TYPE_R5 0x50 +#define SD_RESP_TYPE_R6 0x60 +#define SD_RESP_TYPE_R7 0x70 + +int atmel_sdhci_init(struct at91_sdhci *host); +void atmel_sdhci_prep(struct at91_sdhci *host, void __iomem *base); +int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *sd_cmd, + struct mci_data *data); +bool at91_sdhci_is_card_inserted(struct at91_sdhci *host); +void at91_sdhci_host_capability(struct at91_sdhci *host, + unsigned int *voltages); + +#endif diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h index a307dc97cd9a..48dc3f36530c 100644 --- a/drivers/mci/sdhci.h +++ b/drivers/mci/sdhci.h @@ -38,6 +38,7 @@ #define SDHCI_RESPONSE_1 0x14 #define SDHCI_RESPONSE_2 0x18 #define SDHCI_RESPONSE_3 0x1c +#define SDHCI_RESPONSE(i) (SDHCI_RESPONSE_0 + 4 * (i)) #define SDHCI_BUFFER 0x20 #define SDHCI_PRESENT_STATE 0x24 #define SDHCI_WRITE_PROTECT BIT(19) @@ -53,6 +54,7 @@ #define SDHCI_CARD_DETECT_SIGNAL_SELECTION BIT(7) #define SDHCI_CARD_DETECT_TEST_LEVEL BIT(6) #define SDHCI_DATA_WIDTH_8BIT BIT(5) +#define SDHCI_DMASEL_ADMA32 BIT(4) #define SDHCI_HIGHSPEED_EN BIT(2) #define SDHCI_DATA_WIDTH_4BIT BIT(1) #define SDHCI_POWER_CONTROL 0x29 @@ -68,8 +70,11 @@ #define SDHCI_TIMEOUT_CONTROL 0x2e #define SDHCI_SOFTWARE_RESET 0x2f #define SDHCI_RESET_ALL BIT(0) +#define SDHCI_RESET_CMD BIT(1) +#define SDHCI_RESET_DATA BIT(2) #define SDHCI_INT_STATUS 0x30 #define SDHCI_INT_NORMAL_STATUS 0x30 +#define SDHCI_INT_ERROR_ADMA BIT(25) #define SDHCI_INT_DATA_END_BIT BIT(22) #define SDHCI_INT_DATA_CRC BIT(21) #define SDHCI_INT_DATA_TIMEOUT BIT(20) @@ -79,6 +84,7 @@ #define SDHCI_INT_TIMEOUT BIT(16) #define SDHCI_INT_ERROR BIT(15) #define SDHCI_INT_CARD_INT BIT(8) +#define SDHCI_INT_CARD_INSERT BIT(6) #define SDHCI_INT_DATA_AVAIL BIT(5) #define SDHCI_INT_SPACE_AVAIL BIT(4) #define SDHCI_INT_DMA BIT(3) @@ -86,7 +92,11 @@ #define SDHCI_INT_CMD_COMPLETE BIT(0) #define SDHCI_INT_ERROR_STATUS 0x32 #define SDHCI_INT_ENABLE 0x34 +#define SDHCI_INT_NORMAL_ENABLE 0x34 +#define SDHCI_INT_ERROR_ENABLE 0x36 #define SDHCI_SIGNAL_ENABLE 0x38 +#define SDHCI_SIGNAL_NORMAL_ENABLE 0x38 +#define SDHCI_SIGNAL_ERROR_ENABLE 0x3a #define SDHCI_ACMD12_ERR__HOST_CONTROL2 0x3C #define SDHCI_CAPABILITIES 0x40 #define SDHCI_CAPABILITIES_1 0x42 @@ -96,6 +106,13 @@ #define SDHCI_HOSTCAP_HIGHSPEED BIT(5) #define SDHCI_HOSTCAP_8BIT BIT(2) +#define SDHCI_PRESET_FOR_SDR12 0x66 +#define SDHCI_PRESET_FOR_SDR25 0x68 +#define SDHCI_PRESET_FOR_SDR50 0x6A +#define SDHCI_PRESET_FOR_SDR104 0x6C +#define SDHCI_PRESET_FOR_DDR50 0x6E +#define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */ + #define SDHCI_SPEC_200_MAX_CLK_DIVIDER 256 #define SDHCI_MMC_BOOT 0xC4 -- 2.20.1 _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox