[PATCH 1/3] drivers/misc: Add realtek card reader core driver

wei_wang at realsil.com.cn wei_wang at realsil.com.cn
Fri Jul 20 10:09:18 UTC 2012


From: Wei WANG <wei_wang at 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 at 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 at 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 at 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 at 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




More information about the devel mailing list