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

wei_wang
2012-07-25T03:02:51Z

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