[U-Boot] [PATCH 4/5] Add support for mmc read and writes

From: Mohamed Haneef mohamed.haneef@lntinfotech.com
*Support for msm7x30 mmc read and writes
Signed-off-by: Mohamed Haneef mohamed.haneef@lntinfotech.com --- arch/arm/include/asm/arch-msm7630/mmc.h | 399 +++++++++++++++++++++ drivers/mmc/Makefile | 1 + drivers/mmc/qc_mmc.c | 584 +++++++++++++++++++++++++++++++ 3 files changed, 984 insertions(+), 0 deletions(-) create mode 100644 arch/arm/include/asm/arch-msm7630/mmc.h create mode 100644 drivers/mmc/qc_mmc.c
diff --git a/arch/arm/include/asm/arch-msm7630/mmc.h b/arch/arm/include/asm/arch-msm7630/mmc.h new file mode 100644 index 0000000..949e43c --- /dev/null +++ b/arch/arm/include/asm/arch-msm7630/mmc.h @@ -0,0 +1,399 @@ +/* + * (C) Copyright 2012 + * LARSEN & TOUBRO INFOTECH LTD <www.lntinfotech.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __MMC_H__ +#define __MMC_H__ + +#ifndef MMC_SLOT +#define MMC_SLOT 0 +#endif + +#include <common.h> +#include <part.h> +#include <mmc.h> + +/* + * Define Macros for SDCC Registers + */ +/* 8 bit */ +#define MMC_BOOT_MCI_POWER 0x000 + +/* MCICMD output control - 6th bit */ +#ifdef PLATFORM_MSM7X30 +#define MMC_BOOT_MCI_OPEN_DRAIN (1 << 6) +#define MMC_BOOT_MCI_PWR_OFF 0x00 +#define MMC_BOOT_MCI_PWR_UP 0x01 +#define MMC_BOOT_MCI_PWR_ON 0x01 +#else +#define MMC_BOOT_MCI_OPEN_DRAIN (1 << 6) +#define MMC_BOOT_MCI_PWR_OFF 0x00 +#define MMC_BOOT_MCI_PWR_UP 0x02 +#define MMC_BOOT_MCI_PWR_ON 0x03 +#endif + +/* 16 bits */ +#define MMC_BOOT_MCI_CLK 0x004 +/* Enable MCI bus clock - 0: clock disabled 1: enabled */ +#define MMC_BOOT_MCI_CLK_ENABLE (1 << 8) +/* Disable clk o/p when bus idle- 0:always enabled 1:enabled when bus active */ +#define MMC_BOOT_MCI_CLK_PWRSAVE (1 << 9) +/* Enable Widebus mode - 00: 1 bit mode 10:4 bit mode 01/11: 8 bit mode */ +#define MMC_BOOT_MCI_CLK_WIDEBUS_MODE (3 << 10) +#define MMC_BOOT_MCI_CLK_WIDEBUS_1_BIT 0 +#define MMC_BOOT_MCI_CLK_WIDEBUS_4_BIT (2 << 10) +#define MMC_BOOT_MCI_CLK_WIDEBUS_8_BIT (1 << 10) +/* Enable flow control- 0: disable 1: enable */ +#define MMC_BOOT_MCI_CLK_ENA_FLOW (1 << 12) +/* Set/clear to select rising/falling edge for data/cmd output */ +#define MMC_BOOT_MCI_CLK_INVERT_OUT (1 << 13) +/* Select to lach data/cmd coming in falling/rising/feedbk/loopbk of MCIclk */ +#define MMC_BOOT_MCI_CLK_IN_FALLING 0x0 +#define MMC_BOOT_MCI_CLK_IN_RISING (1 << 14) +#define MMC_BOOT_MCI_CLK_IN_FEEDBACK (2 << 14) +#define MMC_BOOT_MCI_CLK_IN_LOOPBACK (3 << 14) + +/* Bus Width */ +#define MMC_BOOT_BUS_WIDTH_1_BIT 0 +#define MMC_BOOT_BUS_WIDTH_4_BIT 2 +#define MMC_BOOT_BUS_WIDTH_8_BIT 3 + +/* 32 bits */ +#define MMC_BOOT_MCI_ARGUMENT 0x008 + +/* 16 bits */ +#define MMC_BOOT_MCI_CMD 0x00C +/* Command Index: 0 -5 */ +/* Waits for response if set */ +#define MMC_BOOT_MCI_CMD_RESPONSE (1 << 6) +/* Receives a 136-bit long response if set */ +#define MMC_BOOT_MCI_CMD_LONGRSP (1 << 7) +/* If set, CPSM disables command timer and waits for interrupt */ +#define MMC_BOOT_MCI_CMD_INTERRUPT (1 << 8) +/* If set waits for CmdPend before starting to send a command */ +#define MMC_BOOT_MCI_CMD_PENDING (1 << 9) +/* CPSM is enabled if set */ +#define MMC_BOOT_MCI_CMD_ENABLE (1 << 10) +/* If set PROG_DONE status bit asserted when busy is de-asserted */ +#define MMC_BOOT_MCI_CMD_PROG_ENA (1 << 11) +/* To indicate that this is a Command with Data (for SDIO interrupts) */ +#define MMC_BOOT_MCI_CMD_DAT_CMD (1 << 12) +/* Signals the next command to be an abort (stop) command. Always read 0 */ +#define MMC_BOOT_MCI_CMD_MCIABORT (1 << 13) +/* Waits for Command Completion Signal if set */ +#define MMC_BOOT_MCI_CMD_CCS_ENABLE (1 << 14) +/* If set sends CCS disable sequence */ +#define MMC_BOOT_MCI_CMD_CCS_DISABLE (1 << 15) + +#define MMC_BOOT_MCI_RESP_CMD 0x010 + +#define MMC_BOOT_MCI_RESP_0 0x014 +#define MMC_BOOT_MCI_RESP_1 0x018 +#define MMC_BOOT_MCI_RESP_2 0x01C +#define MMC_BOOT_MCI_RESP_3 0x020 + +#define MMC_BOOT_MCI_DATA_TIMER 0x024 +#define MMC_BOOT_MCI_DATA_LENGTH 0x028 +/* 16 bits */ +#define MMC_BOOT_MCI_DATA_CTL 0x02C +/* Data transfer enabled */ +#define MMC_BOOT_MCI_DATA_ENABLE (1 << 0) +/* Data transfer direction - 0: controller to card 1:card to controller */ +#define MMC_BOOT_MCI_DATA_DIR (1 << 1) +/* Data transfer mode - 0: block data transfer 1: stream data transfer */ +#define MMC_BOOT_MCI_DATA_MODE (1 << 2) +/* Enable DM interface - 0: DM disabled 1: DM enabled */ +#define MMC_BOOT_MCI_DATA_DM_ENABLE (1 << 3) +/* Data block length in bytes (1-4096) */ +#define MMC_BOOT_MCI_BLKSIZE_POS 4 +#define MMC_BOOT_MCI_DATA_COUNT 0x030 +#define MMC_BOOT_MCI_STATUS 0x034 +/* Command response received - CRC check failed */ +#define MMC_BOOT_MCI_STAT_CMD_CRC_FAIL (1 << 0) +/* Data block sent/received - CRC check failed */ +#define MMC_BOOT_MCI_STAT_DATA_CRC_FAIL (1 << 1) +/* Command resonse timeout */ +#define MMC_BOOT_MCI_STAT_CMD_TIMEOUT (1 << 2) +/* Data timeout */ +#define MMC_BOOT_MCI_STAT_DATA_TIMEOUT (1 << 3) +/* Transmit FIFO underrun error */ +#define MMC_BOOT_MCI_STAT_TX_UNDRUN (1 << 4) +/* Receive FIFO overrun error */ +#define MMC_BOOT_MCI_STAT_RX_OVRRUN (1 << 5) +/* Command response received - CRC check passed */ +#define MMC_BOOT_MCI_STAT_CMD_RESP_END (1 << 6) +/* Command sent - no response required */ +#define MMC_BOOT_MCI_STAT_CMD_SENT (1 << 7) +/* Data end - data counter zero */ +#define MMC_BOOT_MCI_STAT_DATA_END (1 << 8) +/* Start bit not detected on all data signals in wide bus mode */ +#define MMC_BOOT_MCI_STAT_START_BIT_ERR (1 << 9) +/* Data block sent/received - CRC check passed */ +#define MMC_BOOT_MCI_STAT_DATA_BLK_END (1 << 10) +/* Command transfer in progress */ +#define MMC_BOOT_MCI_STAT_CMD_ACTIVE (1 << 11) +/* Data transmit in progress */ +#define MMC_BOOT_MCI_STAT_TX_ACTIVE (1 << 12) +/* Data receive in progress */ +#define MMC_BOOT_MCI_STAT_RX_ACTIVE (1 << 13) +/* Transmit FIFO half full */ +#define MMC_BOOT_MCI_STAT_TX_FIFO_HFULL (1 << 14) +/* Receive FIFO half full */ +#define MMC_BOOT_MCI_STAT_RX_FIFO_HFULL (1 << 15) +/* Transmit FIFO full */ +#define MMC_BOOT_MCI_STAT_TX_FIFO_FULL (1 << 16) +/* Receive FIFO full */ +#define MMC_BOOT_MCI_STAT_RX_FIFO_FULL (1 << 17) +/* Transmit FIFO empty */ +#define MMC_BOOT_MCI_STAT_TX_FIFO_EMPTY (1 << 18) +/* Receive FIFO empty */ +#define MMC_BOOT_MCI_STAT_RX_FIFO_EMPTY (1 << 19) +/* Data available in transmit FIFO */ +#define MMC_BOOT_MCI_STAT_TX_DATA_AVLBL (1 << 20) +/* Data available in receive FIFO */ +#define MMC_BOOT_MCI_STAT_RX_DATA_AVLBL (1 << 21) +/* SDIO interrupt indicator for wake-up */ +#define MMC_BOOT_MCI_STAT_SDIO_INTR (1 << 22) +/* Programming done */ +#define MMC_BOOT_MCI_STAT_PROG_DONE (1 << 23) +/* CE-ATA command completion signal detected */ +#define MMC_BOOT_MCI_STAT_ATA_CMD_CMPL (1 << 24) +/* SDIO interrupt indicator for normal operation */ +#define MMC_BOOT_MCI_STAT_SDIO_INTR_OP (1 << 25) +/* Commpand completion signal timeout */ +#define MMC_BOOT_MCI_STAT_CCS_TIMEOUT (1 << 26) + +#define MMC_BOOT_MCI_STATIC_STATUS (MMC_BOOT_MCI_STAT_CMD_CRC_FAIL| \ + MMC_BOOT_MCI_STAT_DATA_CRC_FAIL| \ + MMC_BOOT_MCI_STAT_CMD_TIMEOUT| \ + MMC_BOOT_MCI_STAT_DATA_TIMEOUT| \ + MMC_BOOT_MCI_STAT_TX_UNDRUN| \ + MMC_BOOT_MCI_STAT_RX_OVRRUN| \ + MMC_BOOT_MCI_STAT_CMD_RESP_END| \ + MMC_BOOT_MCI_STAT_CMD_SENT| \ + MMC_BOOT_MCI_STAT_DATA_END| \ + MMC_BOOT_MCI_STAT_START_BIT_ERR| \ + MMC_BOOT_MCI_STAT_DATA_BLK_END| \ + MMC_BOOT_MCI_SDIO_INTR_CLR| \ + MMC_BOOT_MCI_STAT_PROG_DONE| \ + MMC_BOOT_MCI_STAT_ATA_CMD_CMPL |\ + MMC_BOOT_MCI_STAT_CCS_TIMEOUT) + +#define MMC_BOOT_MCI_CLEAR 0x038 +#define MMC_BOOT_MCI_CMD_CRC_FAIL_CLR (1 << 0) +#define MMC_BOOT_MCI_DATA_CRC_FAIL_CLR (1 << 1) +#define MMC_BOOT_MCI_CMD_TIMEOUT_CLR (1 << 2) +#define MMC_BOOT_MCI_DATA_TIMEOUT_CLR (1 << 3) +#define MMC_BOOT_MCI_TX_UNDERRUN_CLR (1 << 4) +#define MMC_BOOT_MCI_RX_OVERRUN_CLR (1 << 5) +#define MMC_BOOT_MCI_CMD_RESP_END_CLR (1 << 6) +#define MMC_BOOT_MCI_CMD_SENT_CLR (1 << 7) +#define MMC_BOOT_MCI_DATA_END_CLR (1 << 8) +#define MMC_BOOT_MCI_START_BIT_ERR_CLR (1 << 9) +#define MMC_BOOT_MCI_DATA_BLK_END_CLR (1 << 10) +#define MMC_BOOT_MCI_SDIO_INTR_CLR (1 << 22) +#define MMC_BOOT_MCI_PROG_DONE_CLR (1 << 23) +#define MMC_BOOT_MCI_ATA_CMD_COMPLR_CLR (1 << 24) +#define MMC_BOOT_MCI_CCS_TIMEOUT_CLR (1 << 25) + +#define MMC_BOOT_MCI_INT_MASK0 0x03C +#define MMC_BOOT_MCI_CMD_CRC_FAIL_MASK (1 << 0) +#define MMC_BOOT_MCI_DATA_CRC_FAIL_MASK (1 << 1) +#define MMC_BOOT_MCI_CMD_TIMEOUT_MASK (1 << 2) +#define MMC_BOOT_MCI_DATA_TIMEOUT_MASK (1 << 3) +#define MMC_BOOT_MCI_TX_OVERRUN_MASK (1 << 4) +#define MMC_BOOT_MCI_RX_OVERRUN_MASK (1 << 5) +#define MMC_BOOT_MCI_CMD_RESP_END_MASK (1 << 6) +#define MMC_BOOT_MCI_CMD_SENT_MASK (1 << 7) +#define MMC_BOOT_MCI_DATA_END_MASK (1 << 8) +#define MMC_BOOT_MCI_START_BIT_ERR_MASK (1 << 9) +#define MMC_BOOT_MCI_DATA_BLK_END_MASK (1 << 10) +#define MMC_BOOT_MCI_CMD_ACTIVE_MASK (1 << 11) +#define MMC_BOOT_MCI_TX_ACTIVE_MASK (1 << 12) +#define MMC_BOOT_MCI_RX_ACTIVE_MASK (1 << 13) +#define MMC_BOOT_MCI_TX_FIFO_HFULL_MASK (1 << 14) +#define MMC_BOOT_MCI_RX_FIFO_HFULL_MASK (1 << 15) +#define MMC_BOOT_MCI_TX_FIFO_FULL_MASK (1 << 16) +#define MMC_BOOT_MCI_RX_FIFO_FULL_MASK (1 << 17) +#define MMC_BOOT_MCI_TX_FIFO_EMPTY_MASK (1 << 18) +#define MMC_BOOT_MCI_RX_FIFO_EMPTY_MASK (1 << 19) +#define MMC_BOOT_MCI_TX_DATA_AVLBL_MASK (1 << 20) +#define MMC_BOOT_MCI_RX_DATA_AVLBL_MASK (1 << 21) +#define MMC_BOOT_MCI_SDIO_INT_MASK (1 << 22) +#define MMC_BOOT_MCI_PROG_DONE_MASK (1 << 23) +#define MMC_BOOT_MCI_ATA_CMD_COMPL_MASK (1 << 24) +#define MMC_BOOT_MCI_SDIO_INT_OPER_MASK (1 << 25) +#define MMC_BOOT_MCI_CCS_TIME_OUT_MASK (1 << 26) + +#define MMC_BOOT_MCI_INT_MASK1 0x040 + +#define MMC_BOOT_MCI_FIFO_COUNT 0x044 + +#define MMC_BOOT_MCI_CCS_TIMER 0x0058 + +#define MMC_BOOT_MCI_FIFO 0x080 + +/* OCR Register */ +#define MMC_BOOT_OCR_17_19 (1 << 7) +#define MMC_BOOT_OCR_27_36 (0x1FF << 15) +#define MMC_BOOT_OCR_SEC_MODE (2 << 29) +#define MMC_BOOT_OCR_BUSY (1 << 31) + +/* Commands type */ +#define MMC_BOOT_CMD_BCAST (1 << 0) +#define MMC_BOOT_CMD_BCAST_W_RESP (1 << 1) +#define MMC_BOOT_CMD_ADDRESS (1 << 2) +#define MMC_BOOT_CMD_ADDR_DATA_XFER (1 << 3) + +/* Card Status bits (R1 register) */ +#define MMC_BOOT_R1_AKE_SEQ_ERROR (1 << 3) +#define MMC_BOOT_R1_APP_CMD (1 << 5) +#define MMC_BOOT_R1_RDY_FOR_DATA (1 << 6) +#define MMC_BOOT_R1_CURR_STATE_IDLE (0 << 9) +#define MMC_BOOT_R1_CURR_STATE_RDY (1 << 9) +#define MMC_BOOT_R1_CURR_STATE_IDENT (2 << 9) +#define MMC_BOOT_R1_CURR_STATE_STBY (3 << 9) +#define MMC_BOOT_R1_CURR_STATE_TRAN (4 << 9) +#define MMC_BOOT_R1_CURR_STATE_DATA (5 << 9) +#define MMC_BOOT_R1_CURR_STATE_RCV (6 << 9) +#define MMC_BOOT_R1_CURR_STATE_PRG (7 << 9) +#define MMC_BOOT_R1_CURR_STATE_DIS (8 << 9) +#define MMC_BOOT_R1_ERASE_RESET (1 << 13) +#define MMC_BOOT_R1_CARD_ECC_DISABLED (1 << 14) +#define MMC_BOOT_R1_WP_ERASE_SKIP (1 << 15) +#define MMC_BOOT_R1_ERROR (1 << 19) +#define MMC_BOOT_R1_CC_ERROR (1 << 20) +#define MMC_BOOT_R1_CARD_ECC_FAILED (1 << 21) +#define MMC_BOOT_R1_ILLEGAL_CMD (1 << 22) +#define MMC_BOOT_R1_COM_CRC_ERR (1 << 23) +#define MMC_BOOT_R1_LOCK_UNLOCK_FAIL (1 << 24) +#define MMC_BOOT_R1_CARD_IS_LOCKED (1 << 25) +#define MMC_BOOT_R1_WP_VIOLATION (1 << 26) +#define MMC_BOOT_R1_ERASE_PARAM (1 << 27) +#define MMC_BOOT_R1_ERASE_SEQ_ERR (1 << 28) +#define MMC_BOOT_R1_BLOCK_LEN_ERR (1 << 29) +#define MMC_BOOT_R1_ADDR_ERR (1 << 30) +#define MMC_BOOT_R1_OUT_OF_RANGE (1 << 31) + +/* Macros for Common Errors */ +#define MMC_BOOT_E_SUCCESS 0 +#define MMC_BOOT_E_FAILURE 1 +/* Not used..use instead TIMEOUT in include/mmc.h */ +#define MMC_BOOT_E_TIMEOUT 2 +#define MMC_BOOT_E_INVAL 3 +#define MMC_BOOT_E_CRC_FAIL 4 +#define MMC_BOOT_E_INIT_FAIL 5 +#define MMC_BOOT_E_CMD_INDX_MISMATCH 6 +#define MMC_BOOT_E_RESP_VERIFY_FAIL 7 +#define MMC_BOOT_E_NOT_SUPPORTED 8 +#define MMC_BOOT_E_CARD_BUSY 9 +#define MMC_BOOT_E_MEM_ALLOC_FAIL 10 +#define MMC_BOOT_E_CLK_ENABLE_FAIL 11 +#define MMC_BOOT_E_CMMC_DECODE_FAIL 12 +#define MMC_BOOT_E_CID_DECODE_FAIL 13 +#define MMC_BOOT_E_BLOCKLEN_ERR 14 +#define MMC_BOOT_E_ADDRESS_ERR 15 +#define MMC_BOOT_E_DATA_CRC_FAIL 16 +#define MMC_BOOT_E_DATA_TIMEOUT 17 +#define MMC_BOOT_E_RX_OVRRUN 18 +#define MMC_BOOT_E_VREG_SET_FAILED 19 +#define MMC_BOOT_E_GPIO_CFG_FAIL 20 +#define MMC_BOOT_E_DATA_ADM_ERR 21 + +/* EXT_CSD */ +#define MMC_BOOT_ACCESS_WRITE 0x3 +#define MMC_BOOT_EXT_CMMC_HS_TIMING 185 +#define MMC_BOOT_EXT_CMMC_BUS_WIDTH 183 + +#define MMC_BOOT_EXT_USER_WP 171 +#define MMC_BOOT_EXT_ERASE_GROUP_DEF 175 +#define MMC_BOOT_EXT_HC_WP_GRP_SIZE 221 +#define MMC_BOOT_EXT_HC_ERASE_GRP_SIZE 224 + +#define MMC_BOOT_US_PERM_WP_EN 2 +#define MMC_BOOT_US_PWR_WP_DIS 3 + +#define MMC_BOOT_US_PERM_WP_DIS (1<<4) +#define MMC_BOOT_US_PWR_WP_EN 1 + +/* For SD */ +#define MMC_BOOT_SD_HC_VOLT_SUPPLIED 0x000001AA +#define MMC_BOOT_SD_NEG_OCR 0x00FF8000 +#define MMC_BOOT_SD_HC_HCS 0x40000000 +#define MMC_BOOT_SD_DEV_READY 0x80000000 +#define MMC_BOOT_SD_SWITCH_HS 0x80FFFFF1 + +/* Data structure definitions */ + +#define MMC_BOOT_XFER_MODE_BLOCK 0 +#define MMC_BOOT_XFER_MODE_STREAM 1 +#define MMC_BOOT_PROGRAM_ENABLED 2 + + +#define MMC_RCA 2 + +#define MMC_BOOT_MAX_COMMAND_RETRY 1000 +#define MMC_BOOT_RD_BLOCK_LEN 512 +#define MMC_BOOT_WR_BLOCK_LEN 512 + +/* We have 16 32-bits FIFO registers */ +#define MMC_BOOT_MCI_FIFO_DEPTH 16 +#define MMC_BOOT_MCI_HFIFO_COUNT (MMC_BOOT_MCI_FIFO_DEPTH / 2) +#define MMC_BOOT_MCI_FIFO_SIZE (MMC_BOOT_MCI_FIFO_DEPTH * 4) + +#define MAX_PARTITIONS 64 + +#define MMC_BOOT_CHECK_PATTERN 0xAA /* 10101010b */ + +#define MMC_CLK_400KHZ 400000 +#define MMC_CLK_144KHZ 144000 +#define MMC_CLK_20MHZ 20000000 +#define MMC_CLK_24MHZ 24000000 +#define MMC_CLK_25MHZ 25000000 +#define MMC_CLK_26MHZ 26000000 +#define MMC_CLK_48MHZ 48000000 +#define MMC_CLK_50MHZ 49152000 +#define MMC_CLK_52MHZ 52000000 + +#define MMC_CLK_ENABLE 1 +#define MMC_CLK_DISABLE 0 + +#if 0 +#define MSM_SDC1_BASE 0x12400000 +#define MSM_SDC2_BASE 0x12140000 +#define MSM_SDC3_BASE 0x12180000 +#define MSM_SDC4_BASE 0x121C0000 +#endif +struct mmc_priv { + unsigned int instance; + unsigned int base; + unsigned int rd_timeout_ns; /* for read timeout */ +}; + +void mmc_boot_set_ios(struct mmc *mmc); +int mmc_boot_send_command_map(struct mmc *mmc, + struct mmc_cmd *cmd, + struct mmc_data *data); +unsigned long int mmc_boot_mci_reg(unsigned long base, unsigned long offset); +unsigned int mmc_boot_main(struct mmc *mmc); + +extern void clock_config_mmc(uint32_t interface, uint32_t freq); +extern void clock_init_mmc(uint32_t interface); +#endif + diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 506f1d6..2d926ec 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -44,6 +44,7 @@ COBJS-$(CONFIG_S5P_MMC) += s5p_mmc.o COBJS-$(CONFIG_SDHCI) += sdhci.o COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o COBJS-$(CONFIG_TEGRA2_MMC) += tegra2_mmc.o +COBJS-$(CONFIG_QC_MMC) += qc_mmc.o
COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/mmc/qc_mmc.c b/drivers/mmc/qc_mmc.c new file mode 100644 index 0000000..7b7a1ed --- /dev/null +++ b/drivers/mmc/qc_mmc.c @@ -0,0 +1,584 @@ +/* + * (C) Copyright 2012 + * LARSEN & TOUBRO INFOTECH LTD <www.lntinfotech.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/types.h> +#include <asm/io.h> +#include <asm/arch/mmc.h> +#include <asm/arch/iomap.h> +#include <common.h> + +#if MMC_BOOT_ADM +#include "adm.h" +#endif + +#ifndef NULL +#define NULL 0 +#endif + +#define MMC_BOOT_DATA_READ 0 +#define MMC_BOOT_DATA_WRITE 1 +/* + * Calculates the address of registers according to the base address + */ +unsigned long int mmc_boot_mci_reg(unsigned long base, unsigned long offset) +{ + return base + offset; +} + + +/* + * Sets a timeout for read operation. + */ +static unsigned int mmc_boot_set_read_timeout(struct mmc *mmc) +{ + if (mmc == NULL) + return MMC_BOOT_E_INVAL; + + /* todo: Add a check for HC, only if HC do this. + * If not, taac and nsac must be taken into account + */ + ((struct mmc_priv *)(mmc->priv))->rd_timeout_ns = 100000000; + debug(" Read timeout set: %d ns\n", + ((struct mmc_priv *)(mmc->priv))->rd_timeout_ns); + + return MMC_BOOT_E_SUCCESS; +} + +/* + * Check to ensure that there is no alignment or data length errors + */ +static unsigned int mmc_boot_check_read_data(struct mmc_cmd *cmd) +{ + + if (cmd->response[0] & MMC_BOOT_R1_BLOCK_LEN_ERR) + return MMC_BOOT_E_BLOCKLEN_ERR; + + /* Misaligned address not matching block length */ + if (cmd->response[0] & MMC_BOOT_R1_ADDR_ERR) + return MMC_BOOT_E_ADDRESS_ERR; + + return MMC_BOOT_E_SUCCESS; +} + +/* + * Decode type of error caused during read and write + */ +static unsigned int mmc_boot_status_error(unsigned mmc_status) +{ + unsigned int mmc_ret = MMC_BOOT_E_SUCCESS; + + /* If DATA_CRC_FAIL bit is set to 1 then CRC error + * was detected by card/device during the data transfer + */ + if (mmc_status & MMC_BOOT_MCI_STAT_DATA_CRC_FAIL) + mmc_ret = MMC_BOOT_E_DATA_CRC_FAIL; + /* If DATA_TIMEOUT bit is set to 1 then the data transfer time + * exceeded the data timeout period without completing the + * transfer + */ + else if (mmc_status & MMC_BOOT_MCI_STAT_DATA_TIMEOUT) + mmc_ret = MMC_BOOT_E_DATA_TIMEOUT; + /* If RX_OVERRUN bit is set to 1 then SDCC2 tried to + * receive data from the card before empty storage for new + * received data was available. + * Verify that bit FLOW_ENA in MCI_CLK is set to 1 during + * the data xfer. + */ + else if (mmc_status & MMC_BOOT_MCI_STAT_RX_OVRRUN) + /* Note: We've set FLOW_ENA bit in MCI_CLK to 1. + * so no need to verify for now + */ + mmc_ret = MMC_BOOT_E_RX_OVRRUN; + /* If TX_UNDERRUN bit is set to 1 then SDCC2 tried to send + * data to the card before new data for sending was available. + * Verify that bit FLOW_ENA in MCI_CLK + * is set to 1 during the data xfer. + */ + else if (mmc_status & MMC_BOOT_MCI_STAT_TX_UNDRUN) + /* Note: We've set FLOW_ENA bit in MCI_CLK to 1. + * so skipping it now + */ + mmc_ret = MMC_BOOT_E_RX_OVRRUN; + + return mmc_ret; +} + +/* + * Read data to SDC FIFO. + */ +static unsigned int mmc_boot_fifo_read(unsigned int *mmc_ptr, + unsigned int data_len, + struct mmc *mmc) +{ + unsigned int mmc_ret = MMC_BOOT_E_SUCCESS; + unsigned int mmc_status = 0; + unsigned int mmc_count = 0; + unsigned int read_error = MMC_BOOT_MCI_STAT_DATA_CRC_FAIL | \ + MMC_BOOT_MCI_STAT_DATA_TIMEOUT | \ + MMC_BOOT_MCI_STAT_RX_OVRRUN; + unsigned int i; + struct mmc_priv *priv = (struct mmc_priv *)mmc->priv; + unsigned long reg_status, reg_fifo; + + reg_status = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_STATUS); + reg_fifo = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_FIFO); + + /* Read the data from the MCI_FIFO register as long as + * RXDATA_AVLBL bit of MCI_STATUS register is set to 1 and bits + * DATA_CRC_FAIL, DATA_TIMEOUT, RX_OVERRUN of MCI_STATUS + * register are cleared to 0. + * Continue the reads until the whole transfer data is received + */ + + do { + mmc_ret = MMC_BOOT_E_SUCCESS; + mmc_status = readl(reg_status); + + if (mmc_status & read_error) { + mmc_ret = mmc_boot_status_error(mmc_status); + break; + } + + if (mmc_status & MMC_BOOT_MCI_STAT_RX_DATA_AVLBL) { + unsigned read_count = 1; + if (mmc_status & MMC_BOOT_MCI_STAT_RX_FIFO_HFULL) + read_count = MMC_BOOT_MCI_HFIFO_COUNT; + + for (i = 0; i < read_count; i++) { + /* FIFO contains 16 32-bit data buffer + * on 16 sequential addresses + */ + *mmc_ptr = readl(reg_fifo + + (mmc_count % MMC_BOOT_MCI_FIFO_SIZE)); + mmc_ptr++; + /* increase mmc_count by word size */ + mmc_count += sizeof(unsigned int); + } + /* quit if we have read enough of data */ + if (mmc_count == data_len) + break; + } else if (mmc_status & MMC_BOOT_MCI_STAT_DATA_END) + break; + } while (1); + return mmc_ret; +} + +static unsigned int mmc_boot_fifo_data_transfer(unsigned int *data_ptr, + unsigned int data_len, + unsigned char direction, + struct mmc *mmc) +{ + unsigned int mmc_ret = MMC_BOOT_E_SUCCESS; + + if (direction == MMC_BOOT_DATA_READ) + mmc_ret = mmc_boot_fifo_read(data_ptr, data_len, mmc); + + return mmc_ret; +} + +/* + * Sends specified command to a card and waits for a response. + */ +static unsigned int mmc_boot_send_command(struct mmc_cmd *cmd, + struct mmc *mmc) +{ + unsigned int mmc_cmd = 0; + unsigned int mmc_status = 0; + unsigned int mmc_resp = 0; + unsigned int mmc_return = MMC_BOOT_E_SUCCESS; + unsigned int cmd_index = 0; + int i = 0; + unsigned long reg, reg_status, reg_resp_cmd, reg_resp_0; + struct mmc_priv *priv = (struct mmc_priv *)mmc->priv; + + /* basic check */ + if (cmd == NULL) + return MMC_BOOT_E_INVAL; + + /* 1. Write command argument to MMC_BOOT_MCI_ARGUMENT register */ + reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_ARGUMENT); + writel(cmd->cmdarg, reg); + + /* Writes to MCI port are not effective for 3 ticks of PCLK. + * ticks calculated using 400KHz clock speed + */ + udelay(8); + + /* 2. Set appropriate fields and write MMC_BOOT_MCI_CMD */ + /* 2a. Write command index in CMD_INDEX field */ + cmd_index = cmd->cmdidx; + mmc_cmd |= cmd->cmdidx; + /* 2b. Set RESPONSE bit to 1 for all cmds except CMD0 */ + if (cmd_index != MMC_CMD_GO_IDLE_STATE) + mmc_cmd |= MMC_BOOT_MCI_CMD_RESPONSE; + + /* 2c. Set LONGRESP bit to 1 for CMD2, CMD9 and CMD10 */ + if (cmd->resp_type & MMC_RSP_136) + mmc_cmd |= MMC_BOOT_MCI_CMD_LONGRSP; + + /* 2d. Set INTERRUPT bit to 1 to disable command timeout */ + + /* 2e. Set PENDING bit to 1 for CMD12 in the beginning of stream + * mode data transfer + */ + if (cmd->flags & MMC_BOOT_XFER_MODE_STREAM) + mmc_cmd |= MMC_BOOT_MCI_CMD_PENDING; + + /* 2f. Set ENABLE bit to 1 */ + mmc_cmd |= MMC_BOOT_MCI_CMD_ENABLE; + + /* 2g. Set PROG_ENA bit to 1 for CMD12, CMD13 issued at the end of + * write data transfer + */ + if ((cmd_index == MMC_CMD_STOP_TRANSMISSION || + cmd_index == MMC_CMD_SEND_STATUS) && + (cmd->flags & MMC_BOOT_PROGRAM_ENABLED)) + mmc_cmd |= MMC_BOOT_MCI_CMD_PROG_ENA; + + + /* 2h. Set MCIABORT bit to 1 for CMD12 when working with SDIO card */ + /* 2i. Set CCS_ENABLE bit to 1 for CMD61 when Command Completion Signal + * of CE-ATA device is enabled + */ + + /* 2j. clear all static status bits */ + reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_CLEAR); + writel(MMC_BOOT_MCI_STATIC_STATUS, reg); + + /* 2k. Write to MMC_BOOT_MCI_CMD register */ + reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_CMD); + writel(mmc_cmd, reg); + + /* Writes to MCI port are not effective for 3 ticks of PCLK. + * ticks calculated using 400KHz clock speed + */ + udelay(8); + + reg_status = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_STATUS); + reg_resp_cmd = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_RESP_CMD); + reg_resp_0 = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_RESP_0); + + /* 3. Wait for interrupt or poll on the following bits of MCI_STATUS + * register + */ + do { + /* 3a. Read MCI_STATUS register */ + mmc_status = readl(reg_status); + while (mmc_status & MMC_BOOT_MCI_STAT_CMD_ACTIVE) + mmc_status = readl(reg_status); + + /* 3b. CMD_SENT bit supposed to be set to 1 only + * after CMD0 is sent -no response required. + */ + if ((cmd->resp_type == MMC_RSP_NONE) && + (mmc_status & MMC_BOOT_MCI_STAT_CMD_SENT)) + break; + + /* 3c. If CMD_TIMEOUT bit is set then no response + * was received */ + else if (mmc_status & MMC_BOOT_MCI_STAT_CMD_TIMEOUT) { + mmc_return = TIMEOUT; + break; + } + /* 3d. If CMD_RESPONSE_END bit is set to 1 then command's + * response was received and CRC check passed + * Spcial case for ACMD41: it seems to always fail CRC even if + * the response is valid + */ + else if ((mmc_status & MMC_BOOT_MCI_STAT_CMD_RESP_END) || + (cmd_index == MMC_CMD_SEND_OP_COND) + || (cmd_index == SD_CMD_SEND_IF_COND)) { + /* 3i. Read MCI_RESP_CMD register to verify that + * response index is equal to command index + */ + mmc_resp = readl(reg_resp_cmd) & 0x3F; + + /* However, long response does not contain the + * command index field. + * In that case, response index field must be set to + * 111111b (0x3F) + */ + if ((mmc_resp == cmd_index) || + (cmd->resp_type == MMC_RSP_R2 || + cmd->resp_type == MMC_RSP_R3 || + cmd->resp_type == MMC_RSP_R6 || + cmd->resp_type == MMC_RSP_R7)){ + /* 3j. If resp index is equal to cmd index, + * read command resp from MCI_RESPn registers + * - MCI_RESP0/1/2/3 for CMD2/9/10 + * - MCI_RESP0 for all other registers + */ + if (cmd->resp_type & MMC_RSP_136) { + for (i = 0; i < 4; i++) + cmd->response[3-i] = + readl(reg_resp_0 + \ + (i * 4)); + } else + cmd->response[0] = readl(reg_resp_0); + } else + /* command index mis-match */ + mmc_return = MMC_BOOT_E_CMD_INDX_MISMATCH; + + break; + } + + /* 3e. If CMD_CRC_FAIL bit is set to 1 then cmd's response + * was recvd, but CRC check failed. + */ + else if ((mmc_status & MMC_BOOT_MCI_STAT_CMD_CRC_FAIL)) { + if (cmd_index == SD_CMD_APP_SEND_OP_COND) + cmd->response[0] = readl(reg_resp_0); + else + mmc_return = MMC_BOOT_E_CRC_FAIL; + break; + } + + } while (1); + + return mmc_return; +} + +static void mmc_boot_init_data(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + uint32_t data_ctrl, mmc_reg, data_len; + struct mmc_priv *priv = (struct mmc_priv *)mmc->priv; + unsigned long reg; + + /* Write data timeout period to MCI_DATA_TIMER register. */ + /* Data timeout period should be in card bus clock periods */ + mmc_reg = (unsigned long)(((struct mmc_priv *) + (mmc->priv))->rd_timeout_ns / + 1000000) * (mmc->clock / 1000); + /* add some extra clock cycles to be safe */ + mmc_reg += 1000; + mmc_reg = mmc_reg/2; + + reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_DATA_TIMER); + writel(mmc_reg, reg); + + /* Write the total size of the transfer data to MCI_DATA_LENGTH + * register. For block xfer it must be multiple of the block + * size. + */ + + data_len = data->blocks*data->blocksize; + reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_DATA_LENGTH); + writel(data_len, reg); + + /* Writes to MCI port are not effective for 3 ticks of PCLK. + * ticks calculated using 400KHz clock speed + */ + udelay(8); + + data_ctrl = MMC_BOOT_MCI_DATA_ENABLE | + (data->blocksize << MMC_BOOT_MCI_BLKSIZE_POS); + + + if (data->flags == MMC_DATA_READ) + data_ctrl |= MMC_BOOT_MCI_DATA_DIR; + + reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_DATA_CTL); + writel(data_ctrl, reg); + + /* Writes to MCI port are not effective for 3 ticks of PCLK. + * ticks calculated using 400KHz clock speed + */ + udelay(8); + +} + +/* + * Function to map mmc send command to board send command + */ +int mmc_boot_send_command_map(struct mmc *mmc, + struct mmc_cmd *cmd, + struct mmc_data *data) +{ + unsigned int mmc_ret = MMC_BOOT_E_SUCCESS; + + /* todo: do we need to fill in command type ?? */ + + if (cmd->cmdidx == MMC_CMD_SEND_STATUS) { + /* u-boot doesn't fill in the correct argument value */ + cmd->cmdarg = mmc->rca << 16; + /* this is to explicitly disable the prg enabled flag */ + cmd->flags &= ~MMC_BOOT_PROGRAM_ENABLED; + + } else if (cmd->cmdidx == MMC_CMD_READ_SINGLE_BLOCK) { + if (mmc->read_bl_len != MMC_BOOT_RD_BLOCK_LEN && + mmc->version & SD_VERSION_SD) { + mmc->read_bl_len = MMC_BOOT_RD_BLOCK_LEN; + data->blocksize = MMC_BOOT_RD_BLOCK_LEN; + } + } else if (cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK) { + if (mmc->read_bl_len != MMC_BOOT_RD_BLOCK_LEN && + mmc->version & SD_VERSION_SD) { + mmc->read_bl_len = MMC_BOOT_RD_BLOCK_LEN; + data->blocksize = MMC_BOOT_RD_BLOCK_LEN; + } + } else if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) { + /* explicitly disable the prg enabled flag */ + cmd->flags &= ~MMC_BOOT_PROGRAM_ENABLED; + /* set the XFER mode */ + cmd->flags |= MMC_BOOT_XFER_MODE_BLOCK; + } + + + /* For Data cmd's */ + if (data != NULL) + mmc_boot_init_data(mmc, cmd, data); + + /* send command */ + mmc_ret = mmc_boot_send_command(cmd, mmc); + + if (mmc_ret != MMC_BOOT_E_SUCCESS) { + debug("Return Error = %d\n", mmc_ret); + return mmc_ret; + } + + if (data != NULL) { + mmc_ret = mmc_boot_check_read_data(cmd); + if (mmc_ret) + return mmc_ret; + mmc_boot_fifo_data_transfer((unsigned int *) data->dest, + data->blocks * data->blocksize, + MMC_BOOT_DATA_READ, + mmc); + } + + if (cmd->cmdidx == MMC_CMD_SEND_CSD) { + /* Set read timeout once csd is received */ + mmc_ret = mmc_boot_set_read_timeout(mmc); + if (mmc_ret != MMC_BOOT_E_SUCCESS) { + printf("Error No.%d: Failure setting \ + write Timeout value!\n", + mmc_ret); + return mmc_ret; + } + } else if ((cmd->cmdidx == SD_CMD_SWITCH_FUNC) && (data != NULL)) { + /* cmd6 needs at least 8 clocks after the + * End Data of Status. + * ticks calculated using 400KHz clock speed + */ + udelay(20); + } + + return mmc_ret; +} + +/* + * Initialize host structure, set and enable clock-rate and power mode. + */ +unsigned int mmc_boot_init(struct mmc *mmc) +{ + unsigned int mmc_pwr = 0; + struct mmc_priv *priv = (struct mmc_priv *) mmc->priv; + int mmc_slot = priv->instance; + unsigned long reg; + + /* Initialize any clocks needed for SDC controller */ + clock_init_mmc(mmc_slot); + + /* Setup initial freq to 400KHz */ + clock_config_mmc(mmc_slot, MMC_CLK_400KHZ); + + /* set power mode*/ + mmc_pwr &= ~MMC_BOOT_MCI_PWR_UP; + mmc_pwr |= MMC_BOOT_MCI_PWR_ON; + mmc_pwr |= MMC_BOOT_MCI_PWR_UP; + reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_POWER); + writel(mmc_pwr, reg); + + /* Writes to MCI port are not effective for 3 ticks of PCLK. + * ticks calculated using 400KHz clock speed + */ + udelay(8); + + return MMC_BOOT_E_SUCCESS; +} + +static unsigned int mmc_boot_set_bus_width(unsigned int width, + struct mmc *mmc) +{ + unsigned int mmc_reg; + struct mmc_priv *priv = (struct mmc_priv *)mmc->priv; + unsigned long reg; + + /* set MCI_CLK accordingly */ + reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_CLK); + mmc_reg = readl(reg); + mmc_reg &= ~MMC_BOOT_MCI_CLK_WIDEBUS_MODE; + if (width == 1) + mmc_reg |= MMC_BOOT_MCI_CLK_WIDEBUS_1_BIT; + else if (width == 4) + mmc_reg |= MMC_BOOT_MCI_CLK_WIDEBUS_4_BIT; + else if (width == 8) + mmc_reg |= MMC_BOOT_MCI_CLK_WIDEBUS_8_BIT; + else + return MMC_BOOT_E_INVAL; + + writel(mmc_reg, reg); + + /* Writes to MCI port are not effective for 3 ticks of PCLK. + * ticks calculated using 400KHz clock speed + */ + udelay(8); + + debug("Setting bus width to %d\n", width); + return MMC_BOOT_E_SUCCESS; +} + +void mmc_boot_set_ios(struct mmc *mmc) +{ + unsigned int mmc_ret = MMC_BOOT_E_SUCCESS; + struct mmc_priv *priv = (struct mmc_priv *)mmc->priv; + int mmc_slot = priv->instance; + + /* clock frequency should be less than 400KHz in identification mode */ + clock_config_mmc(mmc_slot, mmc->clock); + /* + * give atleast 2 MCLK cycles delay for clocks + * and SDCC core to stabilize. + * ticks calculated using 400KHz clock speed + */ + udelay(5); + mmc_ret = mmc_boot_set_bus_width(mmc->bus_width, mmc); + if (mmc_ret != MMC_BOOT_E_SUCCESS) + printf("Set bus width error %d\n", mmc_ret); +} + +/* + * Board specific initializations + */ +unsigned int mmc_boot_main(struct mmc *mmc) +{ + unsigned int mmc_ret = MMC_BOOT_E_SUCCESS; + + /* Initialize necessary data structure and enable/set clock and power */ + debug(" Initializing MMC host data structure and clock!\n"); + mmc_ret = mmc_boot_init(mmc); + if (mmc_ret != MMC_BOOT_E_SUCCESS) { + printf("MMC Boot: Error Initializing MMC Card!!!\n"); + return MMC_BOOT_E_FAILURE; + } + + return MMC_BOOT_E_SUCCESS; +} -- 1.7.1
The contents of this e-mail and any attachment(s) may contain confidential or privileged information for the intended recipient(s). Unintended recipients are prohibited from taking action on the basis of information in this e-mail and using or disseminating the information, and must notify the sender and delete it from their system. L&T Infotech will not accept responsibility or liability for the accuracy or completeness of, or the presence of any virus or disabling code in this e-mail"

Hi Mohamed,
Le 16/02/2012 03:59, mohamed.haneef@lntinfotech.com a écrit :
From: Mohamed Haneefmohamed.haneef@lntinfotech.com
*Support for msm7x30 mmc read and writes
The patch title is misleading. MMC reads and writes are already supported in U-Boot; what you add is support for the qc_mc MMC controller.
Amicalement,

* Support for qc_mmc MMC Controller
Signed-off-by: Mohamed Haneef mohamed.haneef@lntinfotech.com --- change for v2: - changed the patch title to "support for qc_mmc MMC Controller"
arch/arm/include/asm/arch-msm7630/mmc.h | 399 +++++++++++++++++++++ drivers/mmc/Makefile | 1 + drivers/mmc/qc_mmc.c | 584 +++++++++++++++++++++++++++++++ 3 files changed, 984 insertions(+), 0 deletions(-) create mode 100644 arch/arm/include/asm/arch-msm7630/mmc.h create mode 100644 drivers/mmc/qc_mmc.c
diff --git a/arch/arm/include/asm/arch-msm7630/mmc.h b/arch/arm/include/asm/arch-msm7630/mmc.h new file mode 100644 index 0000000..949e43c --- /dev/null +++ b/arch/arm/include/asm/arch-msm7630/mmc.h @@ -0,0 +1,399 @@ +/* + * (C) Copyright 2012 + * LARSEN & TOUBRO INFOTECH LTD <www.lntinfotech.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __MMC_H__ +#define __MMC_H__ + +#ifndef MMC_SLOT +#define MMC_SLOT 0 +#endif + +#include <common.h> +#include <part.h> +#include <mmc.h> + +/* + * Define Macros for SDCC Registers + */ +/* 8 bit */ +#define MMC_BOOT_MCI_POWER 0x000 + +/* MCICMD output control - 6th bit */ +#ifdef PLATFORM_MSM7X30 +#define MMC_BOOT_MCI_OPEN_DRAIN (1 << 6) +#define MMC_BOOT_MCI_PWR_OFF 0x00 +#define MMC_BOOT_MCI_PWR_UP 0x01 +#define MMC_BOOT_MCI_PWR_ON 0x01 +#else +#define MMC_BOOT_MCI_OPEN_DRAIN (1 << 6) +#define MMC_BOOT_MCI_PWR_OFF 0x00 +#define MMC_BOOT_MCI_PWR_UP 0x02 +#define MMC_BOOT_MCI_PWR_ON 0x03 +#endif + +/* 16 bits */ +#define MMC_BOOT_MCI_CLK 0x004 +/* Enable MCI bus clock - 0: clock disabled 1: enabled */ +#define MMC_BOOT_MCI_CLK_ENABLE (1 << 8) +/* Disable clk o/p when bus idle- 0:always enabled 1:enabled when bus active */ +#define MMC_BOOT_MCI_CLK_PWRSAVE (1 << 9) +/* Enable Widebus mode - 00: 1 bit mode 10:4 bit mode 01/11: 8 bit mode */ +#define MMC_BOOT_MCI_CLK_WIDEBUS_MODE (3 << 10) +#define MMC_BOOT_MCI_CLK_WIDEBUS_1_BIT 0 +#define MMC_BOOT_MCI_CLK_WIDEBUS_4_BIT (2 << 10) +#define MMC_BOOT_MCI_CLK_WIDEBUS_8_BIT (1 << 10) +/* Enable flow control- 0: disable 1: enable */ +#define MMC_BOOT_MCI_CLK_ENA_FLOW (1 << 12) +/* Set/clear to select rising/falling edge for data/cmd output */ +#define MMC_BOOT_MCI_CLK_INVERT_OUT (1 << 13) +/* Select to lach data/cmd coming in falling/rising/feedbk/loopbk of MCIclk */ +#define MMC_BOOT_MCI_CLK_IN_FALLING 0x0 +#define MMC_BOOT_MCI_CLK_IN_RISING (1 << 14) +#define MMC_BOOT_MCI_CLK_IN_FEEDBACK (2 << 14) +#define MMC_BOOT_MCI_CLK_IN_LOOPBACK (3 << 14) + +/* Bus Width */ +#define MMC_BOOT_BUS_WIDTH_1_BIT 0 +#define MMC_BOOT_BUS_WIDTH_4_BIT 2 +#define MMC_BOOT_BUS_WIDTH_8_BIT 3 + +/* 32 bits */ +#define MMC_BOOT_MCI_ARGUMENT 0x008 + +/* 16 bits */ +#define MMC_BOOT_MCI_CMD 0x00C +/* Command Index: 0 -5 */ +/* Waits for response if set */ +#define MMC_BOOT_MCI_CMD_RESPONSE (1 << 6) +/* Receives a 136-bit long response if set */ +#define MMC_BOOT_MCI_CMD_LONGRSP (1 << 7) +/* If set, CPSM disables command timer and waits for interrupt */ +#define MMC_BOOT_MCI_CMD_INTERRUPT (1 << 8) +/* If set waits for CmdPend before starting to send a command */ +#define MMC_BOOT_MCI_CMD_PENDING (1 << 9) +/* CPSM is enabled if set */ +#define MMC_BOOT_MCI_CMD_ENABLE (1 << 10) +/* If set PROG_DONE status bit asserted when busy is de-asserted */ +#define MMC_BOOT_MCI_CMD_PROG_ENA (1 << 11) +/* To indicate that this is a Command with Data (for SDIO interrupts) */ +#define MMC_BOOT_MCI_CMD_DAT_CMD (1 << 12) +/* Signals the next command to be an abort (stop) command. Always read 0 */ +#define MMC_BOOT_MCI_CMD_MCIABORT (1 << 13) +/* Waits for Command Completion Signal if set */ +#define MMC_BOOT_MCI_CMD_CCS_ENABLE (1 << 14) +/* If set sends CCS disable sequence */ +#define MMC_BOOT_MCI_CMD_CCS_DISABLE (1 << 15) + +#define MMC_BOOT_MCI_RESP_CMD 0x010 + +#define MMC_BOOT_MCI_RESP_0 0x014 +#define MMC_BOOT_MCI_RESP_1 0x018 +#define MMC_BOOT_MCI_RESP_2 0x01C +#define MMC_BOOT_MCI_RESP_3 0x020 + +#define MMC_BOOT_MCI_DATA_TIMER 0x024 +#define MMC_BOOT_MCI_DATA_LENGTH 0x028 +/* 16 bits */ +#define MMC_BOOT_MCI_DATA_CTL 0x02C +/* Data transfer enabled */ +#define MMC_BOOT_MCI_DATA_ENABLE (1 << 0) +/* Data transfer direction - 0: controller to card 1:card to controller */ +#define MMC_BOOT_MCI_DATA_DIR (1 << 1) +/* Data transfer mode - 0: block data transfer 1: stream data transfer */ +#define MMC_BOOT_MCI_DATA_MODE (1 << 2) +/* Enable DM interface - 0: DM disabled 1: DM enabled */ +#define MMC_BOOT_MCI_DATA_DM_ENABLE (1 << 3) +/* Data block length in bytes (1-4096) */ +#define MMC_BOOT_MCI_BLKSIZE_POS 4 +#define MMC_BOOT_MCI_DATA_COUNT 0x030 +#define MMC_BOOT_MCI_STATUS 0x034 +/* Command response received - CRC check failed */ +#define MMC_BOOT_MCI_STAT_CMD_CRC_FAIL (1 << 0) +/* Data block sent/received - CRC check failed */ +#define MMC_BOOT_MCI_STAT_DATA_CRC_FAIL (1 << 1) +/* Command resonse timeout */ +#define MMC_BOOT_MCI_STAT_CMD_TIMEOUT (1 << 2) +/* Data timeout */ +#define MMC_BOOT_MCI_STAT_DATA_TIMEOUT (1 << 3) +/* Transmit FIFO underrun error */ +#define MMC_BOOT_MCI_STAT_TX_UNDRUN (1 << 4) +/* Receive FIFO overrun error */ +#define MMC_BOOT_MCI_STAT_RX_OVRRUN (1 << 5) +/* Command response received - CRC check passed */ +#define MMC_BOOT_MCI_STAT_CMD_RESP_END (1 << 6) +/* Command sent - no response required */ +#define MMC_BOOT_MCI_STAT_CMD_SENT (1 << 7) +/* Data end - data counter zero */ +#define MMC_BOOT_MCI_STAT_DATA_END (1 << 8) +/* Start bit not detected on all data signals in wide bus mode */ +#define MMC_BOOT_MCI_STAT_START_BIT_ERR (1 << 9) +/* Data block sent/received - CRC check passed */ +#define MMC_BOOT_MCI_STAT_DATA_BLK_END (1 << 10) +/* Command transfer in progress */ +#define MMC_BOOT_MCI_STAT_CMD_ACTIVE (1 << 11) +/* Data transmit in progress */ +#define MMC_BOOT_MCI_STAT_TX_ACTIVE (1 << 12) +/* Data receive in progress */ +#define MMC_BOOT_MCI_STAT_RX_ACTIVE (1 << 13) +/* Transmit FIFO half full */ +#define MMC_BOOT_MCI_STAT_TX_FIFO_HFULL (1 << 14) +/* Receive FIFO half full */ +#define MMC_BOOT_MCI_STAT_RX_FIFO_HFULL (1 << 15) +/* Transmit FIFO full */ +#define MMC_BOOT_MCI_STAT_TX_FIFO_FULL (1 << 16) +/* Receive FIFO full */ +#define MMC_BOOT_MCI_STAT_RX_FIFO_FULL (1 << 17) +/* Transmit FIFO empty */ +#define MMC_BOOT_MCI_STAT_TX_FIFO_EMPTY (1 << 18) +/* Receive FIFO empty */ +#define MMC_BOOT_MCI_STAT_RX_FIFO_EMPTY (1 << 19) +/* Data available in transmit FIFO */ +#define MMC_BOOT_MCI_STAT_TX_DATA_AVLBL (1 << 20) +/* Data available in receive FIFO */ +#define MMC_BOOT_MCI_STAT_RX_DATA_AVLBL (1 << 21) +/* SDIO interrupt indicator for wake-up */ +#define MMC_BOOT_MCI_STAT_SDIO_INTR (1 << 22) +/* Programming done */ +#define MMC_BOOT_MCI_STAT_PROG_DONE (1 << 23) +/* CE-ATA command completion signal detected */ +#define MMC_BOOT_MCI_STAT_ATA_CMD_CMPL (1 << 24) +/* SDIO interrupt indicator for normal operation */ +#define MMC_BOOT_MCI_STAT_SDIO_INTR_OP (1 << 25) +/* Commpand completion signal timeout */ +#define MMC_BOOT_MCI_STAT_CCS_TIMEOUT (1 << 26) + +#define MMC_BOOT_MCI_STATIC_STATUS (MMC_BOOT_MCI_STAT_CMD_CRC_FAIL| \ + MMC_BOOT_MCI_STAT_DATA_CRC_FAIL| \ + MMC_BOOT_MCI_STAT_CMD_TIMEOUT| \ + MMC_BOOT_MCI_STAT_DATA_TIMEOUT| \ + MMC_BOOT_MCI_STAT_TX_UNDRUN| \ + MMC_BOOT_MCI_STAT_RX_OVRRUN| \ + MMC_BOOT_MCI_STAT_CMD_RESP_END| \ + MMC_BOOT_MCI_STAT_CMD_SENT| \ + MMC_BOOT_MCI_STAT_DATA_END| \ + MMC_BOOT_MCI_STAT_START_BIT_ERR| \ + MMC_BOOT_MCI_STAT_DATA_BLK_END| \ + MMC_BOOT_MCI_SDIO_INTR_CLR| \ + MMC_BOOT_MCI_STAT_PROG_DONE| \ + MMC_BOOT_MCI_STAT_ATA_CMD_CMPL |\ + MMC_BOOT_MCI_STAT_CCS_TIMEOUT) + +#define MMC_BOOT_MCI_CLEAR 0x038 +#define MMC_BOOT_MCI_CMD_CRC_FAIL_CLR (1 << 0) +#define MMC_BOOT_MCI_DATA_CRC_FAIL_CLR (1 << 1) +#define MMC_BOOT_MCI_CMD_TIMEOUT_CLR (1 << 2) +#define MMC_BOOT_MCI_DATA_TIMEOUT_CLR (1 << 3) +#define MMC_BOOT_MCI_TX_UNDERRUN_CLR (1 << 4) +#define MMC_BOOT_MCI_RX_OVERRUN_CLR (1 << 5) +#define MMC_BOOT_MCI_CMD_RESP_END_CLR (1 << 6) +#define MMC_BOOT_MCI_CMD_SENT_CLR (1 << 7) +#define MMC_BOOT_MCI_DATA_END_CLR (1 << 8) +#define MMC_BOOT_MCI_START_BIT_ERR_CLR (1 << 9) +#define MMC_BOOT_MCI_DATA_BLK_END_CLR (1 << 10) +#define MMC_BOOT_MCI_SDIO_INTR_CLR (1 << 22) +#define MMC_BOOT_MCI_PROG_DONE_CLR (1 << 23) +#define MMC_BOOT_MCI_ATA_CMD_COMPLR_CLR (1 << 24) +#define MMC_BOOT_MCI_CCS_TIMEOUT_CLR (1 << 25) + +#define MMC_BOOT_MCI_INT_MASK0 0x03C +#define MMC_BOOT_MCI_CMD_CRC_FAIL_MASK (1 << 0) +#define MMC_BOOT_MCI_DATA_CRC_FAIL_MASK (1 << 1) +#define MMC_BOOT_MCI_CMD_TIMEOUT_MASK (1 << 2) +#define MMC_BOOT_MCI_DATA_TIMEOUT_MASK (1 << 3) +#define MMC_BOOT_MCI_TX_OVERRUN_MASK (1 << 4) +#define MMC_BOOT_MCI_RX_OVERRUN_MASK (1 << 5) +#define MMC_BOOT_MCI_CMD_RESP_END_MASK (1 << 6) +#define MMC_BOOT_MCI_CMD_SENT_MASK (1 << 7) +#define MMC_BOOT_MCI_DATA_END_MASK (1 << 8) +#define MMC_BOOT_MCI_START_BIT_ERR_MASK (1 << 9) +#define MMC_BOOT_MCI_DATA_BLK_END_MASK (1 << 10) +#define MMC_BOOT_MCI_CMD_ACTIVE_MASK (1 << 11) +#define MMC_BOOT_MCI_TX_ACTIVE_MASK (1 << 12) +#define MMC_BOOT_MCI_RX_ACTIVE_MASK (1 << 13) +#define MMC_BOOT_MCI_TX_FIFO_HFULL_MASK (1 << 14) +#define MMC_BOOT_MCI_RX_FIFO_HFULL_MASK (1 << 15) +#define MMC_BOOT_MCI_TX_FIFO_FULL_MASK (1 << 16) +#define MMC_BOOT_MCI_RX_FIFO_FULL_MASK (1 << 17) +#define MMC_BOOT_MCI_TX_FIFO_EMPTY_MASK (1 << 18) +#define MMC_BOOT_MCI_RX_FIFO_EMPTY_MASK (1 << 19) +#define MMC_BOOT_MCI_TX_DATA_AVLBL_MASK (1 << 20) +#define MMC_BOOT_MCI_RX_DATA_AVLBL_MASK (1 << 21) +#define MMC_BOOT_MCI_SDIO_INT_MASK (1 << 22) +#define MMC_BOOT_MCI_PROG_DONE_MASK (1 << 23) +#define MMC_BOOT_MCI_ATA_CMD_COMPL_MASK (1 << 24) +#define MMC_BOOT_MCI_SDIO_INT_OPER_MASK (1 << 25) +#define MMC_BOOT_MCI_CCS_TIME_OUT_MASK (1 << 26) + +#define MMC_BOOT_MCI_INT_MASK1 0x040 + +#define MMC_BOOT_MCI_FIFO_COUNT 0x044 + +#define MMC_BOOT_MCI_CCS_TIMER 0x0058 + +#define MMC_BOOT_MCI_FIFO 0x080 + +/* OCR Register */ +#define MMC_BOOT_OCR_17_19 (1 << 7) +#define MMC_BOOT_OCR_27_36 (0x1FF << 15) +#define MMC_BOOT_OCR_SEC_MODE (2 << 29) +#define MMC_BOOT_OCR_BUSY (1 << 31) + +/* Commands type */ +#define MMC_BOOT_CMD_BCAST (1 << 0) +#define MMC_BOOT_CMD_BCAST_W_RESP (1 << 1) +#define MMC_BOOT_CMD_ADDRESS (1 << 2) +#define MMC_BOOT_CMD_ADDR_DATA_XFER (1 << 3) + +/* Card Status bits (R1 register) */ +#define MMC_BOOT_R1_AKE_SEQ_ERROR (1 << 3) +#define MMC_BOOT_R1_APP_CMD (1 << 5) +#define MMC_BOOT_R1_RDY_FOR_DATA (1 << 6) +#define MMC_BOOT_R1_CURR_STATE_IDLE (0 << 9) +#define MMC_BOOT_R1_CURR_STATE_RDY (1 << 9) +#define MMC_BOOT_R1_CURR_STATE_IDENT (2 << 9) +#define MMC_BOOT_R1_CURR_STATE_STBY (3 << 9) +#define MMC_BOOT_R1_CURR_STATE_TRAN (4 << 9) +#define MMC_BOOT_R1_CURR_STATE_DATA (5 << 9) +#define MMC_BOOT_R1_CURR_STATE_RCV (6 << 9) +#define MMC_BOOT_R1_CURR_STATE_PRG (7 << 9) +#define MMC_BOOT_R1_CURR_STATE_DIS (8 << 9) +#define MMC_BOOT_R1_ERASE_RESET (1 << 13) +#define MMC_BOOT_R1_CARD_ECC_DISABLED (1 << 14) +#define MMC_BOOT_R1_WP_ERASE_SKIP (1 << 15) +#define MMC_BOOT_R1_ERROR (1 << 19) +#define MMC_BOOT_R1_CC_ERROR (1 << 20) +#define MMC_BOOT_R1_CARD_ECC_FAILED (1 << 21) +#define MMC_BOOT_R1_ILLEGAL_CMD (1 << 22) +#define MMC_BOOT_R1_COM_CRC_ERR (1 << 23) +#define MMC_BOOT_R1_LOCK_UNLOCK_FAIL (1 << 24) +#define MMC_BOOT_R1_CARD_IS_LOCKED (1 << 25) +#define MMC_BOOT_R1_WP_VIOLATION (1 << 26) +#define MMC_BOOT_R1_ERASE_PARAM (1 << 27) +#define MMC_BOOT_R1_ERASE_SEQ_ERR (1 << 28) +#define MMC_BOOT_R1_BLOCK_LEN_ERR (1 << 29) +#define MMC_BOOT_R1_ADDR_ERR (1 << 30) +#define MMC_BOOT_R1_OUT_OF_RANGE (1 << 31) + +/* Macros for Common Errors */ +#define MMC_BOOT_E_SUCCESS 0 +#define MMC_BOOT_E_FAILURE 1 +/* Not used..use instead TIMEOUT in include/mmc.h */ +#define MMC_BOOT_E_TIMEOUT 2 +#define MMC_BOOT_E_INVAL 3 +#define MMC_BOOT_E_CRC_FAIL 4 +#define MMC_BOOT_E_INIT_FAIL 5 +#define MMC_BOOT_E_CMD_INDX_MISMATCH 6 +#define MMC_BOOT_E_RESP_VERIFY_FAIL 7 +#define MMC_BOOT_E_NOT_SUPPORTED 8 +#define MMC_BOOT_E_CARD_BUSY 9 +#define MMC_BOOT_E_MEM_ALLOC_FAIL 10 +#define MMC_BOOT_E_CLK_ENABLE_FAIL 11 +#define MMC_BOOT_E_CMMC_DECODE_FAIL 12 +#define MMC_BOOT_E_CID_DECODE_FAIL 13 +#define MMC_BOOT_E_BLOCKLEN_ERR 14 +#define MMC_BOOT_E_ADDRESS_ERR 15 +#define MMC_BOOT_E_DATA_CRC_FAIL 16 +#define MMC_BOOT_E_DATA_TIMEOUT 17 +#define MMC_BOOT_E_RX_OVRRUN 18 +#define MMC_BOOT_E_VREG_SET_FAILED 19 +#define MMC_BOOT_E_GPIO_CFG_FAIL 20 +#define MMC_BOOT_E_DATA_ADM_ERR 21 + +/* EXT_CSD */ +#define MMC_BOOT_ACCESS_WRITE 0x3 +#define MMC_BOOT_EXT_CMMC_HS_TIMING 185 +#define MMC_BOOT_EXT_CMMC_BUS_WIDTH 183 + +#define MMC_BOOT_EXT_USER_WP 171 +#define MMC_BOOT_EXT_ERASE_GROUP_DEF 175 +#define MMC_BOOT_EXT_HC_WP_GRP_SIZE 221 +#define MMC_BOOT_EXT_HC_ERASE_GRP_SIZE 224 + +#define MMC_BOOT_US_PERM_WP_EN 2 +#define MMC_BOOT_US_PWR_WP_DIS 3 + +#define MMC_BOOT_US_PERM_WP_DIS (1<<4) +#define MMC_BOOT_US_PWR_WP_EN 1 + +/* For SD */ +#define MMC_BOOT_SD_HC_VOLT_SUPPLIED 0x000001AA +#define MMC_BOOT_SD_NEG_OCR 0x00FF8000 +#define MMC_BOOT_SD_HC_HCS 0x40000000 +#define MMC_BOOT_SD_DEV_READY 0x80000000 +#define MMC_BOOT_SD_SWITCH_HS 0x80FFFFF1 + +/* Data structure definitions */ + +#define MMC_BOOT_XFER_MODE_BLOCK 0 +#define MMC_BOOT_XFER_MODE_STREAM 1 +#define MMC_BOOT_PROGRAM_ENABLED 2 + + +#define MMC_RCA 2 + +#define MMC_BOOT_MAX_COMMAND_RETRY 1000 +#define MMC_BOOT_RD_BLOCK_LEN 512 +#define MMC_BOOT_WR_BLOCK_LEN 512 + +/* We have 16 32-bits FIFO registers */ +#define MMC_BOOT_MCI_FIFO_DEPTH 16 +#define MMC_BOOT_MCI_HFIFO_COUNT (MMC_BOOT_MCI_FIFO_DEPTH / 2) +#define MMC_BOOT_MCI_FIFO_SIZE (MMC_BOOT_MCI_FIFO_DEPTH * 4) + +#define MAX_PARTITIONS 64 + +#define MMC_BOOT_CHECK_PATTERN 0xAA /* 10101010b */ + +#define MMC_CLK_400KHZ 400000 +#define MMC_CLK_144KHZ 144000 +#define MMC_CLK_20MHZ 20000000 +#define MMC_CLK_24MHZ 24000000 +#define MMC_CLK_25MHZ 25000000 +#define MMC_CLK_26MHZ 26000000 +#define MMC_CLK_48MHZ 48000000 +#define MMC_CLK_50MHZ 49152000 +#define MMC_CLK_52MHZ 52000000 + +#define MMC_CLK_ENABLE 1 +#define MMC_CLK_DISABLE 0 + +#if 0 +#define MSM_SDC1_BASE 0x12400000 +#define MSM_SDC2_BASE 0x12140000 +#define MSM_SDC3_BASE 0x12180000 +#define MSM_SDC4_BASE 0x121C0000 +#endif +struct mmc_priv { + unsigned int instance; + unsigned int base; + unsigned int rd_timeout_ns; /* for read timeout */ +}; + +void mmc_boot_set_ios(struct mmc *mmc); +int mmc_boot_send_command_map(struct mmc *mmc, + struct mmc_cmd *cmd, + struct mmc_data *data); +unsigned long int mmc_boot_mci_reg(unsigned long base, unsigned long offset); +unsigned int mmc_boot_main(struct mmc *mmc); + +extern void clock_config_mmc(uint32_t interface, uint32_t freq); +extern void clock_init_mmc(uint32_t interface); +#endif + diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 506f1d6..2d926ec 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -44,6 +44,7 @@ COBJS-$(CONFIG_S5P_MMC) += s5p_mmc.o COBJS-$(CONFIG_SDHCI) += sdhci.o COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o COBJS-$(CONFIG_TEGRA2_MMC) += tegra2_mmc.o +COBJS-$(CONFIG_QC_MMC) += qc_mmc.o
COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/mmc/qc_mmc.c b/drivers/mmc/qc_mmc.c new file mode 100644 index 0000000..7b7a1ed --- /dev/null +++ b/drivers/mmc/qc_mmc.c @@ -0,0 +1,584 @@ +/* + * (C) Copyright 2012 + * LARSEN & TOUBRO INFOTECH LTD <www.lntinfotech.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/types.h> +#include <asm/io.h> +#include <asm/arch/mmc.h> +#include <asm/arch/iomap.h> +#include <common.h> + +#if MMC_BOOT_ADM +#include "adm.h" +#endif + +#ifndef NULL +#define NULL 0 +#endif + +#define MMC_BOOT_DATA_READ 0 +#define MMC_BOOT_DATA_WRITE 1 +/* + * Calculates the address of registers according to the base address + */ +unsigned long int mmc_boot_mci_reg(unsigned long base, unsigned long offset) +{ + return base + offset; +} + + +/* + * Sets a timeout for read operation. + */ +static unsigned int mmc_boot_set_read_timeout(struct mmc *mmc) +{ + if (mmc == NULL) + return MMC_BOOT_E_INVAL; + + /* todo: Add a check for HC, only if HC do this. + * If not, taac and nsac must be taken into account + */ + ((struct mmc_priv *)(mmc->priv))->rd_timeout_ns = 100000000; + debug(" Read timeout set: %d ns\n", + ((struct mmc_priv *)(mmc->priv))->rd_timeout_ns); + + return MMC_BOOT_E_SUCCESS; +} + +/* + * Check to ensure that there is no alignment or data length errors + */ +static unsigned int mmc_boot_check_read_data(struct mmc_cmd *cmd) +{ + + if (cmd->response[0] & MMC_BOOT_R1_BLOCK_LEN_ERR) + return MMC_BOOT_E_BLOCKLEN_ERR; + + /* Misaligned address not matching block length */ + if (cmd->response[0] & MMC_BOOT_R1_ADDR_ERR) + return MMC_BOOT_E_ADDRESS_ERR; + + return MMC_BOOT_E_SUCCESS; +} + +/* + * Decode type of error caused during read and write + */ +static unsigned int mmc_boot_status_error(unsigned mmc_status) +{ + unsigned int mmc_ret = MMC_BOOT_E_SUCCESS; + + /* If DATA_CRC_FAIL bit is set to 1 then CRC error + * was detected by card/device during the data transfer + */ + if (mmc_status & MMC_BOOT_MCI_STAT_DATA_CRC_FAIL) + mmc_ret = MMC_BOOT_E_DATA_CRC_FAIL; + /* If DATA_TIMEOUT bit is set to 1 then the data transfer time + * exceeded the data timeout period without completing the + * transfer + */ + else if (mmc_status & MMC_BOOT_MCI_STAT_DATA_TIMEOUT) + mmc_ret = MMC_BOOT_E_DATA_TIMEOUT; + /* If RX_OVERRUN bit is set to 1 then SDCC2 tried to + * receive data from the card before empty storage for new + * received data was available. + * Verify that bit FLOW_ENA in MCI_CLK is set to 1 during + * the data xfer. + */ + else if (mmc_status & MMC_BOOT_MCI_STAT_RX_OVRRUN) + /* Note: We've set FLOW_ENA bit in MCI_CLK to 1. + * so no need to verify for now + */ + mmc_ret = MMC_BOOT_E_RX_OVRRUN; + /* If TX_UNDERRUN bit is set to 1 then SDCC2 tried to send + * data to the card before new data for sending was available. + * Verify that bit FLOW_ENA in MCI_CLK + * is set to 1 during the data xfer. + */ + else if (mmc_status & MMC_BOOT_MCI_STAT_TX_UNDRUN) + /* Note: We've set FLOW_ENA bit in MCI_CLK to 1. + * so skipping it now + */ + mmc_ret = MMC_BOOT_E_RX_OVRRUN; + + return mmc_ret; +} + +/* + * Read data to SDC FIFO. + */ +static unsigned int mmc_boot_fifo_read(unsigned int *mmc_ptr, + unsigned int data_len, + struct mmc *mmc) +{ + unsigned int mmc_ret = MMC_BOOT_E_SUCCESS; + unsigned int mmc_status = 0; + unsigned int mmc_count = 0; + unsigned int read_error = MMC_BOOT_MCI_STAT_DATA_CRC_FAIL | \ + MMC_BOOT_MCI_STAT_DATA_TIMEOUT | \ + MMC_BOOT_MCI_STAT_RX_OVRRUN; + unsigned int i; + struct mmc_priv *priv = (struct mmc_priv *)mmc->priv; + unsigned long reg_status, reg_fifo; + + reg_status = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_STATUS); + reg_fifo = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_FIFO); + + /* Read the data from the MCI_FIFO register as long as + * RXDATA_AVLBL bit of MCI_STATUS register is set to 1 and bits + * DATA_CRC_FAIL, DATA_TIMEOUT, RX_OVERRUN of MCI_STATUS + * register are cleared to 0. + * Continue the reads until the whole transfer data is received + */ + + do { + mmc_ret = MMC_BOOT_E_SUCCESS; + mmc_status = readl(reg_status); + + if (mmc_status & read_error) { + mmc_ret = mmc_boot_status_error(mmc_status); + break; + } + + if (mmc_status & MMC_BOOT_MCI_STAT_RX_DATA_AVLBL) { + unsigned read_count = 1; + if (mmc_status & MMC_BOOT_MCI_STAT_RX_FIFO_HFULL) + read_count = MMC_BOOT_MCI_HFIFO_COUNT; + + for (i = 0; i < read_count; i++) { + /* FIFO contains 16 32-bit data buffer + * on 16 sequential addresses + */ + *mmc_ptr = readl(reg_fifo + + (mmc_count % MMC_BOOT_MCI_FIFO_SIZE)); + mmc_ptr++; + /* increase mmc_count by word size */ + mmc_count += sizeof(unsigned int); + } + /* quit if we have read enough of data */ + if (mmc_count == data_len) + break; + } else if (mmc_status & MMC_BOOT_MCI_STAT_DATA_END) + break; + } while (1); + return mmc_ret; +} + +static unsigned int mmc_boot_fifo_data_transfer(unsigned int *data_ptr, + unsigned int data_len, + unsigned char direction, + struct mmc *mmc) +{ + unsigned int mmc_ret = MMC_BOOT_E_SUCCESS; + + if (direction == MMC_BOOT_DATA_READ) + mmc_ret = mmc_boot_fifo_read(data_ptr, data_len, mmc); + + return mmc_ret; +} + +/* + * Sends specified command to a card and waits for a response. + */ +static unsigned int mmc_boot_send_command(struct mmc_cmd *cmd, + struct mmc *mmc) +{ + unsigned int mmc_cmd = 0; + unsigned int mmc_status = 0; + unsigned int mmc_resp = 0; + unsigned int mmc_return = MMC_BOOT_E_SUCCESS; + unsigned int cmd_index = 0; + int i = 0; + unsigned long reg, reg_status, reg_resp_cmd, reg_resp_0; + struct mmc_priv *priv = (struct mmc_priv *)mmc->priv; + + /* basic check */ + if (cmd == NULL) + return MMC_BOOT_E_INVAL; + + /* 1. Write command argument to MMC_BOOT_MCI_ARGUMENT register */ + reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_ARGUMENT); + writel(cmd->cmdarg, reg); + + /* Writes to MCI port are not effective for 3 ticks of PCLK. + * ticks calculated using 400KHz clock speed + */ + udelay(8); + + /* 2. Set appropriate fields and write MMC_BOOT_MCI_CMD */ + /* 2a. Write command index in CMD_INDEX field */ + cmd_index = cmd->cmdidx; + mmc_cmd |= cmd->cmdidx; + /* 2b. Set RESPONSE bit to 1 for all cmds except CMD0 */ + if (cmd_index != MMC_CMD_GO_IDLE_STATE) + mmc_cmd |= MMC_BOOT_MCI_CMD_RESPONSE; + + /* 2c. Set LONGRESP bit to 1 for CMD2, CMD9 and CMD10 */ + if (cmd->resp_type & MMC_RSP_136) + mmc_cmd |= MMC_BOOT_MCI_CMD_LONGRSP; + + /* 2d. Set INTERRUPT bit to 1 to disable command timeout */ + + /* 2e. Set PENDING bit to 1 for CMD12 in the beginning of stream + * mode data transfer + */ + if (cmd->flags & MMC_BOOT_XFER_MODE_STREAM) + mmc_cmd |= MMC_BOOT_MCI_CMD_PENDING; + + /* 2f. Set ENABLE bit to 1 */ + mmc_cmd |= MMC_BOOT_MCI_CMD_ENABLE; + + /* 2g. Set PROG_ENA bit to 1 for CMD12, CMD13 issued at the end of + * write data transfer + */ + if ((cmd_index == MMC_CMD_STOP_TRANSMISSION || + cmd_index == MMC_CMD_SEND_STATUS) && + (cmd->flags & MMC_BOOT_PROGRAM_ENABLED)) + mmc_cmd |= MMC_BOOT_MCI_CMD_PROG_ENA; + + + /* 2h. Set MCIABORT bit to 1 for CMD12 when working with SDIO card */ + /* 2i. Set CCS_ENABLE bit to 1 for CMD61 when Command Completion Signal + * of CE-ATA device is enabled + */ + + /* 2j. clear all static status bits */ + reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_CLEAR); + writel(MMC_BOOT_MCI_STATIC_STATUS, reg); + + /* 2k. Write to MMC_BOOT_MCI_CMD register */ + reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_CMD); + writel(mmc_cmd, reg); + + /* Writes to MCI port are not effective for 3 ticks of PCLK. + * ticks calculated using 400KHz clock speed + */ + udelay(8); + + reg_status = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_STATUS); + reg_resp_cmd = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_RESP_CMD); + reg_resp_0 = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_RESP_0); + + /* 3. Wait for interrupt or poll on the following bits of MCI_STATUS + * register + */ + do { + /* 3a. Read MCI_STATUS register */ + mmc_status = readl(reg_status); + while (mmc_status & MMC_BOOT_MCI_STAT_CMD_ACTIVE) + mmc_status = readl(reg_status); + + /* 3b. CMD_SENT bit supposed to be set to 1 only + * after CMD0 is sent -no response required. + */ + if ((cmd->resp_type == MMC_RSP_NONE) && + (mmc_status & MMC_BOOT_MCI_STAT_CMD_SENT)) + break; + + /* 3c. If CMD_TIMEOUT bit is set then no response + * was received */ + else if (mmc_status & MMC_BOOT_MCI_STAT_CMD_TIMEOUT) { + mmc_return = TIMEOUT; + break; + } + /* 3d. If CMD_RESPONSE_END bit is set to 1 then command's + * response was received and CRC check passed + * Spcial case for ACMD41: it seems to always fail CRC even if + * the response is valid + */ + else if ((mmc_status & MMC_BOOT_MCI_STAT_CMD_RESP_END) || + (cmd_index == MMC_CMD_SEND_OP_COND) + || (cmd_index == SD_CMD_SEND_IF_COND)) { + /* 3i. Read MCI_RESP_CMD register to verify that + * response index is equal to command index + */ + mmc_resp = readl(reg_resp_cmd) & 0x3F; + + /* However, long response does not contain the + * command index field. + * In that case, response index field must be set to + * 111111b (0x3F) + */ + if ((mmc_resp == cmd_index) || + (cmd->resp_type == MMC_RSP_R2 || + cmd->resp_type == MMC_RSP_R3 || + cmd->resp_type == MMC_RSP_R6 || + cmd->resp_type == MMC_RSP_R7)){ + /* 3j. If resp index is equal to cmd index, + * read command resp from MCI_RESPn registers + * - MCI_RESP0/1/2/3 for CMD2/9/10 + * - MCI_RESP0 for all other registers + */ + if (cmd->resp_type & MMC_RSP_136) { + for (i = 0; i < 4; i++) + cmd->response[3-i] = + readl(reg_resp_0 + \ + (i * 4)); + } else + cmd->response[0] = readl(reg_resp_0); + } else + /* command index mis-match */ + mmc_return = MMC_BOOT_E_CMD_INDX_MISMATCH; + + break; + } + + /* 3e. If CMD_CRC_FAIL bit is set to 1 then cmd's response + * was recvd, but CRC check failed. + */ + else if ((mmc_status & MMC_BOOT_MCI_STAT_CMD_CRC_FAIL)) { + if (cmd_index == SD_CMD_APP_SEND_OP_COND) + cmd->response[0] = readl(reg_resp_0); + else + mmc_return = MMC_BOOT_E_CRC_FAIL; + break; + } + + } while (1); + + return mmc_return; +} + +static void mmc_boot_init_data(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + uint32_t data_ctrl, mmc_reg, data_len; + struct mmc_priv *priv = (struct mmc_priv *)mmc->priv; + unsigned long reg; + + /* Write data timeout period to MCI_DATA_TIMER register. */ + /* Data timeout period should be in card bus clock periods */ + mmc_reg = (unsigned long)(((struct mmc_priv *) + (mmc->priv))->rd_timeout_ns / + 1000000) * (mmc->clock / 1000); + /* add some extra clock cycles to be safe */ + mmc_reg += 1000; + mmc_reg = mmc_reg/2; + + reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_DATA_TIMER); + writel(mmc_reg, reg); + + /* Write the total size of the transfer data to MCI_DATA_LENGTH + * register. For block xfer it must be multiple of the block + * size. + */ + + data_len = data->blocks*data->blocksize; + reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_DATA_LENGTH); + writel(data_len, reg); + + /* Writes to MCI port are not effective for 3 ticks of PCLK. + * ticks calculated using 400KHz clock speed + */ + udelay(8); + + data_ctrl = MMC_BOOT_MCI_DATA_ENABLE | + (data->blocksize << MMC_BOOT_MCI_BLKSIZE_POS); + + + if (data->flags == MMC_DATA_READ) + data_ctrl |= MMC_BOOT_MCI_DATA_DIR; + + reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_DATA_CTL); + writel(data_ctrl, reg); + + /* Writes to MCI port are not effective for 3 ticks of PCLK. + * ticks calculated using 400KHz clock speed + */ + udelay(8); + +} + +/* + * Function to map mmc send command to board send command + */ +int mmc_boot_send_command_map(struct mmc *mmc, + struct mmc_cmd *cmd, + struct mmc_data *data) +{ + unsigned int mmc_ret = MMC_BOOT_E_SUCCESS; + + /* todo: do we need to fill in command type ?? */ + + if (cmd->cmdidx == MMC_CMD_SEND_STATUS) { + /* u-boot doesn't fill in the correct argument value */ + cmd->cmdarg = mmc->rca << 16; + /* this is to explicitly disable the prg enabled flag */ + cmd->flags &= ~MMC_BOOT_PROGRAM_ENABLED; + + } else if (cmd->cmdidx == MMC_CMD_READ_SINGLE_BLOCK) { + if (mmc->read_bl_len != MMC_BOOT_RD_BLOCK_LEN && + mmc->version & SD_VERSION_SD) { + mmc->read_bl_len = MMC_BOOT_RD_BLOCK_LEN; + data->blocksize = MMC_BOOT_RD_BLOCK_LEN; + } + } else if (cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK) { + if (mmc->read_bl_len != MMC_BOOT_RD_BLOCK_LEN && + mmc->version & SD_VERSION_SD) { + mmc->read_bl_len = MMC_BOOT_RD_BLOCK_LEN; + data->blocksize = MMC_BOOT_RD_BLOCK_LEN; + } + } else if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) { + /* explicitly disable the prg enabled flag */ + cmd->flags &= ~MMC_BOOT_PROGRAM_ENABLED; + /* set the XFER mode */ + cmd->flags |= MMC_BOOT_XFER_MODE_BLOCK; + } + + + /* For Data cmd's */ + if (data != NULL) + mmc_boot_init_data(mmc, cmd, data); + + /* send command */ + mmc_ret = mmc_boot_send_command(cmd, mmc); + + if (mmc_ret != MMC_BOOT_E_SUCCESS) { + debug("Return Error = %d\n", mmc_ret); + return mmc_ret; + } + + if (data != NULL) { + mmc_ret = mmc_boot_check_read_data(cmd); + if (mmc_ret) + return mmc_ret; + mmc_boot_fifo_data_transfer((unsigned int *) data->dest, + data->blocks * data->blocksize, + MMC_BOOT_DATA_READ, + mmc); + } + + if (cmd->cmdidx == MMC_CMD_SEND_CSD) { + /* Set read timeout once csd is received */ + mmc_ret = mmc_boot_set_read_timeout(mmc); + if (mmc_ret != MMC_BOOT_E_SUCCESS) { + printf("Error No.%d: Failure setting \ + write Timeout value!\n", + mmc_ret); + return mmc_ret; + } + } else if ((cmd->cmdidx == SD_CMD_SWITCH_FUNC) && (data != NULL)) { + /* cmd6 needs at least 8 clocks after the + * End Data of Status. + * ticks calculated using 400KHz clock speed + */ + udelay(20); + } + + return mmc_ret; +} + +/* + * Initialize host structure, set and enable clock-rate and power mode. + */ +unsigned int mmc_boot_init(struct mmc *mmc) +{ + unsigned int mmc_pwr = 0; + struct mmc_priv *priv = (struct mmc_priv *) mmc->priv; + int mmc_slot = priv->instance; + unsigned long reg; + + /* Initialize any clocks needed for SDC controller */ + clock_init_mmc(mmc_slot); + + /* Setup initial freq to 400KHz */ + clock_config_mmc(mmc_slot, MMC_CLK_400KHZ); + + /* set power mode*/ + mmc_pwr &= ~MMC_BOOT_MCI_PWR_UP; + mmc_pwr |= MMC_BOOT_MCI_PWR_ON; + mmc_pwr |= MMC_BOOT_MCI_PWR_UP; + reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_POWER); + writel(mmc_pwr, reg); + + /* Writes to MCI port are not effective for 3 ticks of PCLK. + * ticks calculated using 400KHz clock speed + */ + udelay(8); + + return MMC_BOOT_E_SUCCESS; +} + +static unsigned int mmc_boot_set_bus_width(unsigned int width, + struct mmc *mmc) +{ + unsigned int mmc_reg; + struct mmc_priv *priv = (struct mmc_priv *)mmc->priv; + unsigned long reg; + + /* set MCI_CLK accordingly */ + reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_CLK); + mmc_reg = readl(reg); + mmc_reg &= ~MMC_BOOT_MCI_CLK_WIDEBUS_MODE; + if (width == 1) + mmc_reg |= MMC_BOOT_MCI_CLK_WIDEBUS_1_BIT; + else if (width == 4) + mmc_reg |= MMC_BOOT_MCI_CLK_WIDEBUS_4_BIT; + else if (width == 8) + mmc_reg |= MMC_BOOT_MCI_CLK_WIDEBUS_8_BIT; + else + return MMC_BOOT_E_INVAL; + + writel(mmc_reg, reg); + + /* Writes to MCI port are not effective for 3 ticks of PCLK. + * ticks calculated using 400KHz clock speed + */ + udelay(8); + + debug("Setting bus width to %d\n", width); + return MMC_BOOT_E_SUCCESS; +} + +void mmc_boot_set_ios(struct mmc *mmc) +{ + unsigned int mmc_ret = MMC_BOOT_E_SUCCESS; + struct mmc_priv *priv = (struct mmc_priv *)mmc->priv; + int mmc_slot = priv->instance; + + /* clock frequency should be less than 400KHz in identification mode */ + clock_config_mmc(mmc_slot, mmc->clock); + /* + * give atleast 2 MCLK cycles delay for clocks + * and SDCC core to stabilize. + * ticks calculated using 400KHz clock speed + */ + udelay(5); + mmc_ret = mmc_boot_set_bus_width(mmc->bus_width, mmc); + if (mmc_ret != MMC_BOOT_E_SUCCESS) + printf("Set bus width error %d\n", mmc_ret); +} + +/* + * Board specific initializations + */ +unsigned int mmc_boot_main(struct mmc *mmc) +{ + unsigned int mmc_ret = MMC_BOOT_E_SUCCESS; + + /* Initialize necessary data structure and enable/set clock and power */ + debug(" Initializing MMC host data structure and clock!\n"); + mmc_ret = mmc_boot_init(mmc); + if (mmc_ret != MMC_BOOT_E_SUCCESS) { + printf("MMC Boot: Error Initializing MMC Card!!!\n"); + return MMC_BOOT_E_FAILURE; + } + + return MMC_BOOT_E_SUCCESS; +} -- 1.7.1
The contents of this e-mail and any attachment(s) may contain confidential or privileged information for the intended recipient(s). Unintended recipients are prohibited from taking action on the basis of information in this e-mail and using or disseminating the information, and must notify the sender and delete it from their system. L&T Infotech will not accept responsibility or liability for the accuracy or completeness of, or the presence of any virus or disabling code in this e-mail"

NAK, allow me to explain below:
On Mon, Mar 5, 2012 at 8:40 AM, Mohamed Haneef mohamed.haneef@lntinfotech.com wrote:
* Support for qc_mmc MMC Controller
Signed-off-by: Mohamed Haneef mohamed.haneef@lntinfotech.com
+#ifndef __MMC_H__ +#define __MMC_H__
+#ifndef MMC_SLOT +#define MMC_SLOT 0 +#endif
This sort of thing should probably be passed in by the board code.
+#if 0 +#define MSM_SDC1_BASE 0x12400000 +#define MSM_SDC2_BASE 0x12140000 +#define MSM_SDC3_BASE 0x12180000 +#define MSM_SDC4_BASE 0x121C0000 +#endif
Why's this still here?
diff --git a/drivers/mmc/qc_mmc.c b/drivers/mmc/qc_mmc.c new file mode 100644 index 0000000..7b7a1ed --- /dev/null +++ b/drivers/mmc/qc_mmc.c @@ -0,0 +1,584 @@
+#if MMC_BOOT_ADM +#include "adm.h" +#endif
+#ifndef NULL +#define NULL 0 +#endif
?? Is NULL ever undefined in C?
+#define MMC_BOOT_DATA_READ 0 +#define MMC_BOOT_DATA_WRITE 1 +/*
- Calculates the address of registers according to the base address
- */
+unsigned long int mmc_boot_mci_reg(unsigned long base, unsigned long offset) +{
- return base + offset;
+}
+/*
- Sets a timeout for read operation.
- */
+static unsigned int mmc_boot_set_read_timeout(struct mmc *mmc) +{
- if (mmc == NULL)
- return MMC_BOOT_E_INVAL;
- /* todo: Add a check for HC, only if HC do this.
- * If not, taac and nsac must be taken into account
- */
- ((struct mmc_priv *)(mmc->priv))->rd_timeout_ns = 100000000;
- debug(" Read timeout set: %d ns\n",
- ((struct mmc_priv *)(mmc->priv))->rd_timeout_ns);
I think it's preferable to do:
struct mmc_priv *priv = mmc->priv;
priv->rd_timeout_ns = 100000000;
- return MMC_BOOT_E_SUCCESS;
+}
+/*
- Check to ensure that there is no alignment or data length errors
- */
+static unsigned int mmc_boot_check_read_data(struct mmc_cmd *cmd) +{
- if (cmd->response[0] & MMC_BOOT_R1_BLOCK_LEN_ERR)
- return MMC_BOOT_E_BLOCKLEN_ERR;
- /* Misaligned address not matching block length */
- if (cmd->response[0] & MMC_BOOT_R1_ADDR_ERR)
- return MMC_BOOT_E_ADDRESS_ERR;
- return MMC_BOOT_E_SUCCESS;
+}
+/*
- Decode type of error caused during read and write
- */
+static unsigned int mmc_boot_status_error(unsigned mmc_status) +{
- unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
- /* If DATA_CRC_FAIL bit is set to 1 then CRC error
- * was detected by card/device during the data transfer
- */
- if (mmc_status & MMC_BOOT_MCI_STAT_DATA_CRC_FAIL)
- mmc_ret = MMC_BOOT_E_DATA_CRC_FAIL;
- /* If DATA_TIMEOUT bit is set to 1 then the data transfer time
- * exceeded the data timeout period without completing the
- * transfer
- */
- else if (mmc_status & MMC_BOOT_MCI_STAT_DATA_TIMEOUT)
- mmc_ret = MMC_BOOT_E_DATA_TIMEOUT;
- /* If RX_OVERRUN bit is set to 1 then SDCC2 tried to
- * receive data from the card before empty storage for new
- * received data was available.
- * Verify that bit FLOW_ENA in MCI_CLK is set to 1 during
- * the data xfer.
- */
- else if (mmc_status & MMC_BOOT_MCI_STAT_RX_OVRRUN)
- /* Note: We've set FLOW_ENA bit in MCI_CLK to 1.
- * so no need to verify for now
- */
- mmc_ret = MMC_BOOT_E_RX_OVRRUN;
- /* If TX_UNDERRUN bit is set to 1 then SDCC2 tried to send
- * data to the card before new data for sending was available.
- * Verify that bit FLOW_ENA in MCI_CLK
- * is set to 1 during the data xfer.
- */
- else if (mmc_status & MMC_BOOT_MCI_STAT_TX_UNDRUN)
- /* Note: We've set FLOW_ENA bit in MCI_CLK to 1.
- * so skipping it now
- */
- mmc_ret = MMC_BOOT_E_RX_OVRRUN;
- return mmc_ret;
+}
+/*
- Read data to SDC FIFO.
- */
+static unsigned int mmc_boot_fifo_read(unsigned int *mmc_ptr,
- unsigned int data_len,
- struct mmc *mmc)
+{
Why is mmc_ptr an unsigned int *?
- unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
- unsigned int mmc_status = 0;
- unsigned int mmc_count = 0;
- unsigned int read_error = MMC_BOOT_MCI_STAT_DATA_CRC_FAIL | \
- MMC_BOOT_MCI_STAT_DATA_TIMEOUT | \
- MMC_BOOT_MCI_STAT_RX_OVRRUN;
- unsigned int i;
- struct mmc_priv *priv = (struct mmc_priv *)mmc->priv;
Unnecessary cast
- unsigned long reg_status, reg_fifo;
- reg_status = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_STATUS);
- reg_fifo = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_FIFO);
- /* Read the data from the MCI_FIFO register as long as
- * RXDATA_AVLBL bit of MCI_STATUS register is set to 1 and bits
- * DATA_CRC_FAIL, DATA_TIMEOUT, RX_OVERRUN of MCI_STATUS
- * register are cleared to 0.
- * Continue the reads until the whole transfer data is received
- */
- do {
- mmc_ret = MMC_BOOT_E_SUCCESS;
- mmc_status = readl(reg_status);
- if (mmc_status & read_error) {
- mmc_ret = mmc_boot_status_error(mmc_status);
- break;
- }
- if (mmc_status & MMC_BOOT_MCI_STAT_RX_DATA_AVLBL) {
- unsigned read_count = 1;
- if (mmc_status & MMC_BOOT_MCI_STAT_RX_FIFO_HFULL)
- read_count = MMC_BOOT_MCI_HFIFO_COUNT;
- for (i = 0; i < read_count; i++) {
- /* FIFO contains 16 32-bit data buffer
- * on 16 sequential addresses
- */
- *mmc_ptr = readl(reg_fifo +
- (mmc_count % MMC_BOOT_MCI_FIFO_SIZE));
- mmc_ptr++;
- /* increase mmc_count by word size */
- mmc_count += sizeof(unsigned int);
I don't remember if readl is explicitly sized, but it seems like there are some implicit and explicit size dependencies, and they aren't currently guaranteed to line up. If you're going to do this, I think mmc_ptr should be u32*. And you should update mmc_count by sizeof(*mmc_ptr) to lock in the connection between the increase in mmc_count and the amount of data read.
Out of curiosity, I notice that you will read from the start of the FIFO each time this function is called. Is there something that resets the FIFO to put the oldest data at the first address? Or is this only called once?
+static void mmc_boot_init_data(struct mmc *mmc, struct mmc_cmd *cmd,
- struct mmc_data *data)
+{
- uint32_t data_ctrl, mmc_reg, data_len;
- struct mmc_priv *priv = (struct mmc_priv *)mmc->priv;
Unnecessary cast
- unsigned long reg;
- /* Write data timeout period to MCI_DATA_TIMER register. */
- /* Data timeout period should be in card bus clock periods */
- mmc_reg = (unsigned long)(((struct mmc_priv *)
- (mmc->priv))->rd_timeout_ns /
- 1000000) * (mmc->clock / 1000);
You've got priv already, why access it via complicated cast?
- /* add some extra clock cycles to be safe */
- mmc_reg += 1000;
- mmc_reg = mmc_reg/2;
I'd prefer that the variables have names that indicate their purpose (e.g. - mmc_timeout). It took me a few times looking at this to realize you were updating a variable that will be written to a reg, rather than a pointer to your mmc register space.
Also, I'm assuming that the register defines the timeout field so that you need ns/2, but you might want to document that as why you divide by 2.
- reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_DATA_TIMER);
- writel(mmc_reg, reg);
If you're going to repeat a sequence like this, frequently, it might be better to create a qc_mmc_write() function which takes priv, reg, and value as arguments:
qc_mmc_write(priv, MMC_BOOT_MCI_DATA_TIMER, mmc_reg);
BTW, are all of these prefixed with MMC_BOOT because this is only used at boot time? Seems limiting. And it makes the constant names very long.
- /* Write the total size of the transfer data to MCI_DATA_LENGTH
- * register. For block xfer it must be multiple of the block
- * size.
- */
- data_len = data->blocks*data->blocksize;
- reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_DATA_LENGTH);
- writel(data_len, reg);
- /* Writes to MCI port are not effective for 3 ticks of PCLK.
- * ticks calculated using 400KHz clock speed
- */
- udelay(8);
- data_ctrl = MMC_BOOT_MCI_DATA_ENABLE |
- (data->blocksize << MMC_BOOT_MCI_BLKSIZE_POS);
- if (data->flags == MMC_DATA_READ)
- data_ctrl |= MMC_BOOT_MCI_DATA_DIR;
- reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_DATA_CTL);
- writel(data_ctrl, reg);
- /* Writes to MCI port are not effective for 3 ticks of PCLK.
- * ticks calculated using 400KHz clock speed
- */
- udelay(8);
+}
+/*
- Function to map mmc send command to board send command
- */
This sounds...odd.
+int mmc_boot_send_command_map(struct mmc *mmc,
- struct mmc_cmd *cmd,
- struct mmc_data *data)
+{
- unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
- /* todo: do we need to fill in command type ?? */
- if (cmd->cmdidx == MMC_CMD_SEND_STATUS) {
- /* u-boot doesn't fill in the correct argument value */
- cmd->cmdarg = mmc->rca << 16;
I'm not sure what this means. But if you think U-Boot should be filling in a different value, we should change it in mmc.c, not one-at-a-time in each driver...
- /* this is to explicitly disable the prg enabled flag */
- cmd->flags &= ~MMC_BOOT_PROGRAM_ENABLED;
1) I'm not sure this field is ever used for anything 2) You shouldn't define your own constants for inspecting/setting data in fields that haven't been explicitly set aside for driver-specific implementation. While it doesn't look like any of the mmc code ever sets this field, it could one day, and break your driver.
- } else if (cmd->cmdidx == MMC_CMD_READ_SINGLE_BLOCK) {
- if (mmc->read_bl_len != MMC_BOOT_RD_BLOCK_LEN &&
- mmc->version & SD_VERSION_SD) {
- mmc->read_bl_len = MMC_BOOT_RD_BLOCK_LEN;
- data->blocksize = MMC_BOOT_RD_BLOCK_LEN;
- }
- } else if (cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK) {
- if (mmc->read_bl_len != MMC_BOOT_RD_BLOCK_LEN &&
- mmc->version & SD_VERSION_SD) {
- mmc->read_bl_len = MMC_BOOT_RD_BLOCK_LEN;
- data->blocksize = MMC_BOOT_RD_BLOCK_LEN;
- }
- } else if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) {
- /* explicitly disable the prg enabled flag */
- cmd->flags &= ~MMC_BOOT_PROGRAM_ENABLED;
- /* set the XFER mode */
- cmd->flags |= MMC_BOOT_XFER_MODE_BLOCK;
- }
- /* For Data cmd's */
nit: no apostrophe needed
+/*
- Initialize host structure, set and enable clock-rate and power mode.
- */
+unsigned int mmc_boot_init(struct mmc *mmc) +{
- unsigned int mmc_pwr = 0;
- struct mmc_priv *priv = (struct mmc_priv *) mmc->priv;
No cast
- int mmc_slot = priv->instance;
Whenever a driver has to be told which instance it is, a kitten dies.... Ok, after reading through I see that it's only used to set clocking. That's probably fine. More on this later.
+/*
- Board specific initializations
- */
+unsigned int mmc_boot_main(struct mmc *mmc) +{
- unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
- /* Initialize necessary data structure and enable/set clock and power */
- debug(" Initializing MMC host data structure and clock!\n");
- mmc_ret = mmc_boot_init(mmc);
- if (mmc_ret != MMC_BOOT_E_SUCCESS) {
- printf("MMC Boot: Error Initializing MMC Card!!!\n");
- return MMC_BOOT_E_FAILURE;
- }
- return MMC_BOOT_E_SUCCESS;
+}
Why not put the actual initialization function here? We usually want to contain the driver information, here. It looks like you have two different initialization paths, but there should be some way to codify that so that future readers/writers can understand which parts of the driver are being used under a given set of conditions. As it currently stands, they will have to investigate the board file to figure it out. Use the board init function to pass in parameters the driver can use to make those choices. For instance, the
The contents of this e-mail and any attachment(s) may contain confidential or privileged information for the intended recipient(s). Unintended recipients are prohibited from taking action on the basis of information in this e-mail and using or disseminating the information, and must notify the sender and delete it from their system. L&T Infotech will not accept responsibility or liability for the accuracy or completeness of, or the presence of any virus or disabling code in this e-mail"
These sorts of disclaimers really serve to irritate open source developers. Also, it puts me in the legal void, as you neglected to CC me, which means I might be an unintended recipient. In defiance of your disclaimer, I decided to take action, anyway. Feel free to sue me. :P
Andy

Hi,
NAK, allow me to explain below:
On Mon, Mar 5, 2012 at 8:40 AM, Mohamed Haneef mohamed.haneef@lntinfotech.com wrote:
* Support for qc_mmc MMC Controller
Signed-off-by: Mohamed Haneef mohamed.haneef@lntinfotech.com
+#ifndef __MMC_H__ +#define __MMC_H__
+#ifndef MMC_SLOT +#define MMC_SLOT 0 +#endif
This sort of thing should probably be passed in by the board code.
it is removed.
+#if 0 +#define MSM_SDC1_BASE 0x12400000 +#define MSM_SDC2_BASE 0x12140000 +#define MSM_SDC3_BASE 0x12180000 +#define MSM_SDC4_BASE 0x121C0000 +#endif
Why's this still here?
this got removed as well
diff --git a/drivers/mmc/qc_mmc.c b/drivers/mmc/qc_mmc.c new file mode 100644 index 0000000..7b7a1ed --- /dev/null +++ b/drivers/mmc/qc_mmc.c @@ -0,0 +1,584 @@
+#if MMC_BOOT_ADM +#include "adm.h" +#endif
+#ifndef NULL +#define NULL 0 +#endif
?? Is NULL ever undefined in C?
+#define MMC_BOOT_DATA_READ 0 +#define MMC_BOOT_DATA_WRITE 1 +/*
- Calculates the address of registers according to the base address
- */
+unsigned long int mmc_boot_mci_reg(unsigned long base, unsigned long offset) +{
return base + offset;
+}
+/*
- Sets a timeout for read operation.
- */
+static unsigned int mmc_boot_set_read_timeout(struct mmc *mmc) +{
if (mmc == NULL)
return MMC_BOOT_E_INVAL;
/* todo: Add a check for HC, only if HC do this.
* If not, taac and nsac must be taken into account
*/
((struct mmc_priv *)(mmc->priv))->rd_timeout_ns = 100000000;
debug(" Read timeout set: %d ns\n",
((struct mmc_priv *)(mmc->priv))->rd_timeout_ns);
I think it's preferable to do:
struct mmc_priv *priv = mmc->priv;
priv->rd_timeout_ns = 100000000;
return MMC_BOOT_E_SUCCESS;
+}
+/*
- Check to ensure that there is no alignment or data length errors
- */
+static unsigned int mmc_boot_check_read_data(struct mmc_cmd *cmd) +{
if (cmd->response[0] & MMC_BOOT_R1_BLOCK_LEN_ERR)
return MMC_BOOT_E_BLOCKLEN_ERR;
/* Misaligned address not matching block length */
if (cmd->response[0] & MMC_BOOT_R1_ADDR_ERR)
return MMC_BOOT_E_ADDRESS_ERR;
return MMC_BOOT_E_SUCCESS;
+}
+/*
- Decode type of error caused during read and write
- */
+static unsigned int mmc_boot_status_error(unsigned mmc_status) +{
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
/* If DATA_CRC_FAIL bit is set to 1 then CRC error
* was detected by card/device during the data transfer
*/
if (mmc_status & MMC_BOOT_MCI_STAT_DATA_CRC_FAIL)
mmc_ret = MMC_BOOT_E_DATA_CRC_FAIL;
/* If DATA_TIMEOUT bit is set to 1 then the data transfer time
* exceeded the data timeout period without completing the
* transfer
*/
else if (mmc_status & MMC_BOOT_MCI_STAT_DATA_TIMEOUT)
mmc_ret = MMC_BOOT_E_DATA_TIMEOUT;
/* If RX_OVERRUN bit is set to 1 then SDCC2 tried to
* receive data from the card before empty storage for new
* received data was available.
* Verify that bit FLOW_ENA in MCI_CLK is set to 1 during
* the data xfer.
*/
else if (mmc_status & MMC_BOOT_MCI_STAT_RX_OVRRUN)
/* Note: We've set FLOW_ENA bit in MCI_CLK to 1.
* so no need to verify for now
*/
mmc_ret = MMC_BOOT_E_RX_OVRRUN;
/* If TX_UNDERRUN bit is set to 1 then SDCC2 tried to send
* data to the card before new data for sending was available.
* Verify that bit FLOW_ENA in MCI_CLK
* is set to 1 during the data xfer.
*/
else if (mmc_status & MMC_BOOT_MCI_STAT_TX_UNDRUN)
/* Note: We've set FLOW_ENA bit in MCI_CLK to 1.
* so skipping it now
*/
mmc_ret = MMC_BOOT_E_RX_OVRRUN;
return mmc_ret;
+}
+/*
- Read data to SDC FIFO.
- */
+static unsigned int mmc_boot_fifo_read(unsigned int *mmc_ptr,
unsigned int data_len,
struct mmc *mmc)
+{
Why is mmc_ptr an unsigned int *?
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
unsigned int mmc_status = 0;
unsigned int mmc_count = 0;
unsigned int read_error = MMC_BOOT_MCI_STAT_DATA_CRC_FAIL | \
MMC_BOOT_MCI_STAT_DATA_TIMEOUT | \
MMC_BOOT_MCI_STAT_RX_OVRRUN;
unsigned int i;
struct mmc_priv *priv = (struct mmc_priv *)mmc->priv;
Unnecessary cast
unsigned long reg_status, reg_fifo;
reg_status = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_STATUS);
reg_fifo = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_FIFO);
/* Read the data from the MCI_FIFO register as long as
* RXDATA_AVLBL bit of MCI_STATUS register is set to 1 and bits
* DATA_CRC_FAIL, DATA_TIMEOUT, RX_OVERRUN of MCI_STATUS
* register are cleared to 0.
* Continue the reads until the whole transfer data is received
*/
do {
mmc_ret = MMC_BOOT_E_SUCCESS;
mmc_status = readl(reg_status);
if (mmc_status & read_error) {
mmc_ret = mmc_boot_status_error(mmc_status);
break;
}
if (mmc_status & MMC_BOOT_MCI_STAT_RX_DATA_AVLBL) {
unsigned read_count = 1;
if (mmc_status & MMC_BOOT_MCI_STAT_RX_FIFO_HFULL)
read_count = MMC_BOOT_MCI_HFIFO_COUNT;
for (i = 0; i < read_count; i++) {
/* FIFO contains 16 32-bit data buffer
* on 16 sequential addresses
*/
*mmc_ptr = readl(reg_fifo +
(mmc_count % MMC_BOOT_MCI_FIFO_SIZE));
mmc_ptr++;
/* increase mmc_count by word size */
mmc_count += sizeof(unsigned int);
I don't remember if readl is explicitly sized, but it seems like there are some implicit and explicit size dependencies, and they aren't currently guaranteed to line up. If you're going to do this, I think mmc_ptr should be u32*. And you should update mmc_count by sizeof(*mmc_ptr) to lock in the connection between the increase in mmc_count and the amount of data read.
Out of curiosity, I notice that you will read from the start of the FIFO each time this function is called. Is there something that resets the FIFO to put the oldest data at the first address? Or is this only called once?
+static void mmc_boot_init_data(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
+{
uint32_t data_ctrl, mmc_reg, data_len;
struct mmc_priv *priv = (struct mmc_priv *)mmc->priv;
Unnecessary cast
unsigned long reg;
/* Write data timeout period to MCI_DATA_TIMER register. */
/* Data timeout period should be in card bus clock periods */
mmc_reg = (unsigned long)(((struct mmc_priv *)
(mmc->priv))->rd_timeout_ns /
1000000) * (mmc->clock / 1000);
You've got priv already, why access it via complicated cast?
/* add some extra clock cycles to be safe */
mmc_reg += 1000;
mmc_reg = mmc_reg/2;
I'd prefer that the variables have names that indicate their purpose (e.g. - mmc_timeout). It took me a few times looking at this to realize you were updating a variable that will be written to a reg, rather than a pointer to your mmc register space.
Also, I'm assuming that the register defines the timeout field so that you need ns/2, but you might want to document that as why you divide by 2.
reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_DATA_TIMER);
writel(mmc_reg, reg);
If you're going to repeat a sequence like this, frequently, it might be better to create a qc_mmc_write() function which takes priv, reg, and value as arguments:
qc_mmc_write(priv, MMC_BOOT_MCI_DATA_TIMER, mmc_reg);
BTW, are all of these prefixed with MMC_BOOT because this is only used at boot time? Seems limiting. And it makes the constant names very long.
/* Write the total size of the transfer data to MCI_DATA_LENGTH
* register. For block xfer it must be multiple of the block
* size.
*/
data_len = data->blocks*data->blocksize;
reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_DATA_LENGTH);
writel(data_len, reg);
/* Writes to MCI port are not effective for 3 ticks of PCLK.
* ticks calculated using 400KHz clock speed
*/
udelay(8);
data_ctrl = MMC_BOOT_MCI_DATA_ENABLE |
(data->blocksize << MMC_BOOT_MCI_BLKSIZE_POS);
if (data->flags == MMC_DATA_READ)
data_ctrl |= MMC_BOOT_MCI_DATA_DIR;
reg = mmc_boot_mci_reg(priv->base, MMC_BOOT_MCI_DATA_CTL);
writel(data_ctrl, reg);
/* Writes to MCI port are not effective for 3 ticks of PCLK.
* ticks calculated using 400KHz clock speed
*/
udelay(8);
+}
+/*
- Function to map mmc send command to board send command
- */
This sounds...odd.
+int mmc_boot_send_command_map(struct mmc *mmc,
struct mmc_cmd *cmd,
struct mmc_data *data)
+{
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
/* todo: do we need to fill in command type ?? */
if (cmd->cmdidx == MMC_CMD_SEND_STATUS) {
/* u-boot doesn't fill in the correct argument value */
cmd->cmdarg = mmc->rca << 16;
I'm not sure what this means. But if you think U-Boot should be filling in a different value, we should change it in mmc.c, not one-at-a-time in each driver...
/* this is to explicitly disable the prg enabled flag */
cmd->flags &= ~MMC_BOOT_PROGRAM_ENABLED;
1) I'm not sure this field is ever used for anything 2) You shouldn't define your own constants for inspecting/setting data in fields that haven't been explicitly set aside for driver-specific implementation. While it doesn't look like any of the mmc code ever sets this field, it could one day, and break your driver.
} else if (cmd->cmdidx == MMC_CMD_READ_SINGLE_BLOCK) {
if (mmc->read_bl_len != MMC_BOOT_RD_BLOCK_LEN &&
mmc->version & SD_VERSION_SD) {
mmc->read_bl_len = MMC_BOOT_RD_BLOCK_LEN;
data->blocksize = MMC_BOOT_RD_BLOCK_LEN;
}
} else if (cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK) {
if (mmc->read_bl_len != MMC_BOOT_RD_BLOCK_LEN &&
mmc->version & SD_VERSION_SD) {
mmc->read_bl_len = MMC_BOOT_RD_BLOCK_LEN;
data->blocksize = MMC_BOOT_RD_BLOCK_LEN;
}
} else if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) {
/* explicitly disable the prg enabled flag */
cmd->flags &= ~MMC_BOOT_PROGRAM_ENABLED;
/* set the XFER mode */
cmd->flags |= MMC_BOOT_XFER_MODE_BLOCK;
}
/* For Data cmd's */
nit: no apostrophe needed
+/*
- Initialize host structure, set and enable clock-rate and power mode.
- */
+unsigned int mmc_boot_init(struct mmc *mmc) +{
unsigned int mmc_pwr = 0;
struct mmc_priv *priv = (struct mmc_priv *) mmc->priv;
No cast
int mmc_slot = priv->instance;
Whenever a driver has to be told which instance it is, a kitten dies.... Ok, after reading through I see that it's only used to set clocking. That's probably fine. More on this later.
+/*
- Board specific initializations
- */
+unsigned int mmc_boot_main(struct mmc *mmc) +{
unsigned int mmc_ret = MMC_BOOT_E_SUCCESS;
/* Initialize necessary data structure and enable/set clock and power */
debug(" Initializing MMC host data structure and clock!\n");
mmc_ret = mmc_boot_init(mmc);
if (mmc_ret != MMC_BOOT_E_SUCCESS) {
printf("MMC Boot: Error Initializing MMC Card!!!\n");
return MMC_BOOT_E_FAILURE;
}
return MMC_BOOT_E_SUCCESS;
+}
Why not put the actual initialization function here? We usually want to contain the driver information, here. It looks like you have two different initialization paths, but there should be some way to codify that so that future readers/writers can understand which parts of the driver are being used under a given set of conditions. As it currently stands, they will have to investigate the board file to figure it out. Use the board init function to pass in parameters the driver can use to make those choices. For instance, the
The contents of this e-mail and any attachment(s) may contain confidential or privileged information for the intended recipient(s). Unintended recipients are prohibited from taking action on the basis of information in this e-mail and using or disseminating the information, and must notify the sender and delete it from their system. L&T Infotech will not accept responsibility or liability for the accuracy or completeness of, or the presence of any virus or disabling code in this e-mail"
These sorts of disclaimers really serve to irritate open source developers. Also, it puts me in the legal void, as you neglected to CC me, which means I might be an unintended recipient. In defiance of your disclaimer, I decided to take action, anyway. Feel free to sue me. :P
participants (5)
-
Albert ARIBAUD
-
Andy Fleming
-
Mohamed Haneef
-
Mohamed Haneef
-
mohamed.haneef@lntinfotech.com