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

Rajeshwari Birje rajeshwari.birje at gmail.com
Wed May 30 15:30:39 CEST 2012


Hi Jaehoon Chung,

Thank you for comments.

On Tue, May 29, 2012 at 5:08 PM, Jaehoon Chung <jh80.chung at samsung.com> wrote:
> Hi Rajeshwari,
>
> i added Some comments.
>
> On 05/25/2012 08:51 PM, Rajeshwari Shinde 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;
>> +};
>
> I think good that register offset is refer to sdhci.h.
-- Here we have a enum instead of defining each offset separately. Did
not understand what you want me to do.
>
>> +
>> +/*
>> + * 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 */
>
> Is Max clock 52MHz?
Yes for high speed it 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
>
> #endif /* .... */
-- will do so.
>
>> 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;
>> +     }
>
> I want to use the error number..That's helpful for debugging. not return -1;
> And Why do you reset each bit?
> If (mshci_setbits(host, CTRL_RESET | FIFO_RESET | DMA_RESET))
> is this problem?
-- no it should not be a problem will do so.
>
>> +
>> +     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);
>> +             }
>
> cmdidx 8, 41, 55 is not readable. Use macro.
-- will correct this
>
>> +                     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);
>
> If !(mask & (DATA_ERR) | DATA_TOUT | INTMSK_DTO) is always true,
> this point should be infinite loop. i think good that prevent it with timeout.
-- This codition cannot be true all time.
>
>> +             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);
>
> why use udelay(100)?
-- This delay is required for all data operations to happen fine,
without this its giving a read, write error.
>
>> +
>> +     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);
>> +     }
>> +}
>
> If (val)
>        writel(CLK_ENABLE, ...);
> else
>        writel(CLK_DISABLE, ...);
>
> writel(0, &host->reg->cmd);
> writel(CMD_ONLY_CLK, &host->reg->cmd);
>
-- will do so.
>> +
>> +/*
>> + * change host controller clock
>> + *
>> + * @param host               pointer to mshci_host
>> + * @param clock              request clock
>> + *
>> + */
>
> Remove unnecessary
-- will do so
>
>> +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++) {
>
> What means 0xff? more readable..
-- will make it more readable
>
>> +             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);
>
> What means 0x3030001 or 03020001...anybody didn't know what mean is that value.
-- will make it more readable
>
>> +}
>> +
>> +/*
>> + * 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 depth is not always 0x80.
-- will correct this
>
>> +
>> +     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;
>> +}
>
>
> Generally, you didn't use return -1...if you used them, we can't debug what is problem.
-- will try to use proper error return at places where ever possible.
> And mshci is not exynos specific feature. It's DesignWare Cores Mobile Storage host contoller.
> It should be common driver. if you want to use this IP for exynos,
> then you must seperate to mshci.c and exynos-mshci.c.
>
-- We use generic mmc and on top of that write dwmmc code. I think it
can be exynos-dwmmc.c.
> IDMAC is used by default?
-- yes
>
> And i want to change the dwmmc.c instead of mshci.c. In kernel side, already used dwmmc.c.
-- will rename all  to dwmmc
> It's confused.
>
> Best Regards,
> Jaehoon Chung
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot

Regards,
Rajeshwari Shinde


More information about the U-Boot mailing list