Hi Andrew Thank you. I will investigate it whether "wait_event_interruptible_timeout" is right. Thanks, Goda akpm@xxxxxxxxxxxxxxxxxxxx wrote: > The patch titled > mmc: add support MMCIF for SuperH > has been added to the -mm tree. Its filename is > mmc-add-support-mmcif-for-superh.patch > > Before you just go and hit "reply", please: > a) Consider who else should be cc'ed > b) Prefer to cc a suitable mailing list as well > c) Ideally: find the original patch on the mailing list and do a > reply-to-all to that, adding suitable additional cc's > > *** Remember to use Documentation/SubmitChecklist when testing your code *** > > See http://userweb.kernel.org/~akpm/stuff/added-to-mm.txt to find > out what to do about this > > The current -mm tree may be found at http://userweb.kernel.org/~akpm/mmotm/ > > ------------------------------------------------------ > Subject: mmc: add support MMCIF for SuperH > From: Yusuke Goda <yusuke.goda.sx@xxxxxxxxxxx> > > MMCIF is the MMC Host Interface in SuperH. > > Signed-off-by: Yusuke Goda <yusuke.goda.sx@xxxxxxxxxxx> > Cc: Ben Hutchings <ben@xxxxxxxxxxxxxxx> > Cc: Paul Mundt <lethal@xxxxxxxxxxxx> > Cc: <linux-mmc@xxxxxxxxxxxxxxx> > Signed-off-by: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx> > --- > > drivers/mmc/host/Kconfig | 8 > drivers/mmc/host/Makefile | 1 > drivers/mmc/host/sh_mmcif.c | 965 +++++++++++++++++++++++++++++++++ > include/linux/mmc/sh_mmcif.h | 39 + > 4 files changed, 1013 insertions(+) > > diff -puN drivers/mmc/host/Kconfig~mmc-add-support-mmcif-for-superh drivers/mmc/host/Kconfig > --- a/drivers/mmc/host/Kconfig~mmc-add-support-mmcif-for-superh > +++ a/drivers/mmc/host/Kconfig > @@ -412,3 +412,11 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKA > depends on SDH_BFIN > help > If you say yes here SD-Cards may work on the EZkit. > + > +config MMC_SH_MMCIF > + tristate "SuperH Internal MMCIF support" > + depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE) > + help > + This selects the MMC Host Interface controler (MMCIF). > + > + This driver supports MMCIF in sh7724/sh7757/sh7372. > diff -puN drivers/mmc/host/Makefile~mmc-add-support-mmcif-for-superh drivers/mmc/host/Makefile > --- a/drivers/mmc/host/Makefile~mmc-add-support-mmcif-for-superh > +++ a/drivers/mmc/host/Makefile > @@ -34,6 +34,7 @@ obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o > obj-$(CONFIG_MMC_CB710) += cb710-mmc.o > obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o > obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o > +obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o > > obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o > sdhci-of-y := sdhci-of-core.o > diff -puN /dev/null drivers/mmc/host/sh_mmcif.c > --- /dev/null > +++ a/drivers/mmc/host/sh_mmcif.c > @@ -0,0 +1,965 @@ > +/* > + * MMCIF eMMC driver. > + * > + * Copyright (C) 2010 Renesas Solutions Corp. > + * Yusuke Goda <yusuke.goda.sx@xxxxxxxxxxx> > + * > + * 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. > + * > + * > + * TODO > + * 1. DMA > + * 2. Power management > + * 3. Handle MMC errors better > + * > + */ > + > +#include <linux/dma-mapping.h> > +#include <linux/mmc/host.h> > +#include <linux/mmc/card.h> > +#include <linux/mmc/core.h> > +#include <linux/mmc/mmc.h> > +#include <linux/mmc/sdio.h> > +#include <linux/delay.h> > +#include <linux/platform_device.h> > +#include <linux/clk.h> > +#include <linux/mmc/sh_mmcif.h> > + > +#define DRIVER_NAME "sh_mmcif" > +#define DRIVER_VERSION "2010-04-28" > + > +#define MMCIF_CE_CMD_SET 0x00000000 > +#define MMCIF_CE_ARG 0x00000008 > +#define MMCIF_CE_ARG_CMD12 0x0000000C > +#define MMCIF_CE_CMD_CTRL 0x00000010 > +#define MMCIF_CE_BLOCK_SET 0x00000014 > +#define MMCIF_CE_CLK_CTRL 0x00000018 > +#define MMCIF_CE_BUF_ACC 0x0000001C > +#define MMCIF_CE_RESP3 0x00000020 > +#define MMCIF_CE_RESP2 0x00000024 > +#define MMCIF_CE_RESP1 0x00000028 > +#define MMCIF_CE_RESP0 0x0000002C > +#define MMCIF_CE_RESP_CMD12 0x00000030 > +#define MMCIF_CE_DATA 0x00000034 > +#define MMCIF_CE_INT 0x00000040 > +#define MMCIF_CE_INT_MASK 0x00000044 > +#define MMCIF_CE_HOST_STS1 0x00000048 > +#define MMCIF_CE_HOST_STS2 0x0000004C > +#define MMCIF_CE_VERSION 0x0000007C > + > +/* CE_CMD_SET */ > +#define CMD_MASK 0x3f000000 > +#define CMD_SET_RTYP_NO ((0 << 23) | (0 << 22)) > +#define CMD_SET_RTYP_6B ((0 << 23) | (1 << 22)) /* R1/R1b/R3/R4/R5 */ > +#define CMD_SET_RTYP_17B ((1 << 23) | (0 << 22)) /* R2 */ > +#define CMD_SET_RBSY (1 << 21) /* R1b */ > +#define CMD_SET_CCSEN (1 << 20) > +#define CMD_SET_WDAT (1 << 19) /* 1: on data, 0: no data */ > +#define CMD_SET_DWEN (1 << 18) /* 1: write, 0: read */ > +#define CMD_SET_CMLTE (1 << 17) /* 1: multi block trans, 0: single */ > +#define CMD_SET_CMD12EN (1 << 16) /* 1: CMD12 auto issue */ > +#define CMD_SET_RIDXC_INDEX ((0 << 15) | (0 << 14)) /* index check */ > +#define CMD_SET_RIDXC_BITS ((0 << 15) | (1 << 14)) /* check bits check */ > +#define CMD_SET_RIDXC_NO ((1 << 15) | (0 << 14)) /* no check */ > +#define CMD_SET_CRC7C ((0 << 13) | (0 << 12)) /* CRC7 check*/ > +#define CMD_SET_CRC7C_BITS ((0 << 13) | (1 << 12)) /* check bits check*/ > +#define CMD_SET_CRC7C_INTERNAL ((1 << 13) | (0 << 12)) /* internal CRC7 check*/ > +#define CMD_SET_CRC16C (1 << 10) /* 0: CRC16 check*/ > +#define CMD_SET_CRCSTE (1 << 8) /* 1: not receive CRC status */ > +#define CMD_SET_TBIT (1 << 7) /* 1: tran mission bit "Low" */ > +#define CMD_SET_OPDM (1 << 6) /* 1: open/drain */ > +#define CMD_SET_CCSH (1 << 5) > +#define CMD_SET_DATW_1 ((0 << 1) | (0 << 0)) /* 1bit */ > +#define CMD_SET_DATW_4 ((0 << 1) | (1 << 0)) /* 4bit */ > +#define CMD_SET_DATW_8 ((1 << 1) | (0 << 0)) /* 8bit */ > + > +/* CE_CMD_CTRL */ > +#define CMD_CTRL_BREAK (1 << 0) > + > +/* CE_BLOCK_SET */ > +#define BLOCK_SIZE_MASK 0x0000ffff > + > +/* CE_CLK_CTRL */ > +#define CLK_ENABLE (1 << 24) /* 1: output mmc clock */ > +#define CLK_CLEAR ((1 << 19) | (1 << 18) | (1 << 17) | (1 << 16)) > +#define CLK_SUP_PCLK ((1 << 19) | (1 << 18) | (1 << 17) | (1 << 16)) > +#define SRSPTO_256 ((1 << 13) | (0 << 12)) /* resp timeout */ > +#define SRBSYTO_29 ((1 << 11) | (1 << 10) | \ > + (1 << 9) | (1 << 8)) /* resp busy timeout */ > +#define SRWDTO_29 ((1 << 7) | (1 << 6) | \ > + (1 << 5) | (1 << 4)) /* read/write timeout */ > +#define SCCSTO_29 ((1 << 3) | (1 << 2) | \ > + (1 << 1) | (1 << 0)) /* ccs timeout */ > + > +/* CE_BUF_ACC */ > +#define BUF_ACC_DMAWEN (1 << 25) > +#define BUF_ACC_DMAREN (1 << 24) > +#define BUF_ACC_BUSW_32 (0 << 17) > +#define BUF_ACC_BUSW_16 (1 << 17) > +#define BUF_ACC_ATYP (1 << 16) > + > +/* CE_INT */ > +#define INT_CCSDE (1 << 29) > +#define INT_CMD12DRE (1 << 26) > +#define INT_CMD12RBE (1 << 25) > +#define INT_CMD12CRE (1 << 24) > +#define INT_DTRANE (1 << 23) > +#define INT_BUFRE (1 << 22) > +#define INT_BUFWEN (1 << 21) > +#define INT_BUFREN (1 << 20) > +#define INT_CCSRCV (1 << 19) > +#define INT_RBSYE (1 << 17) > +#define INT_CRSPE (1 << 16) > +#define INT_CMDVIO (1 << 15) > +#define INT_BUFVIO (1 << 14) > +#define INT_WDATERR (1 << 11) > +#define INT_RDATERR (1 << 10) > +#define INT_RIDXERR (1 << 9) > +#define INT_RSPERR (1 << 8) > +#define INT_CCSTO (1 << 5) > +#define INT_CRCSTO (1 << 4) > +#define INT_WDATTO (1 << 3) > +#define INT_RDATTO (1 << 2) > +#define INT_RBSYTO (1 << 1) > +#define INT_RSPTO (1 << 0) > +#define INT_ERR_STS (INT_CMDVIO | INT_BUFVIO | INT_WDATERR | \ > + INT_RDATERR | INT_RIDXERR | INT_RSPERR | \ > + INT_CCSTO | INT_CRCSTO | INT_WDATTO | \ > + INT_RDATTO | INT_RBSYTO | INT_RSPTO) > + > +/* CE_INT_MASK */ > +#define MASK_ALL 0x00000000 > +#define MASK_MCCSDE (1 << 29) > +#define MASK_MCMD12DRE (1 << 26) > +#define MASK_MCMD12RBE (1 << 25) > +#define MASK_MCMD12CRE (1 << 24) > +#define MASK_MDTRANE (1 << 23) > +#define MASK_MBUFRE (1 << 22) > +#define MASK_MBUFWEN (1 << 21) > +#define MASK_MBUFREN (1 << 20) > +#define MASK_MCCSRCV (1 << 19) > +#define MASK_MRBSYE (1 << 17) > +#define MASK_MCRSPE (1 << 16) > +#define MASK_MCMDVIO (1 << 15) > +#define MASK_MBUFVIO (1 << 14) > +#define MASK_MWDATERR (1 << 11) > +#define MASK_MRDATERR (1 << 10) > +#define MASK_MRIDXERR (1 << 9) > +#define MASK_MRSPERR (1 << 8) > +#define MASK_MCCSTO (1 << 5) > +#define MASK_MCRCSTO (1 << 4) > +#define MASK_MWDATTO (1 << 3) > +#define MASK_MRDATTO (1 << 2) > +#define MASK_MRBSYTO (1 << 1) > +#define MASK_MRSPTO (1 << 0) > + > +/* CE_HOST_STS1 */ > +#define STS1_CMDSEQ (1 << 31) > + > +/* CE_HOST_STS2 */ > +#define STS2_CRCSTE (1 << 31) > +#define STS2_CRC16E (1 << 30) > +#define STS2_AC12CRCE (1 << 29) > +#define STS2_RSPCRC7E (1 << 28) > +#define STS2_CRCSTEBE (1 << 27) > +#define STS2_RDATEBE (1 << 26) > +#define STS2_AC12REBE (1 << 25) > +#define STS2_RSPEBE (1 << 24) > +#define STS2_AC12IDXE (1 << 23) > +#define STS2_RSPIDXE (1 << 22) > +#define STS2_CCSTO (1 << 15) > +#define STS2_RDATTO (1 << 14) > +#define STS2_DATBSYTO (1 << 13) > +#define STS2_CRCSTTO (1 << 12) > +#define STS2_AC12BSYTO (1 << 11) > +#define STS2_RSPBSYTO (1 << 10) > +#define STS2_AC12RSPTO (1 << 9) > +#define STS2_RSPTO (1 << 8) > +#define STS2_CRC_ERR (STS2_CRCSTE | STS2_CRC16E | \ > + STS2_AC12CRCE | STS2_RSPCRC7E | STS2_CRCSTEBE) > +#define STS2_TIMEOUT_ERR (STS2_CCSTO | STS2_RDATTO | \ > + STS2_DATBSYTO | STS2_CRCSTTO | \ > + STS2_AC12BSYTO | STS2_RSPBSYTO | \ > + STS2_AC12RSPTO | STS2_RSPTO) > + > +/* CE_VERSION */ > +#define SOFT_RST_ON (1 << 31) > +#define SOFT_RST_OFF (0 << 31) > + > +#define CLKDEV_EMMC_DATA 52000000 /* 52MHz */ > +#define CLKDEV_MMC_DATA 20000000 /* 20MHz */ > +#define CLKDEV_INIT 400000 /* 400 KHz */ > + > +struct sh_mmcif_host { > + struct mmc_host *mmc; > + struct mmc_data *data; > + struct mmc_command *cmd; > + struct platform_device *pd; > + struct clk *hclk; > + unsigned int clk; > + int bus_width; > + u16 wait_int; > + u16 sd_error; > + long timeout; > + void __iomem *addr; > + wait_queue_head_t intr_wait; > +}; > + > +static inline u32 sh_mmcif_readl(struct sh_mmcif_host *host, unsigned int reg) > +{ > + return readl(host->addr + reg); > +} > + > +static inline void sh_mmcif_writel(struct sh_mmcif_host *host, > + unsigned int reg, u32 val) > +{ > + writel(val, host->addr + reg); > +} > + > +static inline void sh_mmcif_bitset(struct sh_mmcif_host *host, > + unsigned int reg, u32 val) > +{ > + writel(val | sh_mmcif_readl(host, reg), host->addr + reg); > +} > + > +static inline void sh_mmcif_bitclr(struct sh_mmcif_host *host, > + unsigned int reg, u32 val) > +{ > + writel(~val & sh_mmcif_readl(host, reg), host->addr + reg); > +} > + > + > +static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk) > +{ > + struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; > + > + sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE); > + sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR); > + > + if (!clk) > + return; > + if (p->sup_pclk && clk == host->clk) > + sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_SUP_PCLK); > + else > + sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR & > + (ilog2(__rounddown_pow_of_two(host->clk / clk)) << 16)); > + > + sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE); > +} > + > +static void sh_mmcif_sync_reset(struct sh_mmcif_host *host) > +{ > + u32 tmp; > + > + tmp = 0x010f0000 & sh_mmcif_readl(host, MMCIF_CE_CLK_CTRL); > + > + sh_mmcif_writel(host, MMCIF_CE_VERSION, SOFT_RST_ON); > + sh_mmcif_writel(host, MMCIF_CE_VERSION, SOFT_RST_OFF); > + sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, tmp | > + SRSPTO_256 | SRBSYTO_29 | SRWDTO_29 | SCCSTO_29); > + /* byte swap on */ > + sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_ATYP); > +} > + > +static int sh_mmcif_error_manage(struct sh_mmcif_host *host) > +{ > + u32 state1, state2; > + int ret, timeout = 10000000; > + > + host->sd_error = 0; > + host->wait_int = 0; > + > + state1 = sh_mmcif_readl(host, MMCIF_CE_HOST_STS1); > + state2 = sh_mmcif_readl(host, MMCIF_CE_HOST_STS2); > + pr_debug("%s: ERR HOST_STS1 = %08x\n", \ > + DRIVER_NAME, sh_mmcif_readl(host, MMCIF_CE_HOST_STS1)); > + pr_debug("%s: ERR HOST_STS2 = %08x\n", \ > + DRIVER_NAME, sh_mmcif_readl(host, MMCIF_CE_HOST_STS2)); > + > + if (state1 & STS1_CMDSEQ) { > + sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, CMD_CTRL_BREAK); > + sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, ~CMD_CTRL_BREAK); > + while (1) { > + timeout--; > + if (timeout < 0) { > + pr_err(DRIVER_NAME": Forceed end of " \ > + "command sequence timeout err\n"); > + return -EIO; > + } > + if (!(sh_mmcif_readl(host, MMCIF_CE_HOST_STS1) > + & STS1_CMDSEQ)) > + break; > + mdelay(1); > + } > + sh_mmcif_sync_reset(host); > + pr_debug(DRIVER_NAME": Forced end of command sequence\n"); > + return -EIO; > + } > + > + if (state2 & STS2_CRC_ERR) { > + pr_debug(DRIVER_NAME": Happened CRC error\n"); > + ret = -EIO; > + } else if (state2 & STS2_TIMEOUT_ERR) { > + pr_debug(DRIVER_NAME": Happened Timeout error\n"); > + ret = -ETIMEDOUT; > + } else { > + pr_debug(DRIVER_NAME": Happened End/Index error\n"); > + ret = -EIO; > + } > + return ret; > +} > + > +static int sh_mmcif_single_read(struct sh_mmcif_host *host, > + struct mmc_request *mrq) > +{ > + struct mmc_data *data = mrq->data; > + long time; > + u32 blocksize, i, *p = sg_virt(data->sg); > + > + host->wait_int = 0; > + > + /* buf read enable */ > + sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); > + time = wait_event_interruptible_timeout(host->intr_wait, > + host->wait_int == 1 || > + host->sd_error == 1, host->timeout); > + if (host->wait_int != 1 && (time == 0 || host->sd_error != 0)) > + return sh_mmcif_error_manage(host); > + > + host->wait_int = 0; > + blocksize = (BLOCK_SIZE_MASK & > + sh_mmcif_readl(host, MMCIF_CE_BLOCK_SET)) + 3; > + for (i = 0; i < blocksize / 4; i++) > + *p++ = sh_mmcif_readl(host, MMCIF_CE_DATA); > + > + /* buffer read end */ > + sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFRE); > + time = wait_event_interruptible_timeout(host->intr_wait, > + host->wait_int == 1 || > + host->sd_error == 1, host->timeout); > + if (host->wait_int != 1 && (time == 0 || host->sd_error != 0)) > + return sh_mmcif_error_manage(host); > + > + host->wait_int = 0; > + return 0; > +} > + > +static int sh_mmcif_multi_read(struct sh_mmcif_host *host, > + struct mmc_request *mrq) > +{ > + struct mmc_data *data = mrq->data; > + long time; > + u32 blocksize, i, j, sec, *p; > + > + blocksize = BLOCK_SIZE_MASK & sh_mmcif_readl(host, MMCIF_CE_BLOCK_SET); > + for (j = 0; j < data->sg_len; j++) { > + p = sg_virt(data->sg); > + host->wait_int = 0; > + for (sec = 0; sec < data->sg->length / blocksize; sec++) { > + sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); > + /* buf read enable */ > + time = wait_event_interruptible_timeout(host->intr_wait, > + host->wait_int == 1 || > + host->sd_error == 1, host->timeout); > + > + if (host->wait_int != 1 && > + (time == 0 || host->sd_error != 0)) > + return sh_mmcif_error_manage(host); > + > + host->wait_int = 0; > + for (i = 0; i < blocksize / 4; i++) > + *p++ = sh_mmcif_readl(host, MMCIF_CE_DATA); > + } > + if (j < data->sg_len - 1) > + data->sg++; > + } > + return 0; > +} > + > +static int sh_mmcif_single_write(struct sh_mmcif_host *host, > + struct mmc_request *mrq) > +{ > + struct mmc_data *data = mrq->data; > + long time; > + u32 blocksize, i, *p = sg_virt(data->sg); > + > + host->wait_int = 0; > + sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); > + > + /* buf write enable */ > + time = wait_event_interruptible_timeout(host->intr_wait, > + host->wait_int == 1 || > + host->sd_error == 1, host->timeout); > + if (host->wait_int != 1 && (time == 0 || host->sd_error != 0)) > + return sh_mmcif_error_manage(host); > + > + host->wait_int = 0; > + blocksize = (BLOCK_SIZE_MASK & > + sh_mmcif_readl(host, MMCIF_CE_BLOCK_SET)) + 3; > + for (i = 0; i < blocksize / 4; i++) > + sh_mmcif_writel(host, MMCIF_CE_DATA, *p++); > + > + /* buffer write end */ > + sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MDTRANE); > + > + time = wait_event_interruptible_timeout(host->intr_wait, > + host->wait_int == 1 || > + host->sd_error == 1, host->timeout); > + if (host->wait_int != 1 && (time == 0 || host->sd_error != 0)) > + return sh_mmcif_error_manage(host); > + > + host->wait_int = 0; > + return 0; > +} > + > +static int sh_mmcif_multi_write(struct sh_mmcif_host *host, > + struct mmc_request *mrq) > +{ > + struct mmc_data *data = mrq->data; > + long time; > + u32 i, sec, j, blocksize, *p; > + > + blocksize = BLOCK_SIZE_MASK & sh_mmcif_readl(host, MMCIF_CE_BLOCK_SET); > + > + for (j = 0; j < data->sg_len; j++) { > + p = sg_virt(data->sg); > + host->wait_int = 0; > + for (sec = 0; sec < data->sg->length / blocksize; sec++) { > + sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); > + /* buf write enable*/ > + time = wait_event_interruptible_timeout(host->intr_wait, > + host->wait_int == 1 || > + host->sd_error == 1, host->timeout); > + > + if (host->wait_int != 1 && > + (time == 0 || host->sd_error != 0)) > + return sh_mmcif_error_manage(host); > + > + host->wait_int = 0; > + for (i = 0; i < blocksize / 4; i++) > + sh_mmcif_writel(host, MMCIF_CE_DATA, *p++); > + } > + if (j < data->sg_len - 1) > + data->sg++; > + } > + return 0; > +} > + > +static void sh_mmcif_get_response(struct sh_mmcif_host *host, > + struct mmc_command *cmd) > +{ > + if (cmd->flags & MMC_RSP_136) { > + cmd->resp[0] = sh_mmcif_readl(host, MMCIF_CE_RESP3); > + cmd->resp[1] = sh_mmcif_readl(host, MMCIF_CE_RESP2); > + cmd->resp[2] = sh_mmcif_readl(host, MMCIF_CE_RESP1); > + cmd->resp[3] = sh_mmcif_readl(host, MMCIF_CE_RESP0); > + } else > + cmd->resp[0] = sh_mmcif_readl(host, MMCIF_CE_RESP0); > +} > + > +static void sh_mmcif_get_cmd12response(struct sh_mmcif_host *host, > + struct mmc_command *cmd) > +{ > + cmd->resp[0] = sh_mmcif_readl(host, MMCIF_CE_RESP_CMD12); > +} > + > +static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, > + struct mmc_request *mrq, struct mmc_command *cmd, u32 opc) > +{ > + u32 tmp = 0; > + > + /* Response Type check */ > + switch (mmc_resp_type(cmd)) { > + case MMC_RSP_NONE: > + tmp |= CMD_SET_RTYP_NO; > + break; > + case MMC_RSP_R1: > + case MMC_RSP_R1B: > + case MMC_RSP_R3: > + tmp |= CMD_SET_RTYP_6B; > + break; > + case MMC_RSP_R2: > + tmp |= CMD_SET_RTYP_17B; > + break; > + default: > + pr_err(DRIVER_NAME": Not support type response.\n"); > + break; > + } > + switch (opc) { > + /* RBSY */ > + case MMC_SWITCH: > + case MMC_STOP_TRANSMISSION: > + case MMC_SET_WRITE_PROT: > + case MMC_CLR_WRITE_PROT: > + case MMC_ERASE: > + case MMC_GEN_CMD: > + tmp |= CMD_SET_RBSY; > + break; > + } > + /* WDAT / DATW */ > + if (host->data) { > + tmp |= CMD_SET_WDAT; > + switch (host->bus_width) { > + case MMC_BUS_WIDTH_1: > + tmp |= CMD_SET_DATW_1; > + break; > + case MMC_BUS_WIDTH_4: > + tmp |= CMD_SET_DATW_4; > + break; > + case MMC_BUS_WIDTH_8: > + tmp |= CMD_SET_DATW_8; > + break; > + default: > + pr_err(DRIVER_NAME": Not support bus width.\n"); > + break; > + } > + } > + /* DWEN */ > + if (opc == MMC_WRITE_BLOCK || opc == MMC_WRITE_MULTIPLE_BLOCK) > + tmp |= CMD_SET_DWEN; > + /* CMLTE/CMD12EN */ > + if (opc == MMC_READ_MULTIPLE_BLOCK || opc == MMC_WRITE_MULTIPLE_BLOCK) { > + tmp |= CMD_SET_CMLTE | CMD_SET_CMD12EN; > + sh_mmcif_bitset(host, MMCIF_CE_BLOCK_SET, > + mrq->data->blocks << 16); > + } > + /* RIDXC[1:0] check bits */ > + if (opc == MMC_SEND_OP_COND || opc == MMC_ALL_SEND_CID || > + opc == MMC_SEND_CSD || opc == MMC_SEND_CID) > + tmp |= CMD_SET_RIDXC_BITS; > + /* RCRC7C[1:0] check bits */ > + if (opc == MMC_SEND_OP_COND) > + tmp |= CMD_SET_CRC7C_BITS; > + /* RCRC7C[1:0] internal CRC7 */ > + if (opc == MMC_ALL_SEND_CID || > + opc == MMC_SEND_CSD || opc == MMC_SEND_CID) > + tmp |= CMD_SET_CRC7C_INTERNAL; > + > + return opc = ((opc << 24) | tmp); > +} > + > +static u32 sh_mmcif_data_trans(struct sh_mmcif_host *host, > + struct mmc_request *mrq, u32 opc) > +{ > + u32 ret; > + > + switch (opc) { > + case MMC_READ_MULTIPLE_BLOCK: > + ret = sh_mmcif_multi_read(host, mrq); > + break; > + case MMC_WRITE_MULTIPLE_BLOCK: > + ret = sh_mmcif_multi_write(host, mrq); > + break; > + case MMC_WRITE_BLOCK: > + ret = sh_mmcif_single_write(host, mrq); > + break; > + case MMC_READ_SINGLE_BLOCK: > + case MMC_SEND_EXT_CSD: > + ret = sh_mmcif_single_read(host, mrq); > + break; > + default: > + pr_err(DRIVER_NAME": NOT SUPPORT CMD = d'%08d\n", opc); > + ret = -EINVAL; > + break; > + } > + return ret; > +} > + > +static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, > + struct mmc_request *mrq, struct mmc_command *cmd) > +{ > + long time; > + int ret = 0, mask = 0; > + u32 opc = cmd->opcode; > + > + host->cmd = cmd; > + > + switch (opc) { > + /* respons busy check */ > + case MMC_SWITCH: > + case MMC_STOP_TRANSMISSION: > + case MMC_SET_WRITE_PROT: > + case MMC_CLR_WRITE_PROT: > + case MMC_ERASE: > + case MMC_GEN_CMD: > + mask = MASK_MRBSYE; > + break; > + default: > + mask = MASK_MCRSPE; > + break; > + } > + mask |= MASK_MCMDVIO | MASK_MBUFVIO | MASK_MWDATERR | > + MASK_MRDATERR | MASK_MRIDXERR | MASK_MRSPERR | > + MASK_MCCSTO | MASK_MCRCSTO | MASK_MWDATTO | > + MASK_MRDATTO | MASK_MRBSYTO | MASK_MRSPTO; > + > + if (host->data) { > + sh_mmcif_writel(host, MMCIF_CE_BLOCK_SET, 0); > + sh_mmcif_writel(host, MMCIF_CE_BLOCK_SET, mrq->data->blksz); > + } > + opc = sh_mmcif_set_cmd(host, mrq, cmd, opc); > + > + sh_mmcif_writel(host, MMCIF_CE_INT, 0xD80430C0); > + sh_mmcif_writel(host, MMCIF_CE_INT_MASK, mask); > + /* set arg */ > + sh_mmcif_writel(host, MMCIF_CE_ARG, cmd->arg); > + host->wait_int = 0; > + /* set cmd */ > + sh_mmcif_writel(host, MMCIF_CE_CMD_SET, opc); > + > + time = wait_event_interruptible_timeout(host->intr_wait, > + host->wait_int == 1 || host->sd_error == 1, host->timeout); > + if (host->wait_int != 1 && time == 0) { > + cmd->error = sh_mmcif_error_manage(host); > + return; > + } > + if (host->sd_error) { > + switch (cmd->opcode) { > + case MMC_ALL_SEND_CID: > + case MMC_SELECT_CARD: > + case MMC_APP_CMD: > + cmd->error = -ETIMEDOUT; > + break; > + default: > + pr_debug("%s: Cmd(d'%d) err\n", > + DRIVER_NAME, cmd->opcode); > + cmd->error = sh_mmcif_error_manage(host); > + break; > + } > + host->sd_error = 0; > + host->wait_int = 0; > + return; > + } > + if (!(cmd->flags & MMC_RSP_PRESENT)) { > + cmd->error = ret; > + host->wait_int = 0; > + return; > + } > + if (host->wait_int == 1) { > + sh_mmcif_get_response(host, cmd); > + host->wait_int = 0; > + } > + if (host->data) { > + ret = sh_mmcif_data_trans(host, mrq, cmd->opcode); > + if (ret < 0) > + mrq->data->bytes_xfered = 0; > + else > + mrq->data->bytes_xfered = > + mrq->data->blocks * mrq->data->blksz; > + } > + cmd->error = ret; > +} > + > +static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host, > + struct mmc_request *mrq, struct mmc_command *cmd) > +{ > + long time; > + > + if (mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) > + sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE); > + else if (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) > + sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE); > + else { > + pr_err(DRIVER_NAME": not support stop cmd\n"); > + cmd->error = sh_mmcif_error_manage(host); > + return; > + } > + > + time = wait_event_interruptible_timeout(host->intr_wait, > + host->wait_int == 1 || > + host->sd_error == 1, host->timeout); > + if (host->wait_int != 1 && (time == 0 || host->sd_error != 0)) { > + cmd->error = sh_mmcif_error_manage(host); > + return; > + } > + sh_mmcif_get_cmd12response(host, cmd); > + host->wait_int = 0; > + cmd->error = 0; > +} > + > +static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) > +{ > + struct sh_mmcif_host *host = mmc_priv(mmc); > + > + switch (mrq->cmd->opcode) { > + /* MMCIF does not support SD/SDIO command */ > + case SD_IO_SEND_OP_COND: > + case MMC_APP_CMD: > + mrq->cmd->error = -ETIMEDOUT; > + mmc_request_done(mmc, mrq); > + return; > + case MMC_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */ > + if (!mrq->data) { > + /* send_if_cond cmd (not support) */ > + mrq->cmd->error = -ETIMEDOUT; > + mmc_request_done(mmc, mrq); > + return; > + } > + break; > + default: > + break; > + } > + host->data = mrq->data; > + sh_mmcif_start_cmd(host, mrq, mrq->cmd); > + host->data = NULL; > + > + if (mrq->cmd->error != 0) { > + mmc_request_done(mmc, mrq); > + return; > + } > + if (mrq->stop) > + sh_mmcif_stop_cmd(host, mrq, mrq->stop); > + mmc_request_done(mmc, mrq); > +} > + > +static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) > +{ > + struct sh_mmcif_host *host = mmc_priv(mmc); > + struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; > + > + if (ios->power_mode == MMC_POWER_OFF) { > + /* clock stop */ > + sh_mmcif_clock_control(host, 0); > + if (p->down_pwr) > + p->down_pwr(host->pd); > + return; > + } else if (ios->power_mode == MMC_POWER_UP) { > + if (p->set_pwr) > + p->set_pwr(host->pd, ios->power_mode); > + } > + > + if (ios->clock) > + sh_mmcif_clock_control(host, ios->clock); > + > + host->bus_width = ios->bus_width; > +} > + > +static struct mmc_host_ops sh_mmcif_ops = { > + .request = sh_mmcif_request, > + .set_ios = sh_mmcif_set_ios, > +}; > + > +static void sh_mmcif_detect(struct mmc_host *mmc) > +{ > + mmc_detect_change(mmc, 0); > +} > + > +static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) > +{ > + struct sh_mmcif_host *host = dev_id; > + u32 state = 0; > + int err = 0; > + > + state = sh_mmcif_readl(host, MMCIF_CE_INT); > + > + if (state & INT_RBSYE) { > + sh_mmcif_writel(host, MMCIF_CE_INT, ~(INT_RBSYE | INT_CRSPE)); > + sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MRBSYE); > + } else if (state & INT_CRSPE) { > + sh_mmcif_writel(host, MMCIF_CE_INT, ~INT_CRSPE); > + sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCRSPE); > + } else if (state & INT_BUFREN) { > + sh_mmcif_writel(host, MMCIF_CE_INT, ~INT_BUFREN); > + sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); > + } else if (state & INT_BUFWEN) { > + sh_mmcif_writel(host, MMCIF_CE_INT, ~INT_BUFWEN); > + sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); > + } else if (state & INT_CMD12DRE) { > + sh_mmcif_writel(host, MMCIF_CE_INT, > + ~(INT_CMD12DRE | INT_CMD12RBE | > + INT_CMD12CRE | INT_BUFRE)); > + sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE); > + } else if (state & INT_BUFRE) { > + sh_mmcif_writel(host, MMCIF_CE_INT, ~INT_BUFRE); > + sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFRE); > + } else if (state & INT_DTRANE) { > + sh_mmcif_writel(host, MMCIF_CE_INT, ~INT_DTRANE); > + sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MDTRANE); > + } else if (state & INT_CMD12RBE) { > + sh_mmcif_writel(host, MMCIF_CE_INT, > + ~(INT_CMD12RBE | INT_CMD12CRE)); > + sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE); > + } else if (state & INT_ERR_STS) { > + /* err interrupts */ > + sh_mmcif_writel(host, MMCIF_CE_INT, ~state); > + sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state); > + err = 1; > + } else { > + pr_debug("%s: Not support int\n", DRIVER_NAME); > + sh_mmcif_writel(host, MMCIF_CE_INT, ~state); > + sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state); > + err = 1; > + } > + if (err) { > + host->sd_error = 1; > + pr_debug("%s: int err state = %08x\n", DRIVER_NAME, state); > + } > + host->wait_int = 1; > + wake_up(&host->intr_wait); > + > + return IRQ_HANDLED; > +} > + > +static int __devinit sh_mmcif_probe(struct platform_device *pdev) > +{ > + int ret = 0, irq[2]; > + struct mmc_host *mmc; > + struct sh_mmcif_host *host = NULL; > + struct sh_mmcif_plat_data *pd = NULL; > + struct resource *res; > + void __iomem *reg; > + char clk_name[8]; > + > + irq[0] = platform_get_irq(pdev, 0); > + irq[1] = platform_get_irq(pdev, 1); > + if (irq[0] < 0 || irq[1] < 0) { > + pr_err(DRIVER_NAME": Get irq error\n"); > + return -ENXIO; > + } > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) { > + dev_err(&pdev->dev, "platform_get_resource error.\n"); > + return -ENXIO; > + } > + reg = ioremap(res->start, resource_size(res)); > + if (!reg) { > + dev_err(&pdev->dev, "ioremap error.\n"); > + return -ENOMEM; > + } > + pd = (struct sh_mmcif_plat_data *)(pdev->dev.platform_data); > + if (!pd) { > + dev_err(&pdev->dev, "sh_mmcif plat data error.\n"); > + ret = -ENXIO; > + goto clean_up; > + } > + mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), &pdev->dev); > + if (!mmc) { > + ret = -ENOMEM; > + goto clean_up; > + } > + host = mmc_priv(mmc); > + host->mmc = mmc; > + host->addr = reg; > + host->timeout = 1000; > + > + snprintf(clk_name, sizeof(clk_name), "mmc%d", pdev->id); > + host->hclk = clk_get(&pdev->dev, clk_name); > + if (IS_ERR(host->hclk)) { > + dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); > + ret = PTR_ERR(host->hclk); > + goto clean_up1; > + } > + clk_enable(host->hclk); > + host->clk = clk_get_rate(host->hclk); > + host->pd = pdev; > + > + init_waitqueue_head(&host->intr_wait); > + > + mmc->ops = &sh_mmcif_ops; > + mmc->f_max = host->clk; > + /* close to 400KHz */ > + if (mmc->f_max < 51200000) > + mmc->f_min = mmc->f_max / 128; > + else if (mmc->f_max < 102400000) > + mmc->f_min = mmc->f_max / 256; > + else > + mmc->f_min = mmc->f_max / 512; > + if (pd->ocr) > + mmc->ocr_avail = pd->ocr; > + mmc->caps = MMC_CAP_MMC_HIGHSPEED; > + if (pd->caps) > + mmc->caps |= pd->caps; > + mmc->max_phys_segs = 128; > + mmc->max_hw_segs = 128; > + mmc->max_blk_size = 512; > + mmc->max_blk_count = 65535; > + mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; > + mmc->max_seg_size = mmc->max_req_size; > + > + sh_mmcif_sync_reset(host); > + platform_set_drvdata(pdev, host); > + mmc_add_host(mmc); > + > + ret = request_irq(irq[0], sh_mmcif_intr, 0, "sh_mmc:error", host); > + if (ret) { > + pr_err(DRIVER_NAME": request_irq error (sh_mmc:error)\n"); > + goto clean_up2; > + } > + ret = request_irq(irq[1], sh_mmcif_intr, 0, "sh_mmc:int", host); > + if (ret) { > + free_irq(irq[0], host); > + pr_err(DRIVER_NAME": request_irq error (sh_mmc:int)\n"); > + goto clean_up2; > + } > + > + sh_mmcif_writel(host, MMCIF_CE_INT_MASK, MASK_ALL); > + sh_mmcif_detect(host->mmc); > + > + pr_info("%s: driver version %s\n", DRIVER_NAME, DRIVER_VERSION); > + pr_debug("%s: chip ver H'%04x\n", DRIVER_NAME, > + sh_mmcif_readl(host, MMCIF_CE_VERSION) & 0x0000ffff); > + return ret; > + > +clean_up2: > + clk_disable(host->hclk); > +clean_up1: > + mmc_free_host(mmc); > +clean_up: > + if (reg) > + iounmap(reg); > + return ret; > +} > + > +static int __devexit sh_mmcif_remove(struct platform_device *pdev) > +{ > + struct sh_mmcif_host *host = platform_get_drvdata(pdev); > + int irq[2]; > + > + sh_mmcif_writel(host, MMCIF_CE_INT_MASK, MASK_ALL); > + > + irq[0] = platform_get_irq(pdev, 0); > + irq[1] = platform_get_irq(pdev, 1); > + > + if (host->addr) > + iounmap(host->addr); > + > + platform_set_drvdata(pdev, NULL); > + mmc_remove_host(host->mmc); > + > + free_irq(irq[0], host); > + free_irq(irq[1], host); > + > + clk_disable(host->hclk); > + mmc_free_host(host->mmc); > + > + return 0; > +} > + > +static struct platform_driver sh_mmcif_driver = { > + .probe = sh_mmcif_probe, > + .remove = sh_mmcif_remove, > + .driver = { > + .name = DRIVER_NAME, > + }, > +}; > + > +static int __init sh_mmcif_init(void) > +{ > + return platform_driver_register(&sh_mmcif_driver); > +} > + > +static void __exit sh_mmcif_exit(void) > +{ > + platform_driver_unregister(&sh_mmcif_driver); > +} > + > +module_init(sh_mmcif_init); > +module_exit(sh_mmcif_exit); > + > + > +MODULE_DESCRIPTION("SuperH on-chip MMC/eMMC interface driver"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS(DRIVER_NAME); > +MODULE_AUTHOR("Yusuke Goda <yusuke.goda.sx@xxxxxxxxxxx>"); > diff -puN /dev/null include/linux/mmc/sh_mmcif.h > --- /dev/null > +++ a/include/linux/mmc/sh_mmcif.h > @@ -0,0 +1,39 @@ > +/* > + * include/linux/mmc/sh_mmcif.h > + * > + * platform data for eMMC driver > + * > + * Copyright (C) 2010 Renesas Solutions Corp. > + * > + * 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. > + * > + */ > + > +#ifndef __SH_MMCIF_H__ > +#define __SH_MMCIF_H__ > + > +/* > + * MMCIF : CE_CLK_CTRL [19:16] > + * 1000 : Peripheral clock / 512 > + * 0111 : Peripheral clock / 256 > + * 0110 : Peripheral clock / 128 > + * 0101 : Peripheral clock / 64 > + * 0100 : Peripheral clock / 32 > + * 0011 : Peripheral clock / 16 > + * 0010 : Peripheral clock / 8 > + * 0001 : Peripheral clock / 4 > + * 0000 : Peripheral clock / 2 > + * 1111 : Peripheral clock (sup_pclk set '1') > + */ > + > +struct sh_mmcif_plat_data { > + void (*set_pwr)(struct platform_device *pdev, int state); > + void (*down_pwr)(struct platform_device *pdev); > + u8 sup_pclk; /* 1 :SH7757, 0: SH7724/SH7372 */ > + unsigned long caps; > + u32 ocr; > +}; > + > +#endif /* __SH_MMCIF_H__ */ > _ > > Patches currently in -mm which might be from yusuke.goda.sx@xxxxxxxxxxx are > > mmc-add-support-mmcif-for-superh.patch > -- EC No. W -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html