[U-Boot] [PATCH] MMC: MSHCI: Add MSHCI driver

Rajeshwari Birje rajeshwari.birje at gmail.com
Mon May 28 07:16:43 CEST 2012


Hi All,

This patch is based on:  "EXYNOS5: PINMUX: Added default pinumx settings".

Regards,
Rajeshwari Shinde.

On Fri, May 25, 2012 at 5:21 PM, Rajeshwari Shinde
<rajeshwari.s at samsung.com> wrote:
> Add MSHCI driver support and resgister description for same.
>
> Signed-off-by: Alim Akhtar <alim.akhtar at samsung.com>
> Signed-off-by: Terry Lambert <tlambert at chromium.org>
> Signed-off-by: Rajeshwari Shinde <rajeshwari.s at samsung.com>
> ---
>  arch/arm/include/asm/arch-exynos/mshc.h |  174 ++++++++++
>  drivers/mmc/Makefile                    |    1 +
>  drivers/mmc/exynos_mshc.c               |  553 +++++++++++++++++++++++++++++++
>  3 files changed, 728 insertions(+), 0 deletions(-)
>  create mode 100644 arch/arm/include/asm/arch-exynos/mshc.h
>  create mode 100644 drivers/mmc/exynos_mshc.c
>
> diff --git a/arch/arm/include/asm/arch-exynos/mshc.h b/arch/arm/include/asm/arch-exynos/mshc.h
> new file mode 100644
> index 0000000..7226619
> --- /dev/null
> +++ b/arch/arm/include/asm/arch-exynos/mshc.h
> @@ -0,0 +1,174 @@
> +/*
> + * (C) Copyright 2012 SAMSUNG Electronics
> + * Abhilash Kesavan <a.kesavan at samsung.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 __ASM_ARCH_COMMON_MSHC_H
> +#define __ASM_ARCH_COMMON_MSHC_H
> +
> +#include <asm/arch/pinmux.h>
> +#ifndef __ASSEMBLY__
> +struct mshci_host {
> +       struct exynos_mshci     *reg;           /* Mapped address */
> +       unsigned int            clock;          /* Current clock in MHz */
> +       enum periph_id  peripheral;
> +};
> +
> +struct exynos_mshci {
> +       unsigned int    ctrl;
> +       unsigned int    pwren;
> +       unsigned int    clkdiv;
> +       unsigned int    clksrc;
> +       unsigned int    clkena;
> +       unsigned int    tmout;
> +       unsigned int    ctype;
> +       unsigned int    blksiz;
> +       unsigned int    bytcnt;
> +       unsigned int    intmask;
> +       unsigned int    cmdarg;
> +       unsigned int    cmd;
> +       unsigned int    resp0;
> +       unsigned int    resp1;
> +       unsigned int    resp2;
> +       unsigned int    resp3;
> +       unsigned int    mintsts;
> +       unsigned int    rintsts;
> +       unsigned int    status;
> +       unsigned int    fifoth;
> +       unsigned int    cdetect;
> +       unsigned int    wrtprt;
> +       unsigned int    gpio;
> +       unsigned int    tcbcnt;
> +       unsigned int    tbbcnt;
> +       unsigned int    debnce;
> +       unsigned int    usrid;
> +       unsigned int    verid;
> +       unsigned int    hcon;
> +       unsigned int    uhs_reg;
> +       unsigned int    rst_n;
> +       unsigned char   reserved1[4];
> +       unsigned int    bmod;
> +       unsigned int    pldmnd;
> +       unsigned int    dbaddr;
> +       unsigned int    idsts;
> +       unsigned int    idinten;
> +       unsigned int    dscaddr;
> +       unsigned int    bufaddr;
> +       unsigned int    clksel;
> +       unsigned char   reserved2[460];
> +       unsigned int    cardthrctl;
> +};
> +
> +/*
> + * Struct idma
> + * Holds the descriptor list
> + */
> +struct mshci_idmac {
> +       u32     des0;
> +       u32     des1;
> +       u32     des2;
> +       u32     des3;
> +};
> +
> +/*  Control Register  Register */
> +#define CTRL_RESET     (0x1 << 0)
> +#define FIFO_RESET     (0x1 << 1)
> +#define DMA_RESET      (0x1 << 2)
> +#define DMA_ENABLE     (0x1 << 5)
> +#define SEND_AS_CCSD   (0x1 << 10)
> +#define ENABLE_IDMAC    (0x1 << 25)
> +
> +/*  Power Enable Register */
> +#define POWER_ENABLE   (0x1 << 0)
> +
> +/*  Clock Enable Register */
> +#define CLK_ENABLE     (0x1 << 0)
> +#define CLK_DISABLE    (0x0 << 0)
> +
> +/* Timeout Register */
> +#define TMOUT_MAX      0xffffffff
> +
> +/*  Card Type Register */
> +#define PORT0_CARD_WIDTH1      0
> +#define PORT0_CARD_WIDTH4      (0x1 << 0)
> +#define PORT0_CARD_WIDTH8      (0x1 << 16)
> +
> +/*  Interrupt Mask Register */
> +#define INTMSK_ALL     0xffffffff
> +#define INTMSK_RE      (0x1 << 1)
> +#define INTMSK_CDONE   (0x1 << 2)
> +#define INTMSK_DTO     (0x1 << 3)
> +#define INTMSK_DCRC    (0x1 << 7)
> +#define INTMSK_RTO     (0x1 << 8)
> +#define INTMSK_DRTO    (0x1 << 9)
> +#define INTMSK_HTO     (0x1 << 10)
> +#define INTMSK_FRUN    (0x1 << 11)
> +#define INTMSK_HLE     (0x1 << 12)
> +#define INTMSK_SBE     (0x1 << 13)
> +#define INTMSK_ACD     (0x1 << 14)
> +#define INTMSK_EBE     (0x1 << 15)
> +
> +/* Command Register */
> +#define CMD_RESP_EXP_BIT       (0x1 << 6)
> +#define CMD_RESP_LENGTH_BIT    (0x1 << 7)
> +#define CMD_CHECK_CRC_BIT      (0x1 << 8)
> +#define CMD_DATA_EXP_BIT       (0x1 << 9)
> +#define CMD_RW_BIT             (0x1 << 10)
> +#define CMD_SENT_AUTO_STOP_BIT (0x1 << 12)
> +#define CMD_WAIT_PRV_DAT_BIT   (0x1 << 13)
> +#define CMD_SEND_CLK_ONLY      (0x1 << 21)
> +#define CMD_USE_HOLD_REG       (0x1 << 29)
> +#define CMD_STRT_BIT           (0x1 << 31)
> +#define CMD_ONLY_CLK           (CMD_STRT_BIT | CMD_SEND_CLK_ONLY | \
> +                               CMD_WAIT_PRV_DAT_BIT)
> +
> +/*  Raw Interrupt Register */
> +#define DATA_ERR       (INTMSK_EBE | INTMSK_SBE | INTMSK_HLE | \
> +                       INTMSK_FRUN | INTMSK_EBE | INTMSK_DCRC)
> +#define DATA_TOUT      (INTMSK_HTO | INTMSK_DRTO)
> +
> +/*  Status Register */
> +#define DATA_BUSY      (0x1 << 9)
> +
> +/*  FIFO Threshold Watermark Register */
> +#define TX_WMARK       (0xFFF << 0)
> +#define RX_WMARK       (0xFFF << 16)
> +#define MSIZE_MASK     (0x7 << 28)
> +
> +/* DW DMA Mutiple Transaction Size */
> +#define MSIZE_8                (2 << 28)
> +
> +/*  Bus Mode Register */
> +#define BMOD_IDMAC_RESET       (0x1 << 0)
> +#define BMOD_IDMAC_FB          (0x1 << 1)
> +#define BMOD_IDMAC_ENABLE      (0x1 << 7)
> +
> +/* IDMAC bits */
> +#define MSHCI_IDMAC_OWN                (0x1 << 31)
> +#define MSHCI_IDMAC_CH         (0x1 << 4)
> +#define MSHCI_IDMAC_FS         (0x1 << 3)
> +#define MSHCI_IDMAC_LD         (0x1 << 2)
> +
> +#define MAX_MSHCI_CLOCK                52000000 /* Max limit is 52MHz */
> +#define MIN_MSHCI_CLOCK                400000 /* Lower limit is 400KHz */
> +#define COMMAND_TIMEOUT                10000
> +#define TIMEOUT_MS             100
> +
> +int exynos_mshci_init(enum periph_id periph_id, int bus_width);
> +
> +#endif
> +#endif
> diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
> index c245352..84e2d6a 100644
> --- a/drivers/mmc/Makefile
> +++ b/drivers/mmc/Makefile
> @@ -27,6 +27,7 @@ LIB   := $(obj)libmmc.o
>
>  COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
>  COBJS-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o
> +COBJS-$(CONFIG_EXYNOS_MSHCI) += exynos_mshc.o
>  COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
>  COBJS-$(CONFIG_FTSDC010) += ftsdc010_esdhc.o
>  COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
> diff --git a/drivers/mmc/exynos_mshc.c b/drivers/mmc/exynos_mshc.c
> new file mode 100644
> index 0000000..eb133c0
> --- /dev/null
> +++ b/drivers/mmc/exynos_mshc.c
> @@ -0,0 +1,553 @@
> +/*
> + * (C) Copyright 2012 Samsung Electronics Co. Ltd
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * 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 <common.h>
> +#include <mmc.h>
> +#include <asm/arch/clk.h>
> +#include <asm/arch/cpu.h>
> +#include <asm/arch/mshc.h>
> +#include <asm/arch/pinmux.h>
> +
> +/* support 4 mmc hosts */
> +enum {
> +       MAX_MMC_HOSTS   = 4
> +};
> +
> +static struct mmc mshci_dev[MAX_MMC_HOSTS];
> +static struct mshci_host mshci_host[MAX_MMC_HOSTS];
> +static int num_devs;
> +
> +/* Struct to hold mshci register and bus width */
> +struct mshci_config {
> +       struct exynos_mshci *reg; /* registers address in physical memory */
> +       int bus_width; /* bus width */
> +       int removable; /* removable device? */
> +       enum periph_id periph_id; /* Peripheral ID for this peripheral */
> +};
> +
> +/**
> + * Set bits of MSHCI host control register.
> + *
> + * @param host MSHCI host
> + * @param bits bits to be set
> + * @return 0 on success, -1 on failure
> + */
> +static int mshci_setbits(struct mshci_host *host, unsigned int bits)
> +{
> +       ulong start;
> +
> +       setbits_le32(&host->reg->ctrl, bits);
> +
> +       start = get_timer(0);
> +       while (readl(&host->reg->ctrl) & bits) {
> +               if (get_timer(start) > TIMEOUT_MS) {
> +                       debug("Set bits failed\n");
> +                       return -1;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * Reset MSHCI host control register.
> + *
> + * @param host MSHCI host
> + * @return 0 on success, -1 on failure
> + */
> +static int mshci_reset_all(struct mshci_host *host)
> +{
> +       ulong start;
> +
> +       /*
> +       * Before we reset ciu check the DATA0 line.  If it is low and
> +       * we resets the ciu then we might see some errors.
> +       */
> +       start = get_timer(0);
> +       while (readl(&host->reg->status) & DATA_BUSY) {
> +               if (get_timer(start) > TIMEOUT_MS) {
> +                       debug("Controller did not release"
> +                               "data0 before ciu reset\n");
> +                       return -1;
> +               }
> +       }
> +
> +       if (mshci_setbits(host, CTRL_RESET)) {
> +               debug("Fail to reset card.\n");
> +               return -1;
> +       }
> +       if (mshci_setbits(host, FIFO_RESET)) {
> +               debug("Fail to reset fifo.\n");
> +               return -1;
> +       }
> +       if (mshci_setbits(host, DMA_RESET)) {
> +               debug("Fail to reset dma.\n");
> +               return -1;
> +       }
> +
> +       return 0;
> +}
> +
> +static void mshci_set_mdma_desc(u8 *desc_vir, u8 *desc_phy,
> +               unsigned int des0, unsigned int des1, unsigned int des2)
> +{
> +       struct mshci_idmac *desc = (struct mshci_idmac *)desc_vir;
> +
> +       desc->des0 = des0;
> +       desc->des1 = des1;
> +       desc->des2 = des2;
> +       desc->des3 = (unsigned int)desc_phy + sizeof(struct mshci_idmac);
> +}
> +
> +static int mshci_prepare_data(struct mshci_host *host, struct mmc_data *data)
> +{
> +       unsigned int i;
> +       unsigned int data_cnt;
> +       unsigned int des_flag;
> +       unsigned int blksz;
> +       static struct mshci_idmac idmac_desc[0x10000];
> +       struct mshci_idmac *pdesc_dmac;
> +
> +       if (mshci_setbits(host, FIFO_RESET)) {
> +               debug("Fail to reset FIFO\n");
> +               return -1;
> +       }
> +
> +       pdesc_dmac = idmac_desc;
> +       blksz = data->blocksize;
> +       data_cnt = data->blocks;
> +
> +       for  (i = 0;; i++) {
> +               des_flag = (MSHCI_IDMAC_OWN | MSHCI_IDMAC_CH);
> +               des_flag |= (i == 0) ? MSHCI_IDMAC_FS : 0;
> +               if (data_cnt <= 8) {
> +                       des_flag |= MSHCI_IDMAC_LD;
> +                       mshci_set_mdma_desc((u8 *)pdesc_dmac,
> +                       (u8 *)virt_to_phys(pdesc_dmac),
> +                       des_flag, blksz * data_cnt,
> +                       (unsigned int)(virt_to_phys(data->dest)) +
> +                       (unsigned int)(i * 0x1000));
> +                       break;
> +               }
> +               /* max transfer size is 4KB per descriptor */
> +               mshci_set_mdma_desc((u8 *)pdesc_dmac,
> +                       (u8 *)virt_to_phys(pdesc_dmac),
> +                       des_flag, blksz * 8,
> +                       virt_to_phys(data->dest) +
> +                       (unsigned int)(i * 0x1000));
> +
> +               data_cnt -= 8;
> +               pdesc_dmac++;
> +       }
> +
> +       writel((unsigned int)virt_to_phys(idmac_desc), &host->reg->dbaddr);
> +
> +       /* enable the Internal DMA Controller */
> +       setbits_le32(&host->reg->ctrl, ENABLE_IDMAC | DMA_ENABLE);
> +       setbits_le32(&host->reg->bmod, BMOD_IDMAC_ENABLE | BMOD_IDMAC_FB);
> +
> +       writel(data->blocksize, &host->reg->blksiz);
> +       writel(data->blocksize * data->blocks, &host->reg->bytcnt);
> +
> +       return 0;
> +}
> +
> +static int mshci_set_transfer_mode(struct mshci_host *host,
> +       struct mmc_data *data)
> +{
> +       int mode = CMD_DATA_EXP_BIT;
> +
> +       if (data->blocks > 1)
> +               mode |= CMD_SENT_AUTO_STOP_BIT;
> +       if (data->flags & MMC_DATA_WRITE)
> +               mode |= CMD_RW_BIT;
> +
> +       return mode;
> +}
> +
> +/*
> + * Sends a command out on the bus.
> + *
> + * @param mmc  mmc device
> + * @param cmd  mmc_cmd to be sent on bus
> + * @param data mmc data to be sent (optional)
> + *
> + * @return     return 0 if ok, else -1
> + */
> +static int exynos_mshci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
> +               struct mmc_data *data)
> +{
> +       struct mshci_host *host = mmc->priv;
> +
> +       int flags = 0, i;
> +       unsigned int mask;
> +       ulong start;
> +
> +       /*
> +       * We shouldn't wait for data inihibit for stop commands, even
> +       * though they might use busy signaling
> +       */
> +       start = get_timer(0);
> +       while (readl(&host->reg->status) & DATA_BUSY) {
> +               if (get_timer(start) > COMMAND_TIMEOUT) {
> +                       debug("timeout on data busy\n");
> +                       return -1;
> +               }
> +       }
> +
> +       if (readl(&host->reg->rintsts)) {
> +               if ((readl(&host->reg->rintsts) &
> +                               (INTMSK_CDONE | INTMSK_ACD)) == 0)
> +                       debug("there are pending interrupts 0x%x\n",
> +                               readl(&host->reg->rintsts));
> +       }
> +       /* It clears all pending interrupts before sending a command*/
> +       writel(INTMSK_ALL, &host->reg->rintsts);
> +
> +       if (data) {
> +               if (mshci_prepare_data(host, data)) {
> +                       debug("fail to prepare data\n");
> +                       return -1;
> +               }
> +       }
> +
> +       writel(cmd->cmdarg, &host->reg->cmdarg);
> +
> +       if (data)
> +               flags = mshci_set_transfer_mode(host, data);
> +
> +       if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
> +               /* this is out of SD spec */
> +               return -1;
> +
> +       if (cmd->resp_type & MMC_RSP_PRESENT) {
> +               flags |= CMD_RESP_EXP_BIT;
> +               if (cmd->resp_type & MMC_RSP_136)
> +                       flags |= CMD_RESP_LENGTH_BIT;
> +       }
> +
> +       if (cmd->resp_type & MMC_RSP_CRC)
> +               flags |= CMD_CHECK_CRC_BIT;
> +       flags |= (cmd->cmdidx | CMD_STRT_BIT | CMD_USE_HOLD_REG |
> +                       CMD_WAIT_PRV_DAT_BIT);
> +
> +       mask = readl(&host->reg->cmd);
> +       if (mask & CMD_STRT_BIT)
> +               debug("cmd busy, current cmd: %d", cmd->cmdidx);
> +
> +       writel(flags, &host->reg->cmd);
> +       /* wait for command complete by busy waiting. */
> +       for (i = 0; i < COMMAND_TIMEOUT; i++) {
> +               mask = readl(&host->reg->rintsts);
> +               if (mask & INTMSK_CDONE) {
> +                       if (!data)
> +                               writel(mask, &host->reg->rintsts);
> +                       break;
> +               }
> +       }
> +       /* timeout for command complete. */
> +       if (COMMAND_TIMEOUT == i) {
> +               debug("timeout waiting for status update\n");
> +               return TIMEOUT;
> +       }
> +
> +       if (mask & INTMSK_RTO) {
> +               if (((cmd->cmdidx == 8 || cmd->cmdidx == 41 ||
> +                       cmd->cmdidx == 55)) == 0) {
> +                       debug("response timeout error: 0x%x cmd: %d\n",
> +                               mask, cmd->cmdidx);
> +               }
> +                       return TIMEOUT;
> +       } else if (mask & INTMSK_RE) {
> +               debug("response error: 0x%x cmd: %d\n", mask, cmd->cmdidx);
> +               return -1;
> +       }
> +       if (cmd->resp_type & MMC_RSP_PRESENT) {
> +               if (cmd->resp_type & MMC_RSP_136) {
> +                       /* CRC is stripped so we need to do some shifting. */
> +                               cmd->response[0] = readl(&host->reg->resp3);
> +                               cmd->response[1] = readl(&host->reg->resp2);
> +                               cmd->response[2] = readl(&host->reg->resp1);
> +                               cmd->response[3] = readl(&host->reg->resp0);
> +               } else {
> +                       cmd->response[0] = readl(&host->reg->resp0);
> +                       debug("\tcmd->response[0]: 0x%08x\n", cmd->response[0]);
> +               }
> +       }
> +
> +       if (data) {
> +               while (!(mask & (DATA_ERR | DATA_TOUT | INTMSK_DTO)))
> +                       mask = readl(&host->reg->rintsts);
> +               writel(mask, &host->reg->rintsts);
> +               if (mask & (DATA_ERR | DATA_TOUT)) {
> +                       debug("error during transfer: 0x%x\n", mask);
> +                       /* make sure disable IDMAC and IDMAC_Interrupts */
> +                       writel((readl(&host->reg->ctrl) &
> +                       ~(DMA_ENABLE | ENABLE_IDMAC)), &host->reg->ctrl);
> +                       /* mask all interrupt source of IDMAC */
> +                       writel(0, &host->reg->idinten);
> +                       return -1;
> +               } else if (mask & INTMSK_DTO) {
> +                       debug("mshci dma interrupt end\n");
> +               } else {
> +                       debug("unexpected condition 0x%x\n", mask);
> +               }
> +               /* make sure disable IDMAC and IDMAC_Interrupts */
> +               writel((readl(&host->reg->ctrl) & ~(DMA_ENABLE | ENABLE_IDMAC)),
> +                               &host->reg->ctrl);
> +               /* mask all interrupt source of IDMAC */
> +               writel(0, &host->reg->idinten);
> +       }
> +
> +       udelay(100);
> +
> +       return 0;
> +}
> +
> +/*
> + * ON/OFF host controller clock
> + *
> + * @param host         pointer to mshci_host
> + * @param val          to enable/disable clock
> + */
> +static void mshci_clock_onoff(struct mshci_host *host, int val)
> +{
> +
> +       if (val) {
> +               writel(CLK_ENABLE, &host->reg->clkena);
> +               writel(0, &host->reg->cmd);
> +               writel(CMD_ONLY_CLK, &host->reg->cmd);
> +       } else {
> +               writel(CLK_DISABLE, &host->reg->clkena);
> +               writel(0, &host->reg->cmd);
> +               writel(CMD_ONLY_CLK, &host->reg->cmd);
> +       }
> +}
> +
> +/*
> + * change host controller clock
> + *
> + * @param host         pointer to mshci_host
> + * @param clock                request clock
> + *
> + */
> +static void mshci_change_clock(struct mshci_host *host, uint clock)
> +{
> +       int div;
> +       u32 sclk_mshc;
> +
> +       if (clock == host->clock)
> +               return;
> +
> +       /* If Input clock is higher than maximum mshc clock */
> +       if (clock > MAX_MSHCI_CLOCK) {
> +               debug("Input clock is too high\n");
> +               clock = MAX_MSHCI_CLOCK;
> +       }
> +
> +       /* disable the clock before changing it */
> +       mshci_clock_onoff(host, CLK_DISABLE);
> +
> +       /* get the clock division */
> +       if (host->peripheral == PERIPH_ID_SDMMC4)
> +               sclk_mshc = get_mshci_clk_div(host->peripheral) / 2;
> +       else
> +               sclk_mshc = get_mshci_clk_div(host->peripheral) / 4;
> +
> +       /* CLKDIV */
> +       for (div = 1 ; div <= 0xff; div++) {
> +               if ((sclk_mshc / (2 * div)) <= clock) {
> +                       writel(div, &host->reg->clkdiv);
> +                       break;
> +               }
> +       }
> +
> +       writel(0, &host->reg->cmd);
> +       writel(CMD_ONLY_CLK, &host->reg->cmd);
> +
> +       writel(readl(&host->reg->cmd) & (~CMD_SEND_CLK_ONLY),
> +                                       &host->reg->cmd);
> +
> +       mshci_clock_onoff(host, CLK_ENABLE);
> +       host->clock = clock;
> +}
> +
> +/*
> + * Set ios for host controller clock
> + *
> + * This sets the card bus width and clksel
> + */
> +static void exynos_mshci_set_ios(struct mmc *mmc)
> +{
> +       struct mshci_host *host = mmc->priv;
> +
> +       debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock);
> +
> +       if (mmc->clock > 0)
> +               mshci_change_clock(host, mmc->clock);
> +
> +       if (mmc->bus_width == 8)
> +               writel(PORT0_CARD_WIDTH8, &host->reg->ctype);
> +       else if (mmc->bus_width == 4)
> +               writel(PORT0_CARD_WIDTH4, &host->reg->ctype);
> +       else
> +               writel(PORT0_CARD_WIDTH1, &host->reg->ctype);
> +
> +       if (host->peripheral == PERIPH_ID_SDMMC0)
> +               writel(0x03030001, &host->reg->clksel);
> +       if (host->peripheral == PERIPH_ID_SDMMC2)
> +               writel(0x03020001, &host->reg->clksel);
> +       if (host->peripheral == PERIPH_ID_SDMMC4)
> +               writel(0x00020001, &host->reg->clksel);
> +}
> +
> +/*
> + * Fifo init for host controller
> + */
> +static void mshci_fifo_init(struct mshci_host *host)
> +{
> +       int fifo_val, fifo_depth, fifo_threshold;
> +
> +       fifo_val = readl(&host->reg->fifoth);
> +
> +       fifo_depth = 0x80;
> +       fifo_threshold = fifo_depth / 2;
> +
> +       fifo_val &= ~(RX_WMARK | TX_WMARK | MSIZE_MASK);
> +       fifo_val |= (fifo_threshold | (fifo_threshold << 16) | MSIZE_8);
> +       writel(fifo_val, &host->reg->fifoth);
> +}
> +
> +
> +static void mshci_init(struct mshci_host *host)
> +{
> +       /* power on the card */
> +       writel(POWER_ENABLE, &host->reg->pwren);
> +
> +       mshci_reset_all(host);
> +       mshci_fifo_init(host);
> +
> +       /* clear all pending interrupts */
> +       writel(INTMSK_ALL, &host->reg->rintsts);
> +
> +       /* interrupts are not used, disable all */
> +       writel(0, &host->reg->intmask);
> +}
> +
> +static int exynos_mshci_initialize(struct mmc *mmc)
> +{
> +       struct mshci_host *host = (struct mshci_host *)mmc->priv;
> +       unsigned int ier;
> +
> +       mshci_init(host);
> +
> +       /* enumerate at 400KHz */
> +       mshci_change_clock(host, MIN_MSHCI_CLOCK);
> +
> +       /* set auto stop command */
> +       ier = readl(&host->reg->ctrl);
> +       ier |= SEND_AS_CCSD;
> +       writel(ier, &host->reg->ctrl);
> +
> +       /* set 1bit card mode */
> +       writel(PORT0_CARD_WIDTH1, &host->reg->ctype);
> +
> +       writel(0xfffff, &host->reg->debnce);
> +
> +       /* set bus mode register for IDMAC */
> +       writel(BMOD_IDMAC_RESET, &host->reg->bmod);
> +
> +       writel(0x0, &host->reg->idinten);
> +
> +       /* set the max timeout for data and response */
> +       writel(TMOUT_MAX, &host->reg->tmout);
> +
> +       return 0;
> +}
> +
> +static int mshci_initialize(struct mshci_config *config)
> +{
> +       struct mshci_host *mmc_host;
> +       struct mmc *mmc;
> +
> +       if (num_devs == MAX_MMC_HOSTS) {
> +               debug("%s: Too many hosts\n", __func__);
> +               return -1;
> +       }
> +
> +       /* set the clock for mshci controller */
> +       if (set_mshci_clk_div(config->periph_id)) {
> +               debug("clock_set_mshci failed\n");
> +               return -1;
> +       }
> +
> +       mmc = &mshci_dev[num_devs];
> +       mmc_host = &mshci_host[num_devs];
> +
> +       sprintf(mmc->name, "EXYNOS MSHCI%d", num_devs);
> +       num_devs++;
> +
> +       mmc->priv = mmc_host;
> +       mmc->send_cmd = exynos_mshci_send_command;
> +       mmc->set_ios = exynos_mshci_set_ios;
> +       mmc->init = exynos_mshci_initialize;
> +
> +       mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
> +       mmc->host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC;
> +
> +       if (config->bus_width == 8)
> +               mmc->host_caps |= MMC_MODE_8BIT;
> +       else
> +               mmc->host_caps |= MMC_MODE_4BIT;
> +
> +       mmc->f_min = MIN_MSHCI_CLOCK;
> +       mmc->f_max = MAX_MSHCI_CLOCK;
> +
> +       exynos_pinmux_config(config->periph_id,
> +                       config->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : 0);
> +
> +       mmc_host->clock = 0;
> +       mmc_host->reg =  config->reg;
> +       mmc_host->peripheral =  config->periph_id;
> +       mmc->b_max = 1;
> +       mmc_register(mmc);
> +       mmc->block_dev.removable = config->removable;
> +       debug("exynos_mshci: periph_id=%d, width=%d, reg=%p\n",
> +             config->periph_id, config->bus_width, config->reg);
> +
> +       return 0;
> +}
> +
> +int exynos_mshci_init(enum periph_id periph_id, int bus_width)
> +{
> +       struct mshci_config config;
> +       int ret = 0;
> +       config.bus_width = bus_width;
> +       config.reg = (struct exynos_mshci *)samsung_get_base_mshci();
> +       config.periph_id = periph_id;
> +       config.removable = 1;
> +       if (mshci_initialize(&config)) {
> +               debug("%s: Failed to init MSHCI\n", __func__);
> +               ret = -1;
> +       }
> +       return ret;
> +}
> --
> 1.7.4.4
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot


More information about the U-Boot mailing list