All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 3/3] drivers/mmc/host: Add realtek sdmmc interface driver
@ 2012-07-19  9:55 wei_wang
  2012-07-19 12:26 ` Oliver Neukum
  0 siblings, 1 reply; 11+ messages in thread
From: wei_wang @ 2012-07-19  9:55 UTC (permalink / raw)
  To: gregkh, devel, linux-kernel; +Cc: Wei WANG

From: Wei WANG <wei_wang@realsil.com.cn>

Realtek SD/MMC card interface driver is used to access
SD/MMC card, with the help of Realtek card reader adapter driver.

Signed-off-by: Wei WANG <wei_wang@realsil.com.cn>
---
 drivers/mmc/host/Kconfig      |    7 +
 drivers/mmc/host/Makefile     |    2 +
 drivers/mmc/host/rtsx_sdmmc.c |  354 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 363 insertions(+)
 create mode 100644 drivers/mmc/host/rtsx_sdmmc.c

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index aa131b3..d9942e6 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -612,3 +612,10 @@ config MMC_USHC
 
 	  Note: These controllers only support SDIO cards and do not
 	  support MMC or SD memory cards.
+
+config MMC_REALTEK
+	tristate "Realtek SD/MMC Card Interface Driver"
+	depends on REALTEK_CR_CORE
+	help
+	  Say Y here to include driver code to support the Realtek
+	  SD/MMC card interface.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 8922b06..a4cd15a 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -45,6 +45,8 @@ obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
 obj-$(CONFIG_MMC_VUB300)	+= vub300.o
 obj-$(CONFIG_MMC_USHC)		+= ushc.o
 
+obj-$(CONFIG_MMC_REALTEK)	+= rtsx_sdmmc.o
+
 obj-$(CONFIG_MMC_SDHCI_PLTFM)		+= sdhci-pltfm.o
 obj-$(CONFIG_MMC_SDHCI_CNS3XXX)		+= sdhci-cns3xxx.o
 obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX)	+= sdhci-esdhc-imx.o
diff --git a/drivers/mmc/host/rtsx_sdmmc.c b/drivers/mmc/host/rtsx_sdmmc.c
new file mode 100644
index 0000000..031e527
--- /dev/null
+++ b/drivers/mmc/host/rtsx_sdmmc.c
@@ -0,0 +1,354 @@
+/* Realtek SD/MMC Card Interface driver
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * 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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG <wei_wang@realsil.com.cn>
+ *   No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ */
+
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/delay.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/card.h>
+
+#include <linux/rtsx_core.h>
+
+#define DRV_NAME	"rtsx_sdmmc"
+
+#define MAX_RW_REG_CNT			1024
+
+#define RTSX_MAX_BLOCK_COUNT		65536
+#define RTSX_MAX_BLOCK_LENGTH		2048
+
+struct realtek_sdmmc {
+	struct rtsx_dev		*dev;
+	struct mmc_host		*mmc;
+	struct mmc_request	*mrq;
+
+	struct mutex		host_mutex;
+
+	int			eject;
+};
+
+static struct rtsx_device_id realtek_sdmmc_ids[] = {
+	{RTSX_TYPE_SD},
+	{}
+};
+
+MODULE_DEVICE_TABLE(rtsx, realtek_sdmmc_ids);
+
+static void sd_send_cmd_get_rsp(struct realtek_sdmmc *host,
+		struct mmc_command *cmd)
+{
+	cmd->error = rtsx_sdmmc_send_cmd_get_rsp(host->dev, (u8)cmd->opcode,
+			cmd->arg, mmc_resp_type(cmd), cmd->resp);
+}
+
+static int sd_rw_multi(struct realtek_sdmmc *host, struct mmc_request *mrq)
+{
+	struct mmc_host *mmc = host->mmc;
+	struct mmc_card *card = mmc->card;
+	struct mmc_data *data = mrq->data;
+	int uhs = mmc_sd_card_uhs(card);
+	int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
+
+	return rtsx_sdmmc_rw_multi(host->dev, data->sg, data->blksz,
+			data->blocks, data->sg_len, read, uhs);
+}
+
+static void sd_normal_rw(struct realtek_sdmmc *host, struct mmc_request *mrq)
+{
+	struct mmc_command *cmd = mrq->cmd;
+	struct mmc_data *data = mrq->data;
+	u8 _cmd[5], *buf;
+
+	_cmd[0] = 0x40 | (u8)cmd->opcode;
+	_cmd[1] = (u8)(cmd->arg >> 24);
+	_cmd[2] = (u8)(cmd->arg >> 16);
+	_cmd[3] = (u8)(cmd->arg >> 8);
+	_cmd[4] = (u8)cmd->arg;
+
+	buf = kzalloc(data->blksz, GFP_KERNEL);
+
+	if (data->flags & MMC_DATA_READ) {
+		cmd->error = rtsx_sdmmc_read_data(host->dev, _cmd,
+				(u16)data->blksz, buf, data->blksz, 200);
+		sg_copy_from_buffer(data->sg, data->sg_len, buf, data->blksz);
+	} else {
+		sg_copy_to_buffer(data->sg, data->sg_len, buf, data->blksz);
+		cmd->error = rtsx_sdmmc_write_data(host->dev, _cmd,
+				(u16)data->blksz, buf, data->blksz, 200);
+	}
+
+	kfree(buf);
+}
+
+static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+	struct mmc_command *cmd = mrq->cmd;
+	struct mmc_data *data = mrq->data;
+	unsigned int data_size = 0;
+
+	if (host->eject) {
+		cmd->error = -ENOMEDIUM;
+		goto finish;
+	}
+
+	mutex_lock(&host->host_mutex);
+	host->mrq = mrq;
+	mutex_unlock(&host->host_mutex);
+
+	if (mrq->data)
+		data_size = data->blocks * data->blksz;
+
+	if (!data_size || mmc_op_multi(cmd->opcode) ||
+			(cmd->opcode == MMC_READ_SINGLE_BLOCK) ||
+			(cmd->opcode == MMC_WRITE_BLOCK)) {
+		sd_send_cmd_get_rsp(host, cmd);
+
+		if (!cmd->error && data_size) {
+			sd_rw_multi(host, mrq);
+
+			if (mmc_op_multi(cmd->opcode) && mrq->stop)
+				sd_send_cmd_get_rsp(host, mrq->stop);
+		}
+	} else {
+		sd_normal_rw(host, mrq);
+	}
+
+	if (mrq->data) {
+		if (cmd->error || data->error)
+			data->bytes_xfered = 0;
+		else
+			data->bytes_xfered = data->blocks * data->blksz;
+	}
+
+finish:
+	if (cmd->error)
+		pr_debug("cmd->error = %d\n", cmd->error);
+
+	mutex_lock(&host->host_mutex);
+	host->mrq = NULL;
+	mutex_unlock(&host->host_mutex);
+
+	mmc_request_done(mmc, mrq);
+}
+
+static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+	int vpclk = 0, double_clk = 1;
+	u8 ssc_depth;
+
+	rtsx_sdmmc_set_bus_width(host->dev, ios->bus_width);
+	rtsx_sdmmc_set_power_mode(host->dev, ios->power_mode);
+	rtsx_sdmmc_set_timing(host->dev, ios->timing);
+
+	switch (ios->timing) {
+	case MMC_TIMING_UHS_SDR104:
+	case MMC_TIMING_UHS_SDR50:
+		ssc_depth = RTSX_SSC_DEPTH_2M;
+		vpclk = 1;
+		double_clk = 0;
+		break;
+
+	case MMC_TIMING_UHS_DDR50:
+	case MMC_TIMING_UHS_SDR25:
+/*	case MMC_TIMING_MMC_HS200: */
+		ssc_depth = RTSX_SSC_DEPTH_1M;
+		break;
+
+	default:
+		ssc_depth = RTSX_SSC_DEPTH_500K;
+		break;
+	}
+
+	rtsx_switch_clock(host->dev,
+			ios->clock, ssc_depth, double_clk, vpclk);
+}
+
+static int sdmmc_get_ro(struct mmc_host *mmc)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_get_ro(host->dev);
+}
+
+static int sdmmc_get_cd(struct mmc_host *mmc)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_get_cd(host->dev);
+}
+
+static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_switch_voltage(host->dev, ios->signal_voltage);
+}
+
+static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_execute_tuning(host->dev);
+}
+
+static const struct mmc_host_ops realtek_sdmmc_ops = {
+	.request = sdmmc_request,
+	.set_ios = sdmmc_set_ios,
+	.get_ro = sdmmc_get_ro,
+	.get_cd = sdmmc_get_cd,
+	.start_signal_voltage_switch = sdmmc_switch_voltage,
+	.execute_tuning = sdmmc_execute_tuning,
+};
+
+static void init_extra_caps(struct realtek_sdmmc *host)
+{
+	struct mmc_host *mmc = host->mmc;
+	struct rtsx_dev *sock = host->dev;
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	pr_debug("adapter->extra_caps = 0x%x\n", adapter->extra_caps);
+
+	if (adapter->extra_caps & EXTRA_CAPS_SD_SDR50)
+		mmc->caps |= MMC_CAP_UHS_SDR50;
+	if (adapter->extra_caps & EXTRA_CAPS_SD_SDR104)
+		mmc->caps |= MMC_CAP_UHS_SDR104;
+	if (adapter->extra_caps & EXTRA_CAPS_SD_DDR50)
+		mmc->caps |= MMC_CAP_UHS_DDR50;
+	if (adapter->extra_caps & EXTRA_CAPS_MMC_HSDDR)
+		mmc->caps |= MMC_CAP_1_8V_DDR;
+	if (adapter->extra_caps & EXTRA_CAPS_MMC_8BIT)
+		mmc->caps |= MMC_CAP_8_BIT_DATA;
+/*
+	if (adapter->extra_caps & EXTRA_CAPS_MMC_HS200)
+		mmc->caps2 |= MMC_CAP2_HS200;
+*/
+}
+
+static void realtek_init_host(struct realtek_sdmmc *host)
+{
+	struct mmc_host *mmc = host->mmc;
+
+	mmc->f_min = 250000;
+	mmc->f_max = 208000000;
+	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
+	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
+		MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
+		MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
+		MMC_CAP_MAX_CURRENT_200 | MMC_CAP_MAX_CURRENT_400 |
+		MMC_CAP_MAX_CURRENT_600 | MMC_CAP_MAX_CURRENT_800;
+	mmc->ops = &realtek_sdmmc_ops;
+
+	init_extra_caps(host);
+
+	mmc->max_segs = 256;
+	mmc->max_blk_size = RTSX_MAX_BLOCK_LENGTH;
+	mmc->max_blk_count = RTSX_MAX_BLOCK_COUNT;
+	mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count;
+	mmc->max_req_size = mmc->max_seg_size;
+}
+
+static int realtek_sdmmc_probe(struct rtsx_dev *sock)
+{
+	struct mmc_host *mmc;
+	struct realtek_sdmmc *host;
+
+	pr_info(DRV_NAME ": Realtek SDMMC controller found\n");
+
+	mmc = mmc_alloc_host(sizeof(struct realtek_sdmmc), &sock->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	host = mmc_priv(mmc);
+	rtsx_set_drvdata(sock, mmc);
+	host->dev = sock;
+	host->mmc = mmc;
+
+	mutex_init(&host->host_mutex);
+
+	realtek_init_host(host);
+
+	mmc_add_host(mmc);
+
+	return 0;
+}
+
+static void __devexit realtek_sdmmc_remove(struct rtsx_dev *sock)
+{
+	struct mmc_host *mmc = rtsx_get_drvdata(sock);
+	struct realtek_sdmmc *host;
+
+	host = mmc_priv(mmc);
+	host->eject = 1;
+
+	mutex_lock(&host->host_mutex);
+	if (host->mrq) {
+		pr_debug("%s: Controller removed during transfer\n",
+				mmc_hostname(mmc));
+
+		rtsx_complete_unfinished_transfer(sock);
+
+		host->mrq->cmd->error = -ENOMEDIUM;
+		if (host->mrq->stop)
+			host->mrq->stop->error = -ENOMEDIUM;
+		mmc_request_done(mmc, host->mrq);
+	}
+	mutex_unlock(&host->host_mutex);
+
+	mmc_remove_host(mmc);
+	mmc_free_host(mmc);
+
+	pr_info(DRV_NAME
+		": Realtek SDMMC controller has been removed\n");
+}
+
+static struct rtsx_driver realtek_sdmmc_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.id_table = realtek_sdmmc_ids,
+	.probe = realtek_sdmmc_probe,
+	.remove = realtek_sdmmc_remove,
+};
+
+static int __init realtek_sdmmc_drv_init(void)
+{
+	pr_info(DRV_NAME ": Realtek SD/MMC Card Reader driver\n");
+
+	return rtsx_register_driver(&realtek_sdmmc_driver);
+}
+
+static void __exit realtek_sdmmc_drv_exit(void)
+{
+	rtsx_unregister_driver(&realtek_sdmmc_driver);
+}
+
+module_init(realtek_sdmmc_drv_init);
+module_exit(realtek_sdmmc_drv_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Realtek Corp.");
+MODULE_DESCRIPTION("Realtek SD/MMC Card Interface Driver");
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH 3/3] drivers/mmc/host: Add realtek sdmmc interface driver
  2012-07-19  9:55 [PATCH 3/3] drivers/mmc/host: Add realtek sdmmc interface driver wei_wang
@ 2012-07-19 12:26 ` Oliver Neukum
  2012-07-20  1:48   ` wwang
  0 siblings, 1 reply; 11+ messages in thread
From: Oliver Neukum @ 2012-07-19 12:26 UTC (permalink / raw)
  To: wei_wang; +Cc: gregkh, devel, linux-kernel

On Thursday 19 July 2012 17:55:18 wei_wang@realsil.com.cn wrote:

> +static void sd_normal_rw(struct realtek_sdmmc *host, struct mmc_request *mrq)
> +{
> +	struct mmc_command *cmd = mrq->cmd;
> +	struct mmc_data *data = mrq->data;
> +	u8 _cmd[5], *buf;
> +
> +	_cmd[0] = 0x40 | (u8)cmd->opcode;
> +	_cmd[1] = (u8)(cmd->arg >> 24);
> +	_cmd[2] = (u8)(cmd->arg >> 16);
> +	_cmd[3] = (u8)(cmd->arg >> 8);
> +	_cmd[4] = (u8)cmd->arg;

Please use the predefined macro for endianness conversion.

> +	buf = kzalloc(data->blksz, GFP_KERNEL);

1. You must handle a failure to allocate a buffer
2. You must use GFP_NOIO as you are in a block driver

	Regards
		Oliver


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 3/3] drivers/mmc/host: Add realtek sdmmc interface driver
  2012-07-19 12:26 ` Oliver Neukum
@ 2012-07-20  1:48   ` wwang
  0 siblings, 0 replies; 11+ messages in thread
From: wwang @ 2012-07-20  1:48 UTC (permalink / raw)
  To: Oliver Neukum; +Cc: gregkh, devel, linux-kernel

Hi Oliver:

I will fix it. Should I resend all three patches, or just this one?

BR,
Wei WANG

于 2012年07月19日 20:26, Oliver Neukum 写道:
> On Thursday 19 July 2012 17:55:18 wei_wang@realsil.com.cn wrote:
>
>> +static void sd_normal_rw(struct realtek_sdmmc *host, struct mmc_request *mrq)
>> +{
>> +	struct mmc_command *cmd = mrq->cmd;
>> +	struct mmc_data *data = mrq->data;
>> +	u8 _cmd[5], *buf;
>> +
>> +	_cmd[0] = 0x40 | (u8)cmd->opcode;
>> +	_cmd[1] = (u8)(cmd->arg >> 24);
>> +	_cmd[2] = (u8)(cmd->arg >> 16);
>> +	_cmd[3] = (u8)(cmd->arg >> 8);
>> +	_cmd[4] = (u8)cmd->arg;
> Please use the predefined macro for endianness conversion.
>
>> +	buf = kzalloc(data->blksz, GFP_KERNEL);
> 1. You must handle a failure to allocate a buffer
> 2. You must use GFP_NOIO as you are in a block driver
>
> 	Regards
> 		Oliver
>


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 3/3] drivers/mmc/host: Add realtek sdmmc interface driver
  2012-07-31  7:42 ` wei_wang
  (?)
  (?)
@ 2012-07-31 18:11 ` Borislav Petkov
  -1 siblings, 0 replies; 11+ messages in thread
From: Borislav Petkov @ 2012-07-31 18:11 UTC (permalink / raw)
  To: wei_wang; +Cc: gregkh, devel, linux-kernel, linux-mmc, cjb, arnd, aaron.lu

On Tue, Jul 31, 2012 at 03:42:56PM +0800, wei_wang@realsil.com.cn wrote:
> From: Wei WANG <wei_wang@realsil.com.cn>
> 
> Realtek SD/MMC card interface driver is used to access
> SD/MMC card, with the help of Realtek card reader adapter driver.
> 
> Signed-off-by: Wei WANG <wei_wang@realsil.com.cn>
> ---

[ … ]

> +static void __devexit realtek_sdmmc_remove(struct rtsx_dev *sock)
> +{
> +	struct mmc_host *mmc = rtsx_get_drvdata(sock);
> +	struct realtek_sdmmc *host;
> +
> +	host = mmc_priv(mmc);
> +	host->eject = 1;
> +
> +	mutex_lock(&host->host_mutex);
> +	if (host->mrq) {
> +		dev_dbg(&(sock->dev),
> +			"%s: Controller removed during transfer\n",
> +			mmc_hostname(mmc));
> +
> +		rtsx_complete_unfinished_transfer(sock);
> +
> +		host->mrq->cmd->error = -ENOMEDIUM;
> +		if (host->mrq->stop)
> +			host->mrq->stop->error = -ENOMEDIUM;
> +		mmc_request_done(mmc, host->mrq);
> +	}
> +	mutex_unlock(&host->host_mutex);
> +
> +	mmc_remove_host(mmc);
> +	mmc_free_host(mmc);
> +
> +	pr_info(DRV_NAME
> +		": Realtek SDMMC controller has been removed\n");

Oh and this appears in the logs when I remove the card:

[11721.313471] rtsx_sdmmc: Realtek SDMMC controller has been removed

but it is simply polluting the logs with useless info that a card has
been removed.

If you really need those printk (same with the PCI-E driver) you could
try to use dev_dbg() like you do above.

Same for the probe function.

But, more importantly and while playing with this, I replugged the card
and it said:

[12072.632332] rtsx_core: MMC/SD card detected in socket 0:0
[12072.642855] rtsx_sdmmc: Realtek SDMMC controller found
[12073.839561] mmc0: error -110 whilst initialising SD card
[12075.138833] mmc0: error -110 whilst initialising SD card
[12076.438158] mmc0: error -110 whilst initialising SD card

and now I can't mount the card anymore:

$ mount /dev/mmcblk0 /mnt/tmp/
mount: /dev/mmcblk0 is not a valid block device

$ mount /dev/mmcblk0p1 /mnt/tmp/
mount: /dev/mmcblk0p1 is not a valid block device

Hmm..

-- 
Regards/Gruss,
Boris.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 3/3] drivers/mmc/host: Add realtek sdmmc interface driver
  2012-07-31  7:42 ` wei_wang
  (?)
@ 2012-07-31 15:22 ` Borislav Petkov
  -1 siblings, 0 replies; 11+ messages in thread
From: Borislav Petkov @ 2012-07-31 15:22 UTC (permalink / raw)
  To: wei_wang; +Cc: gregkh, devel, linux-kernel, linux-mmc, cjb, arnd, aaron.lu

On Tue, Jul 31, 2012 at 03:42:56PM +0800, wei_wang@realsil.com.cn wrote:
> From: Wei WANG <wei_wang@realsil.com.cn>
> 
> Realtek SD/MMC card interface driver is used to access
> SD/MMC card, with the help of Realtek card reader adapter driver.

Yep,

it builds and boots fine with latest Linus tree.

When I put the card in, the kernel says:

[ 1162.197980] rtsx_core: MMC/SD card detected in socket 0:0
[ 1162.302571] rtsx_sdmmc: Realtek SDMMC controller found
[ 1163.202172] mmc0: new ultra high speed SDR50 SDHC card at address e624
[ 1163.237151] mmcblk0: mmc0:e624 SU16G 14.8 GiB
[ 1163.263627]  mmcblk0: p1

Then when I format the card it says:

[ 1284.457681] DMA: Out of SW-IOMMU space for 524288 bytes at device 0000:03:00.0
[ 1284.475972] DMA: Out of SW-IOMMU space for 368640 bytes at device 0000:03:00.0

which doesn't look too kosher to me. It comes from
swiotlb.c:swiotlb_full() and it says that in both cases map_single fails
mapping those 524Kb and 368K buffers.

It looks from here as if the driver is trying to map too big buffers
which the swiotlb can't handle? But I'm just stabbing in the dark here.

Accessing the card seems ok, I'll play with it for a while and keep you
posted.

Thanks.

-- 
Regards/Gruss,
Boris.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 3/3] drivers/mmc/host: Add realtek sdmmc interface driver
@ 2012-07-31  7:42 ` wei_wang
  0 siblings, 0 replies; 11+ messages in thread
From: wei_wang @ 2012-07-31  7:42 UTC (permalink / raw)
  To: gregkh, devel, linux-kernel, linux-mmc, cjb, arnd; +Cc: bp, aaron.lu, Wei WANG

From: Wei WANG <wei_wang@realsil.com.cn>

Realtek SD/MMC card interface driver is used to access
SD/MMC card, with the help of Realtek card reader adapter driver.

Signed-off-by: Wei WANG <wei_wang@realsil.com.cn>
---
 drivers/mmc/host/Kconfig      |    7 +
 drivers/mmc/host/Makefile     |    2 +
 drivers/mmc/host/rtsx_sdmmc.c |  350 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 359 insertions(+)
 create mode 100644 drivers/mmc/host/rtsx_sdmmc.c

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index aa131b3..d9942e6 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -612,3 +612,10 @@ config MMC_USHC
 
 	  Note: These controllers only support SDIO cards and do not
 	  support MMC or SD memory cards.
+
+config MMC_REALTEK
+	tristate "Realtek SD/MMC Card Interface Driver"
+	depends on REALTEK_CR_CORE
+	help
+	  Say Y here to include driver code to support the Realtek
+	  SD/MMC card interface.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 8922b06..a4cd15a 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -45,6 +45,8 @@ obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
 obj-$(CONFIG_MMC_VUB300)	+= vub300.o
 obj-$(CONFIG_MMC_USHC)		+= ushc.o
 
+obj-$(CONFIG_MMC_REALTEK)	+= rtsx_sdmmc.o
+
 obj-$(CONFIG_MMC_SDHCI_PLTFM)		+= sdhci-pltfm.o
 obj-$(CONFIG_MMC_SDHCI_CNS3XXX)		+= sdhci-cns3xxx.o
 obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX)	+= sdhci-esdhc-imx.o
diff --git a/drivers/mmc/host/rtsx_sdmmc.c b/drivers/mmc/host/rtsx_sdmmc.c
new file mode 100644
index 0000000..a90dd79a
--- /dev/null
+++ b/drivers/mmc/host/rtsx_sdmmc.c
@@ -0,0 +1,350 @@
+/* Realtek SD/MMC Card Interface driver
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * 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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG <wei_wang@realsil.com.cn>
+ *   No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ */
+
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/delay.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/card.h>
+#include <linux/rtsx_core.h>
+
+#include <asm/unaligned.h>
+
+#define DRV_NAME	"rtsx_sdmmc"
+
+#define MAX_RW_REG_CNT			1024
+
+#define RTSX_MAX_BLOCK_COUNT		65536
+#define RTSX_MAX_BLOCK_LENGTH		2048
+
+struct realtek_sdmmc {
+	struct rtsx_dev		*dev;
+	struct mmc_host		*mmc;
+	struct mmc_request	*mrq;
+
+	struct mutex		host_mutex;
+
+	int			eject;
+};
+
+static struct rtsx_device_id realtek_sdmmc_ids[] = {
+	{RTSX_TYPE_SD},
+	{}
+};
+
+MODULE_DEVICE_TABLE(rtsx, realtek_sdmmc_ids);
+
+static void sd_send_cmd_get_rsp(struct realtek_sdmmc *host,
+		struct mmc_command *cmd)
+{
+	cmd->error = rtsx_sdmmc_send_cmd_get_rsp(host->dev, (u8)cmd->opcode,
+			cmd->arg, mmc_resp_type(cmd), cmd->resp);
+}
+
+static int sd_rw_multi(struct realtek_sdmmc *host, struct mmc_request *mrq)
+{
+	struct mmc_host *mmc = host->mmc;
+	struct mmc_card *card = mmc->card;
+	struct mmc_data *data = mrq->data;
+	int uhs = mmc_sd_card_uhs(card);
+	int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
+
+	return rtsx_sdmmc_rw_multi(host->dev, data->sg, data->blksz,
+			data->blocks, data->sg_len, read, uhs);
+}
+
+static void sd_normal_rw(struct realtek_sdmmc *host, struct mmc_request *mrq)
+{
+	struct mmc_command *cmd = mrq->cmd;
+	struct mmc_data *data = mrq->data;
+	u8 _cmd[5], *buf;
+
+	_cmd[0] = 0x40 | (u8)cmd->opcode;
+	put_unaligned_be32(cmd->arg, (u32 *)(&_cmd[1]));
+
+	buf = kzalloc(data->blksz, GFP_NOIO);
+	if (!buf) {
+		cmd->error = -ENOMEM;
+		return;
+	}
+
+	if (data->flags & MMC_DATA_READ) {
+		cmd->error = rtsx_sdmmc_read_data(host->dev, _cmd,
+				(u16)data->blksz, buf, data->blksz, 200);
+		sg_copy_from_buffer(data->sg, data->sg_len, buf, data->blksz);
+	} else {
+		sg_copy_to_buffer(data->sg, data->sg_len, buf, data->blksz);
+		cmd->error = rtsx_sdmmc_write_data(host->dev, _cmd,
+				(u16)data->blksz, buf, data->blksz, 200);
+	}
+
+	kfree(buf);
+}
+
+static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+	struct mmc_command *cmd = mrq->cmd;
+	struct mmc_data *data = mrq->data;
+	unsigned int data_size = 0;
+
+	if (host->eject) {
+		cmd->error = -ENOMEDIUM;
+		goto finish;
+	}
+
+	mutex_lock(&host->host_mutex);
+	host->mrq = mrq;
+	mutex_unlock(&host->host_mutex);
+
+	if (mrq->data)
+		data_size = data->blocks * data->blksz;
+
+	if (!data_size || mmc_op_multi(cmd->opcode) ||
+			(cmd->opcode == MMC_READ_SINGLE_BLOCK) ||
+			(cmd->opcode == MMC_WRITE_BLOCK)) {
+		sd_send_cmd_get_rsp(host, cmd);
+
+		if (!cmd->error && data_size) {
+			sd_rw_multi(host, mrq);
+
+			if (mmc_op_multi(cmd->opcode) && mrq->stop)
+				sd_send_cmd_get_rsp(host, mrq->stop);
+		}
+	} else {
+		sd_normal_rw(host, mrq);
+	}
+
+	if (mrq->data) {
+		if (cmd->error || data->error)
+			data->bytes_xfered = 0;
+		else
+			data->bytes_xfered = data->blocks * data->blksz;
+	}
+
+finish:
+	if (cmd->error)
+		dev_dbg(&(host->dev->dev), "cmd->error = %d\n", cmd->error);
+
+	mutex_lock(&host->host_mutex);
+	host->mrq = NULL;
+	mutex_unlock(&host->host_mutex);
+
+	mmc_request_done(mmc, mrq);
+}
+
+static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+	int vpclk = 0, double_clk = 1;
+	u8 ssc_depth;
+
+	rtsx_sdmmc_set_bus_width(host->dev, ios->bus_width);
+	rtsx_sdmmc_set_power_mode(host->dev, ios->power_mode);
+	rtsx_sdmmc_set_timing(host->dev, ios->timing);
+
+	switch (ios->timing) {
+	case MMC_TIMING_UHS_SDR104:
+	case MMC_TIMING_UHS_SDR50:
+		ssc_depth = RTSX_SSC_DEPTH_2M;
+		vpclk = 1;
+		double_clk = 0;
+		break;
+
+	case MMC_TIMING_UHS_DDR50:
+	case MMC_TIMING_UHS_SDR25:
+		ssc_depth = RTSX_SSC_DEPTH_1M;
+		break;
+
+	default:
+		ssc_depth = RTSX_SSC_DEPTH_500K;
+		break;
+	}
+
+	rtsx_switch_clock(host->dev,
+			ios->clock, ssc_depth, double_clk, vpclk);
+}
+
+static int sdmmc_get_ro(struct mmc_host *mmc)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_get_ro(host->dev);
+}
+
+static int sdmmc_get_cd(struct mmc_host *mmc)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_get_cd(host->dev);
+}
+
+static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_switch_voltage(host->dev, ios->signal_voltage);
+}
+
+static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_execute_tuning(host->dev);
+}
+
+static const struct mmc_host_ops realtek_sdmmc_ops = {
+	.request = sdmmc_request,
+	.set_ios = sdmmc_set_ios,
+	.get_ro = sdmmc_get_ro,
+	.get_cd = sdmmc_get_cd,
+	.start_signal_voltage_switch = sdmmc_switch_voltage,
+	.execute_tuning = sdmmc_execute_tuning,
+};
+
+static void init_extra_caps(struct realtek_sdmmc *host)
+{
+	struct mmc_host *mmc = host->mmc;
+	struct rtsx_dev *sock = host->dev;
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	dev_dbg(&(sock->dev), "adapter->extra_caps = 0x%x\n",
+			adapter->extra_caps);
+
+	if (adapter->extra_caps & EXTRA_CAPS_SD_SDR50)
+		mmc->caps |= MMC_CAP_UHS_SDR50;
+	if (adapter->extra_caps & EXTRA_CAPS_SD_SDR104)
+		mmc->caps |= MMC_CAP_UHS_SDR104;
+	if (adapter->extra_caps & EXTRA_CAPS_SD_DDR50)
+		mmc->caps |= MMC_CAP_UHS_DDR50;
+	if (adapter->extra_caps & EXTRA_CAPS_MMC_HSDDR)
+		mmc->caps |= MMC_CAP_1_8V_DDR;
+	if (adapter->extra_caps & EXTRA_CAPS_MMC_8BIT)
+		mmc->caps |= MMC_CAP_8_BIT_DATA;
+}
+
+static void realtek_init_host(struct realtek_sdmmc *host)
+{
+	struct mmc_host *mmc = host->mmc;
+
+	mmc->f_min = 250000;
+	mmc->f_max = 208000000;
+	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
+	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
+		MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
+		MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
+	mmc->max_current_330 = 400;
+	mmc->max_current_180 = 800;
+	mmc->ops = &realtek_sdmmc_ops;
+
+	init_extra_caps(host);
+
+	mmc->max_segs = 256;
+	mmc->max_blk_size = RTSX_MAX_BLOCK_LENGTH;
+	mmc->max_blk_count = RTSX_MAX_BLOCK_COUNT;
+	mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count;
+	mmc->max_req_size = mmc->max_seg_size;
+}
+
+static int realtek_sdmmc_probe(struct rtsx_dev *sock)
+{
+	struct mmc_host *mmc;
+	struct realtek_sdmmc *host;
+
+	pr_info(DRV_NAME ": Realtek SDMMC controller found\n");
+
+	mmc = mmc_alloc_host(sizeof(struct realtek_sdmmc), &sock->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	host = mmc_priv(mmc);
+	rtsx_set_drvdata(sock, mmc);
+	host->dev = sock;
+	host->mmc = mmc;
+
+	mutex_init(&host->host_mutex);
+
+	realtek_init_host(host);
+
+	mmc_add_host(mmc);
+
+	return 0;
+}
+
+static void __devexit realtek_sdmmc_remove(struct rtsx_dev *sock)
+{
+	struct mmc_host *mmc = rtsx_get_drvdata(sock);
+	struct realtek_sdmmc *host;
+
+	host = mmc_priv(mmc);
+	host->eject = 1;
+
+	mutex_lock(&host->host_mutex);
+	if (host->mrq) {
+		dev_dbg(&(sock->dev),
+			"%s: Controller removed during transfer\n",
+			mmc_hostname(mmc));
+
+		rtsx_complete_unfinished_transfer(sock);
+
+		host->mrq->cmd->error = -ENOMEDIUM;
+		if (host->mrq->stop)
+			host->mrq->stop->error = -ENOMEDIUM;
+		mmc_request_done(mmc, host->mrq);
+	}
+	mutex_unlock(&host->host_mutex);
+
+	mmc_remove_host(mmc);
+	mmc_free_host(mmc);
+
+	pr_info(DRV_NAME
+		": Realtek SDMMC controller has been removed\n");
+}
+
+static struct rtsx_driver realtek_sdmmc_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.id_table = realtek_sdmmc_ids,
+	.probe = realtek_sdmmc_probe,
+	.remove = realtek_sdmmc_remove,
+};
+
+static int __init realtek_sdmmc_drv_init(void)
+{
+	return rtsx_register_driver(&realtek_sdmmc_driver);
+}
+
+static void __exit realtek_sdmmc_drv_exit(void)
+{
+	rtsx_unregister_driver(&realtek_sdmmc_driver);
+}
+
+module_init(realtek_sdmmc_drv_init);
+module_exit(realtek_sdmmc_drv_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Realtek Corp.");
+MODULE_DESCRIPTION("Realtek SD/MMC Card Interface Driver");
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 3/3] drivers/mmc/host: Add realtek sdmmc interface driver
@ 2012-07-31  7:42 ` wei_wang
  0 siblings, 0 replies; 11+ messages in thread
From: wei_wang @ 2012-07-31  7:42 UTC (permalink / raw)
  To: gregkh, devel, linux-kernel, linux-mmc, cjb, arnd; +Cc: bp, aaron.lu, Wei WANG

From: Wei WANG <wei_wang@realsil.com.cn>

Realtek SD/MMC card interface driver is used to access
SD/MMC card, with the help of Realtek card reader adapter driver.

Signed-off-by: Wei WANG <wei_wang@realsil.com.cn>
---
 drivers/mmc/host/Kconfig      |    7 +
 drivers/mmc/host/Makefile     |    2 +
 drivers/mmc/host/rtsx_sdmmc.c |  350 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 359 insertions(+)
 create mode 100644 drivers/mmc/host/rtsx_sdmmc.c

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index aa131b3..d9942e6 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -612,3 +612,10 @@ config MMC_USHC
 
 	  Note: These controllers only support SDIO cards and do not
 	  support MMC or SD memory cards.
+
+config MMC_REALTEK
+	tristate "Realtek SD/MMC Card Interface Driver"
+	depends on REALTEK_CR_CORE
+	help
+	  Say Y here to include driver code to support the Realtek
+	  SD/MMC card interface.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 8922b06..a4cd15a 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -45,6 +45,8 @@ obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
 obj-$(CONFIG_MMC_VUB300)	+= vub300.o
 obj-$(CONFIG_MMC_USHC)		+= ushc.o
 
+obj-$(CONFIG_MMC_REALTEK)	+= rtsx_sdmmc.o
+
 obj-$(CONFIG_MMC_SDHCI_PLTFM)		+= sdhci-pltfm.o
 obj-$(CONFIG_MMC_SDHCI_CNS3XXX)		+= sdhci-cns3xxx.o
 obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX)	+= sdhci-esdhc-imx.o
diff --git a/drivers/mmc/host/rtsx_sdmmc.c b/drivers/mmc/host/rtsx_sdmmc.c
new file mode 100644
index 0000000..a90dd79a
--- /dev/null
+++ b/drivers/mmc/host/rtsx_sdmmc.c
@@ -0,0 +1,350 @@
+/* Realtek SD/MMC Card Interface driver
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * 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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG <wei_wang@realsil.com.cn>
+ *   No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ */
+
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/delay.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/card.h>
+#include <linux/rtsx_core.h>
+
+#include <asm/unaligned.h>
+
+#define DRV_NAME	"rtsx_sdmmc"
+
+#define MAX_RW_REG_CNT			1024
+
+#define RTSX_MAX_BLOCK_COUNT		65536
+#define RTSX_MAX_BLOCK_LENGTH		2048
+
+struct realtek_sdmmc {
+	struct rtsx_dev		*dev;
+	struct mmc_host		*mmc;
+	struct mmc_request	*mrq;
+
+	struct mutex		host_mutex;
+
+	int			eject;
+};
+
+static struct rtsx_device_id realtek_sdmmc_ids[] = {
+	{RTSX_TYPE_SD},
+	{}
+};
+
+MODULE_DEVICE_TABLE(rtsx, realtek_sdmmc_ids);
+
+static void sd_send_cmd_get_rsp(struct realtek_sdmmc *host,
+		struct mmc_command *cmd)
+{
+	cmd->error = rtsx_sdmmc_send_cmd_get_rsp(host->dev, (u8)cmd->opcode,
+			cmd->arg, mmc_resp_type(cmd), cmd->resp);
+}
+
+static int sd_rw_multi(struct realtek_sdmmc *host, struct mmc_request *mrq)
+{
+	struct mmc_host *mmc = host->mmc;
+	struct mmc_card *card = mmc->card;
+	struct mmc_data *data = mrq->data;
+	int uhs = mmc_sd_card_uhs(card);
+	int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
+
+	return rtsx_sdmmc_rw_multi(host->dev, data->sg, data->blksz,
+			data->blocks, data->sg_len, read, uhs);
+}
+
+static void sd_normal_rw(struct realtek_sdmmc *host, struct mmc_request *mrq)
+{
+	struct mmc_command *cmd = mrq->cmd;
+	struct mmc_data *data = mrq->data;
+	u8 _cmd[5], *buf;
+
+	_cmd[0] = 0x40 | (u8)cmd->opcode;
+	put_unaligned_be32(cmd->arg, (u32 *)(&_cmd[1]));
+
+	buf = kzalloc(data->blksz, GFP_NOIO);
+	if (!buf) {
+		cmd->error = -ENOMEM;
+		return;
+	}
+
+	if (data->flags & MMC_DATA_READ) {
+		cmd->error = rtsx_sdmmc_read_data(host->dev, _cmd,
+				(u16)data->blksz, buf, data->blksz, 200);
+		sg_copy_from_buffer(data->sg, data->sg_len, buf, data->blksz);
+	} else {
+		sg_copy_to_buffer(data->sg, data->sg_len, buf, data->blksz);
+		cmd->error = rtsx_sdmmc_write_data(host->dev, _cmd,
+				(u16)data->blksz, buf, data->blksz, 200);
+	}
+
+	kfree(buf);
+}
+
+static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+	struct mmc_command *cmd = mrq->cmd;
+	struct mmc_data *data = mrq->data;
+	unsigned int data_size = 0;
+
+	if (host->eject) {
+		cmd->error = -ENOMEDIUM;
+		goto finish;
+	}
+
+	mutex_lock(&host->host_mutex);
+	host->mrq = mrq;
+	mutex_unlock(&host->host_mutex);
+
+	if (mrq->data)
+		data_size = data->blocks * data->blksz;
+
+	if (!data_size || mmc_op_multi(cmd->opcode) ||
+			(cmd->opcode == MMC_READ_SINGLE_BLOCK) ||
+			(cmd->opcode == MMC_WRITE_BLOCK)) {
+		sd_send_cmd_get_rsp(host, cmd);
+
+		if (!cmd->error && data_size) {
+			sd_rw_multi(host, mrq);
+
+			if (mmc_op_multi(cmd->opcode) && mrq->stop)
+				sd_send_cmd_get_rsp(host, mrq->stop);
+		}
+	} else {
+		sd_normal_rw(host, mrq);
+	}
+
+	if (mrq->data) {
+		if (cmd->error || data->error)
+			data->bytes_xfered = 0;
+		else
+			data->bytes_xfered = data->blocks * data->blksz;
+	}
+
+finish:
+	if (cmd->error)
+		dev_dbg(&(host->dev->dev), "cmd->error = %d\n", cmd->error);
+
+	mutex_lock(&host->host_mutex);
+	host->mrq = NULL;
+	mutex_unlock(&host->host_mutex);
+
+	mmc_request_done(mmc, mrq);
+}
+
+static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+	int vpclk = 0, double_clk = 1;
+	u8 ssc_depth;
+
+	rtsx_sdmmc_set_bus_width(host->dev, ios->bus_width);
+	rtsx_sdmmc_set_power_mode(host->dev, ios->power_mode);
+	rtsx_sdmmc_set_timing(host->dev, ios->timing);
+
+	switch (ios->timing) {
+	case MMC_TIMING_UHS_SDR104:
+	case MMC_TIMING_UHS_SDR50:
+		ssc_depth = RTSX_SSC_DEPTH_2M;
+		vpclk = 1;
+		double_clk = 0;
+		break;
+
+	case MMC_TIMING_UHS_DDR50:
+	case MMC_TIMING_UHS_SDR25:
+		ssc_depth = RTSX_SSC_DEPTH_1M;
+		break;
+
+	default:
+		ssc_depth = RTSX_SSC_DEPTH_500K;
+		break;
+	}
+
+	rtsx_switch_clock(host->dev,
+			ios->clock, ssc_depth, double_clk, vpclk);
+}
+
+static int sdmmc_get_ro(struct mmc_host *mmc)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_get_ro(host->dev);
+}
+
+static int sdmmc_get_cd(struct mmc_host *mmc)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_get_cd(host->dev);
+}
+
+static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_switch_voltage(host->dev, ios->signal_voltage);
+}
+
+static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_execute_tuning(host->dev);
+}
+
+static const struct mmc_host_ops realtek_sdmmc_ops = {
+	.request = sdmmc_request,
+	.set_ios = sdmmc_set_ios,
+	.get_ro = sdmmc_get_ro,
+	.get_cd = sdmmc_get_cd,
+	.start_signal_voltage_switch = sdmmc_switch_voltage,
+	.execute_tuning = sdmmc_execute_tuning,
+};
+
+static void init_extra_caps(struct realtek_sdmmc *host)
+{
+	struct mmc_host *mmc = host->mmc;
+	struct rtsx_dev *sock = host->dev;
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	dev_dbg(&(sock->dev), "adapter->extra_caps = 0x%x\n",
+			adapter->extra_caps);
+
+	if (adapter->extra_caps & EXTRA_CAPS_SD_SDR50)
+		mmc->caps |= MMC_CAP_UHS_SDR50;
+	if (adapter->extra_caps & EXTRA_CAPS_SD_SDR104)
+		mmc->caps |= MMC_CAP_UHS_SDR104;
+	if (adapter->extra_caps & EXTRA_CAPS_SD_DDR50)
+		mmc->caps |= MMC_CAP_UHS_DDR50;
+	if (adapter->extra_caps & EXTRA_CAPS_MMC_HSDDR)
+		mmc->caps |= MMC_CAP_1_8V_DDR;
+	if (adapter->extra_caps & EXTRA_CAPS_MMC_8BIT)
+		mmc->caps |= MMC_CAP_8_BIT_DATA;
+}
+
+static void realtek_init_host(struct realtek_sdmmc *host)
+{
+	struct mmc_host *mmc = host->mmc;
+
+	mmc->f_min = 250000;
+	mmc->f_max = 208000000;
+	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
+	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
+		MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
+		MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
+	mmc->max_current_330 = 400;
+	mmc->max_current_180 = 800;
+	mmc->ops = &realtek_sdmmc_ops;
+
+	init_extra_caps(host);
+
+	mmc->max_segs = 256;
+	mmc->max_blk_size = RTSX_MAX_BLOCK_LENGTH;
+	mmc->max_blk_count = RTSX_MAX_BLOCK_COUNT;
+	mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count;
+	mmc->max_req_size = mmc->max_seg_size;
+}
+
+static int realtek_sdmmc_probe(struct rtsx_dev *sock)
+{
+	struct mmc_host *mmc;
+	struct realtek_sdmmc *host;
+
+	pr_info(DRV_NAME ": Realtek SDMMC controller found\n");
+
+	mmc = mmc_alloc_host(sizeof(struct realtek_sdmmc), &sock->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	host = mmc_priv(mmc);
+	rtsx_set_drvdata(sock, mmc);
+	host->dev = sock;
+	host->mmc = mmc;
+
+	mutex_init(&host->host_mutex);
+
+	realtek_init_host(host);
+
+	mmc_add_host(mmc);
+
+	return 0;
+}
+
+static void __devexit realtek_sdmmc_remove(struct rtsx_dev *sock)
+{
+	struct mmc_host *mmc = rtsx_get_drvdata(sock);
+	struct realtek_sdmmc *host;
+
+	host = mmc_priv(mmc);
+	host->eject = 1;
+
+	mutex_lock(&host->host_mutex);
+	if (host->mrq) {
+		dev_dbg(&(sock->dev),
+			"%s: Controller removed during transfer\n",
+			mmc_hostname(mmc));
+
+		rtsx_complete_unfinished_transfer(sock);
+
+		host->mrq->cmd->error = -ENOMEDIUM;
+		if (host->mrq->stop)
+			host->mrq->stop->error = -ENOMEDIUM;
+		mmc_request_done(mmc, host->mrq);
+	}
+	mutex_unlock(&host->host_mutex);
+
+	mmc_remove_host(mmc);
+	mmc_free_host(mmc);
+
+	pr_info(DRV_NAME
+		": Realtek SDMMC controller has been removed\n");
+}
+
+static struct rtsx_driver realtek_sdmmc_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.id_table = realtek_sdmmc_ids,
+	.probe = realtek_sdmmc_probe,
+	.remove = realtek_sdmmc_remove,
+};
+
+static int __init realtek_sdmmc_drv_init(void)
+{
+	return rtsx_register_driver(&realtek_sdmmc_driver);
+}
+
+static void __exit realtek_sdmmc_drv_exit(void)
+{
+	rtsx_unregister_driver(&realtek_sdmmc_driver);
+}
+
+module_init(realtek_sdmmc_drv_init);
+module_exit(realtek_sdmmc_drv_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Realtek Corp.");
+MODULE_DESCRIPTION("Realtek SD/MMC Card Interface Driver");
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 3/3] drivers/mmc/host: Add realtek sdmmc interface driver
@ 2012-07-25  3:02 wei_wang
  0 siblings, 0 replies; 11+ messages in thread
From: wei_wang @ 2012-07-25  3:02 UTC (permalink / raw)
  To: gregkh, devel, linux-kernel; +Cc: Wei WANG

From: Wei WANG <wei_wang@realsil.com.cn>

Realtek SD/MMC card interface driver is used to access
SD/MMC card, with the help of Realtek card reader adapter driver.

Signed-off-by: Wei WANG <wei_wang@realsil.com.cn>
---
 drivers/mmc/host/Kconfig      |    7 +
 drivers/mmc/host/Makefile     |    2 +
 drivers/mmc/host/rtsx_sdmmc.c |  350 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 359 insertions(+)
 create mode 100644 drivers/mmc/host/rtsx_sdmmc.c

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index aa131b3..d9942e6 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -612,3 +612,10 @@ config MMC_USHC
 
 	  Note: These controllers only support SDIO cards and do not
 	  support MMC or SD memory cards.
+
+config MMC_REALTEK
+	tristate "Realtek SD/MMC Card Interface Driver"
+	depends on REALTEK_CR_CORE
+	help
+	  Say Y here to include driver code to support the Realtek
+	  SD/MMC card interface.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 8922b06..a4cd15a 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -45,6 +45,8 @@ obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
 obj-$(CONFIG_MMC_VUB300)	+= vub300.o
 obj-$(CONFIG_MMC_USHC)		+= ushc.o
 
+obj-$(CONFIG_MMC_REALTEK)	+= rtsx_sdmmc.o
+
 obj-$(CONFIG_MMC_SDHCI_PLTFM)		+= sdhci-pltfm.o
 obj-$(CONFIG_MMC_SDHCI_CNS3XXX)		+= sdhci-cns3xxx.o
 obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX)	+= sdhci-esdhc-imx.o
diff --git a/drivers/mmc/host/rtsx_sdmmc.c b/drivers/mmc/host/rtsx_sdmmc.c
new file mode 100644
index 0000000..3b587fc
--- /dev/null
+++ b/drivers/mmc/host/rtsx_sdmmc.c
@@ -0,0 +1,350 @@
+/* Realtek SD/MMC Card Interface driver
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * 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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG <wei_wang@realsil.com.cn>
+ *   No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ */
+
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/delay.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/card.h>
+#include <linux/rtsx_core.h>
+
+#include <asm/unaligned.h>
+
+#define DRV_NAME	"rtsx_sdmmc"
+
+#define MAX_RW_REG_CNT			1024
+
+#define RTSX_MAX_BLOCK_COUNT		65536
+#define RTSX_MAX_BLOCK_LENGTH		2048
+
+struct realtek_sdmmc {
+	struct rtsx_dev		*dev;
+	struct mmc_host		*mmc;
+	struct mmc_request	*mrq;
+
+	struct mutex		host_mutex;
+
+	int			eject;
+};
+
+static struct rtsx_device_id realtek_sdmmc_ids[] = {
+	{RTSX_TYPE_SD},
+	{}
+};
+
+MODULE_DEVICE_TABLE(rtsx, realtek_sdmmc_ids);
+
+static void sd_send_cmd_get_rsp(struct realtek_sdmmc *host,
+		struct mmc_command *cmd)
+{
+	cmd->error = rtsx_sdmmc_send_cmd_get_rsp(host->dev, (u8)cmd->opcode,
+			cmd->arg, mmc_resp_type(cmd), cmd->resp);
+}
+
+static int sd_rw_multi(struct realtek_sdmmc *host, struct mmc_request *mrq)
+{
+	struct mmc_host *mmc = host->mmc;
+	struct mmc_card *card = mmc->card;
+	struct mmc_data *data = mrq->data;
+	int uhs = mmc_sd_card_uhs(card);
+	int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
+
+	return rtsx_sdmmc_rw_multi(host->dev, data->sg, data->blksz,
+			data->blocks, data->sg_len, read, uhs);
+}
+
+static void sd_normal_rw(struct realtek_sdmmc *host, struct mmc_request *mrq)
+{
+	struct mmc_command *cmd = mrq->cmd;
+	struct mmc_data *data = mrq->data;
+	u8 _cmd[5], *buf;
+
+	_cmd[0] = 0x40 | (u8)cmd->opcode;
+	put_unaligned_be32(cmd->arg, (u32 *)(&_cmd[1]));
+
+	buf = kzalloc(data->blksz, GFP_NOIO);
+	if (!buf) {
+		cmd->error = -ENOMEM;
+		return;
+	}
+
+	if (data->flags & MMC_DATA_READ) {
+		cmd->error = rtsx_sdmmc_read_data(host->dev, _cmd,
+				(u16)data->blksz, buf, data->blksz, 200);
+		sg_copy_from_buffer(data->sg, data->sg_len, buf, data->blksz);
+	} else {
+		sg_copy_to_buffer(data->sg, data->sg_len, buf, data->blksz);
+		cmd->error = rtsx_sdmmc_write_data(host->dev, _cmd,
+				(u16)data->blksz, buf, data->blksz, 200);
+	}
+
+	kfree(buf);
+}
+
+static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+	struct mmc_command *cmd = mrq->cmd;
+	struct mmc_data *data = mrq->data;
+	unsigned int data_size = 0;
+
+	if (host->eject) {
+		cmd->error = -ENOMEDIUM;
+		goto finish;
+	}
+
+	mutex_lock(&host->host_mutex);
+	host->mrq = mrq;
+	mutex_unlock(&host->host_mutex);
+
+	if (mrq->data)
+		data_size = data->blocks * data->blksz;
+
+	if (!data_size || mmc_op_multi(cmd->opcode) ||
+			(cmd->opcode == MMC_READ_SINGLE_BLOCK) ||
+			(cmd->opcode == MMC_WRITE_BLOCK)) {
+		sd_send_cmd_get_rsp(host, cmd);
+
+		if (!cmd->error && data_size) {
+			sd_rw_multi(host, mrq);
+
+			if (mmc_op_multi(cmd->opcode) && mrq->stop)
+				sd_send_cmd_get_rsp(host, mrq->stop);
+		}
+	} else {
+		sd_normal_rw(host, mrq);
+	}
+
+	if (mrq->data) {
+		if (cmd->error || data->error)
+			data->bytes_xfered = 0;
+		else
+			data->bytes_xfered = data->blocks * data->blksz;
+	}
+
+finish:
+	if (cmd->error)
+		pr_debug("cmd->error = %d\n", cmd->error);
+
+	mutex_lock(&host->host_mutex);
+	host->mrq = NULL;
+	mutex_unlock(&host->host_mutex);
+
+	mmc_request_done(mmc, mrq);
+}
+
+static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+	int vpclk = 0, double_clk = 1;
+	u8 ssc_depth;
+
+	rtsx_sdmmc_set_bus_width(host->dev, ios->bus_width);
+	rtsx_sdmmc_set_power_mode(host->dev, ios->power_mode);
+	rtsx_sdmmc_set_timing(host->dev, ios->timing);
+
+	switch (ios->timing) {
+	case MMC_TIMING_UHS_SDR104:
+	case MMC_TIMING_UHS_SDR50:
+		ssc_depth = RTSX_SSC_DEPTH_2M;
+		vpclk = 1;
+		double_clk = 0;
+		break;
+
+	case MMC_TIMING_UHS_DDR50:
+	case MMC_TIMING_UHS_SDR25:
+		ssc_depth = RTSX_SSC_DEPTH_1M;
+		break;
+
+	default:
+		ssc_depth = RTSX_SSC_DEPTH_500K;
+		break;
+	}
+
+	rtsx_switch_clock(host->dev,
+			ios->clock, ssc_depth, double_clk, vpclk);
+}
+
+static int sdmmc_get_ro(struct mmc_host *mmc)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_get_ro(host->dev);
+}
+
+static int sdmmc_get_cd(struct mmc_host *mmc)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_get_cd(host->dev);
+}
+
+static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_switch_voltage(host->dev, ios->signal_voltage);
+}
+
+static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_execute_tuning(host->dev);
+}
+
+static const struct mmc_host_ops realtek_sdmmc_ops = {
+	.request = sdmmc_request,
+	.set_ios = sdmmc_set_ios,
+	.get_ro = sdmmc_get_ro,
+	.get_cd = sdmmc_get_cd,
+	.start_signal_voltage_switch = sdmmc_switch_voltage,
+	.execute_tuning = sdmmc_execute_tuning,
+};
+
+static void init_extra_caps(struct realtek_sdmmc *host)
+{
+	struct mmc_host *mmc = host->mmc;
+	struct rtsx_dev *sock = host->dev;
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	pr_debug("adapter->extra_caps = 0x%x\n", adapter->extra_caps);
+
+	if (adapter->extra_caps & EXTRA_CAPS_SD_SDR50)
+		mmc->caps |= MMC_CAP_UHS_SDR50;
+	if (adapter->extra_caps & EXTRA_CAPS_SD_SDR104)
+		mmc->caps |= MMC_CAP_UHS_SDR104;
+	if (adapter->extra_caps & EXTRA_CAPS_SD_DDR50)
+		mmc->caps |= MMC_CAP_UHS_DDR50;
+	if (adapter->extra_caps & EXTRA_CAPS_MMC_HSDDR)
+		mmc->caps |= MMC_CAP_1_8V_DDR;
+	if (adapter->extra_caps & EXTRA_CAPS_MMC_8BIT)
+		mmc->caps |= MMC_CAP_8_BIT_DATA;
+}
+
+static void realtek_init_host(struct realtek_sdmmc *host)
+{
+	struct mmc_host *mmc = host->mmc;
+
+	mmc->f_min = 250000;
+	mmc->f_max = 208000000;
+	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
+	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
+		MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
+		MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
+		MMC_CAP_MAX_CURRENT_200 | MMC_CAP_MAX_CURRENT_400 |
+		MMC_CAP_MAX_CURRENT_600 | MMC_CAP_MAX_CURRENT_800;
+	mmc->ops = &realtek_sdmmc_ops;
+
+	init_extra_caps(host);
+
+	mmc->max_segs = 256;
+	mmc->max_blk_size = RTSX_MAX_BLOCK_LENGTH;
+	mmc->max_blk_count = RTSX_MAX_BLOCK_COUNT;
+	mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count;
+	mmc->max_req_size = mmc->max_seg_size;
+}
+
+static int realtek_sdmmc_probe(struct rtsx_dev *sock)
+{
+	struct mmc_host *mmc;
+	struct realtek_sdmmc *host;
+
+	pr_info(DRV_NAME ": Realtek SDMMC controller found\n");
+
+	mmc = mmc_alloc_host(sizeof(struct realtek_sdmmc), &sock->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	host = mmc_priv(mmc);
+	rtsx_set_drvdata(sock, mmc);
+	host->dev = sock;
+	host->mmc = mmc;
+
+	mutex_init(&host->host_mutex);
+
+	realtek_init_host(host);
+
+	mmc_add_host(mmc);
+
+	return 0;
+}
+
+static void __devexit realtek_sdmmc_remove(struct rtsx_dev *sock)
+{
+	struct mmc_host *mmc = rtsx_get_drvdata(sock);
+	struct realtek_sdmmc *host;
+
+	host = mmc_priv(mmc);
+	host->eject = 1;
+
+	mutex_lock(&host->host_mutex);
+	if (host->mrq) {
+		pr_debug("%s: Controller removed during transfer\n",
+				mmc_hostname(mmc));
+
+		rtsx_complete_unfinished_transfer(sock);
+
+		host->mrq->cmd->error = -ENOMEDIUM;
+		if (host->mrq->stop)
+			host->mrq->stop->error = -ENOMEDIUM;
+		mmc_request_done(mmc, host->mrq);
+	}
+	mutex_unlock(&host->host_mutex);
+
+	mmc_remove_host(mmc);
+	mmc_free_host(mmc);
+
+	pr_info(DRV_NAME
+		": Realtek SDMMC controller has been removed\n");
+}
+
+static struct rtsx_driver realtek_sdmmc_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.id_table = realtek_sdmmc_ids,
+	.probe = realtek_sdmmc_probe,
+	.remove = realtek_sdmmc_remove,
+};
+
+static int __init realtek_sdmmc_drv_init(void)
+{
+	pr_info(DRV_NAME ": Realtek SD/MMC Card Reader driver\n");
+
+	return rtsx_register_driver(&realtek_sdmmc_driver);
+}
+
+static void __exit realtek_sdmmc_drv_exit(void)
+{
+	rtsx_unregister_driver(&realtek_sdmmc_driver);
+}
+
+module_init(realtek_sdmmc_drv_init);
+module_exit(realtek_sdmmc_drv_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Realtek Corp.");
+MODULE_DESCRIPTION("Realtek SD/MMC Card Interface Driver");
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH 3/3] drivers/mmc/host: Add realtek sdmmc interface driver
  2012-07-23  9:42 wei_wang
@ 2012-07-23 16:52 ` Borislav Petkov
  0 siblings, 0 replies; 11+ messages in thread
From: Borislav Petkov @ 2012-07-23 16:52 UTC (permalink / raw)
  To: wei_wang; +Cc: gregkh, devel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1497 bytes --]

On Mon, Jul 23, 2012 at 05:42:51PM +0800, wei_wang@realsil.com.cn wrote:
> +static void realtek_init_host(struct realtek_sdmmc *host)
> +{
> +	struct mmc_host *mmc = host->mmc;
> +
> +	mmc->f_min = 250000;
> +	mmc->f_max = 208000000;
> +	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
> +	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
> +		MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
> +		MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
> +		MMC_CAP_MAX_CURRENT_200 | MMC_CAP_MAX_CURRENT_400 |
> +		MMC_CAP_MAX_CURRENT_600 | MMC_CAP_MAX_CURRENT_800;

drivers/mmc/host/rtsx_sdmmc.c: In function ‘realtek_init_host’:
drivers/mmc/host/rtsx_sdmmc.c:263:3: error: ‘MMC_CAP_MAX_CURRENT_200’ undeclared (first use in this function)
drivers/mmc/host/rtsx_sdmmc.c:263:3: note: each undeclared identifier is reported only once for each function it appears in
drivers/mmc/host/rtsx_sdmmc.c:263:29: error: ‘MMC_CAP_MAX_CURRENT_400’ undeclared (first use in this function)
drivers/mmc/host/rtsx_sdmmc.c:264:3: error: ‘MMC_CAP_MAX_CURRENT_600’ undeclared (first use in this function)
drivers/mmc/host/rtsx_sdmmc.c:264:29: error: ‘MMC_CAP_MAX_CURRENT_800’ undeclared (first use in this function)
make[3]: *** [drivers/mmc/host/rtsx_sdmmc.o] Error 1
make[2]: *** [drivers/mmc/host] Error 2
make[1]: *** [drivers/mmc] Error 2
make: *** [drivers] Error 2

This happens when I enable CONFIG_MMC_REALTEK to build rtsx_sdmmc.c

Config attached.

-- 
Regards/Gruss,
Boris.

[-- Attachment #2: config.gz --]
[-- Type: application/octet-stream, Size: 18240 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 3/3] drivers/mmc/host: Add realtek sdmmc interface driver
@ 2012-07-23  9:42 wei_wang
  2012-07-23 16:52 ` Borislav Petkov
  0 siblings, 1 reply; 11+ messages in thread
From: wei_wang @ 2012-07-23  9:42 UTC (permalink / raw)
  To: gregkh, devel, linux-kernel; +Cc: Wei WANG

From: Wei WANG <wei_wang@realsil.com.cn>

Realtek SD/MMC card interface driver is used to access
SD/MMC card, with the help of Realtek card reader adapter driver.

Signed-off-by: Wei WANG <wei_wang@realsil.com.cn>
---
 drivers/mmc/host/Kconfig      |    7 +
 drivers/mmc/host/Makefile     |    2 +
 drivers/mmc/host/rtsx_sdmmc.c |  357 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 366 insertions(+)
 create mode 100644 drivers/mmc/host/rtsx_sdmmc.c

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index aa131b3..d9942e6 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -612,3 +612,10 @@ config MMC_USHC
 
 	  Note: These controllers only support SDIO cards and do not
 	  support MMC or SD memory cards.
+
+config MMC_REALTEK
+	tristate "Realtek SD/MMC Card Interface Driver"
+	depends on REALTEK_CR_CORE
+	help
+	  Say Y here to include driver code to support the Realtek
+	  SD/MMC card interface.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 8922b06..a4cd15a 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -45,6 +45,8 @@ obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
 obj-$(CONFIG_MMC_VUB300)	+= vub300.o
 obj-$(CONFIG_MMC_USHC)		+= ushc.o
 
+obj-$(CONFIG_MMC_REALTEK)	+= rtsx_sdmmc.o
+
 obj-$(CONFIG_MMC_SDHCI_PLTFM)		+= sdhci-pltfm.o
 obj-$(CONFIG_MMC_SDHCI_CNS3XXX)		+= sdhci-cns3xxx.o
 obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX)	+= sdhci-esdhc-imx.o
diff --git a/drivers/mmc/host/rtsx_sdmmc.c b/drivers/mmc/host/rtsx_sdmmc.c
new file mode 100644
index 0000000..540c391
--- /dev/null
+++ b/drivers/mmc/host/rtsx_sdmmc.c
@@ -0,0 +1,357 @@
+/* Realtek SD/MMC Card Interface driver
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * 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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG <wei_wang@realsil.com.cn>
+ *   No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ */
+
+#include <asm-generic/unaligned.h>
+
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/delay.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/card.h>
+
+#include <linux/rtsx_core.h>
+
+#define DRV_NAME	"rtsx_sdmmc"
+
+#define MAX_RW_REG_CNT			1024
+
+#define RTSX_MAX_BLOCK_COUNT		65536
+#define RTSX_MAX_BLOCK_LENGTH		2048
+
+struct realtek_sdmmc {
+	struct rtsx_dev		*dev;
+	struct mmc_host		*mmc;
+	struct mmc_request	*mrq;
+
+	struct mutex		host_mutex;
+
+	int			eject;
+};
+
+static struct rtsx_device_id realtek_sdmmc_ids[] = {
+	{RTSX_TYPE_SD},
+	{}
+};
+
+MODULE_DEVICE_TABLE(rtsx, realtek_sdmmc_ids);
+
+static void sd_send_cmd_get_rsp(struct realtek_sdmmc *host,
+		struct mmc_command *cmd)
+{
+	cmd->error = rtsx_sdmmc_send_cmd_get_rsp(host->dev, (u8)cmd->opcode,
+			cmd->arg, mmc_resp_type(cmd), cmd->resp);
+}
+
+static int sd_rw_multi(struct realtek_sdmmc *host, struct mmc_request *mrq)
+{
+	struct mmc_host *mmc = host->mmc;
+	struct mmc_card *card = mmc->card;
+	struct mmc_data *data = mrq->data;
+	int uhs = mmc_sd_card_uhs(card);
+	int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
+
+	return rtsx_sdmmc_rw_multi(host->dev, data->sg, data->blksz,
+			data->blocks, data->sg_len, read, uhs);
+}
+
+static void sd_normal_rw(struct realtek_sdmmc *host, struct mmc_request *mrq)
+{
+	struct mmc_command *cmd = mrq->cmd;
+	struct mmc_data *data = mrq->data;
+	u8 _cmd[5], *buf;
+
+	_cmd[0] = 0x40 | (u8)cmd->opcode;
+	put_unaligned(cpu_to_be32(cmd->arg), (u32 *)(&_cmd[1]));
+
+	buf = kzalloc(data->blksz, GFP_NOIO);
+	if (!buf) {
+		cmd->error = -ENOMEM;
+		return;
+	}
+
+	if (data->flags & MMC_DATA_READ) {
+		cmd->error = rtsx_sdmmc_read_data(host->dev, _cmd,
+				(u16)data->blksz, buf, data->blksz, 200);
+		sg_copy_from_buffer(data->sg, data->sg_len, buf, data->blksz);
+	} else {
+		sg_copy_to_buffer(data->sg, data->sg_len, buf, data->blksz);
+		cmd->error = rtsx_sdmmc_write_data(host->dev, _cmd,
+				(u16)data->blksz, buf, data->blksz, 200);
+	}
+
+	kfree(buf);
+}
+
+static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+	struct mmc_command *cmd = mrq->cmd;
+	struct mmc_data *data = mrq->data;
+	unsigned int data_size = 0;
+
+	if (host->eject) {
+		cmd->error = -ENOMEDIUM;
+		goto finish;
+	}
+
+	mutex_lock(&host->host_mutex);
+	host->mrq = mrq;
+	mutex_unlock(&host->host_mutex);
+
+	if (mrq->data)
+		data_size = data->blocks * data->blksz;
+
+	if (!data_size || mmc_op_multi(cmd->opcode) ||
+			(cmd->opcode == MMC_READ_SINGLE_BLOCK) ||
+			(cmd->opcode == MMC_WRITE_BLOCK)) {
+		sd_send_cmd_get_rsp(host, cmd);
+
+		if (!cmd->error && data_size) {
+			sd_rw_multi(host, mrq);
+
+			if (mmc_op_multi(cmd->opcode) && mrq->stop)
+				sd_send_cmd_get_rsp(host, mrq->stop);
+		}
+	} else {
+		sd_normal_rw(host, mrq);
+	}
+
+	if (mrq->data) {
+		if (cmd->error || data->error)
+			data->bytes_xfered = 0;
+		else
+			data->bytes_xfered = data->blocks * data->blksz;
+	}
+
+finish:
+	if (cmd->error)
+		pr_debug("cmd->error = %d\n", cmd->error);
+
+	mutex_lock(&host->host_mutex);
+	host->mrq = NULL;
+	mutex_unlock(&host->host_mutex);
+
+	mmc_request_done(mmc, mrq);
+}
+
+static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+	int vpclk = 0, double_clk = 1;
+	u8 ssc_depth;
+
+	rtsx_sdmmc_set_bus_width(host->dev, ios->bus_width);
+	rtsx_sdmmc_set_power_mode(host->dev, ios->power_mode);
+	rtsx_sdmmc_set_timing(host->dev, ios->timing);
+
+	switch (ios->timing) {
+	case MMC_TIMING_UHS_SDR104:
+	case MMC_TIMING_UHS_SDR50:
+		ssc_depth = RTSX_SSC_DEPTH_2M;
+		vpclk = 1;
+		double_clk = 0;
+		break;
+
+	case MMC_TIMING_UHS_DDR50:
+	case MMC_TIMING_UHS_SDR25:
+/*	case MMC_TIMING_MMC_HS200: */
+		ssc_depth = RTSX_SSC_DEPTH_1M;
+		break;
+
+	default:
+		ssc_depth = RTSX_SSC_DEPTH_500K;
+		break;
+	}
+
+	rtsx_switch_clock(host->dev,
+			ios->clock, ssc_depth, double_clk, vpclk);
+}
+
+static int sdmmc_get_ro(struct mmc_host *mmc)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_get_ro(host->dev);
+}
+
+static int sdmmc_get_cd(struct mmc_host *mmc)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_get_cd(host->dev);
+}
+
+static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_switch_voltage(host->dev, ios->signal_voltage);
+}
+
+static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_execute_tuning(host->dev);
+}
+
+static const struct mmc_host_ops realtek_sdmmc_ops = {
+	.request = sdmmc_request,
+	.set_ios = sdmmc_set_ios,
+	.get_ro = sdmmc_get_ro,
+	.get_cd = sdmmc_get_cd,
+	.start_signal_voltage_switch = sdmmc_switch_voltage,
+	.execute_tuning = sdmmc_execute_tuning,
+};
+
+static void init_extra_caps(struct realtek_sdmmc *host)
+{
+	struct mmc_host *mmc = host->mmc;
+	struct rtsx_dev *sock = host->dev;
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	pr_debug("adapter->extra_caps = 0x%x\n", adapter->extra_caps);
+
+	if (adapter->extra_caps & EXTRA_CAPS_SD_SDR50)
+		mmc->caps |= MMC_CAP_UHS_SDR50;
+	if (adapter->extra_caps & EXTRA_CAPS_SD_SDR104)
+		mmc->caps |= MMC_CAP_UHS_SDR104;
+	if (adapter->extra_caps & EXTRA_CAPS_SD_DDR50)
+		mmc->caps |= MMC_CAP_UHS_DDR50;
+	if (adapter->extra_caps & EXTRA_CAPS_MMC_HSDDR)
+		mmc->caps |= MMC_CAP_1_8V_DDR;
+	if (adapter->extra_caps & EXTRA_CAPS_MMC_8BIT)
+		mmc->caps |= MMC_CAP_8_BIT_DATA;
+/*
+	if (adapter->extra_caps & EXTRA_CAPS_MMC_HS200)
+		mmc->caps2 |= MMC_CAP2_HS200;
+*/
+}
+
+static void realtek_init_host(struct realtek_sdmmc *host)
+{
+	struct mmc_host *mmc = host->mmc;
+
+	mmc->f_min = 250000;
+	mmc->f_max = 208000000;
+	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
+	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
+		MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
+		MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
+		MMC_CAP_MAX_CURRENT_200 | MMC_CAP_MAX_CURRENT_400 |
+		MMC_CAP_MAX_CURRENT_600 | MMC_CAP_MAX_CURRENT_800;
+	mmc->ops = &realtek_sdmmc_ops;
+
+	init_extra_caps(host);
+
+	mmc->max_segs = 256;
+	mmc->max_blk_size = RTSX_MAX_BLOCK_LENGTH;
+	mmc->max_blk_count = RTSX_MAX_BLOCK_COUNT;
+	mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count;
+	mmc->max_req_size = mmc->max_seg_size;
+}
+
+static int realtek_sdmmc_probe(struct rtsx_dev *sock)
+{
+	struct mmc_host *mmc;
+	struct realtek_sdmmc *host;
+
+	pr_info(DRV_NAME ": Realtek SDMMC controller found\n");
+
+	mmc = mmc_alloc_host(sizeof(struct realtek_sdmmc), &sock->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	host = mmc_priv(mmc);
+	rtsx_set_drvdata(sock, mmc);
+	host->dev = sock;
+	host->mmc = mmc;
+
+	mutex_init(&host->host_mutex);
+
+	realtek_init_host(host);
+
+	mmc_add_host(mmc);
+
+	return 0;
+}
+
+static void __devexit realtek_sdmmc_remove(struct rtsx_dev *sock)
+{
+	struct mmc_host *mmc = rtsx_get_drvdata(sock);
+	struct realtek_sdmmc *host;
+
+	host = mmc_priv(mmc);
+	host->eject = 1;
+
+	mutex_lock(&host->host_mutex);
+	if (host->mrq) {
+		pr_debug("%s: Controller removed during transfer\n",
+				mmc_hostname(mmc));
+
+		rtsx_complete_unfinished_transfer(sock);
+
+		host->mrq->cmd->error = -ENOMEDIUM;
+		if (host->mrq->stop)
+			host->mrq->stop->error = -ENOMEDIUM;
+		mmc_request_done(mmc, host->mrq);
+	}
+	mutex_unlock(&host->host_mutex);
+
+	mmc_remove_host(mmc);
+	mmc_free_host(mmc);
+
+	pr_info(DRV_NAME
+		": Realtek SDMMC controller has been removed\n");
+}
+
+static struct rtsx_driver realtek_sdmmc_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.id_table = realtek_sdmmc_ids,
+	.probe = realtek_sdmmc_probe,
+	.remove = realtek_sdmmc_remove,
+};
+
+static int __init realtek_sdmmc_drv_init(void)
+{
+	pr_info(DRV_NAME ": Realtek SD/MMC Card Reader driver\n");
+
+	return rtsx_register_driver(&realtek_sdmmc_driver);
+}
+
+static void __exit realtek_sdmmc_drv_exit(void)
+{
+	rtsx_unregister_driver(&realtek_sdmmc_driver);
+}
+
+module_init(realtek_sdmmc_drv_init);
+module_exit(realtek_sdmmc_drv_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Realtek Corp.");
+MODULE_DESCRIPTION("Realtek SD/MMC Card Interface Driver");
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 3/3] drivers/mmc/host: Add realtek sdmmc interface driver
@ 2012-07-20 10:09 wei_wang
  0 siblings, 0 replies; 11+ messages in thread
From: wei_wang @ 2012-07-20 10:09 UTC (permalink / raw)
  To: gregkh, devel, linux-kernel; +Cc: Wei WANG

From: Wei WANG <wei_wang@realsil.com.cn>

Realtek SD/MMC card interface driver is used to access
SD/MMC card, with the help of Realtek card reader adapter driver.

Signed-off-by: Wei WANG <wei_wang@realsil.com.cn>
---
 drivers/mmc/host/Kconfig      |    7 +
 drivers/mmc/host/Makefile     |    2 +
 drivers/mmc/host/rtsx_sdmmc.c |  357 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 366 insertions(+)
 create mode 100644 drivers/mmc/host/rtsx_sdmmc.c

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index aa131b3..d9942e6 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -612,3 +612,10 @@ config MMC_USHC
 
 	  Note: These controllers only support SDIO cards and do not
 	  support MMC or SD memory cards.
+
+config MMC_REALTEK
+	tristate "Realtek SD/MMC Card Interface Driver"
+	depends on REALTEK_CR_CORE
+	help
+	  Say Y here to include driver code to support the Realtek
+	  SD/MMC card interface.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 8922b06..a4cd15a 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -45,6 +45,8 @@ obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
 obj-$(CONFIG_MMC_VUB300)	+= vub300.o
 obj-$(CONFIG_MMC_USHC)		+= ushc.o
 
+obj-$(CONFIG_MMC_REALTEK)	+= rtsx_sdmmc.o
+
 obj-$(CONFIG_MMC_SDHCI_PLTFM)		+= sdhci-pltfm.o
 obj-$(CONFIG_MMC_SDHCI_CNS3XXX)		+= sdhci-cns3xxx.o
 obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX)	+= sdhci-esdhc-imx.o
diff --git a/drivers/mmc/host/rtsx_sdmmc.c b/drivers/mmc/host/rtsx_sdmmc.c
new file mode 100644
index 0000000..540c391
--- /dev/null
+++ b/drivers/mmc/host/rtsx_sdmmc.c
@@ -0,0 +1,357 @@
+/* Realtek SD/MMC Card Interface driver
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * 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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG <wei_wang@realsil.com.cn>
+ *   No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ */
+
+#include <asm-generic/unaligned.h>
+
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/delay.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/card.h>
+
+#include <linux/rtsx_core.h>
+
+#define DRV_NAME	"rtsx_sdmmc"
+
+#define MAX_RW_REG_CNT			1024
+
+#define RTSX_MAX_BLOCK_COUNT		65536
+#define RTSX_MAX_BLOCK_LENGTH		2048
+
+struct realtek_sdmmc {
+	struct rtsx_dev		*dev;
+	struct mmc_host		*mmc;
+	struct mmc_request	*mrq;
+
+	struct mutex		host_mutex;
+
+	int			eject;
+};
+
+static struct rtsx_device_id realtek_sdmmc_ids[] = {
+	{RTSX_TYPE_SD},
+	{}
+};
+
+MODULE_DEVICE_TABLE(rtsx, realtek_sdmmc_ids);
+
+static void sd_send_cmd_get_rsp(struct realtek_sdmmc *host,
+		struct mmc_command *cmd)
+{
+	cmd->error = rtsx_sdmmc_send_cmd_get_rsp(host->dev, (u8)cmd->opcode,
+			cmd->arg, mmc_resp_type(cmd), cmd->resp);
+}
+
+static int sd_rw_multi(struct realtek_sdmmc *host, struct mmc_request *mrq)
+{
+	struct mmc_host *mmc = host->mmc;
+	struct mmc_card *card = mmc->card;
+	struct mmc_data *data = mrq->data;
+	int uhs = mmc_sd_card_uhs(card);
+	int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
+
+	return rtsx_sdmmc_rw_multi(host->dev, data->sg, data->blksz,
+			data->blocks, data->sg_len, read, uhs);
+}
+
+static void sd_normal_rw(struct realtek_sdmmc *host, struct mmc_request *mrq)
+{
+	struct mmc_command *cmd = mrq->cmd;
+	struct mmc_data *data = mrq->data;
+	u8 _cmd[5], *buf;
+
+	_cmd[0] = 0x40 | (u8)cmd->opcode;
+	put_unaligned(cpu_to_be32(cmd->arg), (u32 *)(&_cmd[1]));
+
+	buf = kzalloc(data->blksz, GFP_NOIO);
+	if (!buf) {
+		cmd->error = -ENOMEM;
+		return;
+	}
+
+	if (data->flags & MMC_DATA_READ) {
+		cmd->error = rtsx_sdmmc_read_data(host->dev, _cmd,
+				(u16)data->blksz, buf, data->blksz, 200);
+		sg_copy_from_buffer(data->sg, data->sg_len, buf, data->blksz);
+	} else {
+		sg_copy_to_buffer(data->sg, data->sg_len, buf, data->blksz);
+		cmd->error = rtsx_sdmmc_write_data(host->dev, _cmd,
+				(u16)data->blksz, buf, data->blksz, 200);
+	}
+
+	kfree(buf);
+}
+
+static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+	struct mmc_command *cmd = mrq->cmd;
+	struct mmc_data *data = mrq->data;
+	unsigned int data_size = 0;
+
+	if (host->eject) {
+		cmd->error = -ENOMEDIUM;
+		goto finish;
+	}
+
+	mutex_lock(&host->host_mutex);
+	host->mrq = mrq;
+	mutex_unlock(&host->host_mutex);
+
+	if (mrq->data)
+		data_size = data->blocks * data->blksz;
+
+	if (!data_size || mmc_op_multi(cmd->opcode) ||
+			(cmd->opcode == MMC_READ_SINGLE_BLOCK) ||
+			(cmd->opcode == MMC_WRITE_BLOCK)) {
+		sd_send_cmd_get_rsp(host, cmd);
+
+		if (!cmd->error && data_size) {
+			sd_rw_multi(host, mrq);
+
+			if (mmc_op_multi(cmd->opcode) && mrq->stop)
+				sd_send_cmd_get_rsp(host, mrq->stop);
+		}
+	} else {
+		sd_normal_rw(host, mrq);
+	}
+
+	if (mrq->data) {
+		if (cmd->error || data->error)
+			data->bytes_xfered = 0;
+		else
+			data->bytes_xfered = data->blocks * data->blksz;
+	}
+
+finish:
+	if (cmd->error)
+		pr_debug("cmd->error = %d\n", cmd->error);
+
+	mutex_lock(&host->host_mutex);
+	host->mrq = NULL;
+	mutex_unlock(&host->host_mutex);
+
+	mmc_request_done(mmc, mrq);
+}
+
+static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+	int vpclk = 0, double_clk = 1;
+	u8 ssc_depth;
+
+	rtsx_sdmmc_set_bus_width(host->dev, ios->bus_width);
+	rtsx_sdmmc_set_power_mode(host->dev, ios->power_mode);
+	rtsx_sdmmc_set_timing(host->dev, ios->timing);
+
+	switch (ios->timing) {
+	case MMC_TIMING_UHS_SDR104:
+	case MMC_TIMING_UHS_SDR50:
+		ssc_depth = RTSX_SSC_DEPTH_2M;
+		vpclk = 1;
+		double_clk = 0;
+		break;
+
+	case MMC_TIMING_UHS_DDR50:
+	case MMC_TIMING_UHS_SDR25:
+/*	case MMC_TIMING_MMC_HS200: */
+		ssc_depth = RTSX_SSC_DEPTH_1M;
+		break;
+
+	default:
+		ssc_depth = RTSX_SSC_DEPTH_500K;
+		break;
+	}
+
+	rtsx_switch_clock(host->dev,
+			ios->clock, ssc_depth, double_clk, vpclk);
+}
+
+static int sdmmc_get_ro(struct mmc_host *mmc)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_get_ro(host->dev);
+}
+
+static int sdmmc_get_cd(struct mmc_host *mmc)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_get_cd(host->dev);
+}
+
+static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_switch_voltage(host->dev, ios->signal_voltage);
+}
+
+static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+	struct realtek_sdmmc *host = mmc_priv(mmc);
+
+	return rtsx_sdmmc_execute_tuning(host->dev);
+}
+
+static const struct mmc_host_ops realtek_sdmmc_ops = {
+	.request = sdmmc_request,
+	.set_ios = sdmmc_set_ios,
+	.get_ro = sdmmc_get_ro,
+	.get_cd = sdmmc_get_cd,
+	.start_signal_voltage_switch = sdmmc_switch_voltage,
+	.execute_tuning = sdmmc_execute_tuning,
+};
+
+static void init_extra_caps(struct realtek_sdmmc *host)
+{
+	struct mmc_host *mmc = host->mmc;
+	struct rtsx_dev *sock = host->dev;
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	pr_debug("adapter->extra_caps = 0x%x\n", adapter->extra_caps);
+
+	if (adapter->extra_caps & EXTRA_CAPS_SD_SDR50)
+		mmc->caps |= MMC_CAP_UHS_SDR50;
+	if (adapter->extra_caps & EXTRA_CAPS_SD_SDR104)
+		mmc->caps |= MMC_CAP_UHS_SDR104;
+	if (adapter->extra_caps & EXTRA_CAPS_SD_DDR50)
+		mmc->caps |= MMC_CAP_UHS_DDR50;
+	if (adapter->extra_caps & EXTRA_CAPS_MMC_HSDDR)
+		mmc->caps |= MMC_CAP_1_8V_DDR;
+	if (adapter->extra_caps & EXTRA_CAPS_MMC_8BIT)
+		mmc->caps |= MMC_CAP_8_BIT_DATA;
+/*
+	if (adapter->extra_caps & EXTRA_CAPS_MMC_HS200)
+		mmc->caps2 |= MMC_CAP2_HS200;
+*/
+}
+
+static void realtek_init_host(struct realtek_sdmmc *host)
+{
+	struct mmc_host *mmc = host->mmc;
+
+	mmc->f_min = 250000;
+	mmc->f_max = 208000000;
+	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
+	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
+		MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
+		MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
+		MMC_CAP_MAX_CURRENT_200 | MMC_CAP_MAX_CURRENT_400 |
+		MMC_CAP_MAX_CURRENT_600 | MMC_CAP_MAX_CURRENT_800;
+	mmc->ops = &realtek_sdmmc_ops;
+
+	init_extra_caps(host);
+
+	mmc->max_segs = 256;
+	mmc->max_blk_size = RTSX_MAX_BLOCK_LENGTH;
+	mmc->max_blk_count = RTSX_MAX_BLOCK_COUNT;
+	mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count;
+	mmc->max_req_size = mmc->max_seg_size;
+}
+
+static int realtek_sdmmc_probe(struct rtsx_dev *sock)
+{
+	struct mmc_host *mmc;
+	struct realtek_sdmmc *host;
+
+	pr_info(DRV_NAME ": Realtek SDMMC controller found\n");
+
+	mmc = mmc_alloc_host(sizeof(struct realtek_sdmmc), &sock->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	host = mmc_priv(mmc);
+	rtsx_set_drvdata(sock, mmc);
+	host->dev = sock;
+	host->mmc = mmc;
+
+	mutex_init(&host->host_mutex);
+
+	realtek_init_host(host);
+
+	mmc_add_host(mmc);
+
+	return 0;
+}
+
+static void __devexit realtek_sdmmc_remove(struct rtsx_dev *sock)
+{
+	struct mmc_host *mmc = rtsx_get_drvdata(sock);
+	struct realtek_sdmmc *host;
+
+	host = mmc_priv(mmc);
+	host->eject = 1;
+
+	mutex_lock(&host->host_mutex);
+	if (host->mrq) {
+		pr_debug("%s: Controller removed during transfer\n",
+				mmc_hostname(mmc));
+
+		rtsx_complete_unfinished_transfer(sock);
+
+		host->mrq->cmd->error = -ENOMEDIUM;
+		if (host->mrq->stop)
+			host->mrq->stop->error = -ENOMEDIUM;
+		mmc_request_done(mmc, host->mrq);
+	}
+	mutex_unlock(&host->host_mutex);
+
+	mmc_remove_host(mmc);
+	mmc_free_host(mmc);
+
+	pr_info(DRV_NAME
+		": Realtek SDMMC controller has been removed\n");
+}
+
+static struct rtsx_driver realtek_sdmmc_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.id_table = realtek_sdmmc_ids,
+	.probe = realtek_sdmmc_probe,
+	.remove = realtek_sdmmc_remove,
+};
+
+static int __init realtek_sdmmc_drv_init(void)
+{
+	pr_info(DRV_NAME ": Realtek SD/MMC Card Reader driver\n");
+
+	return rtsx_register_driver(&realtek_sdmmc_driver);
+}
+
+static void __exit realtek_sdmmc_drv_exit(void)
+{
+	rtsx_unregister_driver(&realtek_sdmmc_driver);
+}
+
+module_init(realtek_sdmmc_drv_init);
+module_exit(realtek_sdmmc_drv_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Realtek Corp.");
+MODULE_DESCRIPTION("Realtek SD/MMC Card Interface Driver");
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2012-07-31 18:11 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-07-19  9:55 [PATCH 3/3] drivers/mmc/host: Add realtek sdmmc interface driver wei_wang
2012-07-19 12:26 ` Oliver Neukum
2012-07-20  1:48   ` wwang
2012-07-20 10:09 wei_wang
2012-07-23  9:42 wei_wang
2012-07-23 16:52 ` Borislav Petkov
2012-07-25  3:02 wei_wang
2012-07-31  7:42 wei_wang
2012-07-31  7:42 ` wei_wang
2012-07-31 15:22 ` Borislav Petkov
2012-07-31 18:11 ` Borislav Petkov

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.