From: Shawn Lin <shawn.lin@rock-chips.com>
To: wuht06@gmail.com, ulf.hansson@linaro.org, linux-mmc@vger.kernel.org
Cc: shawn.lin@rock-chips.com, Orson.Zhai@spreadtrum.com,
Chunyan.Zhang@spreadtrum.com, Henry.He@spreadtrum.com,
Jason.Wu@spreadtrum.com
Subject: Re: [RFC PATCH v1 1/1] mmc: sprd: add MMC host driver for Spreadtrum SoC
Date: Mon, 3 Aug 2015 17:54:13 +0800 [thread overview]
Message-ID: <55BF3A45.9070507@rock-chips.com> (raw)
In-Reply-To: <1438347778-31671-1-git-send-email-billows.wu@spreadtrum.com>
On 2015-7-31 21:02, wuht06@gmail.com wrote:
> This patch adds MMC host driver for Spreadtrum SoC.
> The following coding style may be not meet kernel coding style.
> I am not sure this kind of coding style is better or worse.
> 1) A macro that represent some bits of a register is added a prefix "__",
> for example:
> #define SDHOST_16_HOST_CTRL_2 0x3E
> #define __TIMING_MODE_SDR12 0x0000
> #define __TIMING_MODE_SDR25 0x0001
> #define __TIMING_MODE_SDR50 0x0002
> I think it is more useful to distinguish a register from a bit of this
> register.
> 2) A function in order to operate a register is also added a prefix "_".
> If the functions(A) call other function(B), we added a prefix "__" before B,
> for example:
> static inline void _sdhost_enable_int(struct sdhost_host *host, u32 mask)
> {
> __local_writel(mask, host, SDHOST_32_INT_ST_EN);
> __local_writel(mask, host, SDHOST_32_INT_SIG_EN);
> }
> I think this make the relationship of the function call more explicit.
>
> Signed-off-by: Billows Wu(WuHongtao) <wuht06@gmail.com>
> ---
> drivers/mmc/host/Kconfig | 6 +
> drivers/mmc/host/Makefile | 1 +
> drivers/mmc/host/sprd_sdhost.c | 1183 ++++++++++++++++++++++++++++++++
> drivers/mmc/host/sprd_sdhost.h | 592 ++++++++++++++++
> drivers/mmc/host/sprd_sdhost_debugfs.c | 213 ++++++
> drivers/mmc/host/sprd_sdhost_debugfs.h | 27 +
> 6 files changed, 2022 insertions(+)
> create mode 100644 drivers/mmc/host/sprd_sdhost.c
> create mode 100644 drivers/mmc/host/sprd_sdhost.h
> create mode 100644 drivers/mmc/host/sprd_sdhost_debugfs.c
> create mode 100644 drivers/mmc/host/sprd_sdhost_debugfs.h
>
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index fd9a58e..c43d938 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -264,6 +264,12 @@ config MMC_SDHCI_SPEAR
>
> If you have a controller with this interface, say Y or M here.
>
> +config SPRD_MMC_SDHOST
> + tristate "Spreadtrum SDIO host Controller support"
> + help
> + This selects the SDIO Host Controller in spreadtrum platform
> +
> + If you have a controller with this interface, say Y or M here.
> If unsure, say N.
>
> config MMC_SDHCI_S3C_DMA
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index e928d61..e00227f 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -74,6 +74,7 @@ obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
> obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o
> obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
> obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o
> +obj-$(CONFIG_SPRD_MMC_SDHOST) += sprd_sdhost.o sprd_sdhost_debugfs.o
>
> ifeq ($(CONFIG_CB710_DEBUG),y)
> CFLAGS-cb710-mmc += -DDEBUG
> diff --git a/drivers/mmc/host/sprd_sdhost.c b/drivers/mmc/host/sprd_sdhost.c
> new file mode 100644
> index 0000000..18c9449
> --- /dev/null
> +++ b/drivers/mmc/host/sprd_sdhost.c
> @@ -0,0 +1,1183 @@
> +/*
> + * linux/drivers/mmc/host/sprd_sdhost.c - Secure Digital Host Controller
> + * Interface driver
> + *
> + * Copyright (C) 2015 Spreadtrum corporation.
> + *
> + * 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.
> + *
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/highmem.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_gpio.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
> +#include <linux/scatterlist.h>
> +
> +#include "sprd_sdhost.h"
> +#include "sprd_sdhost_debugfs.h"
> +
> +#define DRIVER_NAME "sdhost"
> +#define SDHOST_CAPS \
> + (MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | \
> + MMC_CAP_ERASE | MMC_CAP_UHS_SDR50 | \
> + MMC_CAP_CMD23 | MMC_CAP_HW_RESET)
> +
> +struct sdhost_caps_data {
> + char *name;
> + u32 ocr_avail;
> + u32 caps;
> + u32 caps2;
> + u32 pm_caps;
> + u32 base_clk;
> + u32 signal_default_voltage;
> +};
> +
> +struct sdhost_caps_data caps_info_map[] = {
> + {
> + .name = "sd",
> + .ocr_avail = MMC_VDD_29_30 | MMC_VDD_30_31,
> + .caps = SDHOST_CAPS,
> + .caps2 = MMC_CAP2_HC_ERASE_SZ,
> + .base_clk = 192000000,
> + .signal_default_voltage = 3000000,
> + },
> + {
> + .name = "wifi",
> + .ocr_avail = MMC_VDD_165_195 | MMC_VDD_29_30 |
> + MMC_VDD_30_31 | MMC_VDD_32_33 | MMC_VDD_33_34,
> + .caps = SDHOST_CAPS | MMC_CAP_POWER_OFF_CARD |
> + MMC_CAP_UHS_SDR12,
> + .base_clk = 76000000,
> + },
> + {
> + .name = "emmc",
> + .ocr_avail = MMC_VDD_29_30 | MMC_VDD_30_31,
> + .caps = SDHOST_CAPS |
> + MMC_CAP_8_BIT_DATA | MMC_CAP_UHS_SDR12 |
> + MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_DDR50 |
> + MMC_CAP_MMC_HIGHSPEED,
> + .caps2 = MMC_CAP2_FULL_PWR_CYCLE | MMC_CAP2_HC_ERASE_SZ,
> + .base_clk = 192000000,
> + .signal_default_voltage = 1800000,
> + }
> +};
> +
> +#ifdef CONFIG_PM_RUNTIME
> +static void _pm_runtime_setting(struct platform_device *pdev,
> + struct sdhost_host *host);
> +#else
> +static void _pm_runtime_setting(struct platform_device *pdev,
> + struct sdhost_host *host)
> +{
> +}
> +#endif
> +
> +static void _reset_ios(struct sdhost_host *host)
> +{
> + _sdhost_disable_all_int(host);
> +
> + host->ios.clock = 0;
> + host->ios.vdd = 0;
> + /* host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; */
> + /* host->ios.chip_select = MMC_CS_DONTCARE; */
Redundant code should better be removed, right?
> + host->ios.power_mode = MMC_POWER_OFF;
> + host->ios.bus_width = MMC_BUS_WIDTH_1;
> + host->ios.timing = MMC_TIMING_LEGACY;
> + host->ios.signal_voltage = MMC_SIGNAL_VOLTAGE_330;
> + /* host->ios.drv_type = MMC_SET_DRIVER_TYPE_B; */
Ditto.
> +
> + _sdhost_reset(host, _RST_ALL);
> + _sdhost_set_delay(host, host->write_delay,
> + host->read_pos_delay, host->read_neg_delay);
> +}
> +
> +static int __local_pm_suspend(struct sdhost_host *host)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&host->lock, flags);
> + _sdhost_disable_all_int(host);
> + _sdhost_all_clk_off(host);
> + /* wake lock */
> + spin_unlock_irqrestore(&host->lock, flags);
> + clk_disable(host->clk);
> + clk_unprepare(host->clk);
> + synchronize_irq(host->irq);
> +
> + return 0;
> +}
> +
> +static int __local_pm_resume(struct sdhost_host *host)
> +{
> + unsigned long flags;
> +
> + clk_prepare(host->clk);
> + clk_enable(host->clk);
> + spin_lock_irqsave(&host->lock, flags);
> + if (host->ios.clock) {
> + _sdhost_sd_clk_off(host);
> + _sdhost_clk_set_and_on(host,
> + _sdhost_calc_div(host->base_clk,
> + host->ios.clock));
> + _sdhost_sd_clk_on(host);
> + }
> + _sdhost_set_delay(host, host->write_delay,
> + host->read_pos_delay, host->read_neg_delay);
> + spin_unlock_irqrestore(&host->lock, flags);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_RUNTIME
> +static void _pm_runtime_setting(struct platform_device *pdev,
> + struct sdhost_host *host)
> +{
> + pm_runtime_set_active(&pdev->dev);
> + pm_suspend_ignore_children(&pdev->dev, true);
> + pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
> + pm_runtime_use_autosuspend(&pdev->dev);
> + pm_runtime_enable(&pdev->dev);
> +}
> +#endif
> +
> +static int _runtime_get(struct sdhost_host *host)
> +{
> + return pm_runtime_get_sync(host->mmc->parent);
> +}
> +
> +static int _runtime_put(struct sdhost_host *host)
> +{
> + pm_runtime_mark_last_busy(host->mmc->parent);
> + return pm_runtime_put_autosuspend(host->mmc->parent);
> +}
> +
> +#ifdef CONFIG_PM_RUNTIME
> +static int _runtime_suspend(struct device *dev)
> +{
> + struct platform_device *pdev =
> + container_of(dev, struct platform_device, dev);
> + struct sdhost_host *host = platform_get_drvdata(pdev);
> +
> + return __local_pm_suspend(host);
> +}
> +
> +static int _runtime_resume(struct device *dev)
> +{
> + struct platform_device *pdev =
> + container_of(dev, struct platform_device, dev);
> + struct sdhost_host *host = platform_get_drvdata(pdev);
> +
> + return __local_pm_resume(host);
> +}
> +
> +static int _runtime_idle(struct device *dev)
> +{
> + return 0;
> +}
> +#endif
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int _pm_suspend(struct device *dev)
> +{
> + struct platform_device *pdev =
> + container_of(dev, struct platform_device, dev);
> + struct sdhost_host *host = platform_get_drvdata(pdev);
> +
> + _runtime_get(host);
> + host->mmc->pm_flags = host->mmc->pm_caps;
> +
> + return __local_pm_suspend(host);
> +}
> +
> +static int _pm_resume(struct device *dev)
> +{
> + struct platform_device *pdev =
> + container_of(dev, struct platform_device, dev);
> + struct sdhost_host *host = platform_get_drvdata(pdev);
> + struct mmc_ios ios;
> +
> + __local_pm_resume(host);
> +
> + ios = host->mmc->ios;
> + _reset_ios(host);
> + host->mmc->ops->set_ios(host->mmc, &ios);
> + _runtime_put(host);
> +
> + return 0;
> +}
> +#endif
> +
> +static void __get_rsp(struct sdhost_host *host)
> +{
> + u32 i, offset;
> + unsigned int flags = host->cmd->flags;
> + u32 *resp = host->cmd->resp;
> +
> + if (!(flags & MMC_RSP_PRESENT))
> + return;
> +
> + if (flags & MMC_RSP_136) {
> + /* CRC is stripped so we need to do some shifting. */
> + for (i = 0, offset = 12; i < 3; i++, offset -= 4) {
> + resp[i] =
> + _sdhost_readl(host,
> + SDHOST_32_RESPONSE + offset) << 8;
> + resp[i] |=
> + _sdhost_readb(host,
> + SDHOST_32_RESPONSE + offset - 1);
> + }
> + resp[3] = _sdhost_readl(host, SDHOST_32_RESPONSE) << 8;
> + } else {
> + resp[0] = _sdhost_readl(host, SDHOST_32_RESPONSE);
> + }
> +}
> +
> +static void _send_cmd(struct sdhost_host *host, struct mmc_command *cmd)
> +{
> + struct mmc_data *data = cmd->data;
> + int sg_cnt;
> + u32 flag = 0;
> + u16 rsp_type = 0;
> + int if_has_data = 0;
> + int if_mult = 0;
> + int if_read = 0;
> + int if_dma = 0;
> + u16 auto_cmd = __ACMD_DIS;
> +
> + pr_debug("%s(%s) CMD%d, arg 0x%x, flag 0x%x\n", __func__,
> + host->device_name, cmd->opcode, cmd->arg, cmd->flags);
> + if (cmd->data)
> + pr_debug("%s(%s) block size %d, cnt %d\n", __func__,
> + host->device_name, cmd->data->blksz, cmd->data->blocks);
> +
> + _sdhost_disable_all_int(host);
> +
> + if (38 == cmd->opcode) {
> + /* if it is erase command , it's busy time will long,
> + * so we set long timeout value here.
> + */
> + /* mod_timer(&host->timer, jiffies + msecs_to_jiffies
> + (host->mmc->max_discard_to+1000)); */
> + mod_timer(&host->timer, jiffies + 10 * HZ);
> + _sdhost_writeb(host, __DATA_TIMEOUT_MAX_VAL, SDHOST_8_TIMEOUT);
> + } else {
> + /* mod_timer(&host->timer,
> + jiffies + (SDHOST_MAX_TIMEOUT+1) * HZ); */
> + mod_timer(&host->timer, jiffies + 3 * HZ);
> + _sdhost_writeb(host, host->data_timeout_val, SDHOST_8_TIMEOUT);
> + }
> +
> + host->cmd = cmd;
> + if (data) {
> + /* set data param */
> + WARN_ON((data->blksz * data->blocks > 524288) ||
> + (data->blksz > host->mmc->max_blk_size) ||
> + (data->blocks > 65535));
> +
> + data->bytes_xfered = 0;
> +
> + if_has_data = 1;
> + if_read = (data->flags & MMC_DATA_READ);
> + if_mult = (mmc_op_multi(cmd->opcode) || data->blocks > 1);
> + if (if_read && !if_mult)
> + flag = _DATA_FILTER_RD_SIGLE;
> + else if (if_read && if_mult)
> + flag = _DATA_FILTER_RD_MULTI;
> + else if (!if_read && !if_mult)
> + flag = _DATA_FILTER_WR_SIGLE;
> + else
> + flag = _DATA_FILTER_WR_MULT;
> +
> + if (!host->auto_cmd_mode)
> + flag |= _INT_ERR_ACMD;
> +
> + if_dma = 1;
> + auto_cmd = host->auto_cmd_mode;
> + _sdhost_set_blk_size(host, data->blksz);
> +
> + sg_cnt = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
> + (data->flags & MMC_DATA_READ) ?
> + DMA_FROM_DEVICE : DMA_TO_DEVICE);
> + if (1 == sg_cnt) {
> + _sdhost_set_dma(host, __SDMA_MOD);
> + _sdhost_set_16_blk_cnt(host, data->blocks);
> + _sdhost_writel(host, sg_dma_address(data->sg),
> + SDHOST_32_SYS_ADDR);
> + } else {
> + WARN_ON(1);
> + flag |= _INT_ERR_ADMA;
> + _sdhost_set_dma(host, __32ADMA_MOD);
> + _sdhost_set_32_blk_cnt(host, data->blocks);
> + _sdhost_writel(host, sg_dma_address(data->sg),
> + SDHOST_32_SYS_ADDR);
> + }
> + } else {
> + /* _sdhost_set_trans_mode(host, 0, 0, __ACMD_DIS, 0, 0); */
> + }
> +
> + _sdhost_writel(host, cmd->arg, SDHOST_32_ARG);
> + switch (mmc_resp_type(cmd)) {
> + case MMC_RSP_R1B:
> + rsp_type = _RSP1B_5B;
> + flag |= _CMD_FILTER_R1B;
> + break;
> + case MMC_RSP_NONE:
> + rsp_type = _RSP0;
> + flag |= _CMD_FILTER_R0;
> + break;
> + case MMC_RSP_R2:
> + rsp_type = _RSP2;
> + flag |= _CMD_FILTER_R2;
> + break;
> + case MMC_RSP_R4:
> + rsp_type = _RSP3_4;
> + flag |= _CMD_FILTER_R1_R4_R5_R6_R7;
> + break;
> + case MMC_RSP_R1:
> + case MMC_RSP_R1 & ~MMC_RSP_CRC:
> + rsp_type = _RSP1_5_6_7;
> + flag |= _CMD_FILTER_R1_R4_R5_R6_R7;
> + break;
> + default:
> + WARN_ON(1);
> + break;
> + }
> +
> + host->int_filter = flag;
> + _sdhost_enable_int(host, flag);
> + pr_debug("sdhost %s CMD%d rsp:0x%x intflag:0x%x\n"
> + "if_mult:0x%x if_read:0x%x auto_cmd:0x%x if_dma:0x%x\n",
> + host->device_name, cmd->opcode, mmc_resp_type(cmd),
> + flag, if_mult, if_read, auto_cmd, if_dma);
> +
> + _sdhost_set_trans_and_cmd(host, if_mult, if_read, auto_cmd, if_mult,
> + if_dma, cmd->opcode, if_has_data, rsp_type);
> +}
> +
> +static irqreturn_t _irq(int irq, void *param)
> +{
> + u32 intmask;
> + struct sdhost_host *host = (struct sdhost_host *)param;
> + struct mmc_request *mrq = host->mrq;
> + struct mmc_command *cmd = host->cmd;
> + struct mmc_data *data;
> +
> + spin_lock(&host->lock);
> + /* maybe _timeout_func run in one core and _irq run in
> + * another core, this will panic if access cmd->data
> + */
> + if ((!mrq) || (!cmd)) {
> + spin_unlock(&host->lock);
> + return IRQ_NONE;
> + }
> + data = cmd->data;
> +
> + intmask = _sdhost_readl(host, SDHOST_32_INT_ST);
> + if (!intmask) {
> + spin_unlock(&host->lock);
> + return IRQ_NONE;
> + }
> + pr_debug("%s(%s) CMD%d, intmask 0x%x, filter = 0x%x\n", __func__,
> + host->device_name, cmd->opcode, intmask, host->int_filter);
> +
> + /* disable unused interrupt */
> + _sdhost_clear_int(host, intmask);
> + /* just care about the interrupt that we want */
> + intmask &= host->int_filter;
> +
> + while (intmask) {
> + if (_INT_FILTER_ERR & intmask) {
> + /* some error happened in command */
> + if (_INT_FILTER_ERR_CMD & intmask) {
> + if (_INT_ERR_CMD_TIMEOUT & intmask)
> + cmd->error = -ETIMEDOUT;
> + else
> + cmd->error = -EILSEQ;
> + }
> + /* some error happened in data token or command
> + * with R1B
> + */
> + if (_INT_FILTER_ERR_DATA & intmask) {
> + if (data) {
> + /* current error is happened in data
> + * token
> + */
> + if (_INT_ERR_DATA_TIMEOUT & intmask)
> + data->error = -ETIMEDOUT;
> + else
> + data->error = -EILSEQ;
> + } else {
> + /* current error is happend in response
> + * with busy
> + */
> + if (_INT_ERR_DATA_TIMEOUT & intmask)
> + cmd->error = -ETIMEDOUT;
> + else
> + cmd->error = -EILSEQ;
> + }
> + }
> + if (_INT_ERR_ACMD & intmask) {
> + /* Auto cmd12 and cmd23 error is belong to data
> + * token error
> + */
> + data->error = -EILSEQ;
> + }
> + if (_INT_ERR_ADMA & intmask)
> + data->error = -EIO;
> +
> + pr_debug("sdhost %s int 0x%x\n", host->device_name,
> + intmask);
> + dump_sdio_reg(host);
> + _sdhost_disable_all_int(host);
> + /* if current error happened in data token,
> + * we send cmd12 to stop it
> + */
> + if ((mrq->cmd == cmd) && (mrq->stop)) {
> + _sdhost_reset(host, _RST_CMD | _RST_DATA);
> + _send_cmd(host, mrq->stop);
> + } else {
> + /* request finish with error, so reset it and
> + * stop the request
> + */
> + _sdhost_reset(host, _RST_CMD | _RST_DATA);
> + tasklet_schedule(&host->finish_tasklet);
> + }
> + goto out;
> + } else {
> + /* delete irq that wanted in filter */
> + host->int_filter &= ~(_INT_FILTER_NORMAL & intmask);
> + if (_INT_DMA_END & intmask) {
> + _sdhost_writel(host,
> + _sdhost_readl(host, SDHOST_32_SYS_ADDR),
> + SDHOST_32_SYS_ADDR);
> + }
> + if (_INT_CMD_END & intmask) {
> + cmd->error = 0;
> + __get_rsp(host);
> + }
> + if (_INT_TRAN_END & intmask) {
> + if (data) {
> + dma_unmap_sg(mmc_dev(host->mmc),
> + data->sg, data->sg_len,
> + (data->flags & MMC_DATA_READ) ?
> + DMA_FROM_DEVICE :
> + DMA_TO_DEVICE);
> + data->error = 0;
> + data->bytes_xfered =
> + data->blksz * data->blocks;
> + } else {
> + /* R1B also can produce transferComplete
> + * interrupt
> + */
> + cmd->error = 0;
> + }
> + }
> + if (!(_INT_FILTER_NORMAL & host->int_filter)) {
> + /* current cmd finished */
> + _sdhost_disable_all_int(host);
> + if (mrq->sbc == cmd) {
> + _send_cmd(host, mrq->cmd);
> + } else if ((mrq->cmd == host->cmd)
> + && (mrq->stop)) {
> + _send_cmd(host, mrq->stop);
> + } else {
> + /* finish with success and stop the
> + * request
> + */
> + tasklet_schedule(&host->finish_tasklet);
> + goto out;
> + }
> + }
> + }
> +
> + intmask = _sdhost_readl(host, SDHOST_32_INT_ST);
> + _sdhost_clear_int(host, intmask);
> + intmask &= host->int_filter;
> + };
> +
> +out:
> + spin_unlock(&host->lock);
> + return IRQ_HANDLED;
> +}
> +
> +static void _tasklet(unsigned long param)
> +{
> + struct sdhost_host *host = (struct sdhost_host *)param;
> + unsigned long flags;
> + struct mmc_request *mrq;
> +
> + del_timer(&host->timer);
> +
> + spin_lock_irqsave(&host->lock, flags);
> + if (!host->mrq) {
> + spin_unlock_irqrestore(&host->lock, flags);
> + return;
> + }
> + mrq = host->mrq;
> + host->mrq = NULL;
> + host->cmd = NULL;
> + mmiowb();
> + spin_unlock_irqrestore(&host->lock, flags);
> +
> + pr_debug("sdhost %s cmd %d data %d\n",
> + host->device_name, mrq->cmd->error,
> + ((!!mrq->cmd->data) ? mrq->cmd->data->error : 0));
> + mmc_request_done(host->mmc, mrq);
> + _runtime_put(host);
> +}
> +
> +static void _timeout(unsigned long data)
> +{
> + struct sdhost_host *host = (struct sdhost_host *)data;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&host->lock, flags);
> + if (host->mrq) {
> + pr_info("sdhost %s Timeout waiting for hardware interrupt!\n",
> + host->device_name);
> + dump_sdio_reg(host);
> + if (host->cmd->data)
> + host->cmd->data->error = -ETIMEDOUT;
> + else if (host->cmd)
> + host->cmd->error = -ETIMEDOUT;
> + else
> + host->mrq->cmd->error = -ETIMEDOUT;
> +
> + _sdhost_disable_all_int(host);
> + _sdhost_reset(host, _RST_CMD | _RST_DATA);
> + tasklet_schedule(&host->finish_tasklet);
> + }
> + mmiowb();
> + spin_unlock_irqrestore(&host->lock, flags);
> +}
> +
> +static void sdhost_request(struct mmc_host *mmc, struct mmc_request *mrq)
> +{
> + struct sdhost_host *host = mmc_priv(mmc);
> + unsigned long flags;
> +
> + _runtime_get(host);
> + spin_lock_irqsave(&host->lock, flags);
> +
> + host->mrq = mrq;
> + /* 1 find whether card is still in slot */
> + if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE)) {
> + if (!mmc_gpio_get_cd(host->mmc)) {
> + mrq->cmd->error = -ENOMEDIUM;
> + tasklet_schedule(&host->finish_tasklet);
> + mmiowb();
> + spin_unlock_irqrestore(&host->lock, flags);
> + return;
> + }
> + /* else asume sdcard is present */
> + }
> +
> + /*
> + * in our control we can not use auto cmd12 and auto cmd23 together
> + * so in following program we use auto cmd23 prior to auto cmd12
> + */
> + pr_debug("%s(%s) CMD%d request %d %d %d\n",
> + __func__, host->device_name, mrq->cmd->opcode,
> + !!mrq->sbc, !!mrq->cmd, !!mrq->stop);
> + host->auto_cmd_mode = __ACMD_DIS;
> + if (!mrq->sbc && mrq->stop && SDHOST_FLAG_ENABLE_ACMD12) {
> + host->auto_cmd_mode = __ACMD12;
> + mrq->data->stop = NULL;
> + mrq->stop = NULL;
> + }
> +
> + /* 3 send cmd list */
> + if ((mrq->sbc) && SDHOST_FLAG_ENABLE_ACMD23) {
> + host->auto_cmd_mode = __ACMD23;
> + mrq->data->stop = NULL;
> + mrq->stop = NULL;
> + _send_cmd(host, mrq->cmd);
> + } else if (mrq->sbc) {
> + mrq->data->stop = NULL;
> + mrq->stop = NULL;
> + _send_cmd(host, mrq->sbc);
> + } else {
> + _send_cmd(host, mrq->cmd);
> + }
> +
> + mmiowb();
> + spin_unlock_irqrestore(&host->lock, flags);
> +}
> +
> +static void _signal_voltage_on_off(struct sdhost_host *host, u32 on_off)
> +{
> + if (!host->mmc->supply.vqmmc) {
> + pr_debug("%s(%s) there is no signal voltage!\n",
> + __func__, host->device_name);
> + return;
> + }
> +
> + if (on_off && (!host->sdio_1_8v_signal_enabled)) {
> + if (!regulator_enable(host->mmc->supply.vqmmc) &&
> + regulator_is_enabled(host->mmc->supply.vqmmc)) {
> + host->sdio_1_8v_signal_enabled = true;
> + pr_debug("%s(%s) signal voltage enable success!\n",
> + __func__, host->device_name);
> + } else
> + pr_debug("%s(%s) signal voltage enable fail!\n",
> + __func__, host->device_name);
> +
> + } else if (!on_off && host->sdio_1_8v_signal_enabled) {
> + if (!regulator_disable(host->mmc->supply.vqmmc) &&
> + !regulator_is_enabled(host->mmc->supply.vqmmc)) {
> + host->sdio_1_8v_signal_enabled = false;
> + pr_debug("%s(%s) signal voltage disable success!\n",
> + __func__, host->device_name);
> + } else
> + pr_debug("%s(%s) signal voltage disable fail\n",
> + __func__, host->device_name);
> + }
> +}
> +
> +/*
> + * 1 This votage is always poweron
> + * 2 initial votage is 2.7v~3.6v
> + * 3 It can be reconfig to 1.7v~1.95v
> + */
> +static int sdhost_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios)
> +{
> + struct sdhost_host *host = mmc_priv(mmc);
> + unsigned long flags;
> + int err;
> +
> + _runtime_get(host);
> + spin_lock_irqsave(&host->lock, flags);
> +
> + if (!mmc->supply.vqmmc) {
> + /* there are no 1.8v signal votage. */
> + spin_unlock_irqrestore(&host->lock, flags);
> + _runtime_put(host);
> + err = 0;
> + pr_debug("sdhost %s There is no signalling voltage\n",
> + host->device_name);
> + return err;
> + }
> +
> + /* I/O power supply */
> + if (ios->signal_voltage == host->ios.signal_voltage) {
> + spin_unlock_irqrestore(&host->lock, flags);
> + _runtime_put(host);
> + return 0;
> + }
> +
> + switch (ios->signal_voltage) {
> + case MMC_SIGNAL_VOLTAGE_330:
> + err = regulator_set_voltage(mmc->supply.vqmmc,
> + 3000000, 3000000);
As far as I'm concerned, 2700000 ~ 3600000 here would make sense
> + break;
> + case MMC_SIGNAL_VOLTAGE_180:
> + err = regulator_set_voltage(mmc->supply.vqmmc,
> + 1800000, 1800000);
1700000 ~ 1950000
> + break;
> + case MMC_SIGNAL_VOLTAGE_120:
> + err = regulator_set_voltage(mmc->supply.vqmmc,
> + 1100000, 1300000);
> + break;
> + default:
> + err = -EIO;
> + break;
> + }
> + if (likely(!err))
> + host->ios.signal_voltage = ios->signal_voltage;
> + mmiowb();
> + spin_unlock_irqrestore(&host->lock, flags);
> + _runtime_put(host);
> +
> + if (err)
> + WARN(err, "Switching to signalling voltage failed\n");
> +
> + return err;
> +}
> +
> +static void sdhost_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> +{
> + struct sdhost_host *host = mmc_priv(mmc);
> + unsigned long flags;
> +
> + pr_debug("%s(%s) ios:\n"
> + "sdhost clock = %d-->%d\n"
> + "sdhost vdd = %d-->%d\n"
> + "sdhost bus_mode = %d-->%d\n"
> + "sdhost chip_select = %d-->%d\n"
> + "sdhost power_mode = %d-->%d\n"
> + "sdhost bus_width = %d-->%d\n"
> + "sdhost timing = %d-->%d\n"
> + "sdhost signal_voltage = %d-->%d\n"
> + "sdhost drv_type = %d-->%d\n",
> + __func__, host->device_name,
> + host->ios.clock, ios->clock,
> + host->ios.vdd, ios->vdd,
> + host->ios.bus_mode, ios->bus_mode,
> + host->ios.chip_select, ios->chip_select,
> + host->ios.power_mode, ios->power_mode,
> + host->ios.bus_width, ios->bus_width,
> + host->ios.timing, ios->timing,
> + host->ios.signal_voltage, ios->signal_voltage,
> + host->ios.drv_type, ios->drv_type);
> +
> + _runtime_get(host);
> + spin_lock_irqsave(&host->lock, flags);
> +
> + if (0 == ios->clock) {
> + _sdhost_all_clk_off(host);
> + host->ios.clock = 0;
> + } else if (ios->clock != host->ios.clock) {
> + u32 div;
> +
> + div = _sdhost_calc_div(host->base_clk, ios->clock);
> + _sdhost_sd_clk_off(host);
> + _sdhost_clk_set_and_on(host, div);
> + _sdhost_sd_clk_on(host);
> + host->ios.clock = ios->clock;
> + host->data_timeout_val =
> + _sdhost_calc_timeout(host->base_clk, 3);
> + }
> +
> + if (ios->power_mode != host->ios.power_mode) {
> + switch (ios->power_mode) {
> + case MMC_POWER_OFF:
> + _signal_voltage_on_off(host, 0);
> + if (mmc->supply.vmmc)
> + mmc_regulator_set_ocr(host->mmc,
> + mmc->supply.vmmc, 0);
> + mdelay(50);
Need 50ms during spin_lock_irqsave ?
> + _reset_ios(host);
> + host->ios.power_mode = ios->power_mode;
> + break;
> + case MMC_POWER_ON:
> + case MMC_POWER_UP:
> + if (mmc->supply.vmmc)
> + mmc_regulator_set_ocr(host->mmc,
> + mmc->supply.vmmc,
> + ios->vdd);
> + _signal_voltage_on_off(host, 1);
> + mdelay(50);
Ditto.
> + host->ios.power_mode = ios->power_mode;
> + host->ios.vdd = ios->vdd;
> + break;
> + default:
> + break;
> + }
> + }
> +
> + /* flash power voltage select */
> + if (ios->vdd != host->ios.vdd) {
> + if (mmc->supply.vmmc) {
> + pr_info("sdhost %s 3.0 %d!\n",
> + host->device_name, ios->vdd);
> + mmc_regulator_set_ocr(host->mmc,
> + mmc->supply.vmmc, ios->vdd);
> + mdelay(50);
Ditto.
> + }
> + host->ios.vdd = ios->vdd;
> + }
> +
> + if (ios->bus_width != host->ios.bus_width) {
> + _sdhost_set_buswidth(host, ios->bus_width);
> + host->ios.bus_width = ios->bus_width;
> + }
> +
> + if (ios->timing != host->ios.timing) {
> + /* 1 first close SD clock */
> + _sdhost_sd_clk_off(host);
> + /* 2 set timing mode */
> + switch (ios->timing) { /* timing specification used */
> + case MMC_TIMING_LEGACY:
> + /* basic clock mode */
> + _sdhost_set_uhs_mode(host, __TIMING_MODE_SDR12);
> + break;
> + case MMC_TIMING_MMC_HS:
> + case MMC_TIMING_SD_HS:
> + _sdhost_set_uhs_mode(host, __TIMING_MODE_SDR12);
> + break;
> + case MMC_TIMING_UHS_SDR12:
> + case MMC_TIMING_UHS_SDR25:
> + case MMC_TIMING_UHS_SDR50:
> + case MMC_TIMING_UHS_SDR104:
> + case MMC_TIMING_UHS_DDR50:
> + case MMC_TIMING_MMC_HS200:
> + /* _sdhost_enable_hispd(host); */
> + _sdhost_set_uhs_mode(host, ios->timing -
> + MMC_TIMING_UHS_SDR12 +
> + __TIMING_MODE_SDR12);
> + break;
> + default:
> + break;
> + }
> + /* 3 open SD clock */
> + _sdhost_sd_clk_on(host);
> + host->ios.timing = ios->timing;
> + }
> +
> + mmiowb();
> + spin_unlock_irqrestore(&host->lock, flags);
> + _runtime_put(host);
> +}
> +
> +static int sdhost_get_ro(struct mmc_host *mmc)
> +{
> + struct sdhost_host *host = mmc_priv(mmc);
> + unsigned long flags;
> +
> + _runtime_get(host);
> + spin_lock_irqsave(&host->lock, flags);
> + /* read & write */
> + mmiowb();
> + spin_unlock_irqrestore(&host->lock, flags);
> + _runtime_put(host);
> + return 0;
> +}
> +
> +static int sdhost_get_cd(struct mmc_host *mmc)
> +{
> + struct sdhost_host *host = mmc_priv(mmc);
> + unsigned long flags;
> + int gpio_cd;
> +
> + _runtime_get(host);
> + spin_lock_irqsave(&host->lock, flags);
> +
> + if (host->mmc->caps & MMC_CAP_NONREMOVABLE) {
> + spin_unlock_irqrestore(&host->lock, flags);
> + _runtime_put(host);
> + return 1;
> + }
> +
> + gpio_cd = mmc_gpio_get_cd(host->mmc);
> + if (IS_ERR_VALUE(gpio_cd))
> + gpio_cd = 1;
> + mmiowb();
> + spin_unlock_irqrestore(&host->lock, flags);
> + _runtime_put(host);
> + return !!gpio_cd;
> +}
> +
> +static int sdhost_card_busy(struct mmc_host *mmc)
> +{
> + struct sdhost_host *host = mmc_priv(mmc);
> + unsigned long flags;
> + u32 present_state;
> +
> + _runtime_get(host);
> + spin_lock_irqsave(&host->lock, flags);
> +
> + /* Check whether DAT[3:0] is 0000 */
> + present_state = _sdhost_readl(host, SDHOST_32_PRES_STATE);
> +
> + mmiowb();
> + spin_unlock_irqrestore(&host->lock, flags);
> + _runtime_put(host);
> +
> + return !(present_state & _DATA_LVL_MASK);
> +}
> +
> +static void sdhost_hw_reset(struct mmc_host *mmc)
> +{
> + struct sdhost_host *host = mmc_priv(mmc);
> + unsigned long flags;
> +
> + _runtime_get(host);
> + spin_lock_irqsave(&host->lock, flags);
> +
> + /* close LDO and open LDO again. */
> + _signal_voltage_on_off(host, 0);
> + if (mmc->supply.vmmc)
> + mmc_regulator_set_ocr(host->mmc, mmc->supply.vmmc, 0);
> + mdelay(50);
Ditto.
> + if (mmc->supply.vmmc)
> + mmc_regulator_set_ocr(host->mmc, mmc->supply.vmmc,
> + host->ios.vdd);
> +
> + _signal_voltage_on_off(host, 1);
> + mdelay(50);
> + mmiowb();
> + spin_unlock_irqrestore(&host->lock, flags);
> + _runtime_put(host);
> +
> +}
> +
> +static const struct mmc_host_ops sdhost_ops = {
> + .request = sdhost_request,
> + .set_ios = sdhost_set_ios,
> + .get_ro = sdhost_get_ro,
> + .get_cd = sdhost_get_cd,
> +
> + .start_signal_voltage_switch = sdhost_set_vqmmc,
> + .card_busy = sdhost_card_busy,
> + .hw_reset = sdhost_hw_reset,
> +};
> +
> +static void get_caps_info(struct sdhost_host *host,
> + struct sdhost_caps_data *pdata)
> +{
> + host->ocr_avail = pdata->ocr_avail;
> + host->caps = pdata->caps;
> + host->caps2 = pdata->caps2;
> + host->pm_caps = pdata->pm_caps;
> + host->base_clk = pdata->base_clk;
> + host->signal_default_voltage = pdata->signal_default_voltage;
> +}
> +
> +static int _get_basic_resource(struct platform_device *pdev,
> + struct sdhost_host *host)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + struct resource *res;
> + u32 sdhost_delay[3];
> + struct sdhost_caps_data *pdata = NULL;
> + int ret;
> + int index;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res)
> + return -ENOENT;
> +
> + host->ioaddr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
> + host->mapbase = res->start;
> + host->irq = platform_get_irq(pdev, 0);
> + if (host->irq < 0)
> + return host->irq;
> +
> + host->clk = of_clk_get(np, 0);
> + if (IS_ERR_OR_NULL(host->clk))
> + return PTR_ERR(host->clk);
> +
> + host->clk_parent = of_clk_get(np, 1);
> + if (IS_ERR_OR_NULL(host->clk_parent))
> + return PTR_ERR(host->clk_parent);
> +
> + ret = of_property_read_string(np, "sprd,name", &host->device_name);
> + if (ret)
> + dev_err(&pdev->dev,
> + "can not read the property of sprd name\n");
> +
> + for (index = 0; index < sizeof(caps_info_map) /
> + sizeof(struct sdhost_caps_data); index++) {
> + if (strcmp(host->device_name, caps_info_map[index].name) == 0) {
> + pdata = &caps_info_map[index];
> + break;
> + }
> + }
> +
> + get_caps_info(host, pdata);
> +
> + host->detect_gpio = of_get_named_gpio(np, "cd-gpios", 0);
> + if (!gpio_is_valid(host->detect_gpio))
> + host->detect_gpio = -1;
> +
> + ret = of_property_read_u32_array(np, "sprd,delay", sdhost_delay, 3);
> + if (!ret) {
> + host->write_delay = sdhost_delay[0];
> + host->read_pos_delay = sdhost_delay[1];
> + host->read_neg_delay = sdhost_delay[2];
> + } else
> + dev_err(&pdev->dev,
> + "can not read the property of sprd delay\n");
> +
> + return 0;
> +}
> +
> +static int _get_ext_resource(struct sdhost_host *host)
> +{
> + int err;
> + struct mmc_host *mmc = host->mmc;
> +
> + host->dma_mask = DMA_BIT_MASK(64);
> + host->data_timeout_val = 0;
> +
> + /* 1 LDO */
> + mmc_regulator_get_supply(mmc);
> + if (IS_ERR_OR_NULL(mmc->supply.vmmc)) {
> + pr_err("%s(%s): no vmmc regulator found\n",
> + __func__, host->device_name);
> + mmc->supply.vmmc = NULL;
> + }
> + if (IS_ERR_OR_NULL(mmc->supply.vqmmc)) {
> + pr_err("%s(%s): no vqmmc regulator found\n",
> + __func__, host->device_name);
> + mmc->supply.vqmmc = NULL;
> + } else {
> + regulator_is_supported_voltage(mmc->supply.vqmmc,
> + host->signal_default_voltage,
> + host->signal_default_voltage);
> + regulator_set_voltage(mmc->supply.vqmmc,
> + host->signal_default_voltage,
> + host->signal_default_voltage);
> + }
> + host->mmc = mmc;
> +
> + /* 2 clock */
> + clk_set_parent(host->clk, host->clk_parent);
> + clk_prepare_enable(host->clk);
> +
> + /* 3 reset sdio */
> + _reset_ios(host);
> + err = devm_request_irq(&host->pdev->dev, host->irq, _irq,
> + IRQF_SHARED, mmc_hostname(host->mmc), host);
> + if (err)
> + return err;
> + tasklet_init(&host->finish_tasklet, _tasklet, (unsigned long)host);
> + /* 4 init timer */
> + setup_timer(&host->timer, _timeout, (unsigned long)host);
> +
> + return 0;
> +}
> +
> +static int _set_mmc_struct(struct sdhost_host *host, struct mmc_host *mmc)
> +{
> + int ret = 0;
> +
> + mmc = host->mmc;
> + mmc_dev(host->mmc)->dma_mask = &host->dma_mask;
> + mmc->ops = &sdhost_ops;
> + mmc->f_max = host->base_clk;
> + mmc->f_min = (unsigned int)(host->base_clk / __CLK_MAX_DIV);
> + mmc->max_busy_timeout = (1 << 27) / (host->base_clk / 1000);
> +
> + mmc->caps = host->caps;
> + mmc->caps2 = host->caps2;
> + mmc->pm_caps = host->pm_caps;
> + mmc->pm_flags = host->pm_caps;
> + mmc->ocr_avail = host->ocr_avail;
> + mmc->ocr_avail_sdio = host->ocr_avail;
> + mmc->ocr_avail_sd = host->ocr_avail;
> + mmc->ocr_avail_mmc = host->ocr_avail;
> + mmc->max_current_330 = SDHOST_MAX_CUR;
> + mmc->max_current_300 = SDHOST_MAX_CUR;
> + mmc->max_current_180 = SDHOST_MAX_CUR;
> +
> + mmc->max_segs = 1;
> + mmc->max_req_size = 524288; /* 512k */
> + mmc->max_seg_size = mmc->max_req_size;
> +
> + mmc->max_blk_size = 512;
> + mmc->max_blk_count = 65535;
> +
> + ret = mmc_of_parse(mmc);
> + if (ret) {
> + mmc_free_host(mmc);
> + pr_info("parse sprd %s controller fail\n", host->device_name);
> + return ret;
> + }
> +
> + pr_info("%s(%s): ocr avail = 0x%x\n"
> + "base clock = %u, pm_caps = 0x%x\n"
> + "caps: 0x%x, caps2: 0x%x\n",
> + __func__, host->device_name, mmc->ocr_avail,
> + host->base_clk, host->pm_caps, mmc->caps, mmc->caps2);
> +
> + return ret;
> +}
> +
> +static int sdhost_probe(struct platform_device *pdev)
> +{
> + struct mmc_host *mmc;
> + struct sdhost_host *host;
> + int ret;
> +
> + /* globe resource */
> + mmc = mmc_alloc_host(sizeof(struct sdhost_host), &pdev->dev);
> + if (!mmc) {
> + dev_err(&pdev->dev, "no memory for MMC host\n");
> + return -ENOMEM;
> + }
> +
> + host = mmc_priv(mmc);
> + host->mmc = mmc;
> + host->pdev = pdev;
> + spin_lock_init(&host->lock);
> + platform_set_drvdata(pdev, host);
> +
> + /* get sdio irq and sdio iomem */
> + ret = _get_basic_resource(pdev, host);
> + if (ret) {
> + dev_err(&pdev->dev, "fail to get basic resource: %d\n", ret);
If fails here, any err handles? mmc_free_host?
> + return ret;
> + }
> +
> + ret = _get_ext_resource(host);
> + if (ret) {
> + dev_err(&pdev->dev, "fail to get external resource: %d\n", ret);
Ditto
> + return ret;
> + }
> +
> + ret = _set_mmc_struct(host, mmc);
> + if (ret) {
> + dev_err(&pdev->dev, "fail to set mmc struct: %d\n", ret);
Ditto
> + return ret;
> + }
> +
> + _pm_runtime_setting(pdev, host);
> +
> + /* add host */
> + mmiowb();
> + ret = mmc_add_host(mmc);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to add mmc host: %d\n", ret);
> + mmc_free_host(mmc);
> + }
> +
> + if (-1 != host->detect_gpio) {
> + mmc->caps &= ~MMC_CAP_NONREMOVABLE;
> + mmc_gpio_request_cd(mmc, host->detect_gpio, 0);
> + }
> +
> + sdhost_add_debugfs(host);
> +
> + dev_info(&pdev->dev,
> + "Spreadtrum %s[%s] host controller at 0x%08lx irq %d\n",
> + host->device_name, mmc_hostname(mmc),
> + host->mapbase, host->irq);
> +
> + return ret;
> +}
> +
> +static void sdhost_shutdown(struct platform_device *pdev)
> +{
> +}
> +
> +static const struct dev_pm_ops sdhost_dev_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(_pm_suspend, _pm_resume)
> + SET_RUNTIME_PM_OPS(_runtime_suspend,
> + _runtime_resume, _runtime_idle)
> +};
> +
> +static const struct of_device_id sdhost_of_match[] = {
> + {.compatible = "sprd,sdhost-3.0"},
> + { /* sentinel */ }
> +};
> +
> +MODULE_DEVICE_TABLE(of, sdhost_of_match);
> +
> +static struct platform_driver sdhost_driver = {
> + .probe = sdhost_probe,
> + .shutdown = sdhost_shutdown,
> + .driver = {
> + .owner = THIS_MODULE,
> + .pm = &sdhost_dev_pm_ops,
> + .name = DRIVER_NAME,
> + .of_match_table = of_match_ptr(sdhost_of_match),
> + },
> +};
> +
> +module_platform_driver(sdhost_driver);
> +
> +MODULE_DESCRIPTION("Spreadtrum sdio host controller driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/mmc/host/sprd_sdhost.h b/drivers/mmc/host/sprd_sdhost.h
> new file mode 100644
> index 0000000..f94db9a
> --- /dev/null
> +++ b/drivers/mmc/host/sprd_sdhost.h
> @@ -0,0 +1,592 @@
> +/*
> + * linux/drivers/mmc/host/sprd_sdhost.h - Secure Digital Host Controller
> + * Interface driver
> + *
> + * Copyright (C) 2015 Spreadtrum corporation.
> + *
> + * 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.
> + *
> + */
> +
> +#ifndef __SDHOST_H_
> +#define __SDHOST_H_
> +
> +#include <linux/clk.h>
> +#include <linux/compiler.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mmc/card.h>
> +#include <linux/mmc/host.h>
> +#include <linux/mmc/mmc.h>
> +#include <linux/mmc/slot-gpio.h>
> +#include <linux/scatterlist.h>
> +#include <linux/types.h>
> +
> +/**********************************************************\
> + *
> + * Controller block structure
> + *
> +\**********************************************************/
> +struct sdhost_host {
> + /* --globe resource--- */
> + spinlock_t lock;
> + struct mmc_host *mmc;
> +
> + /*--basic resource-- */
> + void __iomem *ioaddr;
> + int irq;
> + const char *device_name;
> + struct platform_device *pdev;
> + unsigned long mapbase;
> +
> + int detect_gpio;
> + u32 ocr_avail;
> + char *clk_name;
> + char *clk_parent_name;
> + u32 base_clk;
> + u32 caps;
> + u32 caps2;
> + u32 pm_caps;
> + u32 write_delay;
> + u32 read_pos_delay;
> + u32 read_neg_delay;
> +
> + /* --extern resource getted by base resource-- */
> + uint64_t dma_mask;
> + u8 data_timeout_val;
> + u32 signal_default_voltage;
> + bool sdio_1_8v_signal_enabled;
> + struct clk *clk;
> + struct clk *clk_parent;
> + struct tasklet_struct finish_tasklet;
> + struct timer_list timer;
> +
> + /* --runtime param-- */
> + u32 int_filter;
> + struct mmc_ios ios;
> + struct mmc_request *mrq; /* Current request */
> + struct mmc_command *cmd; /* Current command */
> + u16 auto_cmd_mode;
> +
> + /*--debugfs-- */
> + struct dentry *debugfs_root;
> +};
> +
> +/* Controller flag */
> +#define SDHOST_FLAG_ENABLE_ACMD12 0
> +#define SDHOST_FLAG_ENABLE_ACMD23 0
> +#define SDHOST_FLAG_USE_ADMA 1
> +
> +/* Controller registers */
> +#ifdef SPRD_SDHOST_4_BYTE_ALIGNE
> +static inline void __local_writeb(u8 val, struct sdhost_host *host,
> + u32 reg)
> +{
> + u32 addr;
> + u32 value;
> + u32 ofst;
> +
> + ofst = (reg & 0x3) << 3;
> + addr = reg & (~((u32) (0x3)));
> + value = readl_relaxed((host->ioaddr + addr));
> + value &= (~(((u32) ((u8) (-1))) << ofst));
> + value |= (((u32) val) << ofst);
> + writel_relaxed(value, (host->ioaddr + addr));
> +}
> +
> +static inline void __local_writew(u16 val, struct sdhost_host *host,
> + u32 reg)
> +{
> + u32 addr;
> + u32 value;
> + u32 ofst;
> +
> + ofst = (reg & 0x3) << 3;
> + addr = reg & (~((u32) (0x3)));
> + value = readl_relaxed(host->ioaddr + addr);
> + value &= (~(((u32) ((u16) (-1))) << ofst));
> + value |= (((u32) val) << ofst);
> + writel_relaxed(value, (host->ioaddr + addr));
> +}
> +
> +static inline void __local_writel(u32 val, struct sdhost_host *host,
> + u32 reg)
> +{
> + writel_relaxed(val, (host->ioaddr + reg));
> +}
> +
> +static inline u8 __local_readb(struct sdhost_host *host, u32 reg)
> +{
> + u32 addr;
> + u32 value;
> + u32 ofst;
> +
> + ofst = (reg & 0x3) << 3;
> + addr = reg & (~((u32) (0x3)));
> + value = readl_relaxed(host->ioaddr + addr);
> + return ((u8) (value >> ofst));
> +
> +}
> +
> +static inline u16 __local_readw(struct sdhost_host *host, u32 reg)
> +{
> + u32 addr;
> + u32 value;
> + u32 ofst;
> +
> + ofst = (reg & 0x3) << 3;
> + addr = reg & (~((u32) (0x3)));
> + value = readl_relaxed(host->ioaddr + addr);
> +
> + return ((u16) (value >> ofst));
> +
> +}
> +
> +static inline u32 __local_readl(struct sdhsot_host *host, u32 reg)
> +{
> + return readl_relaxed(host->ioaddr + reg);
> +}
> +
> +#else
> +static inline void __local_writeb(u8 val, struct sdhost_host *host,
> + u32 reg)
> +{
> + writeb_relaxed(val, host->ioaddr + reg);
> +}
> +
> +static inline void __local_writew(u16 val, struct sdhost_host *host,
> + u32 reg)
> +{
> + writew_relaxed(val, host->ioaddr + reg);
> +}
> +
> +static inline void __local_writel(u32 val, struct sdhost_host *host,
> + u32 reg)
> +{
> + writel_relaxed(val, host->ioaddr + reg);
> +}
> +
> +static inline u8 __local_readb(struct sdhost_host *host, u32 reg)
> +{
> + return readb_relaxed(host->ioaddr + reg);
> +}
> +
> +static inline u16 __local_readw(struct sdhost_host *host, u32 reg)
> +{
> + return readw_relaxed(host->ioaddr + reg);
> +}
> +
> +static inline u32 __local_readl(struct sdhost_host *host, u32 reg)
> +{
> + return readl_relaxed(host->ioaddr + reg);
> +}
> +#endif
> +
> +static inline void _sdhost_writeb(struct sdhost_host *host, u8 val,
> + int reg)
> +{
> + __local_writeb(val, host, reg);
> +}
> +
> +static inline void _sdhost_writew(struct sdhost_host *host, u16 val,
> + int reg)
> +{
> + __local_writew(val, host, reg);
> +}
> +
> +static inline void _sdhost_writel(struct sdhost_host *host, u32 val,
> + int reg)
> +{
> + __local_writel(val, host, reg);
> +}
> +
> +static inline u8 _sdhost_readb(struct sdhost_host *host, int reg)
> +{
> + return __local_readb(host, reg);
> +}
> +
> +static inline u16 _sdhost_readw(struct sdhost_host *host, int reg)
> +{
> + return __local_readw(host, reg);
> +}
> +
> +static inline u32 _sdhost_readl(struct sdhost_host *host, int reg)
> +{
> + return __local_readl(host, reg);
> +}
> +
> +#define SDHOST_32_SYS_ADDR 0x00
> +/* used in cmd23 with ADMA in sdio 3.0 */
> +#define SDHOST_32_BLK_CNT 0x00
> +#define SDHOST_16_BLK_CNT 0x06
> +
> +static inline void _sdhost_set_16_blk_cnt(struct sdhost_host *host,
> + u32 blk_cnt)
> +{
> + __local_writew((blk_cnt & 0xFFFF), host, SDHOST_16_BLK_CNT);
> +}
> +
> +static inline void _sdhost_set_32_blk_cnt(struct sdhost_host *host,
> + u32 blk_cnt)
> +{
> + __local_writel((blk_cnt & 0xFFFFFFFF), host, SDHOST_32_BLK_CNT);
> +}
> +
> +#define SDHOST_16_BLK_SIZE 0x04
> +
> +static inline void _sdhost_set_blk_size(struct sdhost_host *host,
> + u32 blk_size)
> +{
> + __local_writew((blk_size & 0xFFF) | 0x7000, host, SDHOST_16_BLK_SIZE);
> +}
> +
> +#define SDHOST_32_ARG 0x08
> +#define SDHOST_16_TR_MODE 0x0C
> +#define __ACMD_DIS 0x00
> +#define __ACMD12 0x01
> +#define __ACMD23 0x02
> +
> +static inline void _sdhost_set_trans_mode(struct sdhost_host *host,
> + u16 if_mult, u16 if_read,
> + u16 auto_cmd,
> + u16 if_blk_cnt, u16 if_dma)
> +{
> + __local_writew((((if_mult ? 1 : 0) << 5) |
> + ((if_read ? 1 : 0) << 4) |
> + (auto_cmd << 2) |
> + ((if_blk_cnt ? 1 : 0) << 1) |
> + ((if_dma ? 1 : 0) << 0)), host, SDHOST_16_TR_MODE);
> +}
> +
> +#define SDHOST_16_CMD 0x0E
> +#define _CMD_INDEX_CHK 0x0010
> +#define _CMD_CRC_CHK 0x0008
> +#define _CMD_RSP_NONE 0x0000
> +#define _CMD_RSP_136 0x0001
> +#define _CMD_RSP_48 0x0002
> +#define _CMD_RSP_48_BUSY 0x0003
> +#define _RSP0 0
> +#define _RSP1_5_6_7 \
> + (_CMD_INDEX_CHK | _CMD_CRC_CHK | _CMD_RSP_48)
> +#define _RSP2 \
> + (_CMD_CRC_CHK | _CMD_RSP_136)
> +#define _RSP3_4 \
> + _CMD_RSP_48
> +#define _RSP1B_5B \
> + (_CMD_INDEX_CHK | _CMD_CRC_CHK | _CMD_RSP_48_BUSY)
> +
> +static inline void _sdhost_set_cmd(struct sdhost_host *host, u16 cmd,
> + int if_has_data, u16 rsp_type)
> +{
> + __local_writew(((cmd << 8) |
> + ((if_has_data ? 1 : 0) << 5) |
> + (rsp_type)), host, SDHOST_16_CMD);
> +}
> +
> +#define SDHOST_32_TR_MODE_AND_CMD 0x0C
> +
> +static inline void _sdhost_set_trans_and_cmd(struct sdhost_host *host,
> + int if_mult, int if_read,
> + u16 auto_cmd, int if_blk_cnt,
> + int if_dma, u32 cmd,
> + int if_has_data, u32 rsp_type)
> +{
> + __local_writel((((if_mult ? 1 : 0) << 5) |
> + ((if_read ? 1 : 0) << 4) |
> + (((u32) auto_cmd) << 2) |
> + ((if_blk_cnt ? 1 : 0) << 1) |
> + ((if_dma ? 1 : 0) << 0) |
> + (((u32) cmd) << 24) |
> + ((if_has_data ? 1 : 0) << 21) |
> + (rsp_type << 16)),
> + host, SDHOST_32_TR_MODE_AND_CMD);
> +}
> +
> +#define SDHOST_32_RESPONSE 0x10
> +#define SDHOST_32_PRES_STATE 0x24
> +#define _DATA_LVL_MASK 0x00F00000
> +
> +#define SDHOST_8_HOST_CTRL 0x28
> +#define __8_BIT_MOD 0x20
> +#define __4_BIT_MOD 0x02
> +#define __1_BIT_MOD 0x00
> +#define __SDMA_MOD 0x00
> +#define __32ADMA_MOD 0x10
> +#define __64ADMA_MOD 0x18
> +#define __HISPD_MOD 0x04
> +
> +static inline void _sdhost_set_buswidth(struct sdhost_host *host,
> + u32 buswidth)
> +{
> + u8 ctrl = 0;
> +
> + ctrl = __local_readb(host, SDHOST_8_HOST_CTRL);
> + ctrl &= (~(__8_BIT_MOD | __4_BIT_MOD | __1_BIT_MOD));
> + switch (buswidth) {
> + case MMC_BUS_WIDTH_1:
> + ctrl |= __1_BIT_MOD;
> + break;
> + case MMC_BUS_WIDTH_4:
> + ctrl |= __4_BIT_MOD;
> + break;
> + case MMC_BUS_WIDTH_8:
> + ctrl |= __8_BIT_MOD;
> + break;
> + default:
> + WARN_ON(1);
> + break;
> + }
> + __local_writeb(ctrl, host, SDHOST_8_HOST_CTRL);
> +}
> +
> +static inline void _sdhost_set_dma(struct sdhost_host *host, u8 dma_mode)
> +{
> + u8 ctrl = 0;
> +
> + ctrl = __local_readb(host, SDHOST_8_HOST_CTRL);
> + ctrl &= (~(__SDMA_MOD | __32ADMA_MOD | __64ADMA_MOD));
> + ctrl |= dma_mode;
> + __local_writeb(ctrl, host, SDHOST_8_HOST_CTRL);
> +}
> +
> +static inline void _sdhost_enable_hispd(struct sdhost_host *host)
> +{
> + u8 ctrl = 0;
> +
> + ctrl = __local_readb(host, SDHOST_8_HOST_CTRL);
> + ctrl |= __HISPD_MOD;
> + __local_writeb(ctrl, host, SDHOST_8_HOST_CTRL);
> +}
> +
> +/* #define SDHOST_8_PWR_CTRL 0x29 *//* not used */
> +#define SDHOST_8_BLK_GAP 0x2A /* not used */
> +#define SDHOST_8_WACKUP_CTRL 0x2B /* not used */
> +
> +#define SDHOST_16_CLK_CTRL 0x2C
> +#define __CLK_IN_EN 0x0001
> +#define __CLK_IN_STABLE 0x0002
> +#define __CLK_SD 0x0004
> +#define __CLK_MAX_DIV 2046
> +
> +static inline void _sdhost_all_clk_off(struct sdhost_host *host)
> +{
> + __local_writew(0, host, SDHOST_16_CLK_CTRL);
> +}
> +
> +static inline void _sdhost_sd_clk_off(struct sdhost_host *host)
> +{
> + u16 ctrl = 0;
> +
> + ctrl = __local_readw(host, SDHOST_16_CLK_CTRL);
> + ctrl &= (~__CLK_SD);
> + __local_writew(ctrl, host, SDHOST_16_CLK_CTRL);
> +}
> +
> +static inline void _sdhost_sd_clk_on(struct sdhost_host *host)
> +{
> + u16 ctrl = 0;
> +
> + ctrl = __local_readw(host, SDHOST_16_CLK_CTRL);
> + ctrl |= __CLK_SD;
> + __local_writew(ctrl, host, SDHOST_16_CLK_CTRL);
> +}
> +
> +static inline u32 _sdhost_calc_div(u32 base_clk, u32 clk)
> +{
> + u32 N;
> +
> + if (base_clk <= clk)
> + return 0;
> +
> + N = (u32) (base_clk / clk);
> + N = (N >> 1);
> + if (N)
> + N--;
> + if ((base_clk / ((N + 1) << 1)) > clk)
> + N++;
> + if (__CLK_MAX_DIV < N)
> + N = __CLK_MAX_DIV;
> +
> + return N;
> +}
> +
> +static inline void _sdhost_clk_set_and_on(struct sdhost_host *host,
> + u32 div)
> +{
> + u16 ctrl = 0;
> +
> + __local_writew(0, host, SDHOST_16_CLK_CTRL);
> + ctrl |= (u16) (((div & 0x300) >> 2) | ((div & 0xFF) << 8));
> + ctrl |= __CLK_IN_EN;
> + __local_writew(ctrl, host, SDHOST_16_CLK_CTRL);
> + while (!(__CLK_IN_STABLE & __local_readw(host, SDHOST_16_CLK_CTRL)))
> + ;
> +}
> +
> +#define SDHOST_8_TIMEOUT 0x2E
> +#define __DATA_TIMEOUT_MAX_VAL 0xe
> +
> +static inline u8 _sdhost_calc_timeout(unsigned int clock,
> + u8 timeout_value)
> +{
> + unsigned target_timeout, current_timeout;
> + u8 count;
> +
> + count = 0;
> + current_timeout = 1 << 16;
> + target_timeout = timeout_value * clock;
> +
> + while (target_timeout > current_timeout) {
> + count++;
> + current_timeout <<= 1;
> + }
> + count--;
> + if (count >= 0xF)
> + count = 0xE;
> + return count;
> +}
> +
> +#define SDHOST_8_RST 0x2F
> +#define _RST_ALL 0x01
> +#define _RST_CMD 0x02
> +#define _RST_DATA 0x04
> +#define _RST_EMMC 0x08 /* spredtrum define it byself */
> +
> +static inline void _sdhost_reset(struct sdhost_host *host, u8 mask)
> +{
> + __local_writeb((_RST_EMMC | mask), host, SDHOST_8_RST);
> + /* TODO: change to __local_readb?? */
> + while (_sdhost_readb(host, SDHOST_8_RST) & mask)
> + ;
> +}
> +
> +/* spredtrum define it byself */
> +static inline void _sdhost_reset_emmc(struct sdhost_host *host)
> +{
> + __local_writeb(0, host, SDHOST_8_RST);
> + mdelay(2);
> + __local_writeb(_RST_EMMC, host, SDHOST_8_RST);
> +}
> +
> +#define SDHOST_32_INT_ST 0x30
> +#define SDHOST_32_INT_ST_EN 0x34
> +#define SDHOST_32_INT_SIG_EN 0x38
> +#define _INT_CMD_END 0x00000001
> +#define _INT_TRAN_END 0x00000002
> +#define _INT_DMA_END 0x00000008
> +#define _INT_WR_RDY 0x00000010 /* not used */
> +#define _INT_RD_RDY 0x00000020 /* not used */
> +#define _INT_ERR 0x00008000
> +#define _INT_ERR_CMD_TIMEOUT 0x00010000
> +#define _INT_ERR_CMD_CRC 0x00020000
> +#define _INT_ERR_CMD_END 0x00040000
> +#define _INT_ERR_CMD_INDEX 0x00080000
> +#define _INT_ERR_DATA_TIMEOUT 0x00100000
> +#define _INT_ERR_DATA_CRC 0x00200000
> +#define _INT_ERR_DATA_END 0x00400000
> +#define _INT_ERR_CUR_LIMIT 0x00800000
> +#define _INT_ERR_ACMD 0x01000000
> +#define _INT_ERR_ADMA 0x02000000
> +
> +/* used in irq */
> +#define _INT_FILTER_ERR_CMD \
> + (_INT_ERR_CMD_TIMEOUT | _INT_ERR_CMD_CRC | \
> + _INT_ERR_CMD_END | _INT_ERR_CMD_INDEX)
> +#define _INT_FILTER_ERR_DATA \
> + (_INT_ERR_DATA_TIMEOUT | _INT_ERR_DATA_CRC | \
> + _INT_ERR_DATA_END)
> +#define _INT_FILTER_ERR \
> + (_INT_ERR | _INT_FILTER_ERR_CMD | \
> + _INT_FILTER_ERR_DATA | _INT_ERR_ACMD | \
> + _INT_ERR_ADMA)
> +#define _INT_FILTER_NORMAL \
> + (_INT_CMD_END | _INT_TRAN_END)
> +
> +/* used for setting */
> +#define _DATA_FILTER_RD_SIGLE \
> + (_INT_TRAN_END | _INT_DMA_END | \
> + _INT_ERR | _INT_ERR_DATA_TIMEOUT | \
> + _INT_ERR_DATA_CRC | _INT_ERR_DATA_END)
> +#define _DATA_FILTER_RD_MULTI \
> + (_INT_TRAN_END | _INT_DMA_END | _INT_ERR | \
> + _INT_ERR_DATA_TIMEOUT | _INT_ERR_DATA_CRC | \
> + _INT_ERR_DATA_END)
> +#define _DATA_FILTER_WR_SIGLE \
> + (_INT_TRAN_END | _INT_DMA_END | \
> + _INT_ERR | _INT_ERR_DATA_TIMEOUT | \
> + _INT_ERR_DATA_CRC)
> +#define _DATA_FILTER_WR_MULT \
> + (_INT_TRAN_END | _INT_DMA_END | \
> + _INT_ERR | _INT_ERR_DATA_TIMEOUT | \
> + _INT_ERR_DATA_CRC)
> +#define _CMD_FILTER_R0 \
> + _INT_CMD_END
> +#define _CMD_FILTER_R2 \
> + (_INT_CMD_END | _INT_ERR | \
> + _INT_ERR_CMD_TIMEOUT | _INT_ERR_CMD_CRC | \
> + _INT_ERR_CMD_END)
> +#define _CMD_FILTER_R3 \
> + (_INT_CMD_END | _INT_ERR | \
> + _INT_ERR_CMD_TIMEOUT | _INT_ERR_CMD_END)
> +#define _CMD_FILTER_R1_R4_R5_R6_R7 \
> + (_INT_CMD_END | _INT_ERR | \
> + _INT_ERR_CMD_TIMEOUT | _INT_ERR_CMD_CRC | \
> + _INT_ERR_CMD_END | _INT_ERR_CMD_INDEX)
> +#define _CMD_FILTER_R1B \
> + (_INT_CMD_END | _INT_ERR | \
> + _INT_ERR_CMD_TIMEOUT | _INT_ERR_CMD_CRC | \
> + _INT_ERR_CMD_END | _INT_ERR_CMD_INDEX | \
> + _INT_TRAN_END | _INT_ERR_DATA_TIMEOUT)
> +
> +static inline void _sdhost_disable_all_int(struct sdhost_host *host)
> +{
> + __local_writel(0x0, host, SDHOST_32_INT_SIG_EN);
> + __local_writel(0x0, host, SDHOST_32_INT_ST_EN);
> + __local_writel(0xFFFFFFFF, host, SDHOST_32_INT_ST);
> +}
> +
> +static inline void _sdhost_enable_int(struct sdhost_host *host, u32 mask)
> +{
> + __local_writel(mask, host, SDHOST_32_INT_ST_EN);
> + __local_writel(mask, host, SDHOST_32_INT_SIG_EN);
> +}
> +
> +static inline void _sdhost_clear_int(struct sdhost_host *host, u32 mask)
> +{
> + __local_writel(mask, host, SDHOST_32_INT_ST);
> +}
> +
> +#define SDHOST_16_ACMD_ERR 0x3C
> +
> +#define SDHOST_16_HOST_CTRL_2 0x3E
> +#define __TIMING_MODE_SDR12 0x0000
> +#define __TIMING_MODE_SDR25 0x0001
> +#define __TIMING_MODE_SDR50 0x0002
> +#define __TIMING_MODE_SDR104 0x0003
> +#define __TIMING_MODE_DDR50 0x0004
> +#define __TIMING_MODE_SDR200 0x0005
> +
> +static inline void _sdhost_set_uhs_mode(struct sdhost_host *host, u16 mode)
> +{
> + __local_writew(mode, host, SDHOST_16_HOST_CTRL_2);
> +}
> +
> +#define SDHOST_MAX_CUR 1020
> +
> +/* the following register is defined by spreadtrum self.
> + * It is not standard register of SDIO
> + * */
> +static inline void _sdhost_set_delay(struct sdhost_host *host,
> + u32 write_delay,
> + u32 read_pos_delay,
> + u32 read_neg_delay)
> +{
> + __local_writel(write_delay, host, 0x80);
> + __local_writel(read_pos_delay, host, 0x84);
> + __local_writel(read_neg_delay, host, 0x88);
> +}
> +
> +#endif /* __SDHOST_H_ */
> diff --git a/drivers/mmc/host/sprd_sdhost_debugfs.c b/drivers/mmc/host/sprd_sdhost_debugfs.c
> new file mode 100644
> index 0000000..b80d1ac
> --- /dev/null
> +++ b/drivers/mmc/host/sprd_sdhost_debugfs.c
> @@ -0,0 +1,213 @@
> +/*
> + * linux/drivers/mmc/host/sprd_sdhost_debugfs.c - Secure Digital Host
> + * Controller Interface driver
> + *
> + * Copyright (C) 2015 Spreadtrum corporation.
> + *
> + * 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.
> + *
> + */
> +#include <linux/debugfs.h>
> +#include <linux/delay.h>
> +#include <linux/mmc/host.h>
> +
> +#include "sprd_sdhost_debugfs.h"
> +
> +#define ELEMENT(v) {v, #v}
> +#define ELEMENT_NUM 26
> +struct {
> + uint32_t bit;
> + char *caps_name;
> +} caps_info[3][ELEMENT_NUM] = {
> + {
> + ELEMENT(MMC_CAP_4_BIT_DATA),
> + ELEMENT(MMC_CAP_MMC_HIGHSPEED),
> + ELEMENT(MMC_CAP_SD_HIGHSPEED),
> + ELEMENT(MMC_CAP_SDIO_IRQ),
> + ELEMENT(MMC_CAP_SPI),
> + ELEMENT(MMC_CAP_NEEDS_POLL),
> + ELEMENT(MMC_CAP_8_BIT_DATA),
> + ELEMENT(MMC_CAP_AGGRESSIVE_PM),
> + ELEMENT(MMC_CAP_NONREMOVABLE),
> + ELEMENT(MMC_CAP_WAIT_WHILE_BUSY),
> + ELEMENT(MMC_CAP_ERASE),
> + ELEMENT(MMC_CAP_1_8V_DDR),
> + ELEMENT(MMC_CAP_1_2V_DDR),
> + ELEMENT(MMC_CAP_POWER_OFF_CARD),
> + ELEMENT(MMC_CAP_BUS_WIDTH_TEST),
> + ELEMENT(MMC_CAP_UHS_SDR12),
> + ELEMENT(MMC_CAP_UHS_SDR25),
> + ELEMENT(MMC_CAP_UHS_SDR50),
> + ELEMENT(MMC_CAP_UHS_SDR104),
> + ELEMENT(MMC_CAP_UHS_DDR50),
> + ELEMENT(MMC_CAP_RUNTIME_RESUME),
> + ELEMENT(MMC_CAP_DRIVER_TYPE_A),
> + ELEMENT(MMC_CAP_DRIVER_TYPE_C),
> + ELEMENT(MMC_CAP_DRIVER_TYPE_D),
> + ELEMENT(MMC_CAP_CMD23),
> + ELEMENT(MMC_CAP_HW_RESET)
> + }, {
> + ELEMENT(MMC_CAP2_BOOTPART_NOACC),
> + ELEMENT(MMC_CAP2_FULL_PWR_CYCLE),
> + ELEMENT(MMC_CAP2_HS200_1_8V_SDR),
> + ELEMENT(MMC_CAP2_HS200_1_2V_SDR),
> + ELEMENT(MMC_CAP2_HS200),
> + ELEMENT(MMC_CAP2_HC_ERASE_SZ),
> + ELEMENT(MMC_CAP2_CD_ACTIVE_HIGH),
> + ELEMENT(MMC_CAP2_RO_ACTIVE_HIGH),
> + ELEMENT(MMC_CAP2_PACKED_RD),
> + ELEMENT(MMC_CAP2_PACKED_WR),
> + ELEMENT(MMC_CAP2_PACKED_CMD),
> + ELEMENT(MMC_CAP2_NO_PRESCAN_POWERUP),
> + ELEMENT(MMC_CAP2_HS400_1_8V),
> + ELEMENT(MMC_CAP2_HS400_1_2V),
> + ELEMENT(MMC_CAP2_HS400),
> + ELEMENT(MMC_CAP2_SDIO_IRQ_NOTHREAD)
> + }, {
> + ELEMENT(MMC_PM_KEEP_POWER),
> + ELEMENT(MMC_PM_WAKE_SDIO_IRQ),
> + ELEMENT(MMC_PM_IGNORE_PM_NOTIFY)
> + }
> +
> +};
> +
> +static int sdhost_param_show(struct seq_file *s, void *data)
> +{
> + struct sdhost_host *host = s->private;
> + uint32_t i;
> +
> + seq_printf(s, "\n"
> + "ioaddr\t= 0x%p\n"
> + "irq\t= %d\n"
> + "device_name\t= %s\n"
> + "detect_gpio\t= %d\n"
> + "base_clk\t= %d\n"
> + "write_delay\t= %d\n"
> + "read_pos_delay\t= %d\n"
> + "read_neg_delay\t= %d\n",
> + host->ioaddr, host->irq, host->device_name,
> + host->detect_gpio, host->base_clk,
> + host->write_delay, host->read_pos_delay,
> + host->read_neg_delay);
> + seq_printf(s, "OCR 0x%x\n", host->ocr_avail);
> +
> + for (i = 0; i < ELEMENT_NUM; i++) {
> + if ((caps_info[0][i].bit ==
> + (host->caps & caps_info[0][i].bit))
> + && caps_info[0][i].bit)
> + seq_printf(s, "caps:%s\n", caps_info[0][i].caps_name);
> + }
> + for (i = 0; i < ELEMENT_NUM; i++) {
> + if ((caps_info[1][i].bit ==
> + (host->caps2 & caps_info[1][i].bit))
> + && caps_info[1][i].bit)
> + seq_printf(s, "caps2:%s\n", caps_info[1][i].caps_name);
> + }
> + for (i = 0; i < ELEMENT_NUM; i++) {
> + if ((caps_info[2][i].bit ==
> + (host->pm_caps & caps_info[2][i].bit))
> + && caps_info[2][i].bit)
> + seq_printf(s, "pm_caps:%s\n",
> + caps_info[2][i].caps_name);
> + }
> +
> + return 0;
> +}
> +
> +static int sdhost_param_open(struct inode *inode, struct file *file)
> +{
> + return single_open(file, sdhost_param_show, inode->i_private);
> +}
> +
> +static const struct file_operations sdhost_param_fops = {
> + .open = sdhost_param_open,
> + .read = seq_read,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> +#define SDHOST_ATTR(PARAM_NAME) \
> + static int sdhost_##PARAM_NAME##_get(void *data, u64 *val)\
> + { \
> + struct sdhost_host *host = data;\
> + *val = (u64)host->PARAM_NAME;\
> + return 0;\
> + } \
> + static int sdhost_##PARAM_NAME##_set(void *data, u64 val)\
> + { \
> + struct sdhost_host *host = data;\
> + if (0x7F >= (uint32_t)val) { \
> + host->PARAM_NAME = (uint32_t)val;\
> + _sdhost_set_delay(host, \
> + host->write_delay, \
> + host->read_pos_delay, \
> + host->read_neg_delay);\
> + } \
> + return 0;\
> + } \
> + DEFINE_SIMPLE_ATTRIBUTE(sdhost_##PARAM_NAME##_fops,\
> + sdhost_##PARAM_NAME##_get,\
> + sdhost_##PARAM_NAME##_set,\
> + "%llu\n")
> +
> +SDHOST_ATTR(write_delay);
> +SDHOST_ATTR(read_pos_delay);
> +SDHOST_ATTR(read_neg_delay);
> +
> +void sdhost_add_debugfs(struct sdhost_host *host)
> +{
> + struct dentry *root;
> +
> + root = debugfs_create_dir(host->device_name, NULL);
> + if (IS_ERR(root))
> + /* Don't complain -- debugfs just isn't enabled */
> + return;
> + if (!root)
> + return;
> +
> + host->debugfs_root = root;
> +
> + if (!debugfs_create_file("basic_resource", S_IRUSR, root,
> + (void *)host, &sdhost_param_fops))
> + goto err;
> + if (!debugfs_create_file("write_delay", S_IRUSR | S_IWUSR, root,
> + (void *)host, &sdhost_write_delay_fops))
> + goto err;
> + if (!debugfs_create_file("read_pos_delay", S_IRUSR | S_IWUSR, root,
> + (void *)host, &sdhost_read_pos_delay_fops))
> + goto err;
> + if (!debugfs_create_file("read_neg_delay", S_IRUSR | S_IWUSR, root,
> + (void *)host, &sdhost_read_neg_delay_fops))
> + goto err;
> + return;
> +
> +err:
> + debugfs_remove_recursive(root);
> + host->debugfs_root = 0;
> +}
> +
> +void dump_sdio_reg(struct sdhost_host *host)
> +{
> + unsigned int i;
> +
> + if (!host->mmc->card)
> + return;
> +
> + pr_info("+++++++++++ REGISTER DUMP (%s) ++++++++++\n",
> + host->device_name);
> +
> + for (i = 0; i < 0x09; i++) {
> + pr_info("0x%08x | 0x%08x | 0x%08x | 0x%08x\n\r",
> + _sdhost_readl(host, 0 + (i << 4)),
> + _sdhost_readl(host, 4 + (i << 4)),
> + _sdhost_readl(host, 8 + (i << 4)),
> + _sdhost_readl(host, 12 + (i << 4))
> + );
> + }
> +
> + pr_info("----------------------------------------\n");
> + mdelay(100);
why you need mdelay(100) here?
> +}
> diff --git a/drivers/mmc/host/sprd_sdhost_debugfs.h b/drivers/mmc/host/sprd_sdhost_debugfs.h
> new file mode 100644
> index 0000000..0063e1b
> --- /dev/null
> +++ b/drivers/mmc/host/sprd_sdhost_debugfs.h
> @@ -0,0 +1,27 @@
> +/*
> + * linux/drivers/mmc/host/sprd_sdhost_debugfs.h - Secure Digital Host Controller
> + * Interface driver
> + *
> + * Copyright (C) 2015 Spreadtrum corporation.
> + *
> + * 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.
> + *
> + */
> +
> +#ifndef _SDHOST_DEBUGFS_H_
> +#define _SDHOST_DEBUGFS_H_
> +
> +#include "sprd_sdhost.h"
> +
> +#ifdef CONFIG_DEBUG_FS
> +void sdhost_add_debugfs(struct sdhost_host *host);
> +void dump_sdio_reg(struct sdhost_host *host);
> +#else
> +static inline void sdhost_add_debugfs(struct sdhost_host *host) {}
> +static inline void dump_sdio_reg(struct sdhost_host *host) {}
> +#endif
> +
> +#endif
> --
> 1.7.9.5
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
>
>
prev parent reply other threads:[~2015-08-03 9:54 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-07-31 13:02 [RFC PATCH v1 1/1] mmc: sprd: add MMC host driver for Spreadtrum SoC wuht06
2015-08-03 9:54 ` Shawn Lin [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=55BF3A45.9070507@rock-chips.com \
--to=shawn.lin@rock-chips.com \
--cc=Chunyan.Zhang@spreadtrum.com \
--cc=Henry.He@spreadtrum.com \
--cc=Jason.Wu@spreadtrum.com \
--cc=Orson.Zhai@spreadtrum.com \
--cc=linux-mmc@vger.kernel.org \
--cc=ulf.hansson@linaro.org \
--cc=wuht06@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.