From: Wei WANG <wei_w...@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_w...@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_li...@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_w...@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_w...@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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to