All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/3] drivers/misc: Add realtek card reader core driver
@ 2012-07-23  9:42 wei_wang
  2012-07-23 11:17 ` Dan Carpenter
                   ` (3 more replies)
  0 siblings, 4 replies; 26+ 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 card reader core driver is the bus driver for Realtek
driver-based card reader, which supplies adapter layer to
be used by lower-level pci/usb card reader and upper-level
sdmmc/memstick host driver.

Signed-off-by: Wei WANG <wei_wang@realsil.com.cn>
---
 Documentation/misc-devices/realtek_cr.txt |   27 ++
 drivers/misc/Kconfig                      |    1 +
 drivers/misc/Makefile                     |    1 +
 drivers/misc/realtek_cr/Kconfig           |   26 ++
 drivers/misc/realtek_cr/Makefile          |    7 +
 drivers/misc/realtek_cr/core/Kconfig      |    6 +
 drivers/misc/realtek_cr/core/Makefile     |    1 +
 drivers/misc/realtek_cr/core/rtsx_core.c  |  492 +++++++++++++++++++++++++++++
 include/linux/rtsx_core.h                 |  183 +++++++++++
 9 files changed, 744 insertions(+)
 create mode 100644 Documentation/misc-devices/realtek_cr.txt
 create mode 100644 drivers/misc/realtek_cr/Kconfig
 create mode 100644 drivers/misc/realtek_cr/Makefile
 create mode 100644 drivers/misc/realtek_cr/core/Kconfig
 create mode 100644 drivers/misc/realtek_cr/core/Makefile
 create mode 100644 drivers/misc/realtek_cr/core/rtsx_core.c
 create mode 100644 include/linux/rtsx_core.h

diff --git a/Documentation/misc-devices/realtek_cr.txt b/Documentation/misc-devices/realtek_cr.txt
new file mode 100644
index 0000000..b4e6fbe
--- /dev/null
+++ b/Documentation/misc-devices/realtek_cr.txt
@@ -0,0 +1,27 @@
+Realtek Driver-based Card Reader
+================================
+
+Supported chips:
+RTS5209
+RTS5229
+
+Contact Email:
+pc_sw_linux@realsil.com.cn
+
+
+Description
+-----------
+
+Realtek driver-based card reader supports access to many types of memory cards,
+such as Memory Stick, Memory Stick Pro, Secure Digital and MultiMediaCard.
+
+
+udev rules
+----------
+
+In order to modprobe Realtek SD/MMC interface driver automatically, the following rule
+should be added to the udev rules file:
+
+SUBSYSTEM=="rtsx_cr", ENV{RTSX_CARD_TYPE}=="SD", RUN+="/sbin/modprobe -bv rtsx_sdmmc"
+
+Typically, we may edit /lib/udev/rules.d/80-drivers.rules and copy the rule into it in Ubuntu.
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 2661f6e..09ce905 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -517,4 +517,5 @@ source "drivers/misc/lis3lv02d/Kconfig"
 source "drivers/misc/carma/Kconfig"
 source "drivers/misc/altera-stapl/Kconfig"
 source "drivers/misc/mei/Kconfig"
+source "drivers/misc/realtek_cr/Kconfig"
 endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 456972f..c09f147 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -51,3 +51,4 @@ obj-y				+= carma/
 obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
 obj-$(CONFIG_ALTERA_STAPL)	+=altera-stapl/
 obj-$(CONFIG_INTEL_MEI)		+= mei/
+obj-$(CONFIG_REALTEK_CR_SUPPORT) += realtek_cr/
diff --git a/drivers/misc/realtek_cr/Kconfig b/drivers/misc/realtek_cr/Kconfig
new file mode 100644
index 0000000..303d98a
--- /dev/null
+++ b/drivers/misc/realtek_cr/Kconfig
@@ -0,0 +1,26 @@
+#
+# Realtek driver-based card reader
+#
+
+menuconfig REALTEK_CR_SUPPORT
+	tristate "Realtek driver-based card reader"
+	help
+	  Realtek driver-based card reader supports access to many types of
+	  memory cards, such as Memory Stick, Memory Stick Pro, Secure Digital
+	  and MultiMediaCard.
+
+	  If you want to use Realtek driver-based card reader, enable this
+	  option and other options below.
+
+config REALTEK_CR_DEBUG
+	bool "Realtek driver-based card reader debugging"
+	depends on REALTEK_CR_SUPPORT != n
+	help
+	  This is an option for use by developers; most people should
+	  say N here.  This enables Realtek card reader driver debugging.
+
+if REALTEK_CR_SUPPORT
+
+source "drivers/misc/realtek_cr/core/Kconfig"
+
+endif
diff --git a/drivers/misc/realtek_cr/Makefile b/drivers/misc/realtek_cr/Makefile
new file mode 100644
index 0000000..f4e16ba
--- /dev/null
+++ b/drivers/misc/realtek_cr/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Realtek driver-based card reader.
+#
+
+subdir-ccflags-$(CONFIG_REALTEK_CR_DEBUG) := -DDEBUG
+
+obj-$(CONFIG_REALTEK_CR_SUPPORT)	+= core/
diff --git a/drivers/misc/realtek_cr/core/Kconfig b/drivers/misc/realtek_cr/core/Kconfig
new file mode 100644
index 0000000..5e9f14e
--- /dev/null
+++ b/drivers/misc/realtek_cr/core/Kconfig
@@ -0,0 +1,6 @@
+config REALTEK_CR_CORE
+	tristate "RealTek Card Reader Core Driver"
+	help
+	  Say Y here to include driver code to support the Realtek
+	  driver-based card reader.
+
diff --git a/drivers/misc/realtek_cr/core/Makefile b/drivers/misc/realtek_cr/core/Makefile
new file mode 100644
index 0000000..010055e
--- /dev/null
+++ b/drivers/misc/realtek_cr/core/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_REALTEK_CR_CORE)		+= rtsx_core.o
diff --git a/drivers/misc/realtek_cr/core/rtsx_core.c b/drivers/misc/realtek_cr/core/rtsx_core.c
new file mode 100644
index 0000000..c3472d5
--- /dev/null
+++ b/drivers/misc/realtek_cr/core/rtsx_core.c
@@ -0,0 +1,492 @@
+/* Realtek card reader core 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/idr.h>
+#include <linux/rtsx_core.h>
+
+static struct workqueue_struct *workqueue;
+static DEFINE_IDR(rtsx_adapter_idr);
+static DEFINE_SPINLOCK(rtsx_adapter_lock);
+
+#define DRIVER_NAME	"rtsx_core"
+
+#ifdef CONFIG_PM
+
+static const char *rtsx_media_type_name(unsigned char type, unsigned char nt)
+{
+	const char *card_type_name[3][3] = {
+		{ "SmartMedia/xD", "MemoryStick", "MMC/SD" },
+		{ "XD", "MS", "SD"},
+		{ "xd", "ms", "sd"}
+	};
+
+	if (nt > 2 || type < 1 || type > 3)
+		return NULL;
+	return card_type_name[nt][type - 1];
+}
+
+static int rtsx_dev_match(struct rtsx_dev *sock, struct rtsx_device_id *id)
+{
+	if (sock->type == id->type)
+		return 1;
+	return 0;
+}
+
+static int rtsx_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *rtsx_drv = container_of(drv, struct rtsx_driver,
+						  driver);
+	struct rtsx_device_id *ids = rtsx_drv->id_table;
+
+	if (ids) {
+		while (ids->type) {
+			if (rtsx_dev_match(sock, ids))
+				return 1;
+			++ids;
+		}
+	}
+	return 0;
+}
+
+static int rtsx_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+
+	if (add_uevent_var(env, "RTSX_CARD_TYPE=%s",
+				rtsx_media_type_name(sock->type, 1)))
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int rtsx_device_probe(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+	int rc = -ENODEV;
+
+	get_device(dev);
+	if (dev->driver && drv->probe) {
+		rc = drv->probe(sock);
+		if (!rc)
+			return 0;
+	}
+	put_device(dev);
+	return rc;
+}
+
+static void rtsx_dummy_event(struct rtsx_dev *sock)
+{
+	return;
+}
+
+static int rtsx_device_remove(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+
+	if (dev->driver && drv->remove) {
+		sock->card_event = rtsx_dummy_event;
+		sock->data_event = rtsx_dummy_event;
+		drv->remove(sock);
+		sock->dev.driver = NULL;
+	}
+
+	put_device(dev);
+	return 0;
+}
+
+static int rtsx_device_suspend(struct device *dev, pm_message_t state)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+
+	if (dev->driver && drv->suspend)
+		return drv->suspend(sock, state);
+	return 0;
+}
+
+static int rtsx_device_resume(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+
+	if (dev->driver && drv->resume)
+		return drv->resume(sock);
+	return 0;
+}
+
+#else
+
+#define rtsx_device_suspend NULL
+#define rtsx_device_resume NULL
+
+#endif /* CONFIG_PM */
+
+static ssize_t type_show(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	return sprintf(buf, "%x", sock->type);
+}
+
+static struct device_attribute rtsx_dev_attrs[] = {
+	__ATTR(type, S_IRUGO, type_show, NULL),
+	__ATTR_NULL
+};
+
+static struct bus_type rtsx_bus_type = {
+	.name      = "rtsx_cr",
+	.dev_attrs = rtsx_dev_attrs,
+	.match     = rtsx_bus_match,
+	.uevent    = rtsx_uevent,
+	.probe     = rtsx_device_probe,
+	.remove    = rtsx_device_remove,
+	.suspend   = rtsx_device_suspend,
+	.resume    = rtsx_device_resume
+};
+
+static void rtsx_free(struct device *dev)
+{
+	struct rtsx_adapter *adapter;
+
+	adapter = container_of(dev, struct rtsx_adapter, dev);
+	kfree(adapter);
+}
+
+static struct class rtsx_adapter_class = {
+	.name    = "rtsx_adapter",
+	.dev_release = rtsx_free
+};
+
+struct rtsx_adapter *rtsx_alloc_adapter(unsigned int num_sockets,
+					struct device *dev)
+{
+	struct rtsx_adapter *adapter;
+
+	adapter = kzalloc(sizeof(struct rtsx_adapter)
+		     + sizeof(struct rtsx_dev *) * num_sockets, GFP_KERNEL);
+	if (adapter) {
+		adapter->dev.class = &rtsx_adapter_class;
+		adapter->dev.parent = dev;
+		device_initialize(&adapter->dev);
+		spin_lock_init(&adapter->lock);
+		adapter->num_sockets = num_sockets;
+	}
+	return adapter;
+}
+EXPORT_SYMBOL(rtsx_alloc_adapter);
+
+int rtsx_add_adapter(struct rtsx_adapter *adapter)
+{
+	int rc;
+
+	if (!idr_pre_get(&rtsx_adapter_idr, GFP_KERNEL))
+		return -ENOMEM;
+
+	spin_lock(&rtsx_adapter_lock);
+	rc = idr_get_new(&rtsx_adapter_idr, adapter, &adapter->id);
+	spin_unlock(&rtsx_adapter_lock);
+	if (rc)
+		return rc;
+
+	dev_set_name(&adapter->dev, "rtsx%u", adapter->id);
+	rc = device_add(&adapter->dev);
+	if (rc) {
+		spin_lock(&rtsx_adapter_lock);
+		idr_remove(&rtsx_adapter_idr, adapter->id);
+		spin_unlock(&rtsx_adapter_lock);
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(rtsx_add_adapter);
+
+void rtsx_remove_adapter(struct rtsx_adapter *adapter)
+{
+	unsigned int cnt;
+
+	flush_workqueue(workqueue);
+	for (cnt = 0; cnt < adapter->num_sockets; ++cnt) {
+		if (adapter->sockets[cnt])
+			device_unregister(&adapter->sockets[cnt]->dev);
+	}
+
+	spin_lock(&rtsx_adapter_lock);
+	idr_remove(&rtsx_adapter_idr, adapter->id);
+	spin_unlock(&rtsx_adapter_lock);
+	device_del(&adapter->dev);
+}
+EXPORT_SYMBOL(rtsx_remove_adapter);
+
+void rtsx_free_adapter(struct rtsx_adapter *adapter)
+{
+	put_device(&adapter->dev);
+}
+EXPORT_SYMBOL(rtsx_free_adapter);
+
+void rtsx_free_device(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	kfree(sock);
+}
+EXPORT_SYMBOL(rtsx_free_device);
+
+struct rtsx_dev *rtsx_alloc_device(struct rtsx_adapter *adapter,
+		unsigned int id, unsigned char type)
+{
+	struct rtsx_dev *sock = NULL;
+
+	if (!rtsx_media_type_name(type, 0))
+		return sock;
+
+	sock = kzalloc(sizeof(struct rtsx_dev), GFP_KERNEL);
+	if (sock) {
+		spin_lock_init(&sock->lock);
+		sock->type = type;
+		sock->socket_id = id;
+		sock->card_event = rtsx_dummy_event;
+		sock->data_event = rtsx_dummy_event;
+
+		sock->dev.parent = &(adapter->dev);
+		sock->dev.bus = &rtsx_bus_type;
+		sock->dev.dma_mask = adapter->dev.parent->dma_mask;
+		sock->dev.release = rtsx_free_device;
+
+		dev_set_name(&sock->dev, "rtsx_%s%u:%u",
+			     rtsx_media_type_name(type, 2), adapter->id, id);
+		pr_info(DRIVER_NAME
+		       ": %s card detected in socket %u:%u\n",
+		       rtsx_media_type_name(type, 0), adapter->id, id);
+	}
+	return sock;
+}
+EXPORT_SYMBOL(rtsx_alloc_device);
+
+void rtsx_queue_work(struct work_struct *work)
+{
+	queue_work(workqueue, work);
+}
+EXPORT_SYMBOL(rtsx_queue_work);
+
+void rtsx_queue_delayed_work(struct delayed_work *dwork, unsigned long delay)
+{
+	queue_delayed_work(workqueue, dwork, delay);
+}
+EXPORT_SYMBOL(rtsx_queue_delayed_work);
+
+int rtsx_register_driver(struct rtsx_driver *drv)
+{
+	drv->driver.bus = &rtsx_bus_type;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(rtsx_register_driver);
+
+void rtsx_unregister_driver(struct rtsx_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(rtsx_unregister_driver);
+
+void rtsx_switch_clock(struct rtsx_dev *sock, unsigned int card_clock,
+		u8 ssc_depth, int double_clk, int vpclk)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->switch_clock)
+		adapter->switch_clock(adapter,
+				card_clock, ssc_depth, double_clk, vpclk);
+}
+EXPORT_SYMBOL(rtsx_switch_clock);
+
+void rtsx_complete_unfinished_transfer(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->complete_unfinished_transfer)
+		adapter->complete_unfinished_transfer(adapter);
+}
+EXPORT_SYMBOL(rtsx_complete_unfinished_transfer);
+
+void rtsx_sdmmc_set_bus_width(struct rtsx_dev *sock, unsigned char bus_width)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_set_bus_width)
+		adapter->sdmmc_ops.sdmmc_set_bus_width(adapter, bus_width);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_set_bus_width);
+
+void rtsx_sdmmc_set_power_mode(struct rtsx_dev *sock, unsigned char power_mode)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_set_power_mode)
+		adapter->sdmmc_ops.sdmmc_set_power_mode(adapter, power_mode);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_set_power_mode);
+
+void rtsx_sdmmc_set_timing(struct rtsx_dev *sock, unsigned char timing)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_set_timing)
+		adapter->sdmmc_ops.sdmmc_set_timing(adapter, timing);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_set_timing);
+
+int rtsx_sdmmc_switch_voltage(struct rtsx_dev *sock, unsigned char voltage)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_switch_voltage)
+		return adapter->sdmmc_ops.sdmmc_switch_voltage(adapter,
+				voltage);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_switch_voltage);
+
+int rtsx_sdmmc_get_ro(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_get_ro)
+		return adapter->sdmmc_ops.sdmmc_get_ro(adapter);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_get_ro);
+
+int rtsx_sdmmc_get_cd(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_get_cd)
+		return adapter->sdmmc_ops.sdmmc_get_cd(adapter);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_get_cd);
+
+int rtsx_sdmmc_execute_tuning(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_execute_tuning)
+		return adapter->sdmmc_ops.sdmmc_execute_tuning(adapter);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_execute_tuning);
+
+int rtsx_sdmmc_send_cmd_get_rsp(struct rtsx_dev *sock, u8 cmd_idx,
+		u32 arg, unsigned int resp_type, u32 *resp)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_send_cmd_get_rsp)
+		return adapter->sdmmc_ops.sdmmc_send_cmd_get_rsp(adapter,
+				cmd_idx, arg, resp_type, resp);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_send_cmd_get_rsp);
+
+int rtsx_sdmmc_read_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_read_data)
+		return adapter->sdmmc_ops.sdmmc_read_data(adapter, cmd,
+				byte_cnt, buf, buf_len, timeout);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_read_data);
+
+int rtsx_sdmmc_write_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_write_data)
+		return adapter->sdmmc_ops.sdmmc_write_data(adapter, cmd,
+				byte_cnt, buf, buf_len, timeout);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_write_data);
+
+int rtsx_sdmmc_rw_multi(struct rtsx_dev *sock, void *buf, unsigned int blksz,
+		unsigned int blocks, unsigned int use_sg, int read, int uhs)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_rw_multi)
+		return adapter->sdmmc_ops.sdmmc_rw_multi(adapter, buf, blksz,
+				blocks, use_sg, read, uhs);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_rw_multi);
+
+static int __init rtsx_core_init(void)
+{
+	int rc;
+
+	workqueue = create_freezable_workqueue("rtsx_wq");
+	if (!workqueue)
+		return -ENOMEM;
+
+	rc = bus_register(&rtsx_bus_type);
+	if (rc)
+		return rc;
+
+	rc = class_register(&rtsx_adapter_class);
+	if (rc)
+		return rc;
+
+	return rc;
+}
+
+static void __exit rtsx_core_exit(void)
+{
+	class_unregister(&rtsx_adapter_class);
+	bus_unregister(&rtsx_bus_type);
+	destroy_workqueue(workqueue);
+}
+
+module_init(rtsx_core_init);
+module_exit(rtsx_core_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Realtek Corp.");
+MODULE_DESCRIPTION("Realtek Card Reader Core Driver");
diff --git a/include/linux/rtsx_core.h b/include/linux/rtsx_core.h
new file mode 100644
index 0000000..7cfbc66
--- /dev/null
+++ b/include/linux/rtsx_core.h
@@ -0,0 +1,183 @@
+/* Realtek card reader core 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
+ */
+
+#ifndef __RTSX_CORE_H
+#define __RTSX_CORE_H
+
+#include <linux/pci.h>
+
+#define RTSX_TYPE_XD			1
+#define RTSX_TYPE_MS			2
+#define RTSX_TYPE_SD			3
+
+#define RTSX_SSC_DEPTH_4M		0x01
+#define RTSX_SSC_DEPTH_2M		0x02
+#define RTSX_SSC_DEPTH_1M		0x03
+#define RTSX_SSC_DEPTH_500K		0x04
+#define RTSX_SSC_DEPTH_250K		0x05
+
+#define wait_timeout_x(task_state, msecs)			\
+do {								\
+	set_current_state((task_state));			\
+	schedule_timeout(msecs_to_jiffies(msecs));		\
+} while (0)
+
+#define wait_timeout(msecs)	wait_timeout_x(TASK_INTERRUPTIBLE, (msecs))
+
+#define GET_BE32(ptr)	(((u32)((ptr)[0]) << 24) | ((u32)((ptr)[1]) << 16) | \
+				((u32)((ptr)[2]) << 8) | (ptr)[3])
+
+struct rtsx_device_id {
+	unsigned char type;
+};
+
+struct rtsx_dev {
+	char __iomem  *addr;
+	spinlock_t    lock;
+	unsigned char type;
+	unsigned int  socket_id;
+
+	void          (*card_event)(struct rtsx_dev *sock);
+	void          (*data_event)(struct rtsx_dev *sock);
+
+	struct device dev;
+};
+
+struct rtsx_driver {
+	struct rtsx_device_id *id_table;
+	int                   (*probe)(struct rtsx_dev *dev);
+	void                  (*remove)(struct rtsx_dev *dev);
+	int                   (*suspend)(struct rtsx_dev *dev,
+					 pm_message_t state);
+	int                   (*resume)(struct rtsx_dev *dev);
+
+	struct device_driver  driver;
+};
+
+struct rtsx_adapter;
+struct rtsx_sdmmc_ops {
+	int                (*sdmmc_set_bus_width)(
+				struct rtsx_adapter *adapter,
+				unsigned char bus_width);
+	int                (*sdmmc_set_power_mode)(
+				struct rtsx_adapter *adapter,
+				unsigned char power_mode);
+	int                (*sdmmc_set_timing)(struct rtsx_adapter *adapter,
+				unsigned char timing);
+	int                 (*sdmmc_switch_voltage)(
+				struct rtsx_adapter *adapter,
+				unsigned char signal_voltage);
+	int                (*sdmmc_get_ro)(struct rtsx_adapter *adapter);
+	int                (*sdmmc_get_cd)(struct rtsx_adapter *adapter);
+	int                (*sdmmc_execute_tuning)(
+				struct rtsx_adapter *adapter);
+	int                (*sdmmc_send_cmd_get_rsp)(
+				struct rtsx_adapter *adapter, u8 cmd_idx,
+				u32 arg, unsigned int resp_type, u32 *resp);
+	int                (*sdmmc_read_data)(struct rtsx_adapter *adapter,
+				u8 *cmd, u16 byte_cnt, u8 *buf,
+				int buf_len, int timeout);
+	int                (*sdmmc_write_data)(struct rtsx_adapter *adapter,
+				u8 *cmd, u16 byte_cnt, u8 *buf,
+				int buf_len, int timeout);
+	int                (*sdmmc_rw_multi)(struct rtsx_adapter *adapter,
+				void *buf, unsigned int blksz,
+				unsigned int blocks, unsigned int use_sg,
+				int read, int uhs);
+};
+
+#define EXTRA_CAPS_SD_SDR50		(1 << 0)
+#define EXTRA_CAPS_SD_SDR104		(1 << 1)
+#define EXTRA_CAPS_SD_DDR50		(1 << 2)
+#define EXTRA_CAPS_MMC_HSDDR		(1 << 3)
+#define EXTRA_CAPS_MMC_HS200		(1 << 4)
+#define EXTRA_CAPS_MMC_8BIT		(1 << 5)
+
+struct rtsx_adapter {
+	spinlock_t                lock;
+	unsigned int              id;
+	unsigned int              num_sockets;
+	u32                       extra_caps;
+
+	struct device	          dev;
+
+	int                       (*switch_clock)(struct rtsx_adapter *adapter,
+					unsigned int card_clock, u8 ssc_depth,
+					int double_clk, int vpclk);
+	void                      (*complete_unfinished_transfer)(
+					struct rtsx_adapter *adapter);
+
+	struct rtsx_sdmmc_ops     sdmmc_ops;
+
+	struct rtsx_dev           *sockets[0];
+};
+
+static inline struct rtsx_adapter *sock_to_adapter(struct rtsx_dev *sock)
+{
+	return container_of(sock->dev.parent, struct rtsx_adapter, dev);
+}
+
+struct rtsx_adapter *rtsx_alloc_adapter(unsigned int num_sockets,
+					struct device *dev);
+int rtsx_add_adapter(struct rtsx_adapter *adapter);
+void rtsx_remove_adapter(struct rtsx_adapter *adapter);
+void rtsx_free_adapter(struct rtsx_adapter *adapter);
+void rtsx_free_device(struct device *dev);
+struct rtsx_dev *rtsx_alloc_device(struct rtsx_adapter *adapter,
+		unsigned int id, unsigned char type);
+void rtsx_queue_work(struct work_struct *work);
+void rtsx_queue_delayed_work(struct delayed_work *dwork, unsigned long delay);
+int rtsx_register_driver(struct rtsx_driver *drv);
+void rtsx_unregister_driver(struct rtsx_driver *drv);
+
+void rtsx_switch_clock(struct rtsx_dev *sock, unsigned int card_clock,
+		u8 ssc_depth, int double_clk, int vpclk);
+void rtsx_complete_unfinished_transfer(struct rtsx_dev *sock);
+
+void rtsx_sdmmc_set_bus_width(struct rtsx_dev *sock, unsigned char bus_width);
+void rtsx_sdmmc_set_power_mode(struct rtsx_dev *sock, unsigned char power_mode);
+void rtsx_sdmmc_set_timing(struct rtsx_dev *sock, unsigned char timing);
+int rtsx_sdmmc_switch_voltage(struct rtsx_dev *sock, unsigned char voltage);
+int rtsx_sdmmc_get_ro(struct rtsx_dev *sock);
+int rtsx_sdmmc_get_cd(struct rtsx_dev *sock);
+int rtsx_sdmmc_execute_tuning(struct rtsx_dev *sock);
+int rtsx_sdmmc_send_cmd_get_rsp(struct rtsx_dev *sock, u8 cmd_idx,
+		u32 arg, unsigned int resp_type, u32 *resp);
+int rtsx_sdmmc_read_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout);
+int rtsx_sdmmc_write_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout);
+int rtsx_sdmmc_rw_multi(struct rtsx_dev *sock, void *buf, unsigned int blksz,
+		unsigned int blocks, unsigned int use_sg, int read, int uhs);
+
+static inline void *rtsx_get_drvdata(struct rtsx_dev *dev)
+{
+	return dev_get_drvdata(&dev->dev);
+}
+
+static inline void rtsx_set_drvdata(struct rtsx_dev *dev, void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+#endif
+
-- 
1.7.9.5


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

* Re: [PATCH 1/3] drivers/misc: Add realtek card reader core driver
  2012-07-23  9:42 [PATCH 1/3] drivers/misc: Add realtek card reader core driver wei_wang
@ 2012-07-23 11:17 ` Dan Carpenter
  2012-07-26  3:11   ` wwang
  2012-07-23 11:24 ` [PATCH 1/3] drivers/misc: Add " Borislav Petkov
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 26+ messages in thread
From: Dan Carpenter @ 2012-07-23 11:17 UTC (permalink / raw)
  To: wei_wang; +Cc: gregkh, devel, linux-kernel

On Mon, Jul 23, 2012 at 05:42:38PM +0800, wei_wang@realsil.com.cn wrote:
> +
> +#define wait_timeout_x(task_state, msecs)			\
> +do {								\
> +	set_current_state((task_state));			\
> +	schedule_timeout(msecs_to_jiffies(msecs));		\
> +} while (0)
> +
> +#define wait_timeout(msecs)	wait_timeout_x(TASK_INTERRUPTIBLE, (msecs))
> +
> +#define GET_BE32(ptr)	(((u32)((ptr)[0]) << 24) | ((u32)((ptr)[1]) << 16) | \
> +				((u32)((ptr)[2]) << 8) | (ptr)[3])
> +

This will break on big endian systems.  Use be32_to_cpu().

Use Sparse to check your work:
http://lwn.net/Articles/205624/

regards,
dan carpenter

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

* Re: [PATCH 1/3] drivers/misc: Add realtek card reader core driver
  2012-07-23  9:42 [PATCH 1/3] drivers/misc: Add realtek card reader core driver wei_wang
  2012-07-23 11:17 ` Dan Carpenter
@ 2012-07-23 11:24 ` Borislav Petkov
  2012-07-23 15:14 ` Matthew Garrett
  2012-07-23 16:33 ` Borislav Petkov
  3 siblings, 0 replies; 26+ messages in thread
From: Borislav Petkov @ 2012-07-23 11:24 UTC (permalink / raw)
  To: wei_wang; +Cc: gregkh, devel, linux-kernel

On Mon, Jul 23, 2012 at 05:42:38PM +0800, wei_wang@realsil.com.cn wrote:
> From: Wei WANG <wei_wang@realsil.com.cn>
> 
> Realtek card reader core driver is the bus driver for Realtek
> driver-based card reader, which supplies adapter layer to
> be used by lower-level pci/usb card reader and upper-level
> sdmmc/memstick host driver.
> 
> Signed-off-by: Wei WANG <wei_wang@realsil.com.cn>

Applying: drivers/misc: Add realtek card reader core driver
/home/boris/kernel/linux-2.6/.git/rebase-apply/patch:128: new blank line at EOF.
+
/home/boris/kernel/linux-2.6/.git/rebase-apply/patch:822: new blank line at EOF.
+
warning: 2 lines add whitespace errors.

-- 
Regards/Gruss,
Boris.

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

* Re: [PATCH 1/3] drivers/misc: Add realtek card reader core driver
  2012-07-23  9:42 [PATCH 1/3] drivers/misc: Add realtek card reader core driver wei_wang
  2012-07-23 11:17 ` Dan Carpenter
  2012-07-23 11:24 ` [PATCH 1/3] drivers/misc: Add " Borislav Petkov
@ 2012-07-23 15:14 ` Matthew Garrett
  2012-07-23 16:33 ` Borislav Petkov
  3 siblings, 0 replies; 26+ messages in thread
From: Matthew Garrett @ 2012-07-23 15:14 UTC (permalink / raw)
  To: wei_wang; +Cc: gregkh, devel, linux-kernel

On Mon, Jul 23, 2012 at 05:42:38PM +0800, wei_wang@realsil.com.cn wrote:
> +SUBSYSTEM=="rtsx_cr", ENV{RTSX_CARD_TYPE}=="SD", RUN+="/sbin/modprobe -bv rtsx_sdmmc"

Ugh. Expose a modalias instead? I know tifm does it like this, but 
that's probably also an argument for fixing tifm.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

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

* Re: [PATCH 1/3] drivers/misc: Add realtek card reader core driver
  2012-07-23  9:42 [PATCH 1/3] drivers/misc: Add realtek card reader core driver wei_wang
                   ` (2 preceding siblings ...)
  2012-07-23 15:14 ` Matthew Garrett
@ 2012-07-23 16:33 ` Borislav Petkov
  2012-07-24  1:21   ` wwang
  3 siblings, 1 reply; 26+ messages in thread
From: Borislav Petkov @ 2012-07-23 16:33 UTC (permalink / raw)
  To: wei_wang; +Cc: gregkh, devel, linux-kernel

On Mon, Jul 23, 2012 at 05:42:38PM +0800, wei_wang@realsil.com.cn wrote:
> From: Wei WANG <wei_wang@realsil.com.cn>
> 
> Realtek card reader core driver is the bus driver for Realtek
> driver-based card reader, which supplies adapter layer to
> be used by lower-level pci/usb card reader and upper-level
> sdmmc/memstick host driver.
> 
> Signed-off-by: Wei WANG <wei_wang@realsil.com.cn>
> ---
>  Documentation/misc-devices/realtek_cr.txt |   27 ++
>  drivers/misc/Kconfig                      |    1 +
>  drivers/misc/Makefile                     |    1 +
>  drivers/misc/realtek_cr/Kconfig           |   26 ++
>  drivers/misc/realtek_cr/Makefile          |    7 +
>  drivers/misc/realtek_cr/core/Kconfig      |    6 +
>  drivers/misc/realtek_cr/core/Makefile     |    1 +
>  drivers/misc/realtek_cr/core/rtsx_core.c  |  492 +++++++++++++++++++++++++++++
>  include/linux/rtsx_core.h                 |  183 +++++++++++
>  9 files changed, 744 insertions(+)
>  create mode 100644 Documentation/misc-devices/realtek_cr.txt
>  create mode 100644 drivers/misc/realtek_cr/Kconfig
>  create mode 100644 drivers/misc/realtek_cr/Makefile
>  create mode 100644 drivers/misc/realtek_cr/core/Kconfig
>  create mode 100644 drivers/misc/realtek_cr/core/Makefile
>  create mode 100644 drivers/misc/realtek_cr/core/rtsx_core.c
>  create mode 100644 include/linux/rtsx_core.h
> 
> diff --git a/Documentation/misc-devices/realtek_cr.txt b/Documentation/misc-devices/realtek_cr.txt
> new file mode 100644
> index 0000000..b4e6fbe
> --- /dev/null
> +++ b/Documentation/misc-devices/realtek_cr.txt
> @@ -0,0 +1,27 @@
> +Realtek Driver-based Card Reader
> +================================
> +
> +Supported chips:
> +RTS5209
> +RTS5229
> +
> +Contact Email:
> +pc_sw_linux@realsil.com.cn
> +
> +
> +Description
> +-----------
> +
> +Realtek driver-based card reader supports access to many types of memory cards,
> +such as Memory Stick, Memory Stick Pro, Secure Digital and MultiMediaCard.
> +
> +
> +udev rules
> +----------
> +
> +In order to modprobe Realtek SD/MMC interface driver automatically, the following rule
> +should be added to the udev rules file:
> +
> +SUBSYSTEM=="rtsx_cr", ENV{RTSX_CARD_TYPE}=="SD", RUN+="/sbin/modprobe -bv rtsx_sdmmc"
> +
> +Typically, we may edit /lib/udev/rules.d/80-drivers.rules and copy the rule into it in Ubuntu.
> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> index 2661f6e..09ce905 100644
> --- a/drivers/misc/Kconfig
> +++ b/drivers/misc/Kconfig
> @@ -517,4 +517,5 @@ source "drivers/misc/lis3lv02d/Kconfig"
>  source "drivers/misc/carma/Kconfig"
>  source "drivers/misc/altera-stapl/Kconfig"
>  source "drivers/misc/mei/Kconfig"
> +source "drivers/misc/realtek_cr/Kconfig"
>  endmenu
> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> index 456972f..c09f147 100644
> --- a/drivers/misc/Makefile
> +++ b/drivers/misc/Makefile
> @@ -51,3 +51,4 @@ obj-y				+= carma/
>  obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
>  obj-$(CONFIG_ALTERA_STAPL)	+=altera-stapl/
>  obj-$(CONFIG_INTEL_MEI)		+= mei/
> +obj-$(CONFIG_REALTEK_CR_SUPPORT) += realtek_cr/
> diff --git a/drivers/misc/realtek_cr/Kconfig b/drivers/misc/realtek_cr/Kconfig
> new file mode 100644
> index 0000000..303d98a
> --- /dev/null
> +++ b/drivers/misc/realtek_cr/Kconfig
> @@ -0,0 +1,26 @@
> +#
> +# Realtek driver-based card reader
> +#
> +
> +menuconfig REALTEK_CR_SUPPORT
> +	tristate "Realtek driver-based card reader"
> +	help
> +	  Realtek driver-based card reader supports access to many types of
> +	  memory cards, such as Memory Stick, Memory Stick Pro, Secure Digital
> +	  and MultiMediaCard.
> +
> +	  If you want to use Realtek driver-based card reader, enable this
> +	  option and other options below.
> +
> +config REALTEK_CR_DEBUG
> +	bool "Realtek driver-based card reader debugging"
> +	depends on REALTEK_CR_SUPPORT != n
> +	help
> +	  This is an option for use by developers; most people should
> +	  say N here.  This enables Realtek card reader driver debugging.
> +
> +if REALTEK_CR_SUPPORT
> +
> +source "drivers/misc/realtek_cr/core/Kconfig"
> +
> +endif

Ok, maybe I'm a newbie here but this is a card reader driver and AFAICT
it should be placed under CONFIG_MMC. Why is it under drivers/misc?

-- 
Regards/Gruss,
Boris.

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

* Re: [PATCH 1/3] drivers/misc: Add realtek card reader core driver
  2012-07-23 16:33 ` Borislav Petkov
@ 2012-07-24  1:21   ` wwang
  2012-07-24  6:44     ` Borislav Petkov
  0 siblings, 1 reply; 26+ messages in thread
From: wwang @ 2012-07-24  1:21 UTC (permalink / raw)
  To: Borislav Petkov, gregkh, devel, linux-kernel

Hi Borislav:

Realtek card reader supports not only SDMMC card, but also Memory stick. 
This part is the common code, so it is located in drivers/misc. There is 
also SDMMC-relevant code under CONFIG_MMC. And in the future, 
Memstick-relevant code will be added under CONFIG_MEMSTICK.

BR,
wwang

于 2012年07月24日 00:33, Borislav Petkov 写道:
> On Mon, Jul 23, 2012 at 05:42:38PM +0800, wei_wang@realsil.com.cn wrote:
>> From: Wei WANG <wei_wang@realsil.com.cn>
>>
>> Realtek card reader core driver is the bus driver for Realtek
>> driver-based card reader, which supplies adapter layer to
>> be used by lower-level pci/usb card reader and upper-level
>> sdmmc/memstick host driver.
>>
>> Signed-off-by: Wei WANG <wei_wang@realsil.com.cn>
>> ---
>>   Documentation/misc-devices/realtek_cr.txt |   27 ++
>>   drivers/misc/Kconfig                      |    1 +
>>   drivers/misc/Makefile                     |    1 +
>>   drivers/misc/realtek_cr/Kconfig           |   26 ++
>>   drivers/misc/realtek_cr/Makefile          |    7 +
>>   drivers/misc/realtek_cr/core/Kconfig      |    6 +
>>   drivers/misc/realtek_cr/core/Makefile     |    1 +
>>   drivers/misc/realtek_cr/core/rtsx_core.c  |  492 +++++++++++++++++++++++++++++
>>   include/linux/rtsx_core.h                 |  183 +++++++++++
>>   9 files changed, 744 insertions(+)
>>   create mode 100644 Documentation/misc-devices/realtek_cr.txt
>>   create mode 100644 drivers/misc/realtek_cr/Kconfig
>>   create mode 100644 drivers/misc/realtek_cr/Makefile
>>   create mode 100644 drivers/misc/realtek_cr/core/Kconfig
>>   create mode 100644 drivers/misc/realtek_cr/core/Makefile
>>   create mode 100644 drivers/misc/realtek_cr/core/rtsx_core.c
>>   create mode 100644 include/linux/rtsx_core.h
>>
>> diff --git a/Documentation/misc-devices/realtek_cr.txt b/Documentation/misc-devices/realtek_cr.txt
>> new file mode 100644
>> index 0000000..b4e6fbe
>> --- /dev/null
>> +++ b/Documentation/misc-devices/realtek_cr.txt
>> @@ -0,0 +1,27 @@
>> +Realtek Driver-based Card Reader
>> +================================
>> +
>> +Supported chips:
>> +RTS5209
>> +RTS5229
>> +
>> +Contact Email:
>> +pc_sw_linux@realsil.com.cn
>> +
>> +
>> +Description
>> +-----------
>> +
>> +Realtek driver-based card reader supports access to many types of memory cards,
>> +such as Memory Stick, Memory Stick Pro, Secure Digital and MultiMediaCard.
>> +
>> +
>> +udev rules
>> +----------
>> +
>> +In order to modprobe Realtek SD/MMC interface driver automatically, the following rule
>> +should be added to the udev rules file:
>> +
>> +SUBSYSTEM=="rtsx_cr", ENV{RTSX_CARD_TYPE}=="SD", RUN+="/sbin/modprobe -bv rtsx_sdmmc"
>> +
>> +Typically, we may edit /lib/udev/rules.d/80-drivers.rules and copy the rule into it in Ubuntu.
>> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
>> index 2661f6e..09ce905 100644
>> --- a/drivers/misc/Kconfig
>> +++ b/drivers/misc/Kconfig
>> @@ -517,4 +517,5 @@ source "drivers/misc/lis3lv02d/Kconfig"
>>   source "drivers/misc/carma/Kconfig"
>>   source "drivers/misc/altera-stapl/Kconfig"
>>   source "drivers/misc/mei/Kconfig"
>> +source "drivers/misc/realtek_cr/Kconfig"
>>   endmenu
>> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
>> index 456972f..c09f147 100644
>> --- a/drivers/misc/Makefile
>> +++ b/drivers/misc/Makefile
>> @@ -51,3 +51,4 @@ obj-y				+= carma/
>>   obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
>>   obj-$(CONFIG_ALTERA_STAPL)	+=altera-stapl/
>>   obj-$(CONFIG_INTEL_MEI)		+= mei/
>> +obj-$(CONFIG_REALTEK_CR_SUPPORT) += realtek_cr/
>> diff --git a/drivers/misc/realtek_cr/Kconfig b/drivers/misc/realtek_cr/Kconfig
>> new file mode 100644
>> index 0000000..303d98a
>> --- /dev/null
>> +++ b/drivers/misc/realtek_cr/Kconfig
>> @@ -0,0 +1,26 @@
>> +#
>> +# Realtek driver-based card reader
>> +#
>> +
>> +menuconfig REALTEK_CR_SUPPORT
>> +	tristate "Realtek driver-based card reader"
>> +	help
>> +	  Realtek driver-based card reader supports access to many types of
>> +	  memory cards, such as Memory Stick, Memory Stick Pro, Secure Digital
>> +	  and MultiMediaCard.
>> +
>> +	  If you want to use Realtek driver-based card reader, enable this
>> +	  option and other options below.
>> +
>> +config REALTEK_CR_DEBUG
>> +	bool "Realtek driver-based card reader debugging"
>> +	depends on REALTEK_CR_SUPPORT != n
>> +	help
>> +	  This is an option for use by developers; most people should
>> +	  say N here.  This enables Realtek card reader driver debugging.
>> +
>> +if REALTEK_CR_SUPPORT
>> +
>> +source "drivers/misc/realtek_cr/core/Kconfig"
>> +
>> +endif
> Ok, maybe I'm a newbie here but this is a card reader driver and AFAICT
> it should be placed under CONFIG_MMC. Why is it under drivers/misc?
>


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

* Re: [PATCH 1/3] drivers/misc: Add realtek card reader core driver
  2012-07-24  1:21   ` wwang
@ 2012-07-24  6:44     ` Borislav Petkov
  2012-07-24  8:47       ` wwang
  0 siblings, 1 reply; 26+ messages in thread
From: Borislav Petkov @ 2012-07-24  6:44 UTC (permalink / raw)
  To: wwang; +Cc: gregkh, devel, linux-kernel

On Tue, Jul 24, 2012 at 09:21:34AM +0800, wwang wrote:
> Hi Borislav:
> 
> Realtek card reader supports not only SDMMC card, but also Memory
> stick. This part is the common code, so it is located in
> drivers/misc. There is also SDMMC-relevant code under CONFIG_MMC.
> And in the future, Memstick-relevant code will be added under
> CONFIG_MEMSTICK.

Ok, thanks for explaining.

Btw, the SDMMC part rtsx_sdmmc.c doesn't build here:
http://marc.info/?l=linux-kernel&m=134306261405814&w=2

Thanks.

-- 
Regards/Gruss,
    Boris.

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

* Re: [PATCH 1/3] drivers/misc: Add realtek card reader core driver
  2012-07-24  6:44     ` Borislav Petkov
@ 2012-07-24  8:47       ` wwang
  2012-07-24 14:07         ` Borislav Petkov
  0 siblings, 1 reply; 26+ messages in thread
From: wwang @ 2012-07-24  8:47 UTC (permalink / raw)
  To: Borislav Petkov, gregkh, devel, linux-kernel

于 2012年07月24日 14:44, Borislav Petkov 写道:
> Ok, thanks for explaining.
>
> Btw, the SDMMC part rtsx_sdmmc.c doesn't build here:
> http://marc.info/?l=linux-kernel&m=134306261405814&w=2
>
> Thanks.
Hi Borislav:

This problem doesn't happen in my system. Can you tell me your kernel 
version and give me your kernel config?

BR,
wwang

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

* Re: [PATCH 1/3] drivers/misc: Add realtek card reader core driver
  2012-07-24  8:47       ` wwang
@ 2012-07-24 14:07         ` Borislav Petkov
       [not found]           ` <500F59D7.2050508@realsil.com.cn>
  0 siblings, 1 reply; 26+ messages in thread
From: Borislav Petkov @ 2012-07-24 14:07 UTC (permalink / raw)
  To: wwang; +Cc: gregkh, devel, linux-kernel

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

On Tue, Jul 24, 2012 at 04:47:34PM +0800, wwang wrote:
> This problem doesn't happen in my system. Can you tell me your kernel
> version and give me your kernel config?

Kernel is Linus' from yesterday: v3.5-709-ga6be1fcbc57f
and config is attached.

-- 
Regards/Gruss,
Boris.

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

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

* Re: [PATCH 1/3] drivers/misc: Add realtek card reader core driver
       [not found]           ` <500F59D7.2050508@realsil.com.cn>
@ 2012-07-25  8:04             ` Borislav Petkov
  2012-07-25 10:16               ` Aaron Lu
  0 siblings, 1 reply; 26+ messages in thread
From: Borislav Petkov @ 2012-07-25  8:04 UTC (permalink / raw)
  To: wwang; +Cc: gregkh, devel, linux-kernel, Aaron Lu, Philip Rakity, Chris Ball

On Wed, Jul 25, 2012 at 10:28:39AM +0800, wwang wrote:
> 于 2012年07月24日 22:07, Borislav Petkov 写道:
> >Kernel is Linus' from yesterday: v3.5-709-ga6be1fcbc57f and config
> >is attached.
> 
> The compilation process is still OK with your config.

Yes, but not with the kernel I'm using. If you'd tried that exact kernel
you would've seen that because...

> 
> MMC_CAP_MAX_CURRENT_200 is usually defined in
> include/linux/mmc/host.h, can you help to check it in your kernel
> code?

... MMC_CAP_MAX_CURRENT_* got removed by the patch at the end of this
note and which went in through the MMC tree in this merge window:

commit a6be1fcbc57f95bb47ef3c8e4ee3d83731b8f21e
Merge: 5b160bd42694 30b87c60e9cb
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Sun Jul 22 16:36:08 2012 -0700

    Merge tag 'mmc-merge-for-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
...

I'm adding some more people to Cc so that you can figure it all out
among yourselves how to deal with the situation :)

Thanks.

---

commit 55c4665ea0a42fd6427826bfce96eb4b0389262a
Author: Aaron Lu <aaron.lu@amd.com>
Date:   Wed Jul 4 13:31:48 2012 +0800

    mmc: sd: Fix sd current limit setting
    
    Host has different current capabilities at different voltages, we need
    to record these settings seperately. The defined voltages are 1.8/3.0/3.3.
    For other voltages, we do not touch current limit setting.
    
    Before we set the current limit for the sd card, find out the host's
    operating voltage first and then find out the current capabilities of
    the host at that voltage to set the current limit.
    
    Signed-off-by: Aaron Lu <aaron.lu@amd.com>
    Reviewed-by: Philip Rakity <prakity@marvell.com>
    Signed-off-by: Chris Ball <cjb@laptop.org>

diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 65c64ee578a7..f578a71d82a6 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -189,6 +189,9 @@ struct mmc_host {
        u32                     ocr_avail_sd;   /* SD-specific OCR */
        u32                     ocr_avail_mmc;  /* MMC-specific OCR */
        struct notifier_block   pm_notify;
+       u32                     max_current_330;
+       u32                     max_current_300;
+       u32                     max_current_180;
 
 #define MMC_VDD_165_195                0x00000080      /* VDD voltage 1.65 - 1.95 */
 #define MMC_VDD_20_21          0x00000100      /* VDD voltage 2.0 ~ 2.1 */
@@ -232,16 +235,9 @@ struct mmc_host {
 #define MMC_CAP_UHS_SDR50      (1 << 17)       /* Host supports UHS SDR50 mode */
 #define MMC_CAP_UHS_SDR104     (1 << 18)       /* Host supports UHS SDR104 mode */
 #define MMC_CAP_UHS_DDR50      (1 << 19)       /* Host supports UHS DDR50 mode */
-#define MMC_CAP_SET_XPC_330    (1 << 20)       /* Host supports >150mA current at 3.3V */
-#define MMC_CAP_SET_XPC_300    (1 << 21)       /* Host supports >150mA current at 3.0V */
-#define MMC_CAP_SET_XPC_180    (1 << 22)       /* Host supports >150mA current at 1.8V */
 #define MMC_CAP_DRIVER_TYPE_A  (1 << 23)       /* Host supports Driver Type A */
 #define MMC_CAP_DRIVER_TYPE_C  (1 << 24)       /* Host supports Driver Type C */
 #define MMC_CAP_DRIVER_TYPE_D  (1 << 25)       /* Host supports Driver Type D */
-#define MMC_CAP_MAX_CURRENT_200        (1 << 26)       /* Host max current limit is 200mA */
-#define MMC_CAP_MAX_CURRENT_400        (1 << 27)       /* Host max current limit is 400mA */
-#define MMC_CAP_MAX_CURRENT_600        (1 << 28)       /* Host max current limit is 600mA */
-#define MMC_CAP_MAX_CURRENT_800        (1 << 29)       /* Host max current limit is 800mA */
 #define MMC_CAP_CMD23          (1 << 30)       /* CMD23 supported. */
 #define MMC_CAP_HW_RESET       (1 << 31)       /* Hardware reset */

-- 
Regards/Gruss,
    Boris.

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

* Re: [PATCH 1/3] drivers/misc: Add realtek card reader core driver
  2012-07-25  8:04             ` Borislav Petkov
@ 2012-07-25 10:16               ` Aaron Lu
  2012-07-26  1:34                 ` wwang
  0 siblings, 1 reply; 26+ messages in thread
From: Aaron Lu @ 2012-07-25 10:16 UTC (permalink / raw)
  To: Borislav Petkov, wwang, gregkh, devel, linux-kernel,
	Philip Rakity, Chris Ball

Hi Wei,

We do not use MMC_CAP_MAX_CURRENT_* anymore, for your host, I would
sugget you do something like this:

static void realtek_init(struct realtek_sdmmc *host)
{
	struct mmc_host *mmc = host->mmc;
	... ...
	/*
	 * since you have set MMC_CAP_MAX_CURRENT_800 in your
	 * original code, I assume your host can provide more than
	 * 800mA, and you've set the 1.8v support bit in your original
	 * code for the host, so I also set the max_current_180 to 800mA.
	 */
	mmc->max_current_330 = 800;
	mmc->max_current_180 = 800;
	... ...
}

Does this look right to you?

Thanks,
Aaron

On Wed, Jul 25, 2012 at 10:04:15AM +0200, Borislav Petkov wrote:
> On Wed, Jul 25, 2012 at 10:28:39AM +0800, wwang wrote:
> > 于 2012年07月24日 22:07, Borislav Petkov 写道:
> > >Kernel is Linus' from yesterday: v3.5-709-ga6be1fcbc57f and config
> > >is attached.
> > 
> > The compilation process is still OK with your config.
> 
> Yes, but not with the kernel I'm using. If you'd tried that exact kernel
> you would've seen that because...
> 
> > 
> > MMC_CAP_MAX_CURRENT_200 is usually defined in
> > include/linux/mmc/host.h, can you help to check it in your kernel
> > code?
> 
> ... MMC_CAP_MAX_CURRENT_* got removed by the patch at the end of this
> note and which went in through the MMC tree in this merge window:
> 
> commit a6be1fcbc57f95bb47ef3c8e4ee3d83731b8f21e
> Merge: 5b160bd42694 30b87c60e9cb
> Author: Linus Torvalds <torvalds@linux-foundation.org>
> Date:   Sun Jul 22 16:36:08 2012 -0700
> 
>     Merge tag 'mmc-merge-for-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
> ...
> 
> I'm adding some more people to Cc so that you can figure it all out
> among yourselves how to deal with the situation :)
> 
> Thanks.
> 
> ---
> 
> commit 55c4665ea0a42fd6427826bfce96eb4b0389262a
> Author: Aaron Lu <aaron.lu@amd.com>
> Date:   Wed Jul 4 13:31:48 2012 +0800
> 
>     mmc: sd: Fix sd current limit setting
>     
>     Host has different current capabilities at different voltages, we need
>     to record these settings seperately. The defined voltages are 1.8/3.0/3.3.
>     For other voltages, we do not touch current limit setting.
>     
>     Before we set the current limit for the sd card, find out the host's
>     operating voltage first and then find out the current capabilities of
>     the host at that voltage to set the current limit.
>     
>     Signed-off-by: Aaron Lu <aaron.lu@amd.com>
>     Reviewed-by: Philip Rakity <prakity@marvell.com>
>     Signed-off-by: Chris Ball <cjb@laptop.org>
> 
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index 65c64ee578a7..f578a71d82a6 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -189,6 +189,9 @@ struct mmc_host {
>         u32                     ocr_avail_sd;   /* SD-specific OCR */
>         u32                     ocr_avail_mmc;  /* MMC-specific OCR */
>         struct notifier_block   pm_notify;
> +       u32                     max_current_330;
> +       u32                     max_current_300;
> +       u32                     max_current_180;
>  
>  #define MMC_VDD_165_195                0x00000080      /* VDD voltage 1.65 - 1.95 */
>  #define MMC_VDD_20_21          0x00000100      /* VDD voltage 2.0 ~ 2.1 */
> @@ -232,16 +235,9 @@ struct mmc_host {
>  #define MMC_CAP_UHS_SDR50      (1 << 17)       /* Host supports UHS SDR50 mode */
>  #define MMC_CAP_UHS_SDR104     (1 << 18)       /* Host supports UHS SDR104 mode */
>  #define MMC_CAP_UHS_DDR50      (1 << 19)       /* Host supports UHS DDR50 mode */
> -#define MMC_CAP_SET_XPC_330    (1 << 20)       /* Host supports >150mA current at 3.3V */
> -#define MMC_CAP_SET_XPC_300    (1 << 21)       /* Host supports >150mA current at 3.0V */
> -#define MMC_CAP_SET_XPC_180    (1 << 22)       /* Host supports >150mA current at 1.8V */
>  #define MMC_CAP_DRIVER_TYPE_A  (1 << 23)       /* Host supports Driver Type A */
>  #define MMC_CAP_DRIVER_TYPE_C  (1 << 24)       /* Host supports Driver Type C */
>  #define MMC_CAP_DRIVER_TYPE_D  (1 << 25)       /* Host supports Driver Type D */
> -#define MMC_CAP_MAX_CURRENT_200        (1 << 26)       /* Host max current limit is 200mA */
> -#define MMC_CAP_MAX_CURRENT_400        (1 << 27)       /* Host max current limit is 400mA */
> -#define MMC_CAP_MAX_CURRENT_600        (1 << 28)       /* Host max current limit is 600mA */
> -#define MMC_CAP_MAX_CURRENT_800        (1 << 29)       /* Host max current limit is 800mA */
>  #define MMC_CAP_CMD23          (1 << 30)       /* CMD23 supported. */
>  #define MMC_CAP_HW_RESET       (1 << 31)       /* Hardware reset */
> 
> -- 
> Regards/Gruss,
>     Boris.
> 


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

* Re: [PATCH 1/3] drivers/misc: Add realtek card reader core driver
  2012-07-25 10:16               ` Aaron Lu
@ 2012-07-26  1:34                 ` wwang
  2012-07-26  8:43                   ` Borislav Petkov
  0 siblings, 1 reply; 26+ messages in thread
From: wwang @ 2012-07-26  1:34 UTC (permalink / raw)
  To: Aaron Lu
  Cc: Borislav Petkov, gregkh, devel, linux-kernel, Philip Rakity, Chris Ball

Hi Aaron:

Thank you. I will modify our host per your suggestion.

BR,
wwang

于 2012年07月25日 18:16, Aaron Lu 写道:
> Hi Wei,
>
> We do not use MMC_CAP_MAX_CURRENT_* anymore, for your host, I would
> sugget you do something like this:
>
> static void realtek_init(struct realtek_sdmmc *host)
> {
> 	struct mmc_host *mmc = host->mmc;
> 	... ...
> 	/*
> 	 * since you have set MMC_CAP_MAX_CURRENT_800 in your
> 	 * original code, I assume your host can provide more than
> 	 * 800mA, and you've set the 1.8v support bit in your original
> 	 * code for the host, so I also set the max_current_180 to 800mA.
> 	 */
> 	mmc->max_current_330 = 800;
> 	mmc->max_current_180 = 800;
> 	... ...
> }
>
> Does this look right to you?
>
> Thanks,
> Aaron
>
>


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

* realtek card reader core driver
  2012-07-23 11:17 ` Dan Carpenter
@ 2012-07-26  3:11   ` wwang
  2012-07-26  3:59     ` gregkh
  0 siblings, 1 reply; 26+ messages in thread
From: wwang @ 2012-07-26  3:11 UTC (permalink / raw)
  To: Dan Carpenter; +Cc: gregkh, devel, linux-kernel

Hi Dan:

Sorry to bother you.
I can't find the maintainer of drivers/misc. So if I want to submit my
driver as misc device, who should I contact to send my patch?

Thank you very much. I really appreciate this help.

BR,
wwang

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

* Re: realtek card reader core driver
  2012-07-26  3:11   ` wwang
@ 2012-07-26  3:59     ` gregkh
  0 siblings, 0 replies; 26+ messages in thread
From: gregkh @ 2012-07-26  3:59 UTC (permalink / raw)
  To: wwang; +Cc: Dan Carpenter, linux-kernel, devel

On Thu, Jul 26, 2012 at 11:11:50AM +0800, wwang wrote:
> Hi Dan:
> 
> Sorry to bother you.
> I can't find the maintainer of drivers/misc. So if I want to submit my
> driver as misc device, who should I contact to send my patch?

>From the MAINTAINERS file:

CHAR and MISC DRIVERS
M:      Arnd Bergmann <arnd@arndb.de>
M:      Greg Kroah-Hartman <gregkh@linuxfoundation.org>
T:      git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git
S:      Supported
F:      drivers/char/*
F:      drivers/misc/*

But note, this is the merge window time, when patches are sent to Linus
that have been in linux-next prior to the opening of the window.  That
means I can't look at any new patches like this driver, until after
3.6-rc1 is out in a week or so.

So please be patient.

thanks,

greg k-h

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

* Re: [PATCH 1/3] drivers/misc: Add realtek card reader core driver
  2012-07-26  1:34                 ` wwang
@ 2012-07-26  8:43                   ` Borislav Petkov
  0 siblings, 0 replies; 26+ messages in thread
From: Borislav Petkov @ 2012-07-26  8:43 UTC (permalink / raw)
  To: wwang; +Cc: Aaron Lu, gregkh, devel, linux-kernel, Philip Rakity, Chris Ball

On Thu, Jul 26, 2012 at 09:34:19AM +0800, wwang wrote:
> Hi Aaron:
> 
> Thank you. I will modify our host per your suggestion.

Hi Wei,

please put me on CC for your next submission of the driver so that I can
give it a run.

Thanks.

-- 
Regards/Gruss,
    Boris.

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

* Re: [PATCH 1/3] drivers/misc: Add realtek card reader core driver
  2012-08-01 14:31     ` Arnd Bergmann
  2012-08-03  2:31       ` wwang
@ 2012-08-13 20:59       ` Maxim Levitsky
  1 sibling, 0 replies; 26+ messages in thread
From: Maxim Levitsky @ 2012-08-13 20:59 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: wwang, gregkh, devel, linux-kernel, linux-mmc, cjb, bp

On Wed, 2012-08-01 at 14:31 +0000, Arnd Bergmann wrote: 
> On Wednesday 01 August 2012, wwang wrote:
> > 于 2012年07月31日 19:23, Arnd Bergmann 写道:
> > >
> > > You posted the sdmmc host driver and the pci card reader driver.
> > > I assume that the USB card reader and the memstick host
> > > will also get posted at some point. Do you have a timeframe
> > > for those?
> > 
> > I will submit my memstick host driver around two months later, and
> > submit USB part at the end of this year.
> 
> ok.
> 
> > > The hardware seems to also support xd/smartmedia. Do you have
> > > plans to add those? I think we have some code in drivers/mfd
> > > that acts as an abstraction layer for these, given that the
> > > host has to do the wear leveling there too.
> > 
> > Yes, xD is still in our plan. But because the user population of xD/SM
> > is so small, we put the priority of writing xD host driver in a relevant
> > low level.
> > Maybe we will submit this driver in the next year.
> 
> Ok. When you get to that, I think you should use the code
> from drivers/mtd/nand/sm_common.c, but it's better to confirm
> that with the MTD maintainers first.
Just note that this file doesn't have much stuff as it turned out
that xD card readers and cards are just raw NAND.
Except that large xD cards, still use small block size, and somewhat
different IDs, thus I placed them in this file.

You just need to write a NAND driver exposing raw flash, and my sm_ftl
will take care of wear leveling and everything else.


If any questions, bug-reports, just notes, I glad to hear about this.
Best regards,
Maxim Levitsky


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

* Re: [PATCH 1/3] drivers/misc: Add realtek card reader core driver
  2012-08-03  2:31       ` wwang
@ 2012-08-03 14:39         ` Arnd Bergmann
  0 siblings, 0 replies; 26+ messages in thread
From: Arnd Bergmann @ 2012-08-03 14:39 UTC (permalink / raw)
  To: wwang; +Cc: gregkh, devel, linux-kernel, linux-mmc, cjb, bp

On Friday 03 August 2012, wwang wrote:
> I got your ideas. Bus driver depending on other modules is indeed a bad
> style.
> 
> In our situation, just take pci device for example, pci-common is the
> place to detect card plugged or unplugged, so pci-common is required to
> call and probe sdmmc-pci or memstick-pci. If not using bus driver, I
> need to fulfill my own mechanism, like callback functions and other
> internal data structures, to achieve this goal.

The MFD layer provides some helpers to create "platform" devices for the
child nodes. You can attach all the data you need for those in
the "platform_data" field, including callback pointers if necessary.

> But bus driver can
> easily finish this job. I still prefer to retain bus driver, but detach
> the adapter part from the original bus driver. So the bus driver will
> not depend on other modules any more, and don't need any further
> modification if adding new high-level drivers. I will also move all the
> mmc specific code to sdmmc-pci driver. pci-common only contains the
> generic code related to pci operations. Just like the below diagram:
> 
>    sdmmc-pci      sdmmc-usb        memstick-pci      memstick-usb
>      \   \           /   \             /    \           /   |
>       |   \         /     \           /      \         /    |
>       |    \       /       \___      /        \        |    |
>       \     \ ____/         ___|____/          \_______|    |
>        \     |             /   |                       |    /
>         \____|______      /    |____________      _____|___/
>              |      \    /                  \    /     |   
>              |     pci-common             usb-common   |
>              \              \              /           /
>               \              \            /           /
>                \_____________ \          /___________/
>                              \rtsx-slot-bus/
> 
> 
> And I plan to push sdmmc-pci and sdmmc-usb to drivers/mmc/host, push
> memstick-pci and memstick-usb to drivers/memstick/host, and push
> pci-common, usb-common and rtsx-slot-bus to drivers/mfd/realtek_cr.
> 
> I would like to receive your suggestions. Thank you.

This looks ok, yes. I still don't see the value in having your own
bus_type though, and I believe using a platform device will save
you a significant chunk of code while achieving the same.

In the diagram above, the pci-common and the usb-common modules
each know exactly what their child devices are, so there is little
value creating an device-agnostic abstraction layer beneath it.

Having a module for common stuff in the place of your "rtsx-slot-bus"
is ok to handle stuff like your "ring buffer" that would be needed
by all four devices on the top. But when you introduce infrastructure
like your own bus_type, you should always consider whether that
infrastructure actually does something that is at the same time
common to all of your hardware and different from everything else.
I suspect you will find that your bus_type provide something
specific to realtek card readers and that you can just as well use
the platform bus.

	Arnd

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

* Re: [PATCH 1/3] drivers/misc: Add realtek card reader core driver
  2012-08-01 14:31     ` Arnd Bergmann
@ 2012-08-03  2:31       ` wwang
  2012-08-03 14:39         ` Arnd Bergmann
  2012-08-13 20:59       ` Maxim Levitsky
  1 sibling, 1 reply; 26+ messages in thread
From: wwang @ 2012-08-03  2:31 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: gregkh, devel, linux-kernel, linux-mmc, cjb, bp

于 2012年08月01日 22:31, Arnd Bergmann 写道:
>
> I understand where you are coming from, but IMHO a bus driver would
> make more sense if the bus was a low-level abstraction that allows you
> to add new high-level drivers (memstick, smartmedia, ...) without
> having to modify the low-level drivers, which I believe is not possible
> with your current code.
>
> In reality, what you have seems to be actually more like
>
>     sdmmc    memstick
>         \       /
>          \     /
>           \   /
>      rtsx bus driver
>        /         \
>       /           \
>      /             \
>     /               \
>   rtsx-pci         rtsx-usb
>    /    \           /    \
>  pci-mmc \       usb-mmc  \
>    pci-memstick       usb-memstick
>
> The best driver abstractions have the most specific code as a top-level
> module that calls into more generic code.
>
> What I would suggest you do is to have the code that is common between
> the USB and PCI drivers in a loadable module that both of the other
> modules call into:
>
>
>    sdmmc-pci      sdmmc-usb        memstick-pci      memstick-usb
>      \   \           /   \             /    \           /   |
>       |   \         /     \           /      \         /    |
>       |    \       /       \___      /        \       /     |
>       \    sdmmc-common     ___|____/      memstick-common  |
>        \     |             /   |                       |    /
>         \____|______      /    |____________      _____|___/
>              |      \    /                  \    /     |   
>              |     pci-common             usb-common   |
>              \              \              /           /
>               \              \            /            /
>                \_____________ \          /____________/
>                              \rtsx-common/
>
>
> You can skip a few of these if they are not needed, but in principle
> you should get the idea. In this example, the pci-common and the
> usb-common drivers would each be MFD driver that export a bunch
> of slave devices. All the mmc specific code that you currently
> have in the pci driver would then go all the way to the top into
> the sdmmc-pci driver, and anything that is shared goes into one
> of the lower modules.
>
> 	Arnd

Hi Arnd:

I got your ideas. Bus driver depending on other modules is indeed a bad
style.

In our situation, just take pci device for example, pci-common is the
place to detect card plugged or unplugged, so pci-common is required to
call and probe sdmmc-pci or memstick-pci. If not using bus driver, I
need to fulfill my own mechanism, like callback functions and other
internal data structures, to achieve this goal. But bus driver can
easily finish this job. I still prefer to retain bus driver, but detach
the adapter part from the original bus driver. So the bus driver will
not depend on other modules any more, and don't need any further
modification if adding new high-level drivers. I will also move all the
mmc specific code to sdmmc-pci driver. pci-common only contains the
generic code related to pci operations. Just like the below diagram:

   sdmmc-pci      sdmmc-usb        memstick-pci      memstick-usb
     \   \           /   \             /    \           /   |
      |   \         /     \           /      \         /    |
      |    \       /       \___      /        \        |    |
      \     \ ____/         ___|____/          \_______|    |
       \     |             /   |                       |    /
        \____|______      /    |____________      _____|___/
             |      \    /                  \    /     |   
             |     pci-common             usb-common   |
             \              \              /           /
              \              \            /           /
               \_____________ \          /___________/
                             \rtsx-slot-bus/


And I plan to push sdmmc-pci and sdmmc-usb to drivers/mmc/host, push
memstick-pci and memstick-usb to drivers/memstick/host, and push
pci-common, usb-common and rtsx-slot-bus to drivers/mfd/realtek_cr.

I would like to receive your suggestions. Thank you.

Best regards,
wwang


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

* Re: [PATCH 1/3] drivers/misc: Add realtek card reader core driver
  2012-08-01  6:19   ` wwang
@ 2012-08-01 14:31     ` Arnd Bergmann
  2012-08-03  2:31       ` wwang
  2012-08-13 20:59       ` Maxim Levitsky
  0 siblings, 2 replies; 26+ messages in thread
From: Arnd Bergmann @ 2012-08-01 14:31 UTC (permalink / raw)
  To: wwang; +Cc: gregkh, devel, linux-kernel, linux-mmc, cjb, bp

On Wednesday 01 August 2012, wwang wrote:
> 于 2012年07月31日 19:23, Arnd Bergmann 写道:
> >
> > You posted the sdmmc host driver and the pci card reader driver.
> > I assume that the USB card reader and the memstick host
> > will also get posted at some point. Do you have a timeframe
> > for those?
> 
> I will submit my memstick host driver around two months later, and
> submit USB part at the end of this year.

ok.

> > The hardware seems to also support xd/smartmedia. Do you have
> > plans to add those? I think we have some code in drivers/mfd
> > that acts as an abstraction layer for these, given that the
> > host has to do the wear leveling there too.
> 
> Yes, xD is still in our plan. But because the user population of xD/SM
> is so small, we put the priority of writing xD host driver in a relevant
> low level.
> Maybe we will submit this driver in the next year.

Ok. When you get to that, I think you should use the code
from drivers/mtd/nand/sm_common.c, but it's better to confirm
that with the MTD maintainers first.

> > As usual for most things getting added to drivers/misc, I'm skeptical
> > about it actually fitting in here. Normally I'd put such a multiplexer
> > into drivers/mfd. Given that you actually need your own bus, rather
> > than just a single host with multiple endpoints, drivers/bus/realtek/cr
> > might be best.
> We do need a bus driver. We support multi models of card at the same
> time, so we need a way to bind all of the host drivers. And in the
> internal of our card reader, we have only one DMA engine and one ring
> buffer to handle massive data, so we also need a way to protect the
> critical area between different card hosts. Bus driver is convenient to
> handle this situation. Another way to fulfill is to call every register
> function of different host (like mmc, memstick) in sequence in card
> reader driver, whether pci-based or usb-based, if not using bus driver.
> I prefer the prior scheme, which is more flexible and less redundant code.

I understand where you are coming from, but IMHO a bus driver would
make more sense if the bus was a low-level abstraction that allows you
to add new high-level drivers (memstick, smartmedia, ...) without
having to modify the low-level drivers, which I believe is not possible
with your current code.

> Using bus driver:
> 
> sdmmc memstick
> \ /
> \ /
> \ /
> rtsx bus driver
> / \
> / \
> / \
> / \
> rtsx pci rtsx usb

In reality, what you have seems to be actually more like

    sdmmc    memstick
        \       /
         \     /
          \   /
     rtsx bus driver
       /         \
      /           \
     /             \
    /               \
  rtsx-pci         rtsx-usb
   /    \           /    \
 pci-mmc \       usb-mmc  \
   pci-memstick       usb-memstick

> Not using bus driver:
> 
> sdmmc-pci memstick-pci
> \ /
> \ /
> \ /
> \ /
> rtsx pci
> 
> sdmmc-usb memstick-usb
> \ /
> \ /
> \ /
> \ /
> rtsx usb
> 
> And you said we should put our own bus driver in drivers/bus/realtek/cr,
> but where is drivers/bus? Or can I just put this bus driver and our
> pci/usb card reader driver into drivers/mfd?

The best driver abstractions have the most specific code as a top-level
module that calls into more generic code.

What I would suggest you do is to have the code that is common between
the USB and PCI drivers in a loadable module that both of the other
modules call into:


   sdmmc-pci      sdmmc-usb        memstick-pci      memstick-usb
     \   \           /   \             /    \           /   |
      |   \         /     \           /      \         /    |
      |    \       /       \___      /        \       /     |
      \    sdmmc-common     ___|____/      memstick-common  |
       \     |             /   |                       |    /
        \____|______      /    |____________      _____|___/
             |      \    /                  \    /     |   
             |     pci-common             usb-common   |
             \              \              /           /
              \              \            /            /
               \_____________ \          /____________/
                             \rtsx-common/


You can skip a few of these if they are not needed, but in principle
you should get the idea. In this example, the pci-common and the
usb-common drivers would each be MFD driver that export a bunch
of slave devices. All the mmc specific code that you currently
have in the pci driver would then go all the way to the top into
the sdmmc-pci driver, and anything that is shared goes into one
of the lower modules.

	Arnd

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

* Re: [PATCH 1/3] drivers/misc: Add realtek card reader core driver
  2012-07-31 11:23 ` Arnd Bergmann
@ 2012-08-01  6:19   ` wwang
  2012-08-01 14:31     ` Arnd Bergmann
  0 siblings, 1 reply; 26+ messages in thread
From: wwang @ 2012-08-01  6:19 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: gregkh, devel, linux-kernel, linux-mmc, cjb, bp

于 2012年07月31日 19:23, Arnd Bergmann 写道:
>
> You posted the sdmmc host driver and the pci card reader driver.
> I assume that the USB card reader and the memstick host
> will also get posted at some point. Do you have a timeframe
> for those?

I will submit my memstick host driver around two months later, and
submit USB part at the end of this year.

>
> The hardware seems to also support xd/smartmedia. Do you have
> plans to add those? I think we have some code in drivers/mfd
> that acts as an abstraction layer for these, given that the
> host has to do the wear leveling there too.

Yes, xD is still in our plan. But because the user population of xD/SM
is so small, we put the priority of writing xD host driver in a relevant
low level.
Maybe we will submit this driver in the next year.

> As usual for most things getting added to drivers/misc, I'm skeptical
> about it actually fitting in here. Normally I'd put such a multiplexer
> into drivers/mfd. Given that you actually need your own bus, rather
> than just a single host with multiple endpoints, drivers/bus/realtek/cr
> might be best.
We do need a bus driver. We support multi models of card at the same
time, so we need a way to bind all of the host drivers. And in the
internal of our card reader, we have only one DMA engine and one ring
buffer to handle massive data, so we also need a way to protect the
critical area between different card hosts. Bus driver is convenient to
handle this situation. Another way to fulfill is to call every register
function of different host (like mmc, memstick) in sequence in card
reader driver, whether pci-based or usb-based, if not using bus driver.
I prefer the prior scheme, which is more flexible and less redundant code.

Using bus driver:

sdmmc memstick
\ /
\ /
\ /
rtsx bus driver
/ \
/ \
/ \
/ \
rtsx pci rtsx usb


Not using bus driver:

sdmmc-pci memstick-pci
\ /
\ /
\ /
\ /
rtsx pci

sdmmc-usb memstick-usb
\ /
\ /
\ /
\ /
rtsx usb

And you said we should put our own bus driver in drivers/bus/realtek/cr,
but where is drivers/bus? Or can I just put this bus driver and our
pci/usb card reader driver into drivers/mfd?

>> +udev rules
>> +----------
>> +
>> +In order to modprobe Realtek SD/MMC interface driver automatically, the following rule
>> +should be added to the udev rules file:
>> +
>> +SUBSYSTEM=="rtsx_cr", ENV{RTSX_CARD_TYPE}=="SD", RUN+="/sbin/modprobe -bv rtsx_sdmmc"
> This should not be necessary if you put the right module alias into the driver itself.
>
> You should generally use EXPORT_SYMBOL_GPL for new symbols unless there is
> a strong reason not to (and then you should explain that reason).

Got it, I will modify it.

>> +
>> +void rtsx_queue_work(struct work_struct *work)
>> +{
>> +	queue_work(workqueue, work);
>> +}
>> +EXPORT_SYMBOL(rtsx_queue_work);
>> +
>> +void rtsx_queue_delayed_work(struct delayed_work *dwork, unsigned long delay)
>> +{
>> +	queue_delayed_work(workqueue, dwork, delay);
>> +}
>> +EXPORT_SYMBOL(rtsx_queue_delayed_work);
> I would not bother with this, just move the workqueue into the client driver
> itself, which may result in a few duplicate lines but less code overall.
>
>> +int rtsx_register_driver(struct rtsx_driver *drv)
>> +{
>> +	drv->driver.bus = &rtsx_bus_type;
>> +
>> +	return driver_register(&drv->driver);
>> +}
>> +EXPORT_SYMBOL(rtsx_register_driver);
>> +
>> +void rtsx_unregister_driver(struct rtsx_driver *drv)
>> +{
>> +	driver_unregister(&drv->driver);
>> +}
>> +EXPORT_SYMBOL(rtsx_unregister_driver);
> Same thing here: There will only be very few drivers for this bus, so it's
> easier to call the driver_register function from the drivers. You need to export
> the rtsx_bus_type in that case though, but that is done for most buses.

I will consider this.

> With this level of abstraction in your own driver, I wonder if it wouldn't be
> easier to have completely separate mmc drivers for each of the two host options
> (pci and usb). Apparently you have almost no shared code in the MMC driver
> /or/ in the bus driver.

If we support MMC only, writing separate drivers for pci and usb is more
proper and clear. But we try to support mmc and memstick, and maybe xD
later, it seems that having a bus driver is more convenient.

>
> From this, I think it would be easier to kill off your own bus driver entirely
> and move the rtsx_pci.c driver into drivers/mfd, and then merge your
> pci/sdmmc.c file with rtsx_sdmmc.c.
>
>> +#define wait_timeout_x(task_state, msecs)			\
>> +do {								\
>> +	set_current_state((task_state));			\
>> +	schedule_timeout(msecs_to_jiffies(msecs));		\
>> +} while (0)
>> +
>> +#define wait_timeout(msecs)	wait_timeout_x(TASK_INTERRUPTIBLE, (msecs))
> This is the same as msleep_interruptible, right? Note that with interuptible
> sleep, you should always check if you got interrupted and return -ERESTARTSYS
> to user space.

Thank you for your explanation. It seems that I should call msleep
instead of msleep_interruptible.

Best regards,
wwang


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

* Re: [PATCH 1/3] drivers/misc: Add realtek card reader core driver
  2012-07-31  7:42 ` wei_wang
  (?)
@ 2012-07-31 11:23 ` Arnd Bergmann
  2012-08-01  6:19   ` wwang
  -1 siblings, 1 reply; 26+ messages in thread
From: Arnd Bergmann @ 2012-07-31 11:23 UTC (permalink / raw)
  To: wei_wang; +Cc: gregkh, devel, linux-kernel, linux-mmc, cjb, bp, aaron.lu

On Tuesday 31 July 2012, wei_wang@realsil.com.cn wrote:
> From: Wei WANG <wei_wang@realsil.com.cn>
> 
> Realtek card reader core driver is the bus driver for Realtek
> driver-based card reader, which supplies adapter layer to
> be used by lower-level pci/usb card reader and upper-level
> sdmmc/memstick host driver.
> 
> Signed-off-by: Wei WANG <wei_wang@realsil.com.cn>

You posted the sdmmc host driver and the pci card reader driver.
I assume that the USB card reader and the memstick host
will also get posted at some point. Do you have a timeframe
for those?

The hardware seems to also support xd/smartmedia. Do you have
plans to add those? I think we have some code in drivers/mfd
that acts as an abstraction layer for these, given that the
host has to do the wear leveling there too.

>  Documentation/misc-devices/realtek_cr.txt |   27 ++
>  drivers/misc/Kconfig                      |    1 +
>  drivers/misc/Makefile                     |    1 +
>  drivers/misc/realtek_cr/Kconfig           |   26 ++
>  drivers/misc/realtek_cr/Makefile          |    7 +
>  drivers/misc/realtek_cr/core/Kconfig      |    5 +
>  drivers/misc/realtek_cr/core/Makefile     |    1 +
>  drivers/misc/realtek_cr/core/rtsx_core.c  |  462 +++++++++++++++++++++++++++++
>  include/linux/rtsx_core.h                 |  188 ++++++++++++
>  9 files changed, 718 insertions(+)
>  create mode 100644 Documentation/misc-devices/realtek_cr.txt
>  create mode 100644 drivers/misc/realtek_cr/Kconfig
>  create mode 100644 drivers/misc/realtek_cr/Makefile
>  create mode 100644 drivers/misc/realtek_cr/core/Kconfig
>  create mode 100644 drivers/misc/realtek_cr/core/Makefile
>  create mode 100644 drivers/misc/realtek_cr/core/rtsx_core.c
>  create mode 100644 include/linux/rtsx_core.h

As usual for most things getting added to drivers/misc, I'm skeptical
about it actually fitting in here. Normally I'd put such a multiplexer
into drivers/mfd. Given that you actually need your own bus, rather
than just a single host with multiple endpoints, drivers/bus/realtek/cr
might be best.

> +udev rules
> +----------
> +
> +In order to modprobe Realtek SD/MMC interface driver automatically, the following rule
> +should be added to the udev rules file:
> +
> +SUBSYSTEM=="rtsx_cr", ENV{RTSX_CARD_TYPE}=="SD", RUN+="/sbin/modprobe -bv rtsx_sdmmc"

This should not be necessary if you put the right module alias into the driver itself.

> +struct rtsx_adapter *rtsx_alloc_adapter(unsigned int num_sockets,
> +					struct device *dev)
> +{
> +	struct rtsx_adapter *adapter;
> +
> +	adapter = kzalloc(sizeof(*adapter)
> +		     + sizeof(struct rtsx_dev *) * num_sockets, GFP_KERNEL);
> +	if (adapter) {
> +		adapter->dev.class = &rtsx_adapter_class;
> +		adapter->dev.parent = dev;
> +		device_initialize(&adapter->dev);
> +		spin_lock_init(&adapter->lock);
> +		adapter->num_sockets = num_sockets;
> +	}
> +	return adapter;
> +}
> +EXPORT_SYMBOL(rtsx_alloc_adapter);

You should generally use EXPORT_SYMBOL_GPL for new symbols unless there is
a strong reason not to (and then you should explain that reason).
> +
> +void rtsx_queue_work(struct work_struct *work)
> +{
> +	queue_work(workqueue, work);
> +}
> +EXPORT_SYMBOL(rtsx_queue_work);
> +
> +void rtsx_queue_delayed_work(struct delayed_work *dwork, unsigned long delay)
> +{
> +	queue_delayed_work(workqueue, dwork, delay);
> +}
> +EXPORT_SYMBOL(rtsx_queue_delayed_work);

I would not bother with this, just move the workqueue into the client driver
itself, which may result in a few duplicate lines but less code overall.

> +int rtsx_register_driver(struct rtsx_driver *drv)
> +{
> +	drv->driver.bus = &rtsx_bus_type;
> +
> +	return driver_register(&drv->driver);
> +}
> +EXPORT_SYMBOL(rtsx_register_driver);
> +
> +void rtsx_unregister_driver(struct rtsx_driver *drv)
> +{
> +	driver_unregister(&drv->driver);
> +}
> +EXPORT_SYMBOL(rtsx_unregister_driver);

Same thing here: There will only be very few drivers for this bus, so it's
easier to call the driver_register function from the drivers. You need to export
the rtsx_bus_type in that case though, but that is done for most buses.

> +void rtsx_complete_unfinished_transfer(struct rtsx_dev *sock)
> +{
> +	struct rtsx_adapter *adapter = sock_to_adapter(sock);
> +
> +	adapter->common_ops->complete_unfinished_transfer(adapter);
> +}
> +EXPORT_SYMBOL(rtsx_complete_unfinished_transfer);
> +
> +void rtsx_sdmmc_set_bus_width(struct rtsx_dev *sock, unsigned char bus_width)
> +{
> +	struct rtsx_adapter *adapter = sock_to_adapter(sock);
> +
> +	adapter->sdmmc_ops->sdmmc_set_bus_width(adapter, bus_width);
> +}
> +EXPORT_SYMBOL(rtsx_sdmmc_set_bus_width);
> +
> +void rtsx_sdmmc_set_power_mode(struct rtsx_dev *sock, unsigned char power_mode)
> +{
> +	struct rtsx_adapter *adapter = sock_to_adapter(sock);
> +
> +	adapter->sdmmc_ops->sdmmc_set_power_mode(adapter, power_mode);
> +}
> +EXPORT_SYMBOL(rtsx_sdmmc_set_power_mode);
> +
> +void rtsx_sdmmc_set_timing(struct rtsx_dev *sock, unsigned char timing)
> +{
> +	struct rtsx_adapter *adapter = sock_to_adapter(sock);
> +
> +	adapter->sdmmc_ops->sdmmc_set_timing(adapter, timing);
> +}
> +EXPORT_SYMBOL(rtsx_sdmmc_set_timing);
> +
> +int rtsx_sdmmc_switch_voltage(struct rtsx_dev *sock, unsigned char voltage)
> +{
> +	struct rtsx_adapter *adapter = sock_to_adapter(sock);
> +
> +	return adapter->sdmmc_ops->sdmmc_switch_voltage(adapter, voltage);
> +}
> +EXPORT_SYMBOL(rtsx_sdmmc_switch_voltage);

With this level of abstraction in your own driver, I wonder if it wouldn't be
easier to have completely separate mmc drivers for each of the two host options
(pci and usb). Apparently you have almost no shared code in the MMC driver
/or/ in the bus driver.

>From this, I think it would be easier to kill off your own bus driver entirely
and move the rtsx_pci.c driver into drivers/mfd, and then merge your
pci/sdmmc.c file with rtsx_sdmmc.c.

> +#define wait_timeout_x(task_state, msecs)			\
> +do {								\
> +	set_current_state((task_state));			\
> +	schedule_timeout(msecs_to_jiffies(msecs));		\
> +} while (0)
> +
> +#define wait_timeout(msecs)	wait_timeout_x(TASK_INTERRUPTIBLE, (msecs))

This is the same as msleep_interruptible, right? Note that with interuptible
sleep, you should always check if you got interrupted and return -ERESTARTSYS
to user space.

	Arnd


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

* [PATCH 1/3] drivers/misc: Add realtek card reader core driver
@ 2012-07-31  7:42 ` wei_wang
  0 siblings, 0 replies; 26+ 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 card reader core driver is the bus driver for Realtek
driver-based card reader, which supplies adapter layer to
be used by lower-level pci/usb card reader and upper-level
sdmmc/memstick host driver.

Signed-off-by: Wei WANG <wei_wang@realsil.com.cn>
---
 Documentation/misc-devices/realtek_cr.txt |   27 ++
 drivers/misc/Kconfig                      |    1 +
 drivers/misc/Makefile                     |    1 +
 drivers/misc/realtek_cr/Kconfig           |   26 ++
 drivers/misc/realtek_cr/Makefile          |    7 +
 drivers/misc/realtek_cr/core/Kconfig      |    5 +
 drivers/misc/realtek_cr/core/Makefile     |    1 +
 drivers/misc/realtek_cr/core/rtsx_core.c  |  462 +++++++++++++++++++++++++++++
 include/linux/rtsx_core.h                 |  188 ++++++++++++
 9 files changed, 718 insertions(+)
 create mode 100644 Documentation/misc-devices/realtek_cr.txt
 create mode 100644 drivers/misc/realtek_cr/Kconfig
 create mode 100644 drivers/misc/realtek_cr/Makefile
 create mode 100644 drivers/misc/realtek_cr/core/Kconfig
 create mode 100644 drivers/misc/realtek_cr/core/Makefile
 create mode 100644 drivers/misc/realtek_cr/core/rtsx_core.c
 create mode 100644 include/linux/rtsx_core.h

diff --git a/Documentation/misc-devices/realtek_cr.txt b/Documentation/misc-devices/realtek_cr.txt
new file mode 100644
index 0000000..b4e6fbe
--- /dev/null
+++ b/Documentation/misc-devices/realtek_cr.txt
@@ -0,0 +1,27 @@
+Realtek Driver-based Card Reader
+================================
+
+Supported chips:
+RTS5209
+RTS5229
+
+Contact Email:
+pc_sw_linux@realsil.com.cn
+
+
+Description
+-----------
+
+Realtek driver-based card reader supports access to many types of memory cards,
+such as Memory Stick, Memory Stick Pro, Secure Digital and MultiMediaCard.
+
+
+udev rules
+----------
+
+In order to modprobe Realtek SD/MMC interface driver automatically, the following rule
+should be added to the udev rules file:
+
+SUBSYSTEM=="rtsx_cr", ENV{RTSX_CARD_TYPE}=="SD", RUN+="/sbin/modprobe -bv rtsx_sdmmc"
+
+Typically, we may edit /lib/udev/rules.d/80-drivers.rules and copy the rule into it in Ubuntu.
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 154f3ef..d32076e 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -516,4 +516,5 @@ source "drivers/misc/lis3lv02d/Kconfig"
 source "drivers/misc/carma/Kconfig"
 source "drivers/misc/altera-stapl/Kconfig"
 source "drivers/misc/mei/Kconfig"
+source "drivers/misc/realtek_cr/Kconfig"
 endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index b88df7a..0bb17ce 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -50,3 +50,4 @@ obj-y				+= carma/
 obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
 obj-$(CONFIG_ALTERA_STAPL)	+=altera-stapl/
 obj-$(CONFIG_INTEL_MEI)		+= mei/
+obj-$(CONFIG_REALTEK_CR_SUPPORT) += realtek_cr/
diff --git a/drivers/misc/realtek_cr/Kconfig b/drivers/misc/realtek_cr/Kconfig
new file mode 100644
index 0000000..303d98a
--- /dev/null
+++ b/drivers/misc/realtek_cr/Kconfig
@@ -0,0 +1,26 @@
+#
+# Realtek driver-based card reader
+#
+
+menuconfig REALTEK_CR_SUPPORT
+	tristate "Realtek driver-based card reader"
+	help
+	  Realtek driver-based card reader supports access to many types of
+	  memory cards, such as Memory Stick, Memory Stick Pro, Secure Digital
+	  and MultiMediaCard.
+
+	  If you want to use Realtek driver-based card reader, enable this
+	  option and other options below.
+
+config REALTEK_CR_DEBUG
+	bool "Realtek driver-based card reader debugging"
+	depends on REALTEK_CR_SUPPORT != n
+	help
+	  This is an option for use by developers; most people should
+	  say N here.  This enables Realtek card reader driver debugging.
+
+if REALTEK_CR_SUPPORT
+
+source "drivers/misc/realtek_cr/core/Kconfig"
+
+endif
diff --git a/drivers/misc/realtek_cr/Makefile b/drivers/misc/realtek_cr/Makefile
new file mode 100644
index 0000000..f4e16ba
--- /dev/null
+++ b/drivers/misc/realtek_cr/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Realtek driver-based card reader.
+#
+
+subdir-ccflags-$(CONFIG_REALTEK_CR_DEBUG) := -DDEBUG
+
+obj-$(CONFIG_REALTEK_CR_SUPPORT)	+= core/
diff --git a/drivers/misc/realtek_cr/core/Kconfig b/drivers/misc/realtek_cr/core/Kconfig
new file mode 100644
index 0000000..38fff8a
--- /dev/null
+++ b/drivers/misc/realtek_cr/core/Kconfig
@@ -0,0 +1,5 @@
+config REALTEK_CR_CORE
+	tristate "RealTek Card Reader Core Driver"
+	help
+	  Say Y here to include driver code to support the Realtek
+	  driver-based card reader.
diff --git a/drivers/misc/realtek_cr/core/Makefile b/drivers/misc/realtek_cr/core/Makefile
new file mode 100644
index 0000000..010055e
--- /dev/null
+++ b/drivers/misc/realtek_cr/core/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_REALTEK_CR_CORE)		+= rtsx_core.o
diff --git a/drivers/misc/realtek_cr/core/rtsx_core.c b/drivers/misc/realtek_cr/core/rtsx_core.c
new file mode 100644
index 0000000..9868980
--- /dev/null
+++ b/drivers/misc/realtek_cr/core/rtsx_core.c
@@ -0,0 +1,462 @@
+/* Realtek card reader core 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/idr.h>
+#include <linux/rtsx_core.h>
+
+static struct workqueue_struct *workqueue;
+static DEFINE_IDR(rtsx_adapter_idr);
+static DEFINE_SPINLOCK(rtsx_adapter_lock);
+
+#define DRIVER_NAME	"rtsx_core"
+
+#ifdef CONFIG_PM
+
+static const char *rtsx_media_type_name(unsigned char type, unsigned char nt)
+{
+	const char *card_type_name[3][3] = {
+		{ "SmartMedia/xD", "MemoryStick", "MMC/SD" },
+		{ "XD", "MS", "SD"},
+		{ "xd", "ms", "sd"}
+	};
+
+	if (nt > 2 || type < 1 || type > 3)
+		return NULL;
+	return card_type_name[nt][type - 1];
+}
+
+static int rtsx_dev_match(struct rtsx_dev *sock, struct rtsx_device_id *id)
+{
+	if (sock->type == id->type)
+		return 1;
+	return 0;
+}
+
+static int rtsx_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *rtsx_drv = container_of(drv, struct rtsx_driver,
+						  driver);
+	struct rtsx_device_id *ids = rtsx_drv->id_table;
+
+	if (ids) {
+		while (ids->type) {
+			if (rtsx_dev_match(sock, ids))
+				return 1;
+			++ids;
+		}
+	}
+	return 0;
+}
+
+static int rtsx_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+
+	if (add_uevent_var(env, "RTSX_CARD_TYPE=%s",
+				rtsx_media_type_name(sock->type, 1)))
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int rtsx_device_probe(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+	int rc = -ENODEV;
+
+	get_device(dev);
+	if (dev->driver && drv->probe) {
+		rc = drv->probe(sock);
+		if (!rc)
+			return 0;
+	}
+	put_device(dev);
+	return rc;
+}
+
+static void rtsx_dummy_event(struct rtsx_dev *sock)
+{
+	return;
+}
+
+static int rtsx_device_remove(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+
+	if (dev->driver && drv->remove) {
+		sock->card_event = rtsx_dummy_event;
+		sock->data_event = rtsx_dummy_event;
+		drv->remove(sock);
+		sock->dev.driver = NULL;
+	}
+
+	put_device(dev);
+	return 0;
+}
+
+static int rtsx_device_suspend(struct device *dev, pm_message_t state)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+
+	if (dev->driver && drv->suspend)
+		return drv->suspend(sock, state);
+	return 0;
+}
+
+static int rtsx_device_resume(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+
+	if (dev->driver && drv->resume)
+		return drv->resume(sock);
+	return 0;
+}
+
+#else
+
+#define rtsx_device_suspend NULL
+#define rtsx_device_resume NULL
+
+#endif /* CONFIG_PM */
+
+static ssize_t type_show(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	return sprintf(buf, "%x", sock->type);
+}
+
+static struct device_attribute rtsx_dev_attrs[] = {
+	__ATTR(type, S_IRUGO, type_show, NULL),
+	__ATTR_NULL
+};
+
+static struct bus_type rtsx_bus_type = {
+	.name      = "rtsx_cr",
+	.dev_attrs = rtsx_dev_attrs,
+	.match     = rtsx_bus_match,
+	.uevent    = rtsx_uevent,
+	.probe     = rtsx_device_probe,
+	.remove    = rtsx_device_remove,
+	.suspend   = rtsx_device_suspend,
+	.resume    = rtsx_device_resume
+};
+
+static void rtsx_free(struct device *dev)
+{
+	struct rtsx_adapter *adapter;
+
+	adapter = container_of(dev, struct rtsx_adapter, dev);
+	kfree(adapter);
+}
+
+static struct class rtsx_adapter_class = {
+	.name    = "rtsx_adapter",
+	.dev_release = rtsx_free
+};
+
+struct rtsx_adapter *rtsx_alloc_adapter(unsigned int num_sockets,
+					struct device *dev)
+{
+	struct rtsx_adapter *adapter;
+
+	adapter = kzalloc(sizeof(*adapter)
+		     + sizeof(struct rtsx_dev *) * num_sockets, GFP_KERNEL);
+	if (adapter) {
+		adapter->dev.class = &rtsx_adapter_class;
+		adapter->dev.parent = dev;
+		device_initialize(&adapter->dev);
+		spin_lock_init(&adapter->lock);
+		adapter->num_sockets = num_sockets;
+	}
+	return adapter;
+}
+EXPORT_SYMBOL(rtsx_alloc_adapter);
+
+int rtsx_add_adapter(struct rtsx_adapter *adapter)
+{
+	int rc;
+
+	if (!idr_pre_get(&rtsx_adapter_idr, GFP_KERNEL))
+		return -ENOMEM;
+
+	spin_lock(&rtsx_adapter_lock);
+	rc = idr_get_new(&rtsx_adapter_idr, adapter, &adapter->id);
+	spin_unlock(&rtsx_adapter_lock);
+	if (rc)
+		return rc;
+
+	dev_set_name(&adapter->dev, "rtsx%u", adapter->id);
+	rc = device_add(&adapter->dev);
+	if (rc) {
+		spin_lock(&rtsx_adapter_lock);
+		idr_remove(&rtsx_adapter_idr, adapter->id);
+		spin_unlock(&rtsx_adapter_lock);
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(rtsx_add_adapter);
+
+void rtsx_remove_adapter(struct rtsx_adapter *adapter)
+{
+	unsigned int cnt;
+
+	flush_workqueue(workqueue);
+	for (cnt = 0; cnt < adapter->num_sockets; ++cnt) {
+		if (adapter->sockets[cnt])
+			device_unregister(&adapter->sockets[cnt]->dev);
+	}
+
+	spin_lock(&rtsx_adapter_lock);
+	idr_remove(&rtsx_adapter_idr, adapter->id);
+	spin_unlock(&rtsx_adapter_lock);
+	device_del(&adapter->dev);
+}
+EXPORT_SYMBOL(rtsx_remove_adapter);
+
+void rtsx_free_adapter(struct rtsx_adapter *adapter)
+{
+	put_device(&adapter->dev);
+}
+EXPORT_SYMBOL(rtsx_free_adapter);
+
+void rtsx_free_device(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	kfree(sock);
+}
+EXPORT_SYMBOL(rtsx_free_device);
+
+struct rtsx_dev *rtsx_alloc_device(struct rtsx_adapter *adapter,
+		unsigned int id, unsigned char type)
+{
+	struct rtsx_dev *sock = NULL;
+
+	if (!rtsx_media_type_name(type, 0))
+		return sock;
+
+	sock = kzalloc(sizeof(*sock), GFP_KERNEL);
+	if (sock) {
+		spin_lock_init(&sock->lock);
+		sock->type = type;
+		sock->socket_id = id;
+		sock->card_event = rtsx_dummy_event;
+		sock->data_event = rtsx_dummy_event;
+
+		sock->dev.parent = &(adapter->dev);
+		sock->dev.bus = &rtsx_bus_type;
+		sock->dev.dma_mask = adapter->dev.parent->dma_mask;
+		sock->dev.release = rtsx_free_device;
+
+		dev_set_name(&sock->dev, "rtsx_%s%u:%u",
+			     rtsx_media_type_name(type, 2), adapter->id, id);
+		pr_info(DRIVER_NAME
+		       ": %s card detected in socket %u:%u\n",
+		       rtsx_media_type_name(type, 0), adapter->id, id);
+	}
+	return sock;
+}
+EXPORT_SYMBOL(rtsx_alloc_device);
+
+void rtsx_queue_work(struct work_struct *work)
+{
+	queue_work(workqueue, work);
+}
+EXPORT_SYMBOL(rtsx_queue_work);
+
+void rtsx_queue_delayed_work(struct delayed_work *dwork, unsigned long delay)
+{
+	queue_delayed_work(workqueue, dwork, delay);
+}
+EXPORT_SYMBOL(rtsx_queue_delayed_work);
+
+int rtsx_register_driver(struct rtsx_driver *drv)
+{
+	drv->driver.bus = &rtsx_bus_type;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(rtsx_register_driver);
+
+void rtsx_unregister_driver(struct rtsx_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(rtsx_unregister_driver);
+
+void rtsx_switch_clock(struct rtsx_dev *sock, unsigned int card_clock,
+		u8 ssc_depth, int double_clk, int vpclk)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	adapter->common_ops->switch_clock(adapter,
+				card_clock, ssc_depth, double_clk, vpclk);
+}
+EXPORT_SYMBOL(rtsx_switch_clock);
+
+void rtsx_complete_unfinished_transfer(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	adapter->common_ops->complete_unfinished_transfer(adapter);
+}
+EXPORT_SYMBOL(rtsx_complete_unfinished_transfer);
+
+void rtsx_sdmmc_set_bus_width(struct rtsx_dev *sock, unsigned char bus_width)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	adapter->sdmmc_ops->sdmmc_set_bus_width(adapter, bus_width);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_set_bus_width);
+
+void rtsx_sdmmc_set_power_mode(struct rtsx_dev *sock, unsigned char power_mode)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	adapter->sdmmc_ops->sdmmc_set_power_mode(adapter, power_mode);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_set_power_mode);
+
+void rtsx_sdmmc_set_timing(struct rtsx_dev *sock, unsigned char timing)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	adapter->sdmmc_ops->sdmmc_set_timing(adapter, timing);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_set_timing);
+
+int rtsx_sdmmc_switch_voltage(struct rtsx_dev *sock, unsigned char voltage)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	return adapter->sdmmc_ops->sdmmc_switch_voltage(adapter, voltage);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_switch_voltage);
+
+int rtsx_sdmmc_get_ro(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	return adapter->sdmmc_ops->sdmmc_get_ro(adapter);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_get_ro);
+
+int rtsx_sdmmc_get_cd(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	return adapter->sdmmc_ops->sdmmc_get_cd(adapter);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_get_cd);
+
+int rtsx_sdmmc_execute_tuning(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	return adapter->sdmmc_ops->sdmmc_execute_tuning(adapter);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_execute_tuning);
+
+int rtsx_sdmmc_send_cmd_get_rsp(struct rtsx_dev *sock, u8 cmd_idx,
+		u32 arg, unsigned int resp_type, u32 *resp)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	return adapter->sdmmc_ops->sdmmc_send_cmd_get_rsp(adapter,
+				cmd_idx, arg, resp_type, resp);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_send_cmd_get_rsp);
+
+int rtsx_sdmmc_read_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	return adapter->sdmmc_ops->sdmmc_read_data(adapter, cmd,
+				byte_cnt, buf, buf_len, timeout);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_read_data);
+
+int rtsx_sdmmc_write_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	return adapter->sdmmc_ops->sdmmc_write_data(adapter, cmd,
+				byte_cnt, buf, buf_len, timeout);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_write_data);
+
+int rtsx_sdmmc_rw_multi(struct rtsx_dev *sock, void *buf, unsigned int blksz,
+		unsigned int blocks, unsigned int use_sg, int read, int uhs)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	return adapter->sdmmc_ops->sdmmc_rw_multi(adapter, buf, blksz,
+				blocks, use_sg, read, uhs);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_rw_multi);
+
+static int __init rtsx_core_init(void)
+{
+	int rc;
+
+	workqueue = create_freezable_workqueue("rtsx_wq");
+	if (!workqueue)
+		return -ENOMEM;
+
+	rc = bus_register(&rtsx_bus_type);
+	if (rc)
+		return rc;
+
+	rc = class_register(&rtsx_adapter_class);
+	if (rc)
+		return rc;
+
+	return rc;
+}
+
+static void __exit rtsx_core_exit(void)
+{
+	class_unregister(&rtsx_adapter_class);
+	bus_unregister(&rtsx_bus_type);
+	destroy_workqueue(workqueue);
+}
+
+module_init(rtsx_core_init);
+module_exit(rtsx_core_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Realtek Corp.");
+MODULE_DESCRIPTION("Realtek Card Reader Core Driver");
diff --git a/include/linux/rtsx_core.h b/include/linux/rtsx_core.h
new file mode 100644
index 0000000..564e7b0
--- /dev/null
+++ b/include/linux/rtsx_core.h
@@ -0,0 +1,188 @@
+/* Realtek card reader core 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
+ */
+
+#ifndef __RTSX_CORE_H
+#define __RTSX_CORE_H
+
+#include <linux/pci.h>
+
+#define RTSX_TYPE_XD			1
+#define RTSX_TYPE_MS			2
+#define RTSX_TYPE_SD			3
+
+#define RTSX_SSC_DEPTH_4M		0x01
+#define RTSX_SSC_DEPTH_2M		0x02
+#define RTSX_SSC_DEPTH_1M		0x03
+#define RTSX_SSC_DEPTH_500K		0x04
+#define RTSX_SSC_DEPTH_250K		0x05
+
+#define wait_timeout_x(task_state, msecs)			\
+do {								\
+	set_current_state((task_state));			\
+	schedule_timeout(msecs_to_jiffies(msecs));		\
+} while (0)
+
+#define wait_timeout(msecs)	wait_timeout_x(TASK_INTERRUPTIBLE, (msecs))
+
+struct rtsx_device_id {
+	unsigned char type;
+};
+
+struct rtsx_dev {
+	char __iomem  *addr;
+	spinlock_t    lock;
+	unsigned char type;
+	unsigned int  socket_id;
+
+	void          (*card_event)(struct rtsx_dev *sock);
+	void          (*data_event)(struct rtsx_dev *sock);
+
+	struct device dev;
+};
+
+struct rtsx_driver {
+	struct rtsx_device_id *id_table;
+	int                   (*probe)(struct rtsx_dev *dev);
+	void                  (*remove)(struct rtsx_dev *dev);
+	int                   (*suspend)(struct rtsx_dev *dev,
+					 pm_message_t state);
+	int                   (*resume)(struct rtsx_dev *dev);
+
+	struct device_driver  driver;
+};
+
+struct rtsx_adapter;
+
+struct rtsx_common_ops {
+	int		(*switch_clock)(struct rtsx_adapter *adapter,
+				unsigned int card_clock, u8 ssc_depth,
+				int double_clk, int vpclk);
+	void		(*complete_unfinished_transfer)(
+				struct rtsx_adapter *adapter);
+};
+
+struct rtsx_sdmmc_ops {
+	int		(*sdmmc_set_bus_width)(
+				struct rtsx_adapter *adapter,
+				unsigned char bus_width);
+	int		(*sdmmc_set_power_mode)(
+				struct rtsx_adapter *adapter,
+				unsigned char power_mode);
+	int		(*sdmmc_set_timing)(struct rtsx_adapter *adapter,
+				unsigned char timing);
+	int		(*sdmmc_switch_voltage)(
+				struct rtsx_adapter *adapter,
+				unsigned char signal_voltage);
+	int		(*sdmmc_get_ro)(struct rtsx_adapter *adapter);
+	int		(*sdmmc_get_cd)(struct rtsx_adapter *adapter);
+	int		(*sdmmc_execute_tuning)(
+				struct rtsx_adapter *adapter);
+	int		(*sdmmc_send_cmd_get_rsp)(
+				struct rtsx_adapter *adapter, u8 cmd_idx,
+				u32 arg, unsigned int resp_type, u32 *resp);
+	int		(*sdmmc_read_data)(struct rtsx_adapter *adapter,
+				u8 *cmd, u16 byte_cnt, u8 *buf,
+				int buf_len, int timeout);
+	int		(*sdmmc_write_data)(struct rtsx_adapter *adapter,
+				u8 *cmd, u16 byte_cnt, u8 *buf,
+				int buf_len, int timeout);
+	int		(*sdmmc_rw_multi)(struct rtsx_adapter *adapter,
+				void *buf, unsigned int blksz,
+				unsigned int blocks, unsigned int use_sg,
+				int read, int uhs);
+};
+
+#define EXTRA_CAPS_SD_SDR50		(1 << 0)
+#define EXTRA_CAPS_SD_SDR104		(1 << 1)
+#define EXTRA_CAPS_SD_DDR50		(1 << 2)
+#define EXTRA_CAPS_MMC_HSDDR		(1 << 3)
+#define EXTRA_CAPS_MMC_HS200		(1 << 4)
+#define EXTRA_CAPS_MMC_8BIT		(1 << 5)
+
+struct rtsx_adapter {
+	spinlock_t			lock;
+	unsigned int			id;
+	unsigned int			num_sockets;
+	u32				extra_caps;
+
+	struct device			dev;
+
+	const struct rtsx_common_ops	*common_ops;
+	const struct rtsx_sdmmc_ops	*sdmmc_ops;
+
+	struct rtsx_dev			*sockets[0];
+};
+
+struct rtsx_reg_pair {
+	u16		addr;
+	u8		val;
+};
+
+static inline struct rtsx_adapter *sock_to_adapter(struct rtsx_dev *sock)
+{
+	return container_of(sock->dev.parent, struct rtsx_adapter, dev);
+}
+
+struct rtsx_adapter *rtsx_alloc_adapter(unsigned int num_sockets,
+					struct device *dev);
+int rtsx_add_adapter(struct rtsx_adapter *adapter);
+void rtsx_remove_adapter(struct rtsx_adapter *adapter);
+void rtsx_free_adapter(struct rtsx_adapter *adapter);
+void rtsx_free_device(struct device *dev);
+struct rtsx_dev *rtsx_alloc_device(struct rtsx_adapter *adapter,
+		unsigned int id, unsigned char type);
+void rtsx_queue_work(struct work_struct *work);
+void rtsx_queue_delayed_work(struct delayed_work *dwork, unsigned long delay);
+int rtsx_register_driver(struct rtsx_driver *drv);
+void rtsx_unregister_driver(struct rtsx_driver *drv);
+
+void rtsx_switch_clock(struct rtsx_dev *sock, unsigned int card_clock,
+		u8 ssc_depth, int double_clk, int vpclk);
+void rtsx_complete_unfinished_transfer(struct rtsx_dev *sock);
+
+void rtsx_sdmmc_set_bus_width(struct rtsx_dev *sock, unsigned char bus_width);
+void rtsx_sdmmc_set_power_mode(struct rtsx_dev *sock, unsigned char power_mode);
+void rtsx_sdmmc_set_timing(struct rtsx_dev *sock, unsigned char timing);
+int rtsx_sdmmc_switch_voltage(struct rtsx_dev *sock, unsigned char voltage);
+int rtsx_sdmmc_get_ro(struct rtsx_dev *sock);
+int rtsx_sdmmc_get_cd(struct rtsx_dev *sock);
+int rtsx_sdmmc_execute_tuning(struct rtsx_dev *sock);
+int rtsx_sdmmc_send_cmd_get_rsp(struct rtsx_dev *sock, u8 cmd_idx,
+		u32 arg, unsigned int resp_type, u32 *resp);
+int rtsx_sdmmc_read_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout);
+int rtsx_sdmmc_write_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout);
+int rtsx_sdmmc_rw_multi(struct rtsx_dev *sock, void *buf, unsigned int blksz,
+		unsigned int blocks, unsigned int use_sg, int read, int uhs);
+
+static inline void *rtsx_get_drvdata(struct rtsx_dev *dev)
+{
+	return dev_get_drvdata(&dev->dev);
+}
+
+static inline void rtsx_set_drvdata(struct rtsx_dev *dev, void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+#endif
-- 
1.7.9.5


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

* [PATCH 1/3] drivers/misc: Add realtek card reader core driver
@ 2012-07-31  7:42 ` wei_wang
  0 siblings, 0 replies; 26+ 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 card reader core driver is the bus driver for Realtek
driver-based card reader, which supplies adapter layer to
be used by lower-level pci/usb card reader and upper-level
sdmmc/memstick host driver.

Signed-off-by: Wei WANG <wei_wang@realsil.com.cn>
---
 Documentation/misc-devices/realtek_cr.txt |   27 ++
 drivers/misc/Kconfig                      |    1 +
 drivers/misc/Makefile                     |    1 +
 drivers/misc/realtek_cr/Kconfig           |   26 ++
 drivers/misc/realtek_cr/Makefile          |    7 +
 drivers/misc/realtek_cr/core/Kconfig      |    5 +
 drivers/misc/realtek_cr/core/Makefile     |    1 +
 drivers/misc/realtek_cr/core/rtsx_core.c  |  462 +++++++++++++++++++++++++++++
 include/linux/rtsx_core.h                 |  188 ++++++++++++
 9 files changed, 718 insertions(+)
 create mode 100644 Documentation/misc-devices/realtek_cr.txt
 create mode 100644 drivers/misc/realtek_cr/Kconfig
 create mode 100644 drivers/misc/realtek_cr/Makefile
 create mode 100644 drivers/misc/realtek_cr/core/Kconfig
 create mode 100644 drivers/misc/realtek_cr/core/Makefile
 create mode 100644 drivers/misc/realtek_cr/core/rtsx_core.c
 create mode 100644 include/linux/rtsx_core.h

diff --git a/Documentation/misc-devices/realtek_cr.txt b/Documentation/misc-devices/realtek_cr.txt
new file mode 100644
index 0000000..b4e6fbe
--- /dev/null
+++ b/Documentation/misc-devices/realtek_cr.txt
@@ -0,0 +1,27 @@
+Realtek Driver-based Card Reader
+================================
+
+Supported chips:
+RTS5209
+RTS5229
+
+Contact Email:
+pc_sw_linux@realsil.com.cn
+
+
+Description
+-----------
+
+Realtek driver-based card reader supports access to many types of memory cards,
+such as Memory Stick, Memory Stick Pro, Secure Digital and MultiMediaCard.
+
+
+udev rules
+----------
+
+In order to modprobe Realtek SD/MMC interface driver automatically, the following rule
+should be added to the udev rules file:
+
+SUBSYSTEM=="rtsx_cr", ENV{RTSX_CARD_TYPE}=="SD", RUN+="/sbin/modprobe -bv rtsx_sdmmc"
+
+Typically, we may edit /lib/udev/rules.d/80-drivers.rules and copy the rule into it in Ubuntu.
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 154f3ef..d32076e 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -516,4 +516,5 @@ source "drivers/misc/lis3lv02d/Kconfig"
 source "drivers/misc/carma/Kconfig"
 source "drivers/misc/altera-stapl/Kconfig"
 source "drivers/misc/mei/Kconfig"
+source "drivers/misc/realtek_cr/Kconfig"
 endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index b88df7a..0bb17ce 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -50,3 +50,4 @@ obj-y				+= carma/
 obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
 obj-$(CONFIG_ALTERA_STAPL)	+=altera-stapl/
 obj-$(CONFIG_INTEL_MEI)		+= mei/
+obj-$(CONFIG_REALTEK_CR_SUPPORT) += realtek_cr/
diff --git a/drivers/misc/realtek_cr/Kconfig b/drivers/misc/realtek_cr/Kconfig
new file mode 100644
index 0000000..303d98a
--- /dev/null
+++ b/drivers/misc/realtek_cr/Kconfig
@@ -0,0 +1,26 @@
+#
+# Realtek driver-based card reader
+#
+
+menuconfig REALTEK_CR_SUPPORT
+	tristate "Realtek driver-based card reader"
+	help
+	  Realtek driver-based card reader supports access to many types of
+	  memory cards, such as Memory Stick, Memory Stick Pro, Secure Digital
+	  and MultiMediaCard.
+
+	  If you want to use Realtek driver-based card reader, enable this
+	  option and other options below.
+
+config REALTEK_CR_DEBUG
+	bool "Realtek driver-based card reader debugging"
+	depends on REALTEK_CR_SUPPORT != n
+	help
+	  This is an option for use by developers; most people should
+	  say N here.  This enables Realtek card reader driver debugging.
+
+if REALTEK_CR_SUPPORT
+
+source "drivers/misc/realtek_cr/core/Kconfig"
+
+endif
diff --git a/drivers/misc/realtek_cr/Makefile b/drivers/misc/realtek_cr/Makefile
new file mode 100644
index 0000000..f4e16ba
--- /dev/null
+++ b/drivers/misc/realtek_cr/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Realtek driver-based card reader.
+#
+
+subdir-ccflags-$(CONFIG_REALTEK_CR_DEBUG) := -DDEBUG
+
+obj-$(CONFIG_REALTEK_CR_SUPPORT)	+= core/
diff --git a/drivers/misc/realtek_cr/core/Kconfig b/drivers/misc/realtek_cr/core/Kconfig
new file mode 100644
index 0000000..38fff8a
--- /dev/null
+++ b/drivers/misc/realtek_cr/core/Kconfig
@@ -0,0 +1,5 @@
+config REALTEK_CR_CORE
+	tristate "RealTek Card Reader Core Driver"
+	help
+	  Say Y here to include driver code to support the Realtek
+	  driver-based card reader.
diff --git a/drivers/misc/realtek_cr/core/Makefile b/drivers/misc/realtek_cr/core/Makefile
new file mode 100644
index 0000000..010055e
--- /dev/null
+++ b/drivers/misc/realtek_cr/core/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_REALTEK_CR_CORE)		+= rtsx_core.o
diff --git a/drivers/misc/realtek_cr/core/rtsx_core.c b/drivers/misc/realtek_cr/core/rtsx_core.c
new file mode 100644
index 0000000..9868980
--- /dev/null
+++ b/drivers/misc/realtek_cr/core/rtsx_core.c
@@ -0,0 +1,462 @@
+/* Realtek card reader core 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/idr.h>
+#include <linux/rtsx_core.h>
+
+static struct workqueue_struct *workqueue;
+static DEFINE_IDR(rtsx_adapter_idr);
+static DEFINE_SPINLOCK(rtsx_adapter_lock);
+
+#define DRIVER_NAME	"rtsx_core"
+
+#ifdef CONFIG_PM
+
+static const char *rtsx_media_type_name(unsigned char type, unsigned char nt)
+{
+	const char *card_type_name[3][3] = {
+		{ "SmartMedia/xD", "MemoryStick", "MMC/SD" },
+		{ "XD", "MS", "SD"},
+		{ "xd", "ms", "sd"}
+	};
+
+	if (nt > 2 || type < 1 || type > 3)
+		return NULL;
+	return card_type_name[nt][type - 1];
+}
+
+static int rtsx_dev_match(struct rtsx_dev *sock, struct rtsx_device_id *id)
+{
+	if (sock->type == id->type)
+		return 1;
+	return 0;
+}
+
+static int rtsx_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *rtsx_drv = container_of(drv, struct rtsx_driver,
+						  driver);
+	struct rtsx_device_id *ids = rtsx_drv->id_table;
+
+	if (ids) {
+		while (ids->type) {
+			if (rtsx_dev_match(sock, ids))
+				return 1;
+			++ids;
+		}
+	}
+	return 0;
+}
+
+static int rtsx_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+
+	if (add_uevent_var(env, "RTSX_CARD_TYPE=%s",
+				rtsx_media_type_name(sock->type, 1)))
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int rtsx_device_probe(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+	int rc = -ENODEV;
+
+	get_device(dev);
+	if (dev->driver && drv->probe) {
+		rc = drv->probe(sock);
+		if (!rc)
+			return 0;
+	}
+	put_device(dev);
+	return rc;
+}
+
+static void rtsx_dummy_event(struct rtsx_dev *sock)
+{
+	return;
+}
+
+static int rtsx_device_remove(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+
+	if (dev->driver && drv->remove) {
+		sock->card_event = rtsx_dummy_event;
+		sock->data_event = rtsx_dummy_event;
+		drv->remove(sock);
+		sock->dev.driver = NULL;
+	}
+
+	put_device(dev);
+	return 0;
+}
+
+static int rtsx_device_suspend(struct device *dev, pm_message_t state)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+
+	if (dev->driver && drv->suspend)
+		return drv->suspend(sock, state);
+	return 0;
+}
+
+static int rtsx_device_resume(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+
+	if (dev->driver && drv->resume)
+		return drv->resume(sock);
+	return 0;
+}
+
+#else
+
+#define rtsx_device_suspend NULL
+#define rtsx_device_resume NULL
+
+#endif /* CONFIG_PM */
+
+static ssize_t type_show(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	return sprintf(buf, "%x", sock->type);
+}
+
+static struct device_attribute rtsx_dev_attrs[] = {
+	__ATTR(type, S_IRUGO, type_show, NULL),
+	__ATTR_NULL
+};
+
+static struct bus_type rtsx_bus_type = {
+	.name      = "rtsx_cr",
+	.dev_attrs = rtsx_dev_attrs,
+	.match     = rtsx_bus_match,
+	.uevent    = rtsx_uevent,
+	.probe     = rtsx_device_probe,
+	.remove    = rtsx_device_remove,
+	.suspend   = rtsx_device_suspend,
+	.resume    = rtsx_device_resume
+};
+
+static void rtsx_free(struct device *dev)
+{
+	struct rtsx_adapter *adapter;
+
+	adapter = container_of(dev, struct rtsx_adapter, dev);
+	kfree(adapter);
+}
+
+static struct class rtsx_adapter_class = {
+	.name    = "rtsx_adapter",
+	.dev_release = rtsx_free
+};
+
+struct rtsx_adapter *rtsx_alloc_adapter(unsigned int num_sockets,
+					struct device *dev)
+{
+	struct rtsx_adapter *adapter;
+
+	adapter = kzalloc(sizeof(*adapter)
+		     + sizeof(struct rtsx_dev *) * num_sockets, GFP_KERNEL);
+	if (adapter) {
+		adapter->dev.class = &rtsx_adapter_class;
+		adapter->dev.parent = dev;
+		device_initialize(&adapter->dev);
+		spin_lock_init(&adapter->lock);
+		adapter->num_sockets = num_sockets;
+	}
+	return adapter;
+}
+EXPORT_SYMBOL(rtsx_alloc_adapter);
+
+int rtsx_add_adapter(struct rtsx_adapter *adapter)
+{
+	int rc;
+
+	if (!idr_pre_get(&rtsx_adapter_idr, GFP_KERNEL))
+		return -ENOMEM;
+
+	spin_lock(&rtsx_adapter_lock);
+	rc = idr_get_new(&rtsx_adapter_idr, adapter, &adapter->id);
+	spin_unlock(&rtsx_adapter_lock);
+	if (rc)
+		return rc;
+
+	dev_set_name(&adapter->dev, "rtsx%u", adapter->id);
+	rc = device_add(&adapter->dev);
+	if (rc) {
+		spin_lock(&rtsx_adapter_lock);
+		idr_remove(&rtsx_adapter_idr, adapter->id);
+		spin_unlock(&rtsx_adapter_lock);
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(rtsx_add_adapter);
+
+void rtsx_remove_adapter(struct rtsx_adapter *adapter)
+{
+	unsigned int cnt;
+
+	flush_workqueue(workqueue);
+	for (cnt = 0; cnt < adapter->num_sockets; ++cnt) {
+		if (adapter->sockets[cnt])
+			device_unregister(&adapter->sockets[cnt]->dev);
+	}
+
+	spin_lock(&rtsx_adapter_lock);
+	idr_remove(&rtsx_adapter_idr, adapter->id);
+	spin_unlock(&rtsx_adapter_lock);
+	device_del(&adapter->dev);
+}
+EXPORT_SYMBOL(rtsx_remove_adapter);
+
+void rtsx_free_adapter(struct rtsx_adapter *adapter)
+{
+	put_device(&adapter->dev);
+}
+EXPORT_SYMBOL(rtsx_free_adapter);
+
+void rtsx_free_device(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	kfree(sock);
+}
+EXPORT_SYMBOL(rtsx_free_device);
+
+struct rtsx_dev *rtsx_alloc_device(struct rtsx_adapter *adapter,
+		unsigned int id, unsigned char type)
+{
+	struct rtsx_dev *sock = NULL;
+
+	if (!rtsx_media_type_name(type, 0))
+		return sock;
+
+	sock = kzalloc(sizeof(*sock), GFP_KERNEL);
+	if (sock) {
+		spin_lock_init(&sock->lock);
+		sock->type = type;
+		sock->socket_id = id;
+		sock->card_event = rtsx_dummy_event;
+		sock->data_event = rtsx_dummy_event;
+
+		sock->dev.parent = &(adapter->dev);
+		sock->dev.bus = &rtsx_bus_type;
+		sock->dev.dma_mask = adapter->dev.parent->dma_mask;
+		sock->dev.release = rtsx_free_device;
+
+		dev_set_name(&sock->dev, "rtsx_%s%u:%u",
+			     rtsx_media_type_name(type, 2), adapter->id, id);
+		pr_info(DRIVER_NAME
+		       ": %s card detected in socket %u:%u\n",
+		       rtsx_media_type_name(type, 0), adapter->id, id);
+	}
+	return sock;
+}
+EXPORT_SYMBOL(rtsx_alloc_device);
+
+void rtsx_queue_work(struct work_struct *work)
+{
+	queue_work(workqueue, work);
+}
+EXPORT_SYMBOL(rtsx_queue_work);
+
+void rtsx_queue_delayed_work(struct delayed_work *dwork, unsigned long delay)
+{
+	queue_delayed_work(workqueue, dwork, delay);
+}
+EXPORT_SYMBOL(rtsx_queue_delayed_work);
+
+int rtsx_register_driver(struct rtsx_driver *drv)
+{
+	drv->driver.bus = &rtsx_bus_type;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(rtsx_register_driver);
+
+void rtsx_unregister_driver(struct rtsx_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(rtsx_unregister_driver);
+
+void rtsx_switch_clock(struct rtsx_dev *sock, unsigned int card_clock,
+		u8 ssc_depth, int double_clk, int vpclk)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	adapter->common_ops->switch_clock(adapter,
+				card_clock, ssc_depth, double_clk, vpclk);
+}
+EXPORT_SYMBOL(rtsx_switch_clock);
+
+void rtsx_complete_unfinished_transfer(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	adapter->common_ops->complete_unfinished_transfer(adapter);
+}
+EXPORT_SYMBOL(rtsx_complete_unfinished_transfer);
+
+void rtsx_sdmmc_set_bus_width(struct rtsx_dev *sock, unsigned char bus_width)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	adapter->sdmmc_ops->sdmmc_set_bus_width(adapter, bus_width);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_set_bus_width);
+
+void rtsx_sdmmc_set_power_mode(struct rtsx_dev *sock, unsigned char power_mode)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	adapter->sdmmc_ops->sdmmc_set_power_mode(adapter, power_mode);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_set_power_mode);
+
+void rtsx_sdmmc_set_timing(struct rtsx_dev *sock, unsigned char timing)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	adapter->sdmmc_ops->sdmmc_set_timing(adapter, timing);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_set_timing);
+
+int rtsx_sdmmc_switch_voltage(struct rtsx_dev *sock, unsigned char voltage)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	return adapter->sdmmc_ops->sdmmc_switch_voltage(adapter, voltage);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_switch_voltage);
+
+int rtsx_sdmmc_get_ro(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	return adapter->sdmmc_ops->sdmmc_get_ro(adapter);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_get_ro);
+
+int rtsx_sdmmc_get_cd(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	return adapter->sdmmc_ops->sdmmc_get_cd(adapter);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_get_cd);
+
+int rtsx_sdmmc_execute_tuning(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	return adapter->sdmmc_ops->sdmmc_execute_tuning(adapter);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_execute_tuning);
+
+int rtsx_sdmmc_send_cmd_get_rsp(struct rtsx_dev *sock, u8 cmd_idx,
+		u32 arg, unsigned int resp_type, u32 *resp)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	return adapter->sdmmc_ops->sdmmc_send_cmd_get_rsp(adapter,
+				cmd_idx, arg, resp_type, resp);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_send_cmd_get_rsp);
+
+int rtsx_sdmmc_read_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	return adapter->sdmmc_ops->sdmmc_read_data(adapter, cmd,
+				byte_cnt, buf, buf_len, timeout);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_read_data);
+
+int rtsx_sdmmc_write_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	return adapter->sdmmc_ops->sdmmc_write_data(adapter, cmd,
+				byte_cnt, buf, buf_len, timeout);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_write_data);
+
+int rtsx_sdmmc_rw_multi(struct rtsx_dev *sock, void *buf, unsigned int blksz,
+		unsigned int blocks, unsigned int use_sg, int read, int uhs)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	return adapter->sdmmc_ops->sdmmc_rw_multi(adapter, buf, blksz,
+				blocks, use_sg, read, uhs);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_rw_multi);
+
+static int __init rtsx_core_init(void)
+{
+	int rc;
+
+	workqueue = create_freezable_workqueue("rtsx_wq");
+	if (!workqueue)
+		return -ENOMEM;
+
+	rc = bus_register(&rtsx_bus_type);
+	if (rc)
+		return rc;
+
+	rc = class_register(&rtsx_adapter_class);
+	if (rc)
+		return rc;
+
+	return rc;
+}
+
+static void __exit rtsx_core_exit(void)
+{
+	class_unregister(&rtsx_adapter_class);
+	bus_unregister(&rtsx_bus_type);
+	destroy_workqueue(workqueue);
+}
+
+module_init(rtsx_core_init);
+module_exit(rtsx_core_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Realtek Corp.");
+MODULE_DESCRIPTION("Realtek Card Reader Core Driver");
diff --git a/include/linux/rtsx_core.h b/include/linux/rtsx_core.h
new file mode 100644
index 0000000..564e7b0
--- /dev/null
+++ b/include/linux/rtsx_core.h
@@ -0,0 +1,188 @@
+/* Realtek card reader core 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
+ */
+
+#ifndef __RTSX_CORE_H
+#define __RTSX_CORE_H
+
+#include <linux/pci.h>
+
+#define RTSX_TYPE_XD			1
+#define RTSX_TYPE_MS			2
+#define RTSX_TYPE_SD			3
+
+#define RTSX_SSC_DEPTH_4M		0x01
+#define RTSX_SSC_DEPTH_2M		0x02
+#define RTSX_SSC_DEPTH_1M		0x03
+#define RTSX_SSC_DEPTH_500K		0x04
+#define RTSX_SSC_DEPTH_250K		0x05
+
+#define wait_timeout_x(task_state, msecs)			\
+do {								\
+	set_current_state((task_state));			\
+	schedule_timeout(msecs_to_jiffies(msecs));		\
+} while (0)
+
+#define wait_timeout(msecs)	wait_timeout_x(TASK_INTERRUPTIBLE, (msecs))
+
+struct rtsx_device_id {
+	unsigned char type;
+};
+
+struct rtsx_dev {
+	char __iomem  *addr;
+	spinlock_t    lock;
+	unsigned char type;
+	unsigned int  socket_id;
+
+	void          (*card_event)(struct rtsx_dev *sock);
+	void          (*data_event)(struct rtsx_dev *sock);
+
+	struct device dev;
+};
+
+struct rtsx_driver {
+	struct rtsx_device_id *id_table;
+	int                   (*probe)(struct rtsx_dev *dev);
+	void                  (*remove)(struct rtsx_dev *dev);
+	int                   (*suspend)(struct rtsx_dev *dev,
+					 pm_message_t state);
+	int                   (*resume)(struct rtsx_dev *dev);
+
+	struct device_driver  driver;
+};
+
+struct rtsx_adapter;
+
+struct rtsx_common_ops {
+	int		(*switch_clock)(struct rtsx_adapter *adapter,
+				unsigned int card_clock, u8 ssc_depth,
+				int double_clk, int vpclk);
+	void		(*complete_unfinished_transfer)(
+				struct rtsx_adapter *adapter);
+};
+
+struct rtsx_sdmmc_ops {
+	int		(*sdmmc_set_bus_width)(
+				struct rtsx_adapter *adapter,
+				unsigned char bus_width);
+	int		(*sdmmc_set_power_mode)(
+				struct rtsx_adapter *adapter,
+				unsigned char power_mode);
+	int		(*sdmmc_set_timing)(struct rtsx_adapter *adapter,
+				unsigned char timing);
+	int		(*sdmmc_switch_voltage)(
+				struct rtsx_adapter *adapter,
+				unsigned char signal_voltage);
+	int		(*sdmmc_get_ro)(struct rtsx_adapter *adapter);
+	int		(*sdmmc_get_cd)(struct rtsx_adapter *adapter);
+	int		(*sdmmc_execute_tuning)(
+				struct rtsx_adapter *adapter);
+	int		(*sdmmc_send_cmd_get_rsp)(
+				struct rtsx_adapter *adapter, u8 cmd_idx,
+				u32 arg, unsigned int resp_type, u32 *resp);
+	int		(*sdmmc_read_data)(struct rtsx_adapter *adapter,
+				u8 *cmd, u16 byte_cnt, u8 *buf,
+				int buf_len, int timeout);
+	int		(*sdmmc_write_data)(struct rtsx_adapter *adapter,
+				u8 *cmd, u16 byte_cnt, u8 *buf,
+				int buf_len, int timeout);
+	int		(*sdmmc_rw_multi)(struct rtsx_adapter *adapter,
+				void *buf, unsigned int blksz,
+				unsigned int blocks, unsigned int use_sg,
+				int read, int uhs);
+};
+
+#define EXTRA_CAPS_SD_SDR50		(1 << 0)
+#define EXTRA_CAPS_SD_SDR104		(1 << 1)
+#define EXTRA_CAPS_SD_DDR50		(1 << 2)
+#define EXTRA_CAPS_MMC_HSDDR		(1 << 3)
+#define EXTRA_CAPS_MMC_HS200		(1 << 4)
+#define EXTRA_CAPS_MMC_8BIT		(1 << 5)
+
+struct rtsx_adapter {
+	spinlock_t			lock;
+	unsigned int			id;
+	unsigned int			num_sockets;
+	u32				extra_caps;
+
+	struct device			dev;
+
+	const struct rtsx_common_ops	*common_ops;
+	const struct rtsx_sdmmc_ops	*sdmmc_ops;
+
+	struct rtsx_dev			*sockets[0];
+};
+
+struct rtsx_reg_pair {
+	u16		addr;
+	u8		val;
+};
+
+static inline struct rtsx_adapter *sock_to_adapter(struct rtsx_dev *sock)
+{
+	return container_of(sock->dev.parent, struct rtsx_adapter, dev);
+}
+
+struct rtsx_adapter *rtsx_alloc_adapter(unsigned int num_sockets,
+					struct device *dev);
+int rtsx_add_adapter(struct rtsx_adapter *adapter);
+void rtsx_remove_adapter(struct rtsx_adapter *adapter);
+void rtsx_free_adapter(struct rtsx_adapter *adapter);
+void rtsx_free_device(struct device *dev);
+struct rtsx_dev *rtsx_alloc_device(struct rtsx_adapter *adapter,
+		unsigned int id, unsigned char type);
+void rtsx_queue_work(struct work_struct *work);
+void rtsx_queue_delayed_work(struct delayed_work *dwork, unsigned long delay);
+int rtsx_register_driver(struct rtsx_driver *drv);
+void rtsx_unregister_driver(struct rtsx_driver *drv);
+
+void rtsx_switch_clock(struct rtsx_dev *sock, unsigned int card_clock,
+		u8 ssc_depth, int double_clk, int vpclk);
+void rtsx_complete_unfinished_transfer(struct rtsx_dev *sock);
+
+void rtsx_sdmmc_set_bus_width(struct rtsx_dev *sock, unsigned char bus_width);
+void rtsx_sdmmc_set_power_mode(struct rtsx_dev *sock, unsigned char power_mode);
+void rtsx_sdmmc_set_timing(struct rtsx_dev *sock, unsigned char timing);
+int rtsx_sdmmc_switch_voltage(struct rtsx_dev *sock, unsigned char voltage);
+int rtsx_sdmmc_get_ro(struct rtsx_dev *sock);
+int rtsx_sdmmc_get_cd(struct rtsx_dev *sock);
+int rtsx_sdmmc_execute_tuning(struct rtsx_dev *sock);
+int rtsx_sdmmc_send_cmd_get_rsp(struct rtsx_dev *sock, u8 cmd_idx,
+		u32 arg, unsigned int resp_type, u32 *resp);
+int rtsx_sdmmc_read_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout);
+int rtsx_sdmmc_write_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout);
+int rtsx_sdmmc_rw_multi(struct rtsx_dev *sock, void *buf, unsigned int blksz,
+		unsigned int blocks, unsigned int use_sg, int read, int uhs);
+
+static inline void *rtsx_get_drvdata(struct rtsx_dev *dev)
+{
+	return dev_get_drvdata(&dev->dev);
+}
+
+static inline void rtsx_set_drvdata(struct rtsx_dev *dev, void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+#endif
-- 
1.7.9.5


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

* [PATCH 1/3] drivers/misc: Add realtek card reader core driver
@ 2012-07-25  3:02 wei_wang
  0 siblings, 0 replies; 26+ 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 card reader core driver is the bus driver for Realtek
driver-based card reader, which supplies adapter layer to
be used by lower-level pci/usb card reader and upper-level
sdmmc/memstick host driver.

Signed-off-by: Wei WANG <wei_wang@realsil.com.cn>
---
 Documentation/misc-devices/realtek_cr.txt |   27 ++
 drivers/misc/Kconfig                      |    1 +
 drivers/misc/Makefile                     |    1 +
 drivers/misc/realtek_cr/Kconfig           |   26 ++
 drivers/misc/realtek_cr/Makefile          |    7 +
 drivers/misc/realtek_cr/core/Kconfig      |    6 +
 drivers/misc/realtek_cr/core/Makefile     |    1 +
 drivers/misc/realtek_cr/core/rtsx_core.c  |  492 +++++++++++++++++++++++++++++
 include/linux/rtsx_core.h                 |  180 +++++++++++
 9 files changed, 741 insertions(+)
 create mode 100644 Documentation/misc-devices/realtek_cr.txt
 create mode 100644 drivers/misc/realtek_cr/Kconfig
 create mode 100644 drivers/misc/realtek_cr/Makefile
 create mode 100644 drivers/misc/realtek_cr/core/Kconfig
 create mode 100644 drivers/misc/realtek_cr/core/Makefile
 create mode 100644 drivers/misc/realtek_cr/core/rtsx_core.c
 create mode 100644 include/linux/rtsx_core.h

diff --git a/Documentation/misc-devices/realtek_cr.txt b/Documentation/misc-devices/realtek_cr.txt
new file mode 100644
index 0000000..b4e6fbe
--- /dev/null
+++ b/Documentation/misc-devices/realtek_cr.txt
@@ -0,0 +1,27 @@
+Realtek Driver-based Card Reader
+================================
+
+Supported chips:
+RTS5209
+RTS5229
+
+Contact Email:
+pc_sw_linux@realsil.com.cn
+
+
+Description
+-----------
+
+Realtek driver-based card reader supports access to many types of memory cards,
+such as Memory Stick, Memory Stick Pro, Secure Digital and MultiMediaCard.
+
+
+udev rules
+----------
+
+In order to modprobe Realtek SD/MMC interface driver automatically, the following rule
+should be added to the udev rules file:
+
+SUBSYSTEM=="rtsx_cr", ENV{RTSX_CARD_TYPE}=="SD", RUN+="/sbin/modprobe -bv rtsx_sdmmc"
+
+Typically, we may edit /lib/udev/rules.d/80-drivers.rules and copy the rule into it in Ubuntu.
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 2661f6e..09ce905 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -517,4 +517,5 @@ source "drivers/misc/lis3lv02d/Kconfig"
 source "drivers/misc/carma/Kconfig"
 source "drivers/misc/altera-stapl/Kconfig"
 source "drivers/misc/mei/Kconfig"
+source "drivers/misc/realtek_cr/Kconfig"
 endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 456972f..c09f147 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -51,3 +51,4 @@ obj-y				+= carma/
 obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
 obj-$(CONFIG_ALTERA_STAPL)	+=altera-stapl/
 obj-$(CONFIG_INTEL_MEI)		+= mei/
+obj-$(CONFIG_REALTEK_CR_SUPPORT) += realtek_cr/
diff --git a/drivers/misc/realtek_cr/Kconfig b/drivers/misc/realtek_cr/Kconfig
new file mode 100644
index 0000000..303d98a
--- /dev/null
+++ b/drivers/misc/realtek_cr/Kconfig
@@ -0,0 +1,26 @@
+#
+# Realtek driver-based card reader
+#
+
+menuconfig REALTEK_CR_SUPPORT
+	tristate "Realtek driver-based card reader"
+	help
+	  Realtek driver-based card reader supports access to many types of
+	  memory cards, such as Memory Stick, Memory Stick Pro, Secure Digital
+	  and MultiMediaCard.
+
+	  If you want to use Realtek driver-based card reader, enable this
+	  option and other options below.
+
+config REALTEK_CR_DEBUG
+	bool "Realtek driver-based card reader debugging"
+	depends on REALTEK_CR_SUPPORT != n
+	help
+	  This is an option for use by developers; most people should
+	  say N here.  This enables Realtek card reader driver debugging.
+
+if REALTEK_CR_SUPPORT
+
+source "drivers/misc/realtek_cr/core/Kconfig"
+
+endif
diff --git a/drivers/misc/realtek_cr/Makefile b/drivers/misc/realtek_cr/Makefile
new file mode 100644
index 0000000..f4e16ba
--- /dev/null
+++ b/drivers/misc/realtek_cr/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Realtek driver-based card reader.
+#
+
+subdir-ccflags-$(CONFIG_REALTEK_CR_DEBUG) := -DDEBUG
+
+obj-$(CONFIG_REALTEK_CR_SUPPORT)	+= core/
diff --git a/drivers/misc/realtek_cr/core/Kconfig b/drivers/misc/realtek_cr/core/Kconfig
new file mode 100644
index 0000000..5e9f14e
--- /dev/null
+++ b/drivers/misc/realtek_cr/core/Kconfig
@@ -0,0 +1,6 @@
+config REALTEK_CR_CORE
+	tristate "RealTek Card Reader Core Driver"
+	help
+	  Say Y here to include driver code to support the Realtek
+	  driver-based card reader.
+
diff --git a/drivers/misc/realtek_cr/core/Makefile b/drivers/misc/realtek_cr/core/Makefile
new file mode 100644
index 0000000..010055e
--- /dev/null
+++ b/drivers/misc/realtek_cr/core/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_REALTEK_CR_CORE)		+= rtsx_core.o
diff --git a/drivers/misc/realtek_cr/core/rtsx_core.c b/drivers/misc/realtek_cr/core/rtsx_core.c
new file mode 100644
index 0000000..c3472d5
--- /dev/null
+++ b/drivers/misc/realtek_cr/core/rtsx_core.c
@@ -0,0 +1,492 @@
+/* Realtek card reader core 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/idr.h>
+#include <linux/rtsx_core.h>
+
+static struct workqueue_struct *workqueue;
+static DEFINE_IDR(rtsx_adapter_idr);
+static DEFINE_SPINLOCK(rtsx_adapter_lock);
+
+#define DRIVER_NAME	"rtsx_core"
+
+#ifdef CONFIG_PM
+
+static const char *rtsx_media_type_name(unsigned char type, unsigned char nt)
+{
+	const char *card_type_name[3][3] = {
+		{ "SmartMedia/xD", "MemoryStick", "MMC/SD" },
+		{ "XD", "MS", "SD"},
+		{ "xd", "ms", "sd"}
+	};
+
+	if (nt > 2 || type < 1 || type > 3)
+		return NULL;
+	return card_type_name[nt][type - 1];
+}
+
+static int rtsx_dev_match(struct rtsx_dev *sock, struct rtsx_device_id *id)
+{
+	if (sock->type == id->type)
+		return 1;
+	return 0;
+}
+
+static int rtsx_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *rtsx_drv = container_of(drv, struct rtsx_driver,
+						  driver);
+	struct rtsx_device_id *ids = rtsx_drv->id_table;
+
+	if (ids) {
+		while (ids->type) {
+			if (rtsx_dev_match(sock, ids))
+				return 1;
+			++ids;
+		}
+	}
+	return 0;
+}
+
+static int rtsx_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+
+	if (add_uevent_var(env, "RTSX_CARD_TYPE=%s",
+				rtsx_media_type_name(sock->type, 1)))
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int rtsx_device_probe(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+	int rc = -ENODEV;
+
+	get_device(dev);
+	if (dev->driver && drv->probe) {
+		rc = drv->probe(sock);
+		if (!rc)
+			return 0;
+	}
+	put_device(dev);
+	return rc;
+}
+
+static void rtsx_dummy_event(struct rtsx_dev *sock)
+{
+	return;
+}
+
+static int rtsx_device_remove(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+
+	if (dev->driver && drv->remove) {
+		sock->card_event = rtsx_dummy_event;
+		sock->data_event = rtsx_dummy_event;
+		drv->remove(sock);
+		sock->dev.driver = NULL;
+	}
+
+	put_device(dev);
+	return 0;
+}
+
+static int rtsx_device_suspend(struct device *dev, pm_message_t state)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+
+	if (dev->driver && drv->suspend)
+		return drv->suspend(sock, state);
+	return 0;
+}
+
+static int rtsx_device_resume(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+
+	if (dev->driver && drv->resume)
+		return drv->resume(sock);
+	return 0;
+}
+
+#else
+
+#define rtsx_device_suspend NULL
+#define rtsx_device_resume NULL
+
+#endif /* CONFIG_PM */
+
+static ssize_t type_show(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	return sprintf(buf, "%x", sock->type);
+}
+
+static struct device_attribute rtsx_dev_attrs[] = {
+	__ATTR(type, S_IRUGO, type_show, NULL),
+	__ATTR_NULL
+};
+
+static struct bus_type rtsx_bus_type = {
+	.name      = "rtsx_cr",
+	.dev_attrs = rtsx_dev_attrs,
+	.match     = rtsx_bus_match,
+	.uevent    = rtsx_uevent,
+	.probe     = rtsx_device_probe,
+	.remove    = rtsx_device_remove,
+	.suspend   = rtsx_device_suspend,
+	.resume    = rtsx_device_resume
+};
+
+static void rtsx_free(struct device *dev)
+{
+	struct rtsx_adapter *adapter;
+
+	adapter = container_of(dev, struct rtsx_adapter, dev);
+	kfree(adapter);
+}
+
+static struct class rtsx_adapter_class = {
+	.name    = "rtsx_adapter",
+	.dev_release = rtsx_free
+};
+
+struct rtsx_adapter *rtsx_alloc_adapter(unsigned int num_sockets,
+					struct device *dev)
+{
+	struct rtsx_adapter *adapter;
+
+	adapter = kzalloc(sizeof(struct rtsx_adapter)
+		     + sizeof(struct rtsx_dev *) * num_sockets, GFP_KERNEL);
+	if (adapter) {
+		adapter->dev.class = &rtsx_adapter_class;
+		adapter->dev.parent = dev;
+		device_initialize(&adapter->dev);
+		spin_lock_init(&adapter->lock);
+		adapter->num_sockets = num_sockets;
+	}
+	return adapter;
+}
+EXPORT_SYMBOL(rtsx_alloc_adapter);
+
+int rtsx_add_adapter(struct rtsx_adapter *adapter)
+{
+	int rc;
+
+	if (!idr_pre_get(&rtsx_adapter_idr, GFP_KERNEL))
+		return -ENOMEM;
+
+	spin_lock(&rtsx_adapter_lock);
+	rc = idr_get_new(&rtsx_adapter_idr, adapter, &adapter->id);
+	spin_unlock(&rtsx_adapter_lock);
+	if (rc)
+		return rc;
+
+	dev_set_name(&adapter->dev, "rtsx%u", adapter->id);
+	rc = device_add(&adapter->dev);
+	if (rc) {
+		spin_lock(&rtsx_adapter_lock);
+		idr_remove(&rtsx_adapter_idr, adapter->id);
+		spin_unlock(&rtsx_adapter_lock);
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(rtsx_add_adapter);
+
+void rtsx_remove_adapter(struct rtsx_adapter *adapter)
+{
+	unsigned int cnt;
+
+	flush_workqueue(workqueue);
+	for (cnt = 0; cnt < adapter->num_sockets; ++cnt) {
+		if (adapter->sockets[cnt])
+			device_unregister(&adapter->sockets[cnt]->dev);
+	}
+
+	spin_lock(&rtsx_adapter_lock);
+	idr_remove(&rtsx_adapter_idr, adapter->id);
+	spin_unlock(&rtsx_adapter_lock);
+	device_del(&adapter->dev);
+}
+EXPORT_SYMBOL(rtsx_remove_adapter);
+
+void rtsx_free_adapter(struct rtsx_adapter *adapter)
+{
+	put_device(&adapter->dev);
+}
+EXPORT_SYMBOL(rtsx_free_adapter);
+
+void rtsx_free_device(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	kfree(sock);
+}
+EXPORT_SYMBOL(rtsx_free_device);
+
+struct rtsx_dev *rtsx_alloc_device(struct rtsx_adapter *adapter,
+		unsigned int id, unsigned char type)
+{
+	struct rtsx_dev *sock = NULL;
+
+	if (!rtsx_media_type_name(type, 0))
+		return sock;
+
+	sock = kzalloc(sizeof(struct rtsx_dev), GFP_KERNEL);
+	if (sock) {
+		spin_lock_init(&sock->lock);
+		sock->type = type;
+		sock->socket_id = id;
+		sock->card_event = rtsx_dummy_event;
+		sock->data_event = rtsx_dummy_event;
+
+		sock->dev.parent = &(adapter->dev);
+		sock->dev.bus = &rtsx_bus_type;
+		sock->dev.dma_mask = adapter->dev.parent->dma_mask;
+		sock->dev.release = rtsx_free_device;
+
+		dev_set_name(&sock->dev, "rtsx_%s%u:%u",
+			     rtsx_media_type_name(type, 2), adapter->id, id);
+		pr_info(DRIVER_NAME
+		       ": %s card detected in socket %u:%u\n",
+		       rtsx_media_type_name(type, 0), adapter->id, id);
+	}
+	return sock;
+}
+EXPORT_SYMBOL(rtsx_alloc_device);
+
+void rtsx_queue_work(struct work_struct *work)
+{
+	queue_work(workqueue, work);
+}
+EXPORT_SYMBOL(rtsx_queue_work);
+
+void rtsx_queue_delayed_work(struct delayed_work *dwork, unsigned long delay)
+{
+	queue_delayed_work(workqueue, dwork, delay);
+}
+EXPORT_SYMBOL(rtsx_queue_delayed_work);
+
+int rtsx_register_driver(struct rtsx_driver *drv)
+{
+	drv->driver.bus = &rtsx_bus_type;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(rtsx_register_driver);
+
+void rtsx_unregister_driver(struct rtsx_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(rtsx_unregister_driver);
+
+void rtsx_switch_clock(struct rtsx_dev *sock, unsigned int card_clock,
+		u8 ssc_depth, int double_clk, int vpclk)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->switch_clock)
+		adapter->switch_clock(adapter,
+				card_clock, ssc_depth, double_clk, vpclk);
+}
+EXPORT_SYMBOL(rtsx_switch_clock);
+
+void rtsx_complete_unfinished_transfer(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->complete_unfinished_transfer)
+		adapter->complete_unfinished_transfer(adapter);
+}
+EXPORT_SYMBOL(rtsx_complete_unfinished_transfer);
+
+void rtsx_sdmmc_set_bus_width(struct rtsx_dev *sock, unsigned char bus_width)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_set_bus_width)
+		adapter->sdmmc_ops.sdmmc_set_bus_width(adapter, bus_width);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_set_bus_width);
+
+void rtsx_sdmmc_set_power_mode(struct rtsx_dev *sock, unsigned char power_mode)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_set_power_mode)
+		adapter->sdmmc_ops.sdmmc_set_power_mode(adapter, power_mode);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_set_power_mode);
+
+void rtsx_sdmmc_set_timing(struct rtsx_dev *sock, unsigned char timing)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_set_timing)
+		adapter->sdmmc_ops.sdmmc_set_timing(adapter, timing);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_set_timing);
+
+int rtsx_sdmmc_switch_voltage(struct rtsx_dev *sock, unsigned char voltage)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_switch_voltage)
+		return adapter->sdmmc_ops.sdmmc_switch_voltage(adapter,
+				voltage);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_switch_voltage);
+
+int rtsx_sdmmc_get_ro(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_get_ro)
+		return adapter->sdmmc_ops.sdmmc_get_ro(adapter);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_get_ro);
+
+int rtsx_sdmmc_get_cd(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_get_cd)
+		return adapter->sdmmc_ops.sdmmc_get_cd(adapter);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_get_cd);
+
+int rtsx_sdmmc_execute_tuning(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_execute_tuning)
+		return adapter->sdmmc_ops.sdmmc_execute_tuning(adapter);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_execute_tuning);
+
+int rtsx_sdmmc_send_cmd_get_rsp(struct rtsx_dev *sock, u8 cmd_idx,
+		u32 arg, unsigned int resp_type, u32 *resp)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_send_cmd_get_rsp)
+		return adapter->sdmmc_ops.sdmmc_send_cmd_get_rsp(adapter,
+				cmd_idx, arg, resp_type, resp);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_send_cmd_get_rsp);
+
+int rtsx_sdmmc_read_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_read_data)
+		return adapter->sdmmc_ops.sdmmc_read_data(adapter, cmd,
+				byte_cnt, buf, buf_len, timeout);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_read_data);
+
+int rtsx_sdmmc_write_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_write_data)
+		return adapter->sdmmc_ops.sdmmc_write_data(adapter, cmd,
+				byte_cnt, buf, buf_len, timeout);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_write_data);
+
+int rtsx_sdmmc_rw_multi(struct rtsx_dev *sock, void *buf, unsigned int blksz,
+		unsigned int blocks, unsigned int use_sg, int read, int uhs)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_rw_multi)
+		return adapter->sdmmc_ops.sdmmc_rw_multi(adapter, buf, blksz,
+				blocks, use_sg, read, uhs);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_rw_multi);
+
+static int __init rtsx_core_init(void)
+{
+	int rc;
+
+	workqueue = create_freezable_workqueue("rtsx_wq");
+	if (!workqueue)
+		return -ENOMEM;
+
+	rc = bus_register(&rtsx_bus_type);
+	if (rc)
+		return rc;
+
+	rc = class_register(&rtsx_adapter_class);
+	if (rc)
+		return rc;
+
+	return rc;
+}
+
+static void __exit rtsx_core_exit(void)
+{
+	class_unregister(&rtsx_adapter_class);
+	bus_unregister(&rtsx_bus_type);
+	destroy_workqueue(workqueue);
+}
+
+module_init(rtsx_core_init);
+module_exit(rtsx_core_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Realtek Corp.");
+MODULE_DESCRIPTION("Realtek Card Reader Core Driver");
diff --git a/include/linux/rtsx_core.h b/include/linux/rtsx_core.h
new file mode 100644
index 0000000..ca20e59
--- /dev/null
+++ b/include/linux/rtsx_core.h
@@ -0,0 +1,180 @@
+/* Realtek card reader core 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
+ */
+
+#ifndef __RTSX_CORE_H
+#define __RTSX_CORE_H
+
+#include <linux/pci.h>
+
+#define RTSX_TYPE_XD			1
+#define RTSX_TYPE_MS			2
+#define RTSX_TYPE_SD			3
+
+#define RTSX_SSC_DEPTH_4M		0x01
+#define RTSX_SSC_DEPTH_2M		0x02
+#define RTSX_SSC_DEPTH_1M		0x03
+#define RTSX_SSC_DEPTH_500K		0x04
+#define RTSX_SSC_DEPTH_250K		0x05
+
+#define wait_timeout_x(task_state, msecs)			\
+do {								\
+	set_current_state((task_state));			\
+	schedule_timeout(msecs_to_jiffies(msecs));		\
+} while (0)
+
+#define wait_timeout(msecs)	wait_timeout_x(TASK_INTERRUPTIBLE, (msecs))
+
+struct rtsx_device_id {
+	unsigned char type;
+};
+
+struct rtsx_dev {
+	char __iomem  *addr;
+	spinlock_t    lock;
+	unsigned char type;
+	unsigned int  socket_id;
+
+	void          (*card_event)(struct rtsx_dev *sock);
+	void          (*data_event)(struct rtsx_dev *sock);
+
+	struct device dev;
+};
+
+struct rtsx_driver {
+	struct rtsx_device_id *id_table;
+	int                   (*probe)(struct rtsx_dev *dev);
+	void                  (*remove)(struct rtsx_dev *dev);
+	int                   (*suspend)(struct rtsx_dev *dev,
+					 pm_message_t state);
+	int                   (*resume)(struct rtsx_dev *dev);
+
+	struct device_driver  driver;
+};
+
+struct rtsx_adapter;
+struct rtsx_sdmmc_ops {
+	int                (*sdmmc_set_bus_width)(
+				struct rtsx_adapter *adapter,
+				unsigned char bus_width);
+	int                (*sdmmc_set_power_mode)(
+				struct rtsx_adapter *adapter,
+				unsigned char power_mode);
+	int                (*sdmmc_set_timing)(struct rtsx_adapter *adapter,
+				unsigned char timing);
+	int                 (*sdmmc_switch_voltage)(
+				struct rtsx_adapter *adapter,
+				unsigned char signal_voltage);
+	int                (*sdmmc_get_ro)(struct rtsx_adapter *adapter);
+	int                (*sdmmc_get_cd)(struct rtsx_adapter *adapter);
+	int                (*sdmmc_execute_tuning)(
+				struct rtsx_adapter *adapter);
+	int                (*sdmmc_send_cmd_get_rsp)(
+				struct rtsx_adapter *adapter, u8 cmd_idx,
+				u32 arg, unsigned int resp_type, u32 *resp);
+	int                (*sdmmc_read_data)(struct rtsx_adapter *adapter,
+				u8 *cmd, u16 byte_cnt, u8 *buf,
+				int buf_len, int timeout);
+	int                (*sdmmc_write_data)(struct rtsx_adapter *adapter,
+				u8 *cmd, u16 byte_cnt, u8 *buf,
+				int buf_len, int timeout);
+	int                (*sdmmc_rw_multi)(struct rtsx_adapter *adapter,
+				void *buf, unsigned int blksz,
+				unsigned int blocks, unsigned int use_sg,
+				int read, int uhs);
+};
+
+#define EXTRA_CAPS_SD_SDR50		(1 << 0)
+#define EXTRA_CAPS_SD_SDR104		(1 << 1)
+#define EXTRA_CAPS_SD_DDR50		(1 << 2)
+#define EXTRA_CAPS_MMC_HSDDR		(1 << 3)
+#define EXTRA_CAPS_MMC_HS200		(1 << 4)
+#define EXTRA_CAPS_MMC_8BIT		(1 << 5)
+
+struct rtsx_adapter {
+	spinlock_t                lock;
+	unsigned int              id;
+	unsigned int              num_sockets;
+	u32                       extra_caps;
+
+	struct device	          dev;
+
+	int                       (*switch_clock)(struct rtsx_adapter *adapter,
+					unsigned int card_clock, u8 ssc_depth,
+					int double_clk, int vpclk);
+	void                      (*complete_unfinished_transfer)(
+					struct rtsx_adapter *adapter);
+
+	struct rtsx_sdmmc_ops     sdmmc_ops;
+
+	struct rtsx_dev           *sockets[0];
+};
+
+static inline struct rtsx_adapter *sock_to_adapter(struct rtsx_dev *sock)
+{
+	return container_of(sock->dev.parent, struct rtsx_adapter, dev);
+}
+
+struct rtsx_adapter *rtsx_alloc_adapter(unsigned int num_sockets,
+					struct device *dev);
+int rtsx_add_adapter(struct rtsx_adapter *adapter);
+void rtsx_remove_adapter(struct rtsx_adapter *adapter);
+void rtsx_free_adapter(struct rtsx_adapter *adapter);
+void rtsx_free_device(struct device *dev);
+struct rtsx_dev *rtsx_alloc_device(struct rtsx_adapter *adapter,
+		unsigned int id, unsigned char type);
+void rtsx_queue_work(struct work_struct *work);
+void rtsx_queue_delayed_work(struct delayed_work *dwork, unsigned long delay);
+int rtsx_register_driver(struct rtsx_driver *drv);
+void rtsx_unregister_driver(struct rtsx_driver *drv);
+
+void rtsx_switch_clock(struct rtsx_dev *sock, unsigned int card_clock,
+		u8 ssc_depth, int double_clk, int vpclk);
+void rtsx_complete_unfinished_transfer(struct rtsx_dev *sock);
+
+void rtsx_sdmmc_set_bus_width(struct rtsx_dev *sock, unsigned char bus_width);
+void rtsx_sdmmc_set_power_mode(struct rtsx_dev *sock, unsigned char power_mode);
+void rtsx_sdmmc_set_timing(struct rtsx_dev *sock, unsigned char timing);
+int rtsx_sdmmc_switch_voltage(struct rtsx_dev *sock, unsigned char voltage);
+int rtsx_sdmmc_get_ro(struct rtsx_dev *sock);
+int rtsx_sdmmc_get_cd(struct rtsx_dev *sock);
+int rtsx_sdmmc_execute_tuning(struct rtsx_dev *sock);
+int rtsx_sdmmc_send_cmd_get_rsp(struct rtsx_dev *sock, u8 cmd_idx,
+		u32 arg, unsigned int resp_type, u32 *resp);
+int rtsx_sdmmc_read_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout);
+int rtsx_sdmmc_write_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout);
+int rtsx_sdmmc_rw_multi(struct rtsx_dev *sock, void *buf, unsigned int blksz,
+		unsigned int blocks, unsigned int use_sg, int read, int uhs);
+
+static inline void *rtsx_get_drvdata(struct rtsx_dev *dev)
+{
+	return dev_get_drvdata(&dev->dev);
+}
+
+static inline void rtsx_set_drvdata(struct rtsx_dev *dev, void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+#endif
+
-- 
1.7.9.5


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

* [PATCH 1/3] drivers/misc: Add realtek card reader core driver
@ 2012-07-20 10:09 wei_wang
  0 siblings, 0 replies; 26+ 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 card reader core driver is the bus driver for Realtek
driver-based card reader, which supplies adapter layer to
be used by lower-level pci/usb card reader and upper-level
sdmmc/memstick host driver.

Signed-off-by: Wei WANG <wei_wang@realsil.com.cn>
---
 Documentation/misc-devices/realtek_cr.txt |   27 ++
 drivers/misc/Kconfig                      |    1 +
 drivers/misc/Makefile                     |    1 +
 drivers/misc/realtek_cr/Kconfig           |   26 ++
 drivers/misc/realtek_cr/Makefile          |    7 +
 drivers/misc/realtek_cr/core/Kconfig      |    6 +
 drivers/misc/realtek_cr/core/Makefile     |    1 +
 drivers/misc/realtek_cr/core/rtsx_core.c  |  492 +++++++++++++++++++++++++++++
 include/linux/rtsx_core.h                 |  183 +++++++++++
 9 files changed, 744 insertions(+)
 create mode 100644 Documentation/misc-devices/realtek_cr.txt
 create mode 100644 drivers/misc/realtek_cr/Kconfig
 create mode 100644 drivers/misc/realtek_cr/Makefile
 create mode 100644 drivers/misc/realtek_cr/core/Kconfig
 create mode 100644 drivers/misc/realtek_cr/core/Makefile
 create mode 100644 drivers/misc/realtek_cr/core/rtsx_core.c
 create mode 100644 include/linux/rtsx_core.h

diff --git a/Documentation/misc-devices/realtek_cr.txt b/Documentation/misc-devices/realtek_cr.txt
new file mode 100644
index 0000000..b4e6fbe
--- /dev/null
+++ b/Documentation/misc-devices/realtek_cr.txt
@@ -0,0 +1,27 @@
+Realtek Driver-based Card Reader
+================================
+
+Supported chips:
+RTS5209
+RTS5229
+
+Contact Email:
+pc_sw_linux@realsil.com.cn
+
+
+Description
+-----------
+
+Realtek driver-based card reader supports access to many types of memory cards,
+such as Memory Stick, Memory Stick Pro, Secure Digital and MultiMediaCard.
+
+
+udev rules
+----------
+
+In order to modprobe Realtek SD/MMC interface driver automatically, the following rule
+should be added to the udev rules file:
+
+SUBSYSTEM=="rtsx_cr", ENV{RTSX_CARD_TYPE}=="SD", RUN+="/sbin/modprobe -bv rtsx_sdmmc"
+
+Typically, we may edit /lib/udev/rules.d/80-drivers.rules and copy the rule into it in Ubuntu.
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 2661f6e..09ce905 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -517,4 +517,5 @@ source "drivers/misc/lis3lv02d/Kconfig"
 source "drivers/misc/carma/Kconfig"
 source "drivers/misc/altera-stapl/Kconfig"
 source "drivers/misc/mei/Kconfig"
+source "drivers/misc/realtek_cr/Kconfig"
 endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 456972f..c09f147 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -51,3 +51,4 @@ obj-y				+= carma/
 obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
 obj-$(CONFIG_ALTERA_STAPL)	+=altera-stapl/
 obj-$(CONFIG_INTEL_MEI)		+= mei/
+obj-$(CONFIG_REALTEK_CR_SUPPORT) += realtek_cr/
diff --git a/drivers/misc/realtek_cr/Kconfig b/drivers/misc/realtek_cr/Kconfig
new file mode 100644
index 0000000..303d98a
--- /dev/null
+++ b/drivers/misc/realtek_cr/Kconfig
@@ -0,0 +1,26 @@
+#
+# Realtek driver-based card reader
+#
+
+menuconfig REALTEK_CR_SUPPORT
+	tristate "Realtek driver-based card reader"
+	help
+	  Realtek driver-based card reader supports access to many types of
+	  memory cards, such as Memory Stick, Memory Stick Pro, Secure Digital
+	  and MultiMediaCard.
+
+	  If you want to use Realtek driver-based card reader, enable this
+	  option and other options below.
+
+config REALTEK_CR_DEBUG
+	bool "Realtek driver-based card reader debugging"
+	depends on REALTEK_CR_SUPPORT != n
+	help
+	  This is an option for use by developers; most people should
+	  say N here.  This enables Realtek card reader driver debugging.
+
+if REALTEK_CR_SUPPORT
+
+source "drivers/misc/realtek_cr/core/Kconfig"
+
+endif
diff --git a/drivers/misc/realtek_cr/Makefile b/drivers/misc/realtek_cr/Makefile
new file mode 100644
index 0000000..f4e16ba
--- /dev/null
+++ b/drivers/misc/realtek_cr/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Realtek driver-based card reader.
+#
+
+subdir-ccflags-$(CONFIG_REALTEK_CR_DEBUG) := -DDEBUG
+
+obj-$(CONFIG_REALTEK_CR_SUPPORT)	+= core/
diff --git a/drivers/misc/realtek_cr/core/Kconfig b/drivers/misc/realtek_cr/core/Kconfig
new file mode 100644
index 0000000..5e9f14e
--- /dev/null
+++ b/drivers/misc/realtek_cr/core/Kconfig
@@ -0,0 +1,6 @@
+config REALTEK_CR_CORE
+	tristate "RealTek Card Reader Core Driver"
+	help
+	  Say Y here to include driver code to support the Realtek
+	  driver-based card reader.
+
diff --git a/drivers/misc/realtek_cr/core/Makefile b/drivers/misc/realtek_cr/core/Makefile
new file mode 100644
index 0000000..010055e
--- /dev/null
+++ b/drivers/misc/realtek_cr/core/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_REALTEK_CR_CORE)		+= rtsx_core.o
diff --git a/drivers/misc/realtek_cr/core/rtsx_core.c b/drivers/misc/realtek_cr/core/rtsx_core.c
new file mode 100644
index 0000000..c3472d5
--- /dev/null
+++ b/drivers/misc/realtek_cr/core/rtsx_core.c
@@ -0,0 +1,492 @@
+/* Realtek card reader core 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/idr.h>
+#include <linux/rtsx_core.h>
+
+static struct workqueue_struct *workqueue;
+static DEFINE_IDR(rtsx_adapter_idr);
+static DEFINE_SPINLOCK(rtsx_adapter_lock);
+
+#define DRIVER_NAME	"rtsx_core"
+
+#ifdef CONFIG_PM
+
+static const char *rtsx_media_type_name(unsigned char type, unsigned char nt)
+{
+	const char *card_type_name[3][3] = {
+		{ "SmartMedia/xD", "MemoryStick", "MMC/SD" },
+		{ "XD", "MS", "SD"},
+		{ "xd", "ms", "sd"}
+	};
+
+	if (nt > 2 || type < 1 || type > 3)
+		return NULL;
+	return card_type_name[nt][type - 1];
+}
+
+static int rtsx_dev_match(struct rtsx_dev *sock, struct rtsx_device_id *id)
+{
+	if (sock->type == id->type)
+		return 1;
+	return 0;
+}
+
+static int rtsx_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *rtsx_drv = container_of(drv, struct rtsx_driver,
+						  driver);
+	struct rtsx_device_id *ids = rtsx_drv->id_table;
+
+	if (ids) {
+		while (ids->type) {
+			if (rtsx_dev_match(sock, ids))
+				return 1;
+			++ids;
+		}
+	}
+	return 0;
+}
+
+static int rtsx_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+
+	if (add_uevent_var(env, "RTSX_CARD_TYPE=%s",
+				rtsx_media_type_name(sock->type, 1)))
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int rtsx_device_probe(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+	int rc = -ENODEV;
+
+	get_device(dev);
+	if (dev->driver && drv->probe) {
+		rc = drv->probe(sock);
+		if (!rc)
+			return 0;
+	}
+	put_device(dev);
+	return rc;
+}
+
+static void rtsx_dummy_event(struct rtsx_dev *sock)
+{
+	return;
+}
+
+static int rtsx_device_remove(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+
+	if (dev->driver && drv->remove) {
+		sock->card_event = rtsx_dummy_event;
+		sock->data_event = rtsx_dummy_event;
+		drv->remove(sock);
+		sock->dev.driver = NULL;
+	}
+
+	put_device(dev);
+	return 0;
+}
+
+static int rtsx_device_suspend(struct device *dev, pm_message_t state)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+
+	if (dev->driver && drv->suspend)
+		return drv->suspend(sock, state);
+	return 0;
+}
+
+static int rtsx_device_resume(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+
+	if (dev->driver && drv->resume)
+		return drv->resume(sock);
+	return 0;
+}
+
+#else
+
+#define rtsx_device_suspend NULL
+#define rtsx_device_resume NULL
+
+#endif /* CONFIG_PM */
+
+static ssize_t type_show(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	return sprintf(buf, "%x", sock->type);
+}
+
+static struct device_attribute rtsx_dev_attrs[] = {
+	__ATTR(type, S_IRUGO, type_show, NULL),
+	__ATTR_NULL
+};
+
+static struct bus_type rtsx_bus_type = {
+	.name      = "rtsx_cr",
+	.dev_attrs = rtsx_dev_attrs,
+	.match     = rtsx_bus_match,
+	.uevent    = rtsx_uevent,
+	.probe     = rtsx_device_probe,
+	.remove    = rtsx_device_remove,
+	.suspend   = rtsx_device_suspend,
+	.resume    = rtsx_device_resume
+};
+
+static void rtsx_free(struct device *dev)
+{
+	struct rtsx_adapter *adapter;
+
+	adapter = container_of(dev, struct rtsx_adapter, dev);
+	kfree(adapter);
+}
+
+static struct class rtsx_adapter_class = {
+	.name    = "rtsx_adapter",
+	.dev_release = rtsx_free
+};
+
+struct rtsx_adapter *rtsx_alloc_adapter(unsigned int num_sockets,
+					struct device *dev)
+{
+	struct rtsx_adapter *adapter;
+
+	adapter = kzalloc(sizeof(struct rtsx_adapter)
+		     + sizeof(struct rtsx_dev *) * num_sockets, GFP_KERNEL);
+	if (adapter) {
+		adapter->dev.class = &rtsx_adapter_class;
+		adapter->dev.parent = dev;
+		device_initialize(&adapter->dev);
+		spin_lock_init(&adapter->lock);
+		adapter->num_sockets = num_sockets;
+	}
+	return adapter;
+}
+EXPORT_SYMBOL(rtsx_alloc_adapter);
+
+int rtsx_add_adapter(struct rtsx_adapter *adapter)
+{
+	int rc;
+
+	if (!idr_pre_get(&rtsx_adapter_idr, GFP_KERNEL))
+		return -ENOMEM;
+
+	spin_lock(&rtsx_adapter_lock);
+	rc = idr_get_new(&rtsx_adapter_idr, adapter, &adapter->id);
+	spin_unlock(&rtsx_adapter_lock);
+	if (rc)
+		return rc;
+
+	dev_set_name(&adapter->dev, "rtsx%u", adapter->id);
+	rc = device_add(&adapter->dev);
+	if (rc) {
+		spin_lock(&rtsx_adapter_lock);
+		idr_remove(&rtsx_adapter_idr, adapter->id);
+		spin_unlock(&rtsx_adapter_lock);
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(rtsx_add_adapter);
+
+void rtsx_remove_adapter(struct rtsx_adapter *adapter)
+{
+	unsigned int cnt;
+
+	flush_workqueue(workqueue);
+	for (cnt = 0; cnt < adapter->num_sockets; ++cnt) {
+		if (adapter->sockets[cnt])
+			device_unregister(&adapter->sockets[cnt]->dev);
+	}
+
+	spin_lock(&rtsx_adapter_lock);
+	idr_remove(&rtsx_adapter_idr, adapter->id);
+	spin_unlock(&rtsx_adapter_lock);
+	device_del(&adapter->dev);
+}
+EXPORT_SYMBOL(rtsx_remove_adapter);
+
+void rtsx_free_adapter(struct rtsx_adapter *adapter)
+{
+	put_device(&adapter->dev);
+}
+EXPORT_SYMBOL(rtsx_free_adapter);
+
+void rtsx_free_device(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	kfree(sock);
+}
+EXPORT_SYMBOL(rtsx_free_device);
+
+struct rtsx_dev *rtsx_alloc_device(struct rtsx_adapter *adapter,
+		unsigned int id, unsigned char type)
+{
+	struct rtsx_dev *sock = NULL;
+
+	if (!rtsx_media_type_name(type, 0))
+		return sock;
+
+	sock = kzalloc(sizeof(struct rtsx_dev), GFP_KERNEL);
+	if (sock) {
+		spin_lock_init(&sock->lock);
+		sock->type = type;
+		sock->socket_id = id;
+		sock->card_event = rtsx_dummy_event;
+		sock->data_event = rtsx_dummy_event;
+
+		sock->dev.parent = &(adapter->dev);
+		sock->dev.bus = &rtsx_bus_type;
+		sock->dev.dma_mask = adapter->dev.parent->dma_mask;
+		sock->dev.release = rtsx_free_device;
+
+		dev_set_name(&sock->dev, "rtsx_%s%u:%u",
+			     rtsx_media_type_name(type, 2), adapter->id, id);
+		pr_info(DRIVER_NAME
+		       ": %s card detected in socket %u:%u\n",
+		       rtsx_media_type_name(type, 0), adapter->id, id);
+	}
+	return sock;
+}
+EXPORT_SYMBOL(rtsx_alloc_device);
+
+void rtsx_queue_work(struct work_struct *work)
+{
+	queue_work(workqueue, work);
+}
+EXPORT_SYMBOL(rtsx_queue_work);
+
+void rtsx_queue_delayed_work(struct delayed_work *dwork, unsigned long delay)
+{
+	queue_delayed_work(workqueue, dwork, delay);
+}
+EXPORT_SYMBOL(rtsx_queue_delayed_work);
+
+int rtsx_register_driver(struct rtsx_driver *drv)
+{
+	drv->driver.bus = &rtsx_bus_type;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(rtsx_register_driver);
+
+void rtsx_unregister_driver(struct rtsx_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(rtsx_unregister_driver);
+
+void rtsx_switch_clock(struct rtsx_dev *sock, unsigned int card_clock,
+		u8 ssc_depth, int double_clk, int vpclk)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->switch_clock)
+		adapter->switch_clock(adapter,
+				card_clock, ssc_depth, double_clk, vpclk);
+}
+EXPORT_SYMBOL(rtsx_switch_clock);
+
+void rtsx_complete_unfinished_transfer(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->complete_unfinished_transfer)
+		adapter->complete_unfinished_transfer(adapter);
+}
+EXPORT_SYMBOL(rtsx_complete_unfinished_transfer);
+
+void rtsx_sdmmc_set_bus_width(struct rtsx_dev *sock, unsigned char bus_width)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_set_bus_width)
+		adapter->sdmmc_ops.sdmmc_set_bus_width(adapter, bus_width);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_set_bus_width);
+
+void rtsx_sdmmc_set_power_mode(struct rtsx_dev *sock, unsigned char power_mode)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_set_power_mode)
+		adapter->sdmmc_ops.sdmmc_set_power_mode(adapter, power_mode);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_set_power_mode);
+
+void rtsx_sdmmc_set_timing(struct rtsx_dev *sock, unsigned char timing)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_set_timing)
+		adapter->sdmmc_ops.sdmmc_set_timing(adapter, timing);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_set_timing);
+
+int rtsx_sdmmc_switch_voltage(struct rtsx_dev *sock, unsigned char voltage)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_switch_voltage)
+		return adapter->sdmmc_ops.sdmmc_switch_voltage(adapter,
+				voltage);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_switch_voltage);
+
+int rtsx_sdmmc_get_ro(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_get_ro)
+		return adapter->sdmmc_ops.sdmmc_get_ro(adapter);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_get_ro);
+
+int rtsx_sdmmc_get_cd(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_get_cd)
+		return adapter->sdmmc_ops.sdmmc_get_cd(adapter);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_get_cd);
+
+int rtsx_sdmmc_execute_tuning(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_execute_tuning)
+		return adapter->sdmmc_ops.sdmmc_execute_tuning(adapter);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_execute_tuning);
+
+int rtsx_sdmmc_send_cmd_get_rsp(struct rtsx_dev *sock, u8 cmd_idx,
+		u32 arg, unsigned int resp_type, u32 *resp)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_send_cmd_get_rsp)
+		return adapter->sdmmc_ops.sdmmc_send_cmd_get_rsp(adapter,
+				cmd_idx, arg, resp_type, resp);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_send_cmd_get_rsp);
+
+int rtsx_sdmmc_read_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_read_data)
+		return adapter->sdmmc_ops.sdmmc_read_data(adapter, cmd,
+				byte_cnt, buf, buf_len, timeout);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_read_data);
+
+int rtsx_sdmmc_write_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_write_data)
+		return adapter->sdmmc_ops.sdmmc_write_data(adapter, cmd,
+				byte_cnt, buf, buf_len, timeout);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_write_data);
+
+int rtsx_sdmmc_rw_multi(struct rtsx_dev *sock, void *buf, unsigned int blksz,
+		unsigned int blocks, unsigned int use_sg, int read, int uhs)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_rw_multi)
+		return adapter->sdmmc_ops.sdmmc_rw_multi(adapter, buf, blksz,
+				blocks, use_sg, read, uhs);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_rw_multi);
+
+static int __init rtsx_core_init(void)
+{
+	int rc;
+
+	workqueue = create_freezable_workqueue("rtsx_wq");
+	if (!workqueue)
+		return -ENOMEM;
+
+	rc = bus_register(&rtsx_bus_type);
+	if (rc)
+		return rc;
+
+	rc = class_register(&rtsx_adapter_class);
+	if (rc)
+		return rc;
+
+	return rc;
+}
+
+static void __exit rtsx_core_exit(void)
+{
+	class_unregister(&rtsx_adapter_class);
+	bus_unregister(&rtsx_bus_type);
+	destroy_workqueue(workqueue);
+}
+
+module_init(rtsx_core_init);
+module_exit(rtsx_core_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Realtek Corp.");
+MODULE_DESCRIPTION("Realtek Card Reader Core Driver");
diff --git a/include/linux/rtsx_core.h b/include/linux/rtsx_core.h
new file mode 100644
index 0000000..7cfbc66
--- /dev/null
+++ b/include/linux/rtsx_core.h
@@ -0,0 +1,183 @@
+/* Realtek card reader core 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
+ */
+
+#ifndef __RTSX_CORE_H
+#define __RTSX_CORE_H
+
+#include <linux/pci.h>
+
+#define RTSX_TYPE_XD			1
+#define RTSX_TYPE_MS			2
+#define RTSX_TYPE_SD			3
+
+#define RTSX_SSC_DEPTH_4M		0x01
+#define RTSX_SSC_DEPTH_2M		0x02
+#define RTSX_SSC_DEPTH_1M		0x03
+#define RTSX_SSC_DEPTH_500K		0x04
+#define RTSX_SSC_DEPTH_250K		0x05
+
+#define wait_timeout_x(task_state, msecs)			\
+do {								\
+	set_current_state((task_state));			\
+	schedule_timeout(msecs_to_jiffies(msecs));		\
+} while (0)
+
+#define wait_timeout(msecs)	wait_timeout_x(TASK_INTERRUPTIBLE, (msecs))
+
+#define GET_BE32(ptr)	(((u32)((ptr)[0]) << 24) | ((u32)((ptr)[1]) << 16) | \
+				((u32)((ptr)[2]) << 8) | (ptr)[3])
+
+struct rtsx_device_id {
+	unsigned char type;
+};
+
+struct rtsx_dev {
+	char __iomem  *addr;
+	spinlock_t    lock;
+	unsigned char type;
+	unsigned int  socket_id;
+
+	void          (*card_event)(struct rtsx_dev *sock);
+	void          (*data_event)(struct rtsx_dev *sock);
+
+	struct device dev;
+};
+
+struct rtsx_driver {
+	struct rtsx_device_id *id_table;
+	int                   (*probe)(struct rtsx_dev *dev);
+	void                  (*remove)(struct rtsx_dev *dev);
+	int                   (*suspend)(struct rtsx_dev *dev,
+					 pm_message_t state);
+	int                   (*resume)(struct rtsx_dev *dev);
+
+	struct device_driver  driver;
+};
+
+struct rtsx_adapter;
+struct rtsx_sdmmc_ops {
+	int                (*sdmmc_set_bus_width)(
+				struct rtsx_adapter *adapter,
+				unsigned char bus_width);
+	int                (*sdmmc_set_power_mode)(
+				struct rtsx_adapter *adapter,
+				unsigned char power_mode);
+	int                (*sdmmc_set_timing)(struct rtsx_adapter *adapter,
+				unsigned char timing);
+	int                 (*sdmmc_switch_voltage)(
+				struct rtsx_adapter *adapter,
+				unsigned char signal_voltage);
+	int                (*sdmmc_get_ro)(struct rtsx_adapter *adapter);
+	int                (*sdmmc_get_cd)(struct rtsx_adapter *adapter);
+	int                (*sdmmc_execute_tuning)(
+				struct rtsx_adapter *adapter);
+	int                (*sdmmc_send_cmd_get_rsp)(
+				struct rtsx_adapter *adapter, u8 cmd_idx,
+				u32 arg, unsigned int resp_type, u32 *resp);
+	int                (*sdmmc_read_data)(struct rtsx_adapter *adapter,
+				u8 *cmd, u16 byte_cnt, u8 *buf,
+				int buf_len, int timeout);
+	int                (*sdmmc_write_data)(struct rtsx_adapter *adapter,
+				u8 *cmd, u16 byte_cnt, u8 *buf,
+				int buf_len, int timeout);
+	int                (*sdmmc_rw_multi)(struct rtsx_adapter *adapter,
+				void *buf, unsigned int blksz,
+				unsigned int blocks, unsigned int use_sg,
+				int read, int uhs);
+};
+
+#define EXTRA_CAPS_SD_SDR50		(1 << 0)
+#define EXTRA_CAPS_SD_SDR104		(1 << 1)
+#define EXTRA_CAPS_SD_DDR50		(1 << 2)
+#define EXTRA_CAPS_MMC_HSDDR		(1 << 3)
+#define EXTRA_CAPS_MMC_HS200		(1 << 4)
+#define EXTRA_CAPS_MMC_8BIT		(1 << 5)
+
+struct rtsx_adapter {
+	spinlock_t                lock;
+	unsigned int              id;
+	unsigned int              num_sockets;
+	u32                       extra_caps;
+
+	struct device	          dev;
+
+	int                       (*switch_clock)(struct rtsx_adapter *adapter,
+					unsigned int card_clock, u8 ssc_depth,
+					int double_clk, int vpclk);
+	void                      (*complete_unfinished_transfer)(
+					struct rtsx_adapter *adapter);
+
+	struct rtsx_sdmmc_ops     sdmmc_ops;
+
+	struct rtsx_dev           *sockets[0];
+};
+
+static inline struct rtsx_adapter *sock_to_adapter(struct rtsx_dev *sock)
+{
+	return container_of(sock->dev.parent, struct rtsx_adapter, dev);
+}
+
+struct rtsx_adapter *rtsx_alloc_adapter(unsigned int num_sockets,
+					struct device *dev);
+int rtsx_add_adapter(struct rtsx_adapter *adapter);
+void rtsx_remove_adapter(struct rtsx_adapter *adapter);
+void rtsx_free_adapter(struct rtsx_adapter *adapter);
+void rtsx_free_device(struct device *dev);
+struct rtsx_dev *rtsx_alloc_device(struct rtsx_adapter *adapter,
+		unsigned int id, unsigned char type);
+void rtsx_queue_work(struct work_struct *work);
+void rtsx_queue_delayed_work(struct delayed_work *dwork, unsigned long delay);
+int rtsx_register_driver(struct rtsx_driver *drv);
+void rtsx_unregister_driver(struct rtsx_driver *drv);
+
+void rtsx_switch_clock(struct rtsx_dev *sock, unsigned int card_clock,
+		u8 ssc_depth, int double_clk, int vpclk);
+void rtsx_complete_unfinished_transfer(struct rtsx_dev *sock);
+
+void rtsx_sdmmc_set_bus_width(struct rtsx_dev *sock, unsigned char bus_width);
+void rtsx_sdmmc_set_power_mode(struct rtsx_dev *sock, unsigned char power_mode);
+void rtsx_sdmmc_set_timing(struct rtsx_dev *sock, unsigned char timing);
+int rtsx_sdmmc_switch_voltage(struct rtsx_dev *sock, unsigned char voltage);
+int rtsx_sdmmc_get_ro(struct rtsx_dev *sock);
+int rtsx_sdmmc_get_cd(struct rtsx_dev *sock);
+int rtsx_sdmmc_execute_tuning(struct rtsx_dev *sock);
+int rtsx_sdmmc_send_cmd_get_rsp(struct rtsx_dev *sock, u8 cmd_idx,
+		u32 arg, unsigned int resp_type, u32 *resp);
+int rtsx_sdmmc_read_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout);
+int rtsx_sdmmc_write_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout);
+int rtsx_sdmmc_rw_multi(struct rtsx_dev *sock, void *buf, unsigned int blksz,
+		unsigned int blocks, unsigned int use_sg, int read, int uhs);
+
+static inline void *rtsx_get_drvdata(struct rtsx_dev *dev)
+{
+	return dev_get_drvdata(&dev->dev);
+}
+
+static inline void rtsx_set_drvdata(struct rtsx_dev *dev, void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+#endif
+
-- 
1.7.9.5


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

* [PATCH 1/3] drivers/misc: Add realtek card reader core driver
@ 2012-07-19  9:54 wei_wang
  0 siblings, 0 replies; 26+ messages in thread
From: wei_wang @ 2012-07-19  9:54 UTC (permalink / raw)
  To: gregkh, devel, linux-kernel; +Cc: Wei WANG

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

Realtek card reader core driver is the bus driver for Realtek
driver-based card reader, which supplies adapter layer to
be used by lower-level pci/usb card reader and upper-level
sdmmc/memstick host driver.

Signed-off-by: Wei WANG <wei_wang@realsil.com.cn>
---
 Documentation/misc-devices/realtek_cr.txt |   27 ++
 drivers/misc/Kconfig                      |    1 +
 drivers/misc/Makefile                     |    1 +
 drivers/misc/realtek_cr/Kconfig           |   26 ++
 drivers/misc/realtek_cr/Makefile          |    7 +
 drivers/misc/realtek_cr/core/Kconfig      |    6 +
 drivers/misc/realtek_cr/core/Makefile     |    1 +
 drivers/misc/realtek_cr/core/rtsx_core.c  |  492 +++++++++++++++++++++++++++++
 include/linux/rtsx_core.h                 |  183 +++++++++++
 9 files changed, 744 insertions(+)
 create mode 100644 Documentation/misc-devices/realtek_cr.txt
 create mode 100644 drivers/misc/realtek_cr/Kconfig
 create mode 100644 drivers/misc/realtek_cr/Makefile
 create mode 100644 drivers/misc/realtek_cr/core/Kconfig
 create mode 100644 drivers/misc/realtek_cr/core/Makefile
 create mode 100644 drivers/misc/realtek_cr/core/rtsx_core.c
 create mode 100644 include/linux/rtsx_core.h

diff --git a/Documentation/misc-devices/realtek_cr.txt b/Documentation/misc-devices/realtek_cr.txt
new file mode 100644
index 0000000..b4e6fbe
--- /dev/null
+++ b/Documentation/misc-devices/realtek_cr.txt
@@ -0,0 +1,27 @@
+Realtek Driver-based Card Reader
+================================
+
+Supported chips:
+RTS5209
+RTS5229
+
+Contact Email:
+pc_sw_linux@realsil.com.cn
+
+
+Description
+-----------
+
+Realtek driver-based card reader supports access to many types of memory cards,
+such as Memory Stick, Memory Stick Pro, Secure Digital and MultiMediaCard.
+
+
+udev rules
+----------
+
+In order to modprobe Realtek SD/MMC interface driver automatically, the following rule
+should be added to the udev rules file:
+
+SUBSYSTEM=="rtsx_cr", ENV{RTSX_CARD_TYPE}=="SD", RUN+="/sbin/modprobe -bv rtsx_sdmmc"
+
+Typically, we may edit /lib/udev/rules.d/80-drivers.rules and copy the rule into it in Ubuntu.
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 2661f6e..09ce905 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -517,4 +517,5 @@ source "drivers/misc/lis3lv02d/Kconfig"
 source "drivers/misc/carma/Kconfig"
 source "drivers/misc/altera-stapl/Kconfig"
 source "drivers/misc/mei/Kconfig"
+source "drivers/misc/realtek_cr/Kconfig"
 endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 456972f..c09f147 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -51,3 +51,4 @@ obj-y				+= carma/
 obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
 obj-$(CONFIG_ALTERA_STAPL)	+=altera-stapl/
 obj-$(CONFIG_INTEL_MEI)		+= mei/
+obj-$(CONFIG_REALTEK_CR_SUPPORT) += realtek_cr/
diff --git a/drivers/misc/realtek_cr/Kconfig b/drivers/misc/realtek_cr/Kconfig
new file mode 100644
index 0000000..303d98a
--- /dev/null
+++ b/drivers/misc/realtek_cr/Kconfig
@@ -0,0 +1,26 @@
+#
+# Realtek driver-based card reader
+#
+
+menuconfig REALTEK_CR_SUPPORT
+	tristate "Realtek driver-based card reader"
+	help
+	  Realtek driver-based card reader supports access to many types of
+	  memory cards, such as Memory Stick, Memory Stick Pro, Secure Digital
+	  and MultiMediaCard.
+
+	  If you want to use Realtek driver-based card reader, enable this
+	  option and other options below.
+
+config REALTEK_CR_DEBUG
+	bool "Realtek driver-based card reader debugging"
+	depends on REALTEK_CR_SUPPORT != n
+	help
+	  This is an option for use by developers; most people should
+	  say N here.  This enables Realtek card reader driver debugging.
+
+if REALTEK_CR_SUPPORT
+
+source "drivers/misc/realtek_cr/core/Kconfig"
+
+endif
diff --git a/drivers/misc/realtek_cr/Makefile b/drivers/misc/realtek_cr/Makefile
new file mode 100644
index 0000000..f4e16ba
--- /dev/null
+++ b/drivers/misc/realtek_cr/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Realtek driver-based card reader.
+#
+
+subdir-ccflags-$(CONFIG_REALTEK_CR_DEBUG) := -DDEBUG
+
+obj-$(CONFIG_REALTEK_CR_SUPPORT)	+= core/
diff --git a/drivers/misc/realtek_cr/core/Kconfig b/drivers/misc/realtek_cr/core/Kconfig
new file mode 100644
index 0000000..5e9f14e
--- /dev/null
+++ b/drivers/misc/realtek_cr/core/Kconfig
@@ -0,0 +1,6 @@
+config REALTEK_CR_CORE
+	tristate "RealTek Card Reader Core Driver"
+	help
+	  Say Y here to include driver code to support the Realtek
+	  driver-based card reader.
+
diff --git a/drivers/misc/realtek_cr/core/Makefile b/drivers/misc/realtek_cr/core/Makefile
new file mode 100644
index 0000000..010055e
--- /dev/null
+++ b/drivers/misc/realtek_cr/core/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_REALTEK_CR_CORE)		+= rtsx_core.o
diff --git a/drivers/misc/realtek_cr/core/rtsx_core.c b/drivers/misc/realtek_cr/core/rtsx_core.c
new file mode 100644
index 0000000..c3472d5
--- /dev/null
+++ b/drivers/misc/realtek_cr/core/rtsx_core.c
@@ -0,0 +1,492 @@
+/* Realtek card reader core 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/idr.h>
+#include <linux/rtsx_core.h>
+
+static struct workqueue_struct *workqueue;
+static DEFINE_IDR(rtsx_adapter_idr);
+static DEFINE_SPINLOCK(rtsx_adapter_lock);
+
+#define DRIVER_NAME	"rtsx_core"
+
+#ifdef CONFIG_PM
+
+static const char *rtsx_media_type_name(unsigned char type, unsigned char nt)
+{
+	const char *card_type_name[3][3] = {
+		{ "SmartMedia/xD", "MemoryStick", "MMC/SD" },
+		{ "XD", "MS", "SD"},
+		{ "xd", "ms", "sd"}
+	};
+
+	if (nt > 2 || type < 1 || type > 3)
+		return NULL;
+	return card_type_name[nt][type - 1];
+}
+
+static int rtsx_dev_match(struct rtsx_dev *sock, struct rtsx_device_id *id)
+{
+	if (sock->type == id->type)
+		return 1;
+	return 0;
+}
+
+static int rtsx_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *rtsx_drv = container_of(drv, struct rtsx_driver,
+						  driver);
+	struct rtsx_device_id *ids = rtsx_drv->id_table;
+
+	if (ids) {
+		while (ids->type) {
+			if (rtsx_dev_match(sock, ids))
+				return 1;
+			++ids;
+		}
+	}
+	return 0;
+}
+
+static int rtsx_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+
+	if (add_uevent_var(env, "RTSX_CARD_TYPE=%s",
+				rtsx_media_type_name(sock->type, 1)))
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int rtsx_device_probe(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+	int rc = -ENODEV;
+
+	get_device(dev);
+	if (dev->driver && drv->probe) {
+		rc = drv->probe(sock);
+		if (!rc)
+			return 0;
+	}
+	put_device(dev);
+	return rc;
+}
+
+static void rtsx_dummy_event(struct rtsx_dev *sock)
+{
+	return;
+}
+
+static int rtsx_device_remove(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+
+	if (dev->driver && drv->remove) {
+		sock->card_event = rtsx_dummy_event;
+		sock->data_event = rtsx_dummy_event;
+		drv->remove(sock);
+		sock->dev.driver = NULL;
+	}
+
+	put_device(dev);
+	return 0;
+}
+
+static int rtsx_device_suspend(struct device *dev, pm_message_t state)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+
+	if (dev->driver && drv->suspend)
+		return drv->suspend(sock, state);
+	return 0;
+}
+
+static int rtsx_device_resume(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	struct rtsx_driver *drv = container_of(dev->driver, struct rtsx_driver,
+					       driver);
+
+	if (dev->driver && drv->resume)
+		return drv->resume(sock);
+	return 0;
+}
+
+#else
+
+#define rtsx_device_suspend NULL
+#define rtsx_device_resume NULL
+
+#endif /* CONFIG_PM */
+
+static ssize_t type_show(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	return sprintf(buf, "%x", sock->type);
+}
+
+static struct device_attribute rtsx_dev_attrs[] = {
+	__ATTR(type, S_IRUGO, type_show, NULL),
+	__ATTR_NULL
+};
+
+static struct bus_type rtsx_bus_type = {
+	.name      = "rtsx_cr",
+	.dev_attrs = rtsx_dev_attrs,
+	.match     = rtsx_bus_match,
+	.uevent    = rtsx_uevent,
+	.probe     = rtsx_device_probe,
+	.remove    = rtsx_device_remove,
+	.suspend   = rtsx_device_suspend,
+	.resume    = rtsx_device_resume
+};
+
+static void rtsx_free(struct device *dev)
+{
+	struct rtsx_adapter *adapter;
+
+	adapter = container_of(dev, struct rtsx_adapter, dev);
+	kfree(adapter);
+}
+
+static struct class rtsx_adapter_class = {
+	.name    = "rtsx_adapter",
+	.dev_release = rtsx_free
+};
+
+struct rtsx_adapter *rtsx_alloc_adapter(unsigned int num_sockets,
+					struct device *dev)
+{
+	struct rtsx_adapter *adapter;
+
+	adapter = kzalloc(sizeof(struct rtsx_adapter)
+		     + sizeof(struct rtsx_dev *) * num_sockets, GFP_KERNEL);
+	if (adapter) {
+		adapter->dev.class = &rtsx_adapter_class;
+		adapter->dev.parent = dev;
+		device_initialize(&adapter->dev);
+		spin_lock_init(&adapter->lock);
+		adapter->num_sockets = num_sockets;
+	}
+	return adapter;
+}
+EXPORT_SYMBOL(rtsx_alloc_adapter);
+
+int rtsx_add_adapter(struct rtsx_adapter *adapter)
+{
+	int rc;
+
+	if (!idr_pre_get(&rtsx_adapter_idr, GFP_KERNEL))
+		return -ENOMEM;
+
+	spin_lock(&rtsx_adapter_lock);
+	rc = idr_get_new(&rtsx_adapter_idr, adapter, &adapter->id);
+	spin_unlock(&rtsx_adapter_lock);
+	if (rc)
+		return rc;
+
+	dev_set_name(&adapter->dev, "rtsx%u", adapter->id);
+	rc = device_add(&adapter->dev);
+	if (rc) {
+		spin_lock(&rtsx_adapter_lock);
+		idr_remove(&rtsx_adapter_idr, adapter->id);
+		spin_unlock(&rtsx_adapter_lock);
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(rtsx_add_adapter);
+
+void rtsx_remove_adapter(struct rtsx_adapter *adapter)
+{
+	unsigned int cnt;
+
+	flush_workqueue(workqueue);
+	for (cnt = 0; cnt < adapter->num_sockets; ++cnt) {
+		if (adapter->sockets[cnt])
+			device_unregister(&adapter->sockets[cnt]->dev);
+	}
+
+	spin_lock(&rtsx_adapter_lock);
+	idr_remove(&rtsx_adapter_idr, adapter->id);
+	spin_unlock(&rtsx_adapter_lock);
+	device_del(&adapter->dev);
+}
+EXPORT_SYMBOL(rtsx_remove_adapter);
+
+void rtsx_free_adapter(struct rtsx_adapter *adapter)
+{
+	put_device(&adapter->dev);
+}
+EXPORT_SYMBOL(rtsx_free_adapter);
+
+void rtsx_free_device(struct device *dev)
+{
+	struct rtsx_dev *sock = container_of(dev, struct rtsx_dev, dev);
+	kfree(sock);
+}
+EXPORT_SYMBOL(rtsx_free_device);
+
+struct rtsx_dev *rtsx_alloc_device(struct rtsx_adapter *adapter,
+		unsigned int id, unsigned char type)
+{
+	struct rtsx_dev *sock = NULL;
+
+	if (!rtsx_media_type_name(type, 0))
+		return sock;
+
+	sock = kzalloc(sizeof(struct rtsx_dev), GFP_KERNEL);
+	if (sock) {
+		spin_lock_init(&sock->lock);
+		sock->type = type;
+		sock->socket_id = id;
+		sock->card_event = rtsx_dummy_event;
+		sock->data_event = rtsx_dummy_event;
+
+		sock->dev.parent = &(adapter->dev);
+		sock->dev.bus = &rtsx_bus_type;
+		sock->dev.dma_mask = adapter->dev.parent->dma_mask;
+		sock->dev.release = rtsx_free_device;
+
+		dev_set_name(&sock->dev, "rtsx_%s%u:%u",
+			     rtsx_media_type_name(type, 2), adapter->id, id);
+		pr_info(DRIVER_NAME
+		       ": %s card detected in socket %u:%u\n",
+		       rtsx_media_type_name(type, 0), adapter->id, id);
+	}
+	return sock;
+}
+EXPORT_SYMBOL(rtsx_alloc_device);
+
+void rtsx_queue_work(struct work_struct *work)
+{
+	queue_work(workqueue, work);
+}
+EXPORT_SYMBOL(rtsx_queue_work);
+
+void rtsx_queue_delayed_work(struct delayed_work *dwork, unsigned long delay)
+{
+	queue_delayed_work(workqueue, dwork, delay);
+}
+EXPORT_SYMBOL(rtsx_queue_delayed_work);
+
+int rtsx_register_driver(struct rtsx_driver *drv)
+{
+	drv->driver.bus = &rtsx_bus_type;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(rtsx_register_driver);
+
+void rtsx_unregister_driver(struct rtsx_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(rtsx_unregister_driver);
+
+void rtsx_switch_clock(struct rtsx_dev *sock, unsigned int card_clock,
+		u8 ssc_depth, int double_clk, int vpclk)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->switch_clock)
+		adapter->switch_clock(adapter,
+				card_clock, ssc_depth, double_clk, vpclk);
+}
+EXPORT_SYMBOL(rtsx_switch_clock);
+
+void rtsx_complete_unfinished_transfer(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->complete_unfinished_transfer)
+		adapter->complete_unfinished_transfer(adapter);
+}
+EXPORT_SYMBOL(rtsx_complete_unfinished_transfer);
+
+void rtsx_sdmmc_set_bus_width(struct rtsx_dev *sock, unsigned char bus_width)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_set_bus_width)
+		adapter->sdmmc_ops.sdmmc_set_bus_width(adapter, bus_width);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_set_bus_width);
+
+void rtsx_sdmmc_set_power_mode(struct rtsx_dev *sock, unsigned char power_mode)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_set_power_mode)
+		adapter->sdmmc_ops.sdmmc_set_power_mode(adapter, power_mode);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_set_power_mode);
+
+void rtsx_sdmmc_set_timing(struct rtsx_dev *sock, unsigned char timing)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_set_timing)
+		adapter->sdmmc_ops.sdmmc_set_timing(adapter, timing);
+}
+EXPORT_SYMBOL(rtsx_sdmmc_set_timing);
+
+int rtsx_sdmmc_switch_voltage(struct rtsx_dev *sock, unsigned char voltage)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_switch_voltage)
+		return adapter->sdmmc_ops.sdmmc_switch_voltage(adapter,
+				voltage);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_switch_voltage);
+
+int rtsx_sdmmc_get_ro(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_get_ro)
+		return adapter->sdmmc_ops.sdmmc_get_ro(adapter);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_get_ro);
+
+int rtsx_sdmmc_get_cd(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_get_cd)
+		return adapter->sdmmc_ops.sdmmc_get_cd(adapter);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_get_cd);
+
+int rtsx_sdmmc_execute_tuning(struct rtsx_dev *sock)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_execute_tuning)
+		return adapter->sdmmc_ops.sdmmc_execute_tuning(adapter);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_execute_tuning);
+
+int rtsx_sdmmc_send_cmd_get_rsp(struct rtsx_dev *sock, u8 cmd_idx,
+		u32 arg, unsigned int resp_type, u32 *resp)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_send_cmd_get_rsp)
+		return adapter->sdmmc_ops.sdmmc_send_cmd_get_rsp(adapter,
+				cmd_idx, arg, resp_type, resp);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_send_cmd_get_rsp);
+
+int rtsx_sdmmc_read_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_read_data)
+		return adapter->sdmmc_ops.sdmmc_read_data(adapter, cmd,
+				byte_cnt, buf, buf_len, timeout);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_read_data);
+
+int rtsx_sdmmc_write_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_write_data)
+		return adapter->sdmmc_ops.sdmmc_write_data(adapter, cmd,
+				byte_cnt, buf, buf_len, timeout);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_write_data);
+
+int rtsx_sdmmc_rw_multi(struct rtsx_dev *sock, void *buf, unsigned int blksz,
+		unsigned int blocks, unsigned int use_sg, int read, int uhs)
+{
+	struct rtsx_adapter *adapter = sock_to_adapter(sock);
+
+	if (adapter->sdmmc_ops.sdmmc_rw_multi)
+		return adapter->sdmmc_ops.sdmmc_rw_multi(adapter, buf, blksz,
+				blocks, use_sg, read, uhs);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtsx_sdmmc_rw_multi);
+
+static int __init rtsx_core_init(void)
+{
+	int rc;
+
+	workqueue = create_freezable_workqueue("rtsx_wq");
+	if (!workqueue)
+		return -ENOMEM;
+
+	rc = bus_register(&rtsx_bus_type);
+	if (rc)
+		return rc;
+
+	rc = class_register(&rtsx_adapter_class);
+	if (rc)
+		return rc;
+
+	return rc;
+}
+
+static void __exit rtsx_core_exit(void)
+{
+	class_unregister(&rtsx_adapter_class);
+	bus_unregister(&rtsx_bus_type);
+	destroy_workqueue(workqueue);
+}
+
+module_init(rtsx_core_init);
+module_exit(rtsx_core_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Realtek Corp.");
+MODULE_DESCRIPTION("Realtek Card Reader Core Driver");
diff --git a/include/linux/rtsx_core.h b/include/linux/rtsx_core.h
new file mode 100644
index 0000000..7cfbc66
--- /dev/null
+++ b/include/linux/rtsx_core.h
@@ -0,0 +1,183 @@
+/* Realtek card reader core 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
+ */
+
+#ifndef __RTSX_CORE_H
+#define __RTSX_CORE_H
+
+#include <linux/pci.h>
+
+#define RTSX_TYPE_XD			1
+#define RTSX_TYPE_MS			2
+#define RTSX_TYPE_SD			3
+
+#define RTSX_SSC_DEPTH_4M		0x01
+#define RTSX_SSC_DEPTH_2M		0x02
+#define RTSX_SSC_DEPTH_1M		0x03
+#define RTSX_SSC_DEPTH_500K		0x04
+#define RTSX_SSC_DEPTH_250K		0x05
+
+#define wait_timeout_x(task_state, msecs)			\
+do {								\
+	set_current_state((task_state));			\
+	schedule_timeout(msecs_to_jiffies(msecs));		\
+} while (0)
+
+#define wait_timeout(msecs)	wait_timeout_x(TASK_INTERRUPTIBLE, (msecs))
+
+#define GET_BE32(ptr)	(((u32)((ptr)[0]) << 24) | ((u32)((ptr)[1]) << 16) | \
+				((u32)((ptr)[2]) << 8) | (ptr)[3])
+
+struct rtsx_device_id {
+	unsigned char type;
+};
+
+struct rtsx_dev {
+	char __iomem  *addr;
+	spinlock_t    lock;
+	unsigned char type;
+	unsigned int  socket_id;
+
+	void          (*card_event)(struct rtsx_dev *sock);
+	void          (*data_event)(struct rtsx_dev *sock);
+
+	struct device dev;
+};
+
+struct rtsx_driver {
+	struct rtsx_device_id *id_table;
+	int                   (*probe)(struct rtsx_dev *dev);
+	void                  (*remove)(struct rtsx_dev *dev);
+	int                   (*suspend)(struct rtsx_dev *dev,
+					 pm_message_t state);
+	int                   (*resume)(struct rtsx_dev *dev);
+
+	struct device_driver  driver;
+};
+
+struct rtsx_adapter;
+struct rtsx_sdmmc_ops {
+	int                (*sdmmc_set_bus_width)(
+				struct rtsx_adapter *adapter,
+				unsigned char bus_width);
+	int                (*sdmmc_set_power_mode)(
+				struct rtsx_adapter *adapter,
+				unsigned char power_mode);
+	int                (*sdmmc_set_timing)(struct rtsx_adapter *adapter,
+				unsigned char timing);
+	int                 (*sdmmc_switch_voltage)(
+				struct rtsx_adapter *adapter,
+				unsigned char signal_voltage);
+	int                (*sdmmc_get_ro)(struct rtsx_adapter *adapter);
+	int                (*sdmmc_get_cd)(struct rtsx_adapter *adapter);
+	int                (*sdmmc_execute_tuning)(
+				struct rtsx_adapter *adapter);
+	int                (*sdmmc_send_cmd_get_rsp)(
+				struct rtsx_adapter *adapter, u8 cmd_idx,
+				u32 arg, unsigned int resp_type, u32 *resp);
+	int                (*sdmmc_read_data)(struct rtsx_adapter *adapter,
+				u8 *cmd, u16 byte_cnt, u8 *buf,
+				int buf_len, int timeout);
+	int                (*sdmmc_write_data)(struct rtsx_adapter *adapter,
+				u8 *cmd, u16 byte_cnt, u8 *buf,
+				int buf_len, int timeout);
+	int                (*sdmmc_rw_multi)(struct rtsx_adapter *adapter,
+				void *buf, unsigned int blksz,
+				unsigned int blocks, unsigned int use_sg,
+				int read, int uhs);
+};
+
+#define EXTRA_CAPS_SD_SDR50		(1 << 0)
+#define EXTRA_CAPS_SD_SDR104		(1 << 1)
+#define EXTRA_CAPS_SD_DDR50		(1 << 2)
+#define EXTRA_CAPS_MMC_HSDDR		(1 << 3)
+#define EXTRA_CAPS_MMC_HS200		(1 << 4)
+#define EXTRA_CAPS_MMC_8BIT		(1 << 5)
+
+struct rtsx_adapter {
+	spinlock_t                lock;
+	unsigned int              id;
+	unsigned int              num_sockets;
+	u32                       extra_caps;
+
+	struct device	          dev;
+
+	int                       (*switch_clock)(struct rtsx_adapter *adapter,
+					unsigned int card_clock, u8 ssc_depth,
+					int double_clk, int vpclk);
+	void                      (*complete_unfinished_transfer)(
+					struct rtsx_adapter *adapter);
+
+	struct rtsx_sdmmc_ops     sdmmc_ops;
+
+	struct rtsx_dev           *sockets[0];
+};
+
+static inline struct rtsx_adapter *sock_to_adapter(struct rtsx_dev *sock)
+{
+	return container_of(sock->dev.parent, struct rtsx_adapter, dev);
+}
+
+struct rtsx_adapter *rtsx_alloc_adapter(unsigned int num_sockets,
+					struct device *dev);
+int rtsx_add_adapter(struct rtsx_adapter *adapter);
+void rtsx_remove_adapter(struct rtsx_adapter *adapter);
+void rtsx_free_adapter(struct rtsx_adapter *adapter);
+void rtsx_free_device(struct device *dev);
+struct rtsx_dev *rtsx_alloc_device(struct rtsx_adapter *adapter,
+		unsigned int id, unsigned char type);
+void rtsx_queue_work(struct work_struct *work);
+void rtsx_queue_delayed_work(struct delayed_work *dwork, unsigned long delay);
+int rtsx_register_driver(struct rtsx_driver *drv);
+void rtsx_unregister_driver(struct rtsx_driver *drv);
+
+void rtsx_switch_clock(struct rtsx_dev *sock, unsigned int card_clock,
+		u8 ssc_depth, int double_clk, int vpclk);
+void rtsx_complete_unfinished_transfer(struct rtsx_dev *sock);
+
+void rtsx_sdmmc_set_bus_width(struct rtsx_dev *sock, unsigned char bus_width);
+void rtsx_sdmmc_set_power_mode(struct rtsx_dev *sock, unsigned char power_mode);
+void rtsx_sdmmc_set_timing(struct rtsx_dev *sock, unsigned char timing);
+int rtsx_sdmmc_switch_voltage(struct rtsx_dev *sock, unsigned char voltage);
+int rtsx_sdmmc_get_ro(struct rtsx_dev *sock);
+int rtsx_sdmmc_get_cd(struct rtsx_dev *sock);
+int rtsx_sdmmc_execute_tuning(struct rtsx_dev *sock);
+int rtsx_sdmmc_send_cmd_get_rsp(struct rtsx_dev *sock, u8 cmd_idx,
+		u32 arg, unsigned int resp_type, u32 *resp);
+int rtsx_sdmmc_read_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout);
+int rtsx_sdmmc_write_data(struct rtsx_dev *sock, u8 *cmd,
+		u16 byte_cnt, u8 *buf, int buf_len, int timeout);
+int rtsx_sdmmc_rw_multi(struct rtsx_dev *sock, void *buf, unsigned int blksz,
+		unsigned int blocks, unsigned int use_sg, int read, int uhs);
+
+static inline void *rtsx_get_drvdata(struct rtsx_dev *dev)
+{
+	return dev_get_drvdata(&dev->dev);
+}
+
+static inline void rtsx_set_drvdata(struct rtsx_dev *dev, void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+#endif
+
-- 
1.7.9.5


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

end of thread, other threads:[~2012-08-13 20:59 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-07-23  9:42 [PATCH 1/3] drivers/misc: Add realtek card reader core driver wei_wang
2012-07-23 11:17 ` Dan Carpenter
2012-07-26  3:11   ` wwang
2012-07-26  3:59     ` gregkh
2012-07-23 11:24 ` [PATCH 1/3] drivers/misc: Add " Borislav Petkov
2012-07-23 15:14 ` Matthew Garrett
2012-07-23 16:33 ` Borislav Petkov
2012-07-24  1:21   ` wwang
2012-07-24  6:44     ` Borislav Petkov
2012-07-24  8:47       ` wwang
2012-07-24 14:07         ` Borislav Petkov
     [not found]           ` <500F59D7.2050508@realsil.com.cn>
2012-07-25  8:04             ` Borislav Petkov
2012-07-25 10:16               ` Aaron Lu
2012-07-26  1:34                 ` wwang
2012-07-26  8:43                   ` Borislav Petkov
  -- strict thread matches above, loose matches on Subject: below --
2012-07-31  7:42 wei_wang
2012-07-31  7:42 ` wei_wang
2012-07-31 11:23 ` Arnd Bergmann
2012-08-01  6:19   ` wwang
2012-08-01 14:31     ` Arnd Bergmann
2012-08-03  2:31       ` wwang
2012-08-03 14:39         ` Arnd Bergmann
2012-08-13 20:59       ` Maxim Levitsky
2012-07-25  3:02 wei_wang
2012-07-20 10:09 wei_wang
2012-07-19  9:54 wei_wang

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.