* [PATCH net-next V2 3/3] i2400m-sdio: select IWMC3200TOP in Kconfig
From: Tomas Winkler @ 2009-10-17 19:09 UTC (permalink / raw)
To: davem-fT/PcQaiUtIeIZ0/mPfg9Q, linville-2XuSBdqkA4R54TAoqtyWWQ,
netdev-u79uwXL29TY76Z2rM5mHXA,
linux-wireless-u79uwXL29TY76Z2rM5mHXA,
linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: yi.zhu-ral2JQCrhuEAvxtiuMwx3w,
inaky.perez-gonzalez-ral2JQCrhuEAvxtiuMwx3w,
cindy.h.kao-ral2JQCrhuEAvxtiuMwx3w,
guy.cohen-ral2JQCrhuEAvxtiuMwx3w,
ron.rindjunsky-ral2JQCrhuEAvxtiuMwx3w, Tomas Winkler
In-Reply-To: <1255806576-26869-2-git-send-email-tomas.winkler-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
i2400m-sdio requires iwmc3200top for its operation
Signed-off-by: Tomas Winkler <tomas.winkler-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Acked-by: Inaky Perez-Gonzalez <inaky-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
---
drivers/net/wimax/i2400m/Kconfig | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/drivers/net/wimax/i2400m/Kconfig b/drivers/net/wimax/i2400m/Kconfig
index d623b3d..32cb214 100644
--- a/drivers/net/wimax/i2400m/Kconfig
+++ b/drivers/net/wimax/i2400m/Kconfig
@@ -31,6 +31,14 @@ config WIMAX_I2400M_SDIO
If unsure, it is safe to select M (module).
+config WIMAX_IWMC3200_SDIO
+ bool "Intel Wireless Multicom WiMAX Connection 3200 over SDIO"
+ depends on WIMAX_I2400M_SDIO
+ select IWMC3200TOP
+ help
+ Select if you have a device based on the Intel Multicom WiMAX
+ Connection 3200 over SDIO.
+
config WIMAX_I2400M_DEBUG_LEVEL
int "WiMAX i2400m debug level"
depends on WIMAX_I2400M
--
1.6.0.6
---------------------------------------------------------------------
Intel Israel (74) Limited
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH net-next V2 2/3] iwmc3200wifi: select IWMC3200TOP in Kconfig
From: Tomas Winkler @ 2009-10-17 19:09 UTC (permalink / raw)
To: davem, linville, netdev, linux-wireless, linux-mmc
Cc: yi.zhu, inaky.perez-gonzalez, cindy.h.kao, guy.cohen,
ron.rindjunsky, Tomas Winkler
In-Reply-To: <1255806576-26869-1-git-send-email-tomas.winkler@intel.com>
iwmc3200wifi requires iwmc3200top for its operation
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Acked-by: Zhu Yi <yi.zhu@intel.com>
---
drivers/net/wireless/iwmc3200wifi/Kconfig | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/drivers/net/wireless/iwmc3200wifi/Kconfig b/drivers/net/wireless/iwmc3200wifi/Kconfig
index c25a043..9606b31 100644
--- a/drivers/net/wireless/iwmc3200wifi/Kconfig
+++ b/drivers/net/wireless/iwmc3200wifi/Kconfig
@@ -3,6 +3,7 @@ config IWM
depends on MMC && WLAN_80211 && EXPERIMENTAL
depends on CFG80211
select FW_LOADER
+ select IWMC3200TOP
help
The Intel Wireless Multicomm 3200 hardware is a combo
card with GPS, Bluetooth, WiMax and 802.11 radios. It
--
1.6.0.6
---------------------------------------------------------------------
Intel Israel (74) Limited
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply related
* [PATCH net-next V2 1/3] iwmc3200top: Add Intel Wireless MultiCom 3200 top driver.
From: Tomas Winkler @ 2009-10-17 19:09 UTC (permalink / raw)
To: davem-fT/PcQaiUtIeIZ0/mPfg9Q, linville-2XuSBdqkA4R54TAoqtyWWQ,
netdev-u79uwXL29TY76Z2rM5mHXA,
linux-wireless-u79uwXL29TY76Z2rM5mHXA,
linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: yi.zhu-ral2JQCrhuEAvxtiuMwx3w,
inaky.perez-gonzalez-ral2JQCrhuEAvxtiuMwx3w,
cindy.h.kao-ral2JQCrhuEAvxtiuMwx3w,
guy.cohen-ral2JQCrhuEAvxtiuMwx3w,
ron.rindjunsky-ral2JQCrhuEAvxtiuMwx3w, Tomas Winkler
This patch adds Intel Wireless MultiCom 3200 top driver.
IWMC3200 is 4Wireless Com CHIP (GPS/BT/WiFi/WiMAX).
Top driver is responsible for device initialization and firmware download.
Firmware handled by top is responsible for top itself and
as well as bluetooth and GPS coms. (Wifi and WiMax provide their own firmware)
In addition top driver is used to retrieve firmware logs
and supports other debugging features
Signed-off-by: Tomas Winkler <tomas.winkler-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---
V2:
1. fixing barker management
2. use dev_get_drvdata instead of d->driver_data
3. cleanup debugfs
drivers/misc/Kconfig | 1 +
drivers/misc/Makefile | 1 +
drivers/misc/iwmc3200top/Kconfig | 20 +
drivers/misc/iwmc3200top/Makefile | 29 ++
drivers/misc/iwmc3200top/debugfs.c | 133 ++++++
drivers/misc/iwmc3200top/debugfs.h | 58 +++
drivers/misc/iwmc3200top/fw-download.c | 359 ++++++++++++++++
drivers/misc/iwmc3200top/fw-msg.h | 113 +++++
drivers/misc/iwmc3200top/iwmc3200top.h | 206 ++++++++++
drivers/misc/iwmc3200top/log.c | 347 ++++++++++++++++
drivers/misc/iwmc3200top/log.h | 158 +++++++
drivers/misc/iwmc3200top/main.c | 699 ++++++++++++++++++++++++++++++++
12 files changed, 2124 insertions(+), 0 deletions(-)
create mode 100644 drivers/misc/iwmc3200top/Kconfig
create mode 100644 drivers/misc/iwmc3200top/Makefile
create mode 100644 drivers/misc/iwmc3200top/debugfs.c
create mode 100644 drivers/misc/iwmc3200top/debugfs.h
create mode 100644 drivers/misc/iwmc3200top/fw-download.c
create mode 100644 drivers/misc/iwmc3200top/fw-msg.h
create mode 100644 drivers/misc/iwmc3200top/iwmc3200top.h
create mode 100644 drivers/misc/iwmc3200top/log.c
create mode 100644 drivers/misc/iwmc3200top/log.h
create mode 100644 drivers/misc/iwmc3200top/main.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index df1f86b..a2ea383 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -249,5 +249,6 @@ config EP93XX_PWM
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
+source "drivers/misc/iwmc3200top/Kconfig"
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index f982d2e..e311267 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -21,5 +21,6 @@ obj-$(CONFIG_HP_ILO) += hpilo.o
obj-$(CONFIG_ISL29003) += isl29003.o
obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o
obj-$(CONFIG_C2PORT) += c2port/
+obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/
obj-y += eeprom/
obj-y += cb710/
diff --git a/drivers/misc/iwmc3200top/Kconfig b/drivers/misc/iwmc3200top/Kconfig
new file mode 100644
index 0000000..9e4b88f
--- /dev/null
+++ b/drivers/misc/iwmc3200top/Kconfig
@@ -0,0 +1,20 @@
+config IWMC3200TOP
+ tristate "Intel Wireless MultiCom Top Driver"
+ depends on MMC && EXPERIMENTAL
+ select FW_LOADER
+ ---help---
+ Intel Wireless MultiCom 3200 Top driver is responsible for
+ for firmware load and enabled coms enumeration
+
+config IWMC3200TOP_DEBUG
+ bool "Enable full debug output of iwmc3200top Driver"
+ depends on IWMC3200TOP
+ ---help---
+ Enable full debug output of iwmc3200top Driver
+
+config IWMC3200TOP_DEBUGFS
+ bool "Enable Debugfs debugging interface for iwmc3200top"
+ depends on IWMC3200TOP
+ ---help---
+ Enable creation of debugfs files for iwmc3200top
+
diff --git a/drivers/misc/iwmc3200top/Makefile b/drivers/misc/iwmc3200top/Makefile
new file mode 100644
index 0000000..fbf53fb
--- /dev/null
+++ b/drivers/misc/iwmc3200top/Makefile
@@ -0,0 +1,29 @@
+# iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+# drivers/misc/iwmc3200top/Makefile
+#
+# Copyright (C) 2009 Intel Corporation. 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 version
+# 2 as published by the Free Software Foundation.
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+#
+# Author Name: Maxim Grabarnik <maxim.grabarnink-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
+# -
+#
+#
+
+obj-$(CONFIG_IWMC3200TOP) += iwmc3200top.o
+iwmc3200top-objs := main.o fw-download.o
+iwmc3200top-$(CONFIG_IWMC3200TOP_DEBUG) += log.o
+iwmc3200top-$(CONFIG_IWMC3200TOP_DEBUGFS) += debugfs.o
diff --git a/drivers/misc/iwmc3200top/debugfs.c b/drivers/misc/iwmc3200top/debugfs.c
new file mode 100644
index 0000000..0c8ea0a
--- /dev/null
+++ b/drivers/misc/iwmc3200top/debugfs.c
@@ -0,0 +1,133 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/debufs.c
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 version
+ * 2 as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
+ * -
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio.h>
+#include <linux/debugfs.h>
+
+#include "iwmc3200top.h"
+#include "fw-msg.h"
+#include "log.h"
+#include "debugfs.h"
+
+
+
+/* Constants definition */
+#define HEXADECIMAL_RADIX 16
+
+/* Functions definition */
+
+
+#define DEBUGFS_ADD(name, parent) do { \
+ dbgfs->dbgfs_##parent##_files.file_##name = \
+ debugfs_create_file(#name, 0644, dbgfs->dir_##parent, priv, \
+ &iwmct_dbgfs_##name##_ops); \
+} while (0)
+
+#define DEBUGFS_RM(name) do { \
+ debugfs_remove(name); \
+ name = NULL; \
+} while (0)
+
+#define DEBUGFS_READ_FUNC(name) \
+ssize_t iwmct_dbgfs_##name##_read(struct file *file, \
+ char __user *user_buf, \
+ size_t count, loff_t *ppos);
+
+#define DEBUGFS_WRITE_FUNC(name) \
+ssize_t iwmct_dbgfs_##name##_write(struct file *file, \
+ const char __user *user_buf, \
+ size_t count, loff_t *ppos);
+
+#define DEBUGFS_READ_FILE_OPS(name) \
+ DEBUGFS_READ_FUNC(name) \
+ static const struct file_operations iwmct_dbgfs_##name##_ops = { \
+ .read = iwmct_dbgfs_##name##_read, \
+ .open = iwmct_dbgfs_open_file_generic, \
+ };
+
+#define DEBUGFS_WRITE_FILE_OPS(name) \
+ DEBUGFS_WRITE_FUNC(name) \
+ static const struct file_operations iwmct_dbgfs_##name##_ops = { \
+ .write = iwmct_dbgfs_##name##_write, \
+ .open = iwmct_dbgfs_open_file_generic, \
+ };
+
+#define DEBUGFS_READ_WRITE_FILE_OPS(name) \
+ DEBUGFS_READ_FUNC(name) \
+ DEBUGFS_WRITE_FUNC(name) \
+ static const struct file_operations iwmct_dbgfs_##name##_ops = {\
+ .write = iwmct_dbgfs_##name##_write, \
+ .read = iwmct_dbgfs_##name##_read, \
+ .open = iwmct_dbgfs_open_file_generic, \
+ };
+
+
+/* Debugfs file ops definitions */
+
+/*
+ * Create the debugfs files and directories
+ *
+ */
+void iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name)
+{
+ struct iwmct_debugfs *dbgfs;
+
+ dbgfs = kzalloc(sizeof(struct iwmct_debugfs), GFP_KERNEL);
+ if (!dbgfs) {
+ LOG_ERROR(priv, DEBUGFS, "failed to allocate %zd bytes\n",
+ sizeof(struct iwmct_debugfs));
+ return;
+ }
+
+ priv->dbgfs = dbgfs;
+ dbgfs->name = name;
+ dbgfs->dir_drv = debugfs_create_dir(name, NULL);
+ if (!dbgfs->dir_drv) {
+ LOG_ERROR(priv, DEBUGFS, "failed to create debugfs dir\n");
+ return;
+ }
+
+ return;
+}
+
+/**
+ * Remove the debugfs files and directories
+ *
+ */
+void iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs)
+{
+ if (!dbgfs)
+ return;
+
+ DEBUGFS_RM(dbgfs->dir_drv);
+ kfree(dbgfs);
+ dbgfs = NULL;
+}
+
diff --git a/drivers/misc/iwmc3200top/debugfs.h b/drivers/misc/iwmc3200top/debugfs.h
new file mode 100644
index 0000000..71d4575
--- /dev/null
+++ b/drivers/misc/iwmc3200top/debugfs.h
@@ -0,0 +1,58 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/debufs.h
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 version
+ * 2 as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
+ * -
+ *
+ */
+
+#ifndef __DEBUGFS_H__
+#define __DEBUGFS_H__
+
+
+#ifdef CONFIG_IWMC3200TOP_DEBUGFS
+
+struct iwmct_debugfs {
+ const char *name;
+ struct dentry *dir_drv;
+ struct dir_drv_files {
+ } dbgfs_drv_files;
+};
+
+void iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name);
+void iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs);
+
+#else /* CONFIG_IWMC3200TOP_DEBUGFS */
+
+struct iwmct_debugfs;
+
+static inline void
+iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name)
+{}
+
+static inline void
+iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs)
+{}
+
+#endif /* CONFIG_IWMC3200TOP_DEBUGFS */
+
+#endif /* __DEBUGFS_H__ */
+
diff --git a/drivers/misc/iwmc3200top/fw-download.c b/drivers/misc/iwmc3200top/fw-download.c
new file mode 100644
index 0000000..33cb693
--- /dev/null
+++ b/drivers/misc/iwmc3200top/fw-download.c
@@ -0,0 +1,359 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/fw-download.c
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 version
+ * 2 as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
+ * -
+ *
+ */
+
+#include <linux/firmware.h>
+#include <linux/mmc/sdio_func.h>
+#include <asm/unaligned.h>
+
+#include "iwmc3200top.h"
+#include "log.h"
+#include "fw-msg.h"
+
+#define CHECKSUM_BYTES_NUM sizeof(u32)
+
+/**
+ init parser struct with file
+ */
+static int iwmct_fw_parser_init(struct iwmct_priv *priv, const u8 *file,
+ size_t file_size, size_t block_size)
+{
+ struct iwmct_parser *parser = &priv->parser;
+ struct iwmct_fw_hdr *fw_hdr = &parser->versions;
+
+ LOG_INFOEX(priv, INIT, "-->\n");
+
+ LOG_INFO(priv, FW_DOWNLOAD, "file_size=%zd\n", file_size);
+
+ parser->file = file;
+ parser->file_size = file_size;
+ parser->cur_pos = 0;
+ parser->buf = NULL;
+
+ parser->buf = kzalloc(block_size, GFP_KERNEL);
+ if (!parser->buf) {
+ LOG_ERROR(priv, FW_DOWNLOAD, "kzalloc error\n");
+ return -ENOMEM;
+ }
+ parser->buf_size = block_size;
+
+ /* extract fw versions */
+ memcpy(fw_hdr, parser->file, sizeof(struct iwmct_fw_hdr));
+ LOG_INFO(priv, FW_DOWNLOAD, "fw versions are:\n"
+ "top %u.%u.%u gps %u.%u.%u bt %u.%u.%u tic %s\n",
+ fw_hdr->top_major, fw_hdr->top_minor, fw_hdr->top_revision,
+ fw_hdr->gps_major, fw_hdr->gps_minor, fw_hdr->gps_revision,
+ fw_hdr->bt_major, fw_hdr->bt_minor, fw_hdr->bt_revision,
+ fw_hdr->tic_name);
+
+ parser->cur_pos += sizeof(struct iwmct_fw_hdr);
+
+ LOG_INFOEX(priv, INIT, "<--\n");
+ return 0;
+}
+
+static bool iwmct_checksum(struct iwmct_priv *priv)
+{
+ struct iwmct_parser *parser = &priv->parser;
+ __le32 *file = (__le32 *)parser->file;
+ int i, pad, steps;
+ u32 accum = 0;
+ u32 checksum;
+ u32 mask = 0xffffffff;
+
+ pad = (parser->file_size - CHECKSUM_BYTES_NUM) % 4;
+ steps = (parser->file_size - CHECKSUM_BYTES_NUM) / 4;
+
+ LOG_INFO(priv, FW_DOWNLOAD, "pad=%d steps=%d\n", pad, steps);
+
+ for (i = 0; i < steps; i++)
+ accum += le32_to_cpu(file[i]);
+
+ if (pad) {
+ mask <<= 8 * (4 - pad);
+ accum += le32_to_cpu(file[steps]) & mask;
+ }
+
+ checksum = get_unaligned_le32((__le32 *)(parser->file +
+ parser->file_size - CHECKSUM_BYTES_NUM));
+
+ LOG_INFO(priv, FW_DOWNLOAD,
+ "compare checksum accum=0x%x to checksum=0x%x\n",
+ accum, checksum);
+
+ return checksum == accum;
+}
+
+static int iwmct_parse_next_section(struct iwmct_priv *priv, const u8 **p_sec,
+ size_t *sec_size, __le32 *sec_addr)
+{
+ struct iwmct_parser *parser = &priv->parser;
+ struct iwmct_dbg *dbg = &priv->dbg;
+ struct iwmct_fw_sec_hdr *sec_hdr;
+
+ LOG_INFOEX(priv, INIT, "-->\n");
+
+ while (parser->cur_pos + sizeof(struct iwmct_fw_sec_hdr)
+ <= parser->file_size) {
+
+ sec_hdr = (struct iwmct_fw_sec_hdr *)
+ (parser->file + parser->cur_pos);
+ parser->cur_pos += sizeof(struct iwmct_fw_sec_hdr);
+
+ LOG_INFO(priv, FW_DOWNLOAD,
+ "sec hdr: type=%s addr=0x%x size=%d\n",
+ sec_hdr->type, sec_hdr->target_addr,
+ sec_hdr->data_size);
+
+ if (strcmp(sec_hdr->type, "ENT") == 0)
+ parser->entry_point = le32_to_cpu(sec_hdr->target_addr);
+ else if (strcmp(sec_hdr->type, "LBL") == 0)
+ strcpy(dbg->label_fw, parser->file + parser->cur_pos);
+ else if (((strcmp(sec_hdr->type, "TOP") == 0) &&
+ (priv->barker & BARKER_DNLOAD_TOP_MSK)) ||
+ ((strcmp(sec_hdr->type, "GPS") == 0) &&
+ (priv->barker & BARKER_DNLOAD_GPS_MSK)) ||
+ ((strcmp(sec_hdr->type, "BTH") == 0) &&
+ (priv->barker & BARKER_DNLOAD_BT_MSK))) {
+ *sec_addr = sec_hdr->target_addr;
+ *sec_size = le32_to_cpu(sec_hdr->data_size);
+ *p_sec = parser->file + parser->cur_pos;
+ parser->cur_pos += le32_to_cpu(sec_hdr->data_size);
+ return 1;
+ } else if (strcmp(sec_hdr->type, "LOG") != 0)
+ LOG_WARNING(priv, FW_DOWNLOAD,
+ "skipping section type %s\n",
+ sec_hdr->type);
+
+ parser->cur_pos += le32_to_cpu(sec_hdr->data_size);
+ LOG_INFO(priv, FW_DOWNLOAD,
+ "finished with section cur_pos=%zd\n", parser->cur_pos);
+ }
+
+ LOG_INFOEX(priv, INIT, "<--\n");
+ return 0;
+}
+
+static int iwmct_download_section(struct iwmct_priv *priv, const u8 *p_sec,
+ size_t sec_size, __le32 addr)
+{
+ struct iwmct_parser *parser = &priv->parser;
+ struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf;
+ const u8 *cur_block = p_sec;
+ size_t sent = 0;
+ int cnt = 0;
+ int ret = 0;
+ u32 cmd = 0;
+
+ LOG_INFOEX(priv, INIT, "-->\n");
+ LOG_INFO(priv, FW_DOWNLOAD, "Download address 0x%x size 0x%zx\n",
+ addr, sec_size);
+
+ while (sent < sec_size) {
+ int i;
+ u32 chksm = 0;
+ u32 reset = atomic_read(&priv->reset);
+ /* actual FW data */
+ u32 data_size = min(parser->buf_size - sizeof(*hdr),
+ sec_size - sent);
+ /* Pad to block size */
+ u32 trans_size = (data_size + sizeof(*hdr) +
+ IWMC_SDIO_BLK_SIZE - 1) &
+ ~(IWMC_SDIO_BLK_SIZE - 1);
+ ++cnt;
+
+ /* in case of reset, interrupt FW DOWNLAOD */
+ if (reset) {
+ LOG_INFO(priv, FW_DOWNLOAD,
+ "Reset detected. Abort FW download!!!");
+ ret = -ECANCELED;
+ goto exit;
+ }
+
+ memset(parser->buf, 0, parser->buf_size);
+ cmd |= IWMC_OPCODE_WRITE << CMD_HDR_OPCODE_POS;
+ cmd |= IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS;
+ cmd |= (priv->dbg.direct ? 1 : 0) << CMD_HDR_DIRECT_ACCESS_POS;
+ cmd |= (priv->dbg.checksum ? 1 : 0) << CMD_HDR_USE_CHECKSUM_POS;
+ hdr->data_size = cpu_to_le32(data_size);
+ hdr->target_addr = addr;
+
+ /* checksum is allowed for sizes divisible by 4 */
+ if (data_size & 0x3)
+ cmd &= ~CMD_HDR_USE_CHECKSUM_MSK;
+
+ memcpy(hdr->data, cur_block, data_size);
+
+
+ if (cmd & CMD_HDR_USE_CHECKSUM_MSK) {
+
+ chksm = data_size + le32_to_cpu(addr) + cmd;
+ for (i = 0; i < data_size >> 2; i++)
+ chksm += ((u32 *)cur_block)[i];
+
+ hdr->block_chksm = cpu_to_le32(chksm);
+ LOG_INFO(priv, FW_DOWNLOAD, "Checksum = 0x%X\n",
+ hdr->block_chksm);
+ }
+
+ LOG_INFO(priv, FW_DOWNLOAD, "trans#%d, len=%d, sent=%zd, "
+ "sec_size=%zd, startAddress 0x%X\n",
+ cnt, trans_size, sent, sec_size, addr);
+
+ if (priv->dbg.dump)
+ LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, trans_size);
+
+
+ hdr->cmd = cpu_to_le32(cmd);
+ /* send it down */
+ /* TODO: add more proper sending and error checking */
+ ret = iwmct_tx(priv, 0, parser->buf, trans_size);
+ if (ret != 0) {
+ LOG_INFO(priv, FW_DOWNLOAD,
+ "iwmct_tx returned %d\n", ret);
+ goto exit;
+ }
+
+ addr = cpu_to_le32(le32_to_cpu(addr) + data_size);
+ sent += data_size;
+ cur_block = p_sec + sent;
+
+ if (priv->dbg.blocks && (cnt + 1) >= priv->dbg.blocks) {
+ LOG_INFO(priv, FW_DOWNLOAD,
+ "Block number limit is reached [%d]\n",
+ priv->dbg.blocks);
+ break;
+ }
+ }
+
+ if (sent < sec_size)
+ ret = -EINVAL;
+exit:
+ LOG_INFOEX(priv, INIT, "<--\n");
+ return ret;
+}
+
+static int iwmct_kick_fw(struct iwmct_priv *priv, bool jump)
+{
+ struct iwmct_parser *parser = &priv->parser;
+ struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf;
+ int ret;
+ u32 cmd;
+
+ LOG_INFOEX(priv, INIT, "-->\n");
+
+ memset(parser->buf, 0, parser->buf_size);
+ cmd = IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS;
+ if (jump) {
+ cmd |= IWMC_OPCODE_JUMP << CMD_HDR_OPCODE_POS;
+ hdr->target_addr = cpu_to_le32(parser->entry_point);
+ LOG_INFO(priv, FW_DOWNLOAD, "jump address 0x%x\n",
+ parser->entry_point);
+ } else {
+ cmd |= IWMC_OPCODE_LAST_COMMAND << CMD_HDR_OPCODE_POS;
+ LOG_INFO(priv, FW_DOWNLOAD, "last command\n");
+ }
+
+ hdr->cmd = cpu_to_le32(cmd);
+
+ LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, sizeof(*hdr));
+ /* send it down */
+ /* TODO: add more proper sending and error checking */
+ ret = iwmct_tx(priv, 0, parser->buf, IWMC_SDIO_BLK_SIZE);
+ if (ret)
+ LOG_INFO(priv, FW_DOWNLOAD, "iwmct_tx returned %d", ret);
+
+ LOG_INFOEX(priv, INIT, "<--\n");
+ return 0;
+}
+
+int iwmct_fw_load(struct iwmct_priv *priv)
+{
+ const struct firmware *raw = NULL;
+ __le32 addr;
+ size_t len;
+ const u8 *pdata;
+ const u8 *name = "iwmc3200top.1.fw";
+ int ret = 0;
+
+ /* clear parser struct */
+ memset(&priv->parser, 0, sizeof(struct iwmct_parser));
+ if (!name) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* get the firmware */
+ ret = request_firmware(&raw, name, &priv->func->dev);
+ if (ret < 0) {
+ LOG_ERROR(priv, FW_DOWNLOAD, "%s request_firmware failed %d\n",
+ name, ret);
+ goto exit;
+ }
+
+ if (raw->size < sizeof(struct iwmct_fw_sec_hdr)) {
+ LOG_ERROR(priv, FW_DOWNLOAD, "%s smaller then (%zd) (%zd)\n",
+ name, sizeof(struct iwmct_fw_sec_hdr), raw->size);
+ goto exit;
+ }
+
+ LOG_INFO(priv, FW_DOWNLOAD, "Read firmware '%s'\n", name);
+
+ ret = iwmct_fw_parser_init(priv, raw->data, raw->size, priv->trans_len);
+ if (ret < 0) {
+ LOG_ERROR(priv, FW_DOWNLOAD,
+ "iwmct_parser_init failed: Reason %d\n", ret);
+ goto exit;
+ }
+
+ /* checksum */
+ if (!iwmct_checksum(priv)) {
+ LOG_ERROR(priv, FW_DOWNLOAD, "checksum error\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* download firmware to device */
+ while (iwmct_parse_next_section(priv, &pdata, &len, &addr)) {
+ if (iwmct_download_section(priv, pdata, len, addr)) {
+ LOG_ERROR(priv, FW_DOWNLOAD,
+ "%s download section failed\n", name);
+ ret = -EIO;
+ goto exit;
+ }
+ }
+
+ iwmct_kick_fw(priv, !!(priv->barker & BARKER_DNLOAD_JUMP_MSK));
+
+exit:
+ kfree(priv->parser.buf);
+
+ if (raw)
+ release_firmware(raw);
+
+ raw = NULL;
+
+ return ret;
+}
diff --git a/drivers/misc/iwmc3200top/fw-msg.h b/drivers/misc/iwmc3200top/fw-msg.h
new file mode 100644
index 0000000..9e26b75
--- /dev/null
+++ b/drivers/misc/iwmc3200top/fw-msg.h
@@ -0,0 +1,113 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/fw-msg.h
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 version
+ * 2 as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
+ * -
+ *
+ */
+
+#ifndef __FWMSG_H__
+#define __FWMSG_H__
+
+#define COMM_TYPE_D2H 0xFF
+#define COMM_TYPE_H2D 0xEE
+
+#define COMM_CATEGORY_OPERATIONAL 0x00
+#define COMM_CATEGORY_DEBUG 0x01
+#define COMM_CATEGORY_TESTABILITY 0x02
+#define COMM_CATEGORY_DIAGNOSTICS 0x03
+
+#define OP_DBG_ZSTR_MSG cpu_to_le16(0x1A)
+
+#define FW_LOG_SRC_MAX 32
+#define FW_LOG_SRC_ALL 255
+
+#define FW_STRING_TABLE_ADDR cpu_to_le32(0x0C000000)
+
+#define CMD_DBG_LOG_LEVEL cpu_to_le16(0x0001)
+#define CMD_TST_DEV_RESET cpu_to_le16(0x0060)
+#define CMD_TST_FUNC_RESET cpu_to_le16(0x0062)
+#define CMD_TST_IFACE_RESET cpu_to_le16(0x0064)
+#define CMD_TST_CPU_UTILIZATION cpu_to_le16(0x0065)
+#define CMD_TST_TOP_DEEP_SLEEP cpu_to_le16(0x0080)
+#define CMD_TST_WAKEUP cpu_to_le16(0x0081)
+#define CMD_TST_FUNC_WAKEUP cpu_to_le16(0x0082)
+#define CMD_TST_FUNC_DEEP_SLEEP_REQUEST cpu_to_le16(0x0083)
+#define CMD_TST_GET_MEM_DUMP cpu_to_le16(0x0096)
+
+#define OP_OPR_ALIVE cpu_to_le16(0x0010)
+#define OP_OPR_CMD_ACK cpu_to_le16(0x001F)
+#define OP_OPR_CMD_NACK cpu_to_le16(0x0020)
+#define OP_TST_MEM_DUMP cpu_to_le16(0x0043)
+
+#define CMD_FLAG_PADDING_256 0x80
+
+#define FW_HCMD_BLOCK_SIZE 256
+
+struct msg_hdr {
+ u8 type;
+ u8 category;
+ __le16 opcode;
+ u8 seqnum;
+ u8 flags;
+ __le16 length;
+} __attribute__((__packed__));
+
+struct log_hdr {
+ __le32 timestamp;
+ u8 severity;
+ u8 logsource;
+ __le16 reserved;
+} __attribute__((__packed__));
+
+struct mdump_hdr {
+ u8 dmpid;
+ u8 frag;
+ __le16 size;
+ __le32 addr;
+} __attribute__((__packed__));
+
+struct top_msg {
+ struct msg_hdr hdr;
+ union {
+ /* D2H messages */
+ struct {
+ struct log_hdr log_hdr;
+ u8 data[1];
+ } __attribute__((__packed__)) log;
+
+ struct {
+ struct log_hdr log_hdr;
+ struct mdump_hdr md_hdr;
+ u8 data[1];
+ } __attribute__((__packed__)) mdump;
+
+ /* H2D messages */
+ struct {
+ u8 logsource;
+ u8 sevmask;
+ } __attribute__((__packed__)) logdefs[FW_LOG_SRC_MAX];
+ struct mdump_hdr mdump_req;
+ } u;
+} __attribute__((__packed__));
+
+
+#endif /* __FWMSG_H__ */
diff --git a/drivers/misc/iwmc3200top/iwmc3200top.h b/drivers/misc/iwmc3200top/iwmc3200top.h
new file mode 100644
index 0000000..f572fcf
--- /dev/null
+++ b/drivers/misc/iwmc3200top/iwmc3200top.h
@@ -0,0 +1,206 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/iwmc3200top.h
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 version
+ * 2 as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
+ * -
+ *
+ */
+
+#ifndef __IWMC3200TOP_H__
+#define __IWMC3200TOP_H__
+
+#include <linux/workqueue.h>
+
+#define DRV_NAME "iwmc3200top"
+
+#define IWMC_SDIO_BLK_SIZE 256
+#define IWMC_DEFAULT_TR_BLK 64
+#define IWMC_SDIO_DATA_ADDR 0x0
+#define IWMC_SDIO_INTR_ENABLE_ADDR 0x14
+#define IWMC_SDIO_INTR_STATUS_ADDR 0x13
+#define IWMC_SDIO_INTR_CLEAR_ADDR 0x13
+#define IWMC_SDIO_INTR_GET_SIZE_ADDR 0x2C
+
+#define COMM_HUB_HEADER_LENGTH 16
+#define LOGGER_HEADER_LENGTH 10
+
+
+#define BARKER_DNLOAD_BT_POS 0
+#define BARKER_DNLOAD_BT_MSK BIT(BARKER_DNLOAD_BT_POS)
+#define BARKER_DNLOAD_GPS_POS 1
+#define BARKER_DNLOAD_GPS_MSK BIT(BARKER_DNLOAD_GPS_POS)
+#define BARKER_DNLOAD_TOP_POS 2
+#define BARKER_DNLOAD_TOP_MSK BIT(BARKER_DNLOAD_TOP_POS)
+#define BARKER_DNLOAD_RESERVED1_POS 3
+#define BARKER_DNLOAD_RESERVED1_MSK BIT(BARKER_DNLOAD_RESERVED1_POS)
+#define BARKER_DNLOAD_JUMP_POS 4
+#define BARKER_DNLOAD_JUMP_MSK BIT(BARKER_DNLOAD_JUMP_POS)
+#define BARKER_DNLOAD_SYNC_POS 5
+#define BARKER_DNLOAD_SYNC_MSK BIT(BARKER_DNLOAD_SYNC_POS)
+#define BARKER_DNLOAD_RESERVED2_POS 6
+#define BARKER_DNLOAD_RESERVED2_MSK (0x3 << BARKER_DNLOAD_RESERVED2_POS)
+#define BARKER_DNLOAD_BARKER_POS 8
+#define BARKER_DNLOAD_BARKER_MSK (0xffffff << BARKER_DNLOAD_BARKER_POS)
+
+#define IWMC_BARKER_REBOOT (0xdeadbe << BARKER_DNLOAD_BARKER_POS)
+/* whole field barker */
+#define IWMC_BARKER_ACK 0xfeedbabe
+
+#define IWMC_CMD_SIGNATURE 0xcbbc
+
+#define CMD_HDR_OPCODE_POS 0
+#define CMD_HDR_OPCODE_MSK_MSK (0xf << CMD_HDR_OPCODE_MSK_POS)
+#define CMD_HDR_RESPONSE_CODE_POS 4
+#define CMD_HDR_RESPONSE_CODE_MSK (0xf << CMD_HDR_RESPONSE_CODE_POS)
+#define CMD_HDR_USE_CHECKSUM_POS 8
+#define CMD_HDR_USE_CHECKSUM_MSK BIT(CMD_HDR_USE_CHECKSUM_POS)
+#define CMD_HDR_RESPONSE_REQUIRED_POS 9
+#define CMD_HDR_RESPONSE_REQUIRED_MSK BIT(CMD_HDR_RESPONSE_REQUIRED_POS)
+#define CMD_HDR_DIRECT_ACCESS_POS 10
+#define CMD_HDR_DIRECT_ACCESS_MSK BIT(CMD_HDR_DIRECT_ACCESS_POS)
+#define CMD_HDR_RESERVED_POS 11
+#define CMD_HDR_RESERVED_MSK BIT(0x1f << CMD_HDR_RESERVED_POS)
+#define CMD_HDR_SIGNATURE_POS 16
+#define CMD_HDR_SIGNATURE_MSK BIT(0xffff << CMD_HDR_SIGNATURE_POS)
+
+enum {
+ IWMC_OPCODE_PING = 0,
+ IWMC_OPCODE_READ = 1,
+ IWMC_OPCODE_WRITE = 2,
+ IWMC_OPCODE_JUMP = 3,
+ IWMC_OPCODE_REBOOT = 4,
+ IWMC_OPCODE_PERSISTENT_WRITE = 5,
+ IWMC_OPCODE_PERSISTENT_READ = 6,
+ IWMC_OPCODE_READ_MODIFY_WRITE = 7,
+ IWMC_OPCODE_LAST_COMMAND = 15
+};
+
+struct iwmct_fw_load_hdr {
+ __le32 cmd;
+ __le32 target_addr;
+ __le32 data_size;
+ __le32 block_chksm;
+ u8 data[0];
+};
+
+/**
+ * struct iwmct_fw_hdr
+ * holds all sw components versions
+ */
+struct iwmct_fw_hdr {
+ u8 top_major;
+ u8 top_minor;
+ u8 top_revision;
+ u8 gps_major;
+ u8 gps_minor;
+ u8 gps_revision;
+ u8 bt_major;
+ u8 bt_minor;
+ u8 bt_revision;
+ u8 tic_name[31];
+};
+
+/**
+ * struct iwmct_fw_sec_hdr
+ * @type: function type
+ * @data_size: section's data size
+ * @target_addr: download address
+ */
+struct iwmct_fw_sec_hdr {
+ u8 type[4];
+ __le32 data_size;
+ __le32 target_addr;
+};
+
+/**
+ * struct iwmct_parser
+ * @file: fw image
+ * @file_size: fw size
+ * @cur_pos: position in file
+ * @buf: temp buf for download
+ * @buf_size: size of buf
+ * @entry_point: address to jump in fw kick-off
+ */
+struct iwmct_parser {
+ const u8 *file;
+ size_t file_size;
+ size_t cur_pos;
+ u8 *buf;
+ size_t buf_size;
+ u32 entry_point;
+ struct iwmct_fw_hdr versions;
+};
+
+
+struct iwmct_work_struct {
+ struct list_head list;
+ ssize_t iosize;
+};
+
+struct iwmct_dbg {
+ int blocks;
+ bool dump;
+ bool jump;
+ bool direct;
+ bool checksum;
+ bool fw_download;
+ int block_size;
+ int download_trans_blks;
+
+ char label_fw[256];
+};
+
+struct iwmct_debugfs;
+
+struct iwmct_priv {
+ struct sdio_func *func;
+ struct iwmct_debugfs *dbgfs;
+ struct iwmct_parser parser;
+ atomic_t reset;
+ atomic_t dev_sync;
+ u32 trans_len;
+ u32 barker;
+ struct iwmct_dbg dbg;
+
+ /* drivers work queue */
+ struct workqueue_struct *wq;
+ struct workqueue_struct *bus_rescan_wq;
+ struct work_struct bus_rescan_worker;
+ struct work_struct isr_worker;
+
+ /* drivers wait queue */
+ wait_queue_head_t wait_q;
+
+ /* rx request list */
+ struct list_head read_req_list;
+};
+
+extern int iwmct_tx(struct iwmct_priv *priv, unsigned int addr,
+ void *src, int count);
+
+extern int iwmct_fw_load(struct iwmct_priv *priv);
+
+extern void iwmct_dbg_init_params(struct iwmct_priv *drv);
+extern void iwmct_dbg_init_drv_attrs(struct device_driver *drv);
+extern void iwmct_dbg_remove_drv_attrs(struct device_driver *drv);
+extern int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len);
+
+#endif /* __IWMC3200TOP_H__ */
diff --git a/drivers/misc/iwmc3200top/log.c b/drivers/misc/iwmc3200top/log.c
new file mode 100644
index 0000000..d569279
--- /dev/null
+++ b/drivers/misc/iwmc3200top/log.c
@@ -0,0 +1,347 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/log.c
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 version
+ * 2 as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
+ * -
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/ctype.h>
+#include "fw-msg.h"
+#include "iwmc3200top.h"
+#include "log.h"
+
+/* Maximal hexadecimal string size of the FW memdump message */
+#define LOG_MSG_SIZE_MAX 12400
+
+/* iwmct_logdefs is a global used by log macros */
+u8 iwmct_logdefs[LOG_SRC_MAX];
+static u8 iwmct_fw_logdefs[FW_LOG_SRC_MAX];
+
+
+static int _log_set_log_filter(u8 *logdefs, int size, u8 src, u8 logmask)
+{
+ int i;
+
+ if (src < size)
+ logdefs[src] = logmask;
+ else if (src == LOG_SRC_ALL)
+ for (i = 0; i < size; i++)
+ logdefs[i] = logmask;
+ else
+ return -1;
+
+ return 0;
+}
+
+
+int iwmct_log_set_filter(u8 src, u8 logmask)
+{
+ return _log_set_log_filter(iwmct_logdefs, LOG_SRC_MAX, src, logmask);
+}
+
+
+int iwmct_log_set_fw_filter(u8 src, u8 logmask)
+{
+ return _log_set_log_filter(iwmct_fw_logdefs,
+ FW_LOG_SRC_MAX, src, logmask);
+}
+
+
+static int log_msg_format_hex(char *str, int slen, u8 *ibuf,
+ int ilen, char *pref)
+{
+ int pos = 0;
+ int i;
+ int len;
+
+ for (pos = 0, i = 0; pos < slen - 2 && pref[i] != '\0'; i++, pos++)
+ str[pos] = pref[i];
+
+ for (i = 0; pos < slen - 2 && i < ilen; pos += len, i++)
+ len = snprintf(&str[pos], slen - pos - 1, " %2.2X", ibuf[i]);
+
+ if (i < ilen)
+ return -1;
+
+ return 0;
+}
+
+/* NOTE: This function is not thread safe.
+ Currently it's called only from sdio rx worker - no race there
+*/
+void iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len)
+{
+ struct top_msg *msg;
+ static char logbuf[LOG_MSG_SIZE_MAX];
+
+ msg = (struct top_msg *)buf;
+
+ if (len < sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr)) {
+ LOG_ERROR(priv, FW_MSG, "Log message from TOP "
+ "is too short %d (expected %zd)\n",
+ len, sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr));
+ return;
+ }
+
+ if (!(iwmct_fw_logdefs[msg->u.log.log_hdr.logsource] &
+ BIT(msg->u.log.log_hdr.severity)) ||
+ !(iwmct_logdefs[LOG_SRC_FW_MSG] & BIT(msg->u.log.log_hdr.severity)))
+ return;
+
+ switch (msg->hdr.category) {
+ case COMM_CATEGORY_TESTABILITY:
+ if (!(iwmct_logdefs[LOG_SRC_TST] &
+ BIT(msg->u.log.log_hdr.severity)))
+ return;
+ if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf,
+ le16_to_cpu(msg->hdr.length) +
+ sizeof(msg->hdr), "<TST>"))
+ LOG_WARNING(priv, TST,
+ "TOP TST message is too long, truncating...");
+ LOG_WARNING(priv, TST, "%s\n", logbuf);
+ break;
+ case COMM_CATEGORY_DEBUG:
+ if (msg->hdr.opcode == OP_DBG_ZSTR_MSG)
+ LOG_INFO(priv, FW_MSG, "%s %s", "<DBG>",
+ ((u8 *)msg) + sizeof(msg->hdr)
+ + sizeof(msg->u.log.log_hdr));
+ else {
+ if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf,
+ le16_to_cpu(msg->hdr.length)
+ + sizeof(msg->hdr),
+ "<DBG>"))
+ LOG_WARNING(priv, FW_MSG,
+ "TOP DBG message is too long,"
+ "truncating...");
+ LOG_WARNING(priv, FW_MSG, "%s\n", logbuf);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static int _log_get_filter_str(u8 *logdefs, int logdefsz, char *buf, int size)
+{
+ int i, pos, len;
+ for (i = 0, pos = 0; (pos < size-1) && (i < logdefsz); i++) {
+ len = snprintf(&buf[pos], size - pos - 1, "0x%02X%02X,",
+ i, logdefs[i]);
+ pos += len;
+ }
+ buf[pos-1] = '\n';
+ buf[pos] = '\0';
+
+ if (i < logdefsz)
+ return -1;
+ return 0;
+}
+
+int log_get_filter_str(char *buf, int size)
+{
+ return _log_get_filter_str(iwmct_logdefs, LOG_SRC_MAX, buf, size);
+}
+
+int log_get_fw_filter_str(char *buf, int size)
+{
+ return _log_get_filter_str(iwmct_fw_logdefs, FW_LOG_SRC_MAX, buf, size);
+}
+
+#define HEXADECIMAL_RADIX 16
+#define LOG_SRC_FORMAT 7 /* log level is in format of "0xXXXX," */
+
+ssize_t show_iwmct_log_level(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct iwmct_priv *priv = dev_get_drvdata(d);
+ char *str_buf;
+ int buf_size;
+ ssize_t ret;
+
+ buf_size = (LOG_SRC_FORMAT * LOG_SRC_MAX) + 1;
+ str_buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!str_buf) {
+ LOG_ERROR(priv, DEBUGFS,
+ "failed to allocate %d bytes\n", buf_size);
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ if (log_get_filter_str(str_buf, buf_size) < 0) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = sprintf(buf, "%s", str_buf);
+
+exit:
+ kfree(str_buf);
+ return ret;
+}
+
+ssize_t store_iwmct_log_level(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct iwmct_priv *priv = dev_get_drvdata(d);
+ char *token, *str_buf = NULL;
+ long val;
+ ssize_t ret = count;
+ u8 src, mask;
+
+ if (!count)
+ goto exit;
+
+ str_buf = kzalloc(count, GFP_KERNEL);
+ if (!str_buf) {
+ LOG_ERROR(priv, DEBUGFS,
+ "failed to allocate %zd bytes\n", count);
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ memcpy(str_buf, buf, count);
+
+ while ((token = strsep(&str_buf, ",")) != NULL) {
+ while (isspace(*token))
+ ++token;
+ if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) {
+ LOG_ERROR(priv, DEBUGFS,
+ "failed to convert string to long %s\n",
+ token);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ mask = val & 0xFF;
+ src = (val & 0XFF00) >> 8;
+ iwmct_log_set_filter(src, mask);
+ }
+
+exit:
+ kfree(str_buf);
+ return ret;
+}
+
+ssize_t show_iwmct_log_level_fw(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct iwmct_priv *priv = dev_get_drvdata(d);
+ char *str_buf;
+ int buf_size;
+ ssize_t ret;
+
+ buf_size = (LOG_SRC_FORMAT * FW_LOG_SRC_MAX) + 2;
+
+ str_buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!str_buf) {
+ LOG_ERROR(priv, DEBUGFS,
+ "failed to allocate %d bytes\n", buf_size);
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ if (log_get_fw_filter_str(str_buf, buf_size) < 0) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = sprintf(buf, "%s", str_buf);
+
+exit:
+ kfree(str_buf);
+ return ret;
+}
+
+ssize_t store_iwmct_log_level_fw(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct iwmct_priv *priv = dev_get_drvdata(d);
+ struct top_msg cmd;
+ char *token, *str_buf = NULL;
+ ssize_t ret = count;
+ u16 cmdlen = 0;
+ int i;
+ long val;
+ u8 src, mask;
+
+ if (!count)
+ goto exit;
+
+ str_buf = kzalloc(count, GFP_KERNEL);
+ if (!str_buf) {
+ LOG_ERROR(priv, DEBUGFS,
+ "failed to allocate %zd bytes\n", count);
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ memcpy(str_buf, buf, count);
+
+ cmd.hdr.type = COMM_TYPE_H2D;
+ cmd.hdr.category = COMM_CATEGORY_DEBUG;
+ cmd.hdr.opcode = CMD_DBG_LOG_LEVEL;
+
+ for (i = 0; ((token = strsep(&str_buf, ",")) != NULL) &&
+ (i < FW_LOG_SRC_MAX); i++) {
+
+ while (isspace(*token))
+ ++token;
+
+ if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) {
+ LOG_ERROR(priv, DEBUGFS,
+ "failed to convert string to long %s\n",
+ token);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ mask = val & 0xFF; /* LSB */
+ src = (val & 0XFF00) >> 8; /* 2nd least significant byte. */
+ iwmct_log_set_fw_filter(src, mask);
+
+ cmd.u.logdefs[i].logsource = src;
+ cmd.u.logdefs[i].sevmask = mask;
+ }
+
+ cmd.hdr.length = cpu_to_le16(i * sizeof(cmd.u.logdefs[0]));
+ cmdlen = (i * sizeof(cmd.u.logdefs[0]) + sizeof(cmd.hdr));
+
+ ret = iwmct_send_hcmd(priv, (u8 *)&cmd, cmdlen);
+ if (ret) {
+ LOG_ERROR(priv, DEBUGFS,
+ "Failed to send %d bytes of fwcmd, ret=%zd\n",
+ cmdlen, ret);
+ goto exit;
+ } else
+ LOG_INFO(priv, DEBUGFS, "fwcmd sent (%d bytes)\n", cmdlen);
+
+ ret = count;
+
+exit:
+ kfree(str_buf);
+ return ret;
+}
+
diff --git a/drivers/misc/iwmc3200top/log.h b/drivers/misc/iwmc3200top/log.h
new file mode 100644
index 0000000..aba8121
--- /dev/null
+++ b/drivers/misc/iwmc3200top/log.h
@@ -0,0 +1,158 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/log.h
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 version
+ * 2 as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
+ * -
+ *
+ */
+
+#ifndef __LOG_H__
+#define __LOG_H__
+
+
+/* log severity:
+ * The log levels here match FW log levels
+ * so values need to stay as is */
+#define LOG_SEV_CRITICAL 0
+#define LOG_SEV_ERROR 1
+#define LOG_SEV_WARNING 2
+#define LOG_SEV_INFO 3
+#define LOG_SEV_INFOEX 4
+
+#define LOG_SEV_FILTER_ALL \
+ (BIT(LOG_SEV_CRITICAL) | \
+ BIT(LOG_SEV_ERROR) | \
+ BIT(LOG_SEV_WARNING) | \
+ BIT(LOG_SEV_INFO) | \
+ BIT(LOG_SEV_INFOEX))
+
+/* log source */
+#define LOG_SRC_INIT 0
+#define LOG_SRC_DEBUGFS 1
+#define LOG_SRC_FW_DOWNLOAD 2
+#define LOG_SRC_FW_MSG 3
+#define LOG_SRC_TST 4
+#define LOG_SRC_IRQ 5
+
+#define LOG_SRC_MAX 6
+#define LOG_SRC_ALL 0xFF
+
+/**
+ * Default intitialization runtime log level
+ */
+#ifndef LOG_SEV_FILTER_RUNTIME
+#define LOG_SEV_FILTER_RUNTIME \
+ (BIT(LOG_SEV_CRITICAL) | \
+ BIT(LOG_SEV_ERROR) | \
+ BIT(LOG_SEV_WARNING))
+#endif
+
+#ifndef FW_LOG_SEV_FILTER_RUNTIME
+#define FW_LOG_SEV_FILTER_RUNTIME LOG_SEV_FILTER_ALL
+#endif
+
+#ifdef CONFIG_IWMC3200TOP_DEBUG
+/**
+ * Log macros
+ */
+
+#define priv2dev(priv) (&(priv->func)->dev)
+
+#define LOG_CRITICAL(priv, src, fmt, args...) \
+do { \
+ if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_CRITICAL)) \
+ dev_crit(priv2dev(priv), "%s %d: " fmt, \
+ __func__, __LINE__, ##args); \
+} while (0)
+
+#define LOG_ERROR(priv, src, fmt, args...) \
+do { \
+ if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_ERROR)) \
+ dev_err(priv2dev(priv), "%s %d: " fmt, \
+ __func__, __LINE__, ##args); \
+} while (0)
+
+#define LOG_WARNING(priv, src, fmt, args...) \
+do { \
+ if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_WARNING)) \
+ dev_warn(priv2dev(priv), "%s %d: " fmt, \
+ __func__, __LINE__, ##args); \
+} while (0)
+
+#define LOG_INFO(priv, src, fmt, args...) \
+do { \
+ if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFO)) \
+ dev_info(priv2dev(priv), "%s %d: " fmt, \
+ __func__, __LINE__, ##args); \
+} while (0)
+
+#define LOG_INFOEX(priv, src, fmt, args...) \
+do { \
+ if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFOEX)) \
+ dev_dbg(priv2dev(priv), "%s %d: " fmt, \
+ __func__, __LINE__, ##args); \
+} while (0)
+
+#define LOG_HEXDUMP(src, ptr, len) \
+do { \
+ if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFOEX)) \
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, \
+ 16, 1, ptr, len, false); \
+} while (0)
+
+void iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len);
+
+extern u8 iwmct_logdefs[];
+
+int iwmct_log_set_filter(u8 src, u8 logmask);
+int iwmct_log_set_fw_filter(u8 src, u8 logmask);
+
+ssize_t show_iwmct_log_level(struct device *d,
+ struct device_attribute *attr, char *buf);
+ssize_t store_iwmct_log_level(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+ssize_t show_iwmct_log_level_fw(struct device *d,
+ struct device_attribute *attr, char *buf);
+ssize_t store_iwmct_log_level_fw(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+#else
+
+#define LOG_CRITICAL(priv, src, fmt, args...)
+#define LOG_ERROR(priv, src, fmt, args...)
+#define LOG_WARNING(priv, src, fmt, args...)
+#define LOG_INFO(priv, src, fmt, args...)
+#define LOG_INFOEX(priv, src, fmt, args...)
+#define LOG_HEXDUMP(src, ptr, len)
+
+static inline void iwmct_log_top_message(struct iwmct_priv *priv,
+ u8 *buf, int len) {}
+static inline int iwmct_log_set_filter(u8 src, u8 logmask) { return 0; }
+static inline int iwmct_log_set_fw_filter(u8 src, u8 logmask) { return 0; }
+
+#endif /* CONFIG_IWMC3200TOP_DEBUG */
+
+int log_get_filter_str(char *buf, int size);
+int log_get_fw_filter_str(char *buf, int size);
+
+#endif /* __LOG_H__ */
diff --git a/drivers/misc/iwmc3200top/main.c b/drivers/misc/iwmc3200top/main.c
new file mode 100644
index 0000000..6e4e491
--- /dev/null
+++ b/drivers/misc/iwmc3200top/main.c
@@ -0,0 +1,699 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/main.c
+ *
+ * Copyright (C) 2009 Intel Corporation. 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 version
+ * 2 as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
+ * -
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio.h>
+
+#include "iwmc3200top.h"
+#include "log.h"
+#include "fw-msg.h"
+#include "debugfs.h"
+
+
+#define DRIVER_DESCRIPTION "Intel(R) IWMC 3200 Top Driver"
+#define DRIVER_COPYRIGHT "Copyright (c) 2008 Intel Corporation."
+
+#define IWMCT_VERSION "0.1.62"
+
+#ifdef REPOSITORY_LABEL
+#define RL REPOSITORY_LABEL
+#else
+#define RL local
+#endif
+
+#ifdef CONFIG_IWMC3200TOP_DEBUG
+#define VD "-d"
+#else
+#define VD
+#endif
+
+#define DRIVER_VERSION IWMCT_VERSION "-" __stringify(RL) VD
+
+MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_COPYRIGHT);
+
+
+/* FIXME: These can be found in sdio_ids.h in newer kernels */
+#ifndef SDIO_INTEL_VENDOR_ID
+#define SDIO_INTEL_VENDOR_ID 0x0089
+#endif
+#ifndef SDIO_DEVICE_ID_INTEL_IWMC3200TOP
+#define SDIO_DEVICE_ID_INTEL_IWMC3200TOP 0x1404
+#endif
+
+/*
+ * This workers main task is to wait for OP_OPR_ALIVE
+ * from TOP FW until ALIVE_MSG_TIMOUT timeout is elapsed.
+ * When OP_OPR_ALIVE received it will issue
+ * a call to "bus_rescan_devices".
+ */
+static void iwmct_rescan_worker(struct work_struct *ws)
+{
+ struct iwmct_priv *priv;
+ int ret;
+
+ priv = container_of(ws, struct iwmct_priv, bus_rescan_worker);
+
+ LOG_INFO(priv, FW_MSG, "Calling bus_rescan\n");
+
+ ret = bus_rescan_devices(priv->func->dev.bus);
+ if (ret < 0)
+ LOG_INFO(priv, FW_DOWNLOAD, "bus_rescan_devices FAILED!!!\n");
+}
+
+static void op_top_message(struct iwmct_priv *priv, struct top_msg *msg)
+{
+ switch (msg->hdr.opcode) {
+ case OP_OPR_ALIVE:
+ LOG_INFO(priv, FW_MSG, "Got ALIVE from device, wake rescan\n");
+ queue_work(priv->bus_rescan_wq, &priv->bus_rescan_worker);
+ break;
+ default:
+ LOG_INFO(priv, FW_MSG, "Received msg opcode 0x%X\n",
+ msg->hdr.opcode);
+ break;
+ }
+}
+
+
+static void handle_top_message(struct iwmct_priv *priv, u8 *buf, int len)
+{
+ struct top_msg *msg;
+
+ msg = (struct top_msg *)buf;
+
+ if (msg->hdr.type != COMM_TYPE_D2H) {
+ LOG_ERROR(priv, FW_MSG,
+ "Message from TOP with invalid message type 0x%X\n",
+ msg->hdr.type);
+ return;
+ }
+
+ if (len < sizeof(msg->hdr)) {
+ LOG_ERROR(priv, FW_MSG,
+ "Message from TOP is too short for message header "
+ "received %d bytes, expected at least %zd bytes\n",
+ len, sizeof(msg->hdr));
+ return;
+ }
+
+ if (len < le16_to_cpu(msg->hdr.length) + sizeof(msg->hdr)) {
+ LOG_ERROR(priv, FW_MSG,
+ "Message length (%d bytes) is shorter than "
+ "in header (%d bytes)\n",
+ len, le16_to_cpu(msg->hdr.length));
+ return;
+ }
+
+ switch (msg->hdr.category) {
+ case COMM_CATEGORY_OPERATIONAL:
+ op_top_message(priv, (struct top_msg *)buf);
+ break;
+
+ case COMM_CATEGORY_DEBUG:
+ case COMM_CATEGORY_TESTABILITY:
+ case COMM_CATEGORY_DIAGNOSTICS:
+ iwmct_log_top_message(priv, buf, len);
+ break;
+
+ default:
+ LOG_ERROR(priv, FW_MSG,
+ "Message from TOP with unknown category 0x%X\n",
+ msg->hdr.category);
+ break;
+ }
+}
+
+int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len)
+{
+ int ret;
+ u8 *buf;
+
+ LOG_INFOEX(priv, FW_MSG, "Sending hcmd:\n");
+
+ /* add padding to 256 for IWMC */
+ ((struct top_msg *)cmd)->hdr.flags |= CMD_FLAG_PADDING_256;
+
+ LOG_HEXDUMP(FW_MSG, cmd, len);
+
+ if (len > FW_HCMD_BLOCK_SIZE) {
+ LOG_ERROR(priv, FW_MSG, "size %d exceeded hcmd max size %d\n",
+ len, FW_HCMD_BLOCK_SIZE);
+ return -1;
+ }
+
+ buf = kzalloc(FW_HCMD_BLOCK_SIZE, GFP_KERNEL);
+ if (!buf) {
+ LOG_ERROR(priv, FW_MSG, "kzalloc error, buf size %d\n",
+ FW_HCMD_BLOCK_SIZE);
+ return -1;
+ }
+
+ memcpy(buf, cmd, len);
+
+ sdio_claim_host(priv->func);
+ ret = sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR, buf,
+ FW_HCMD_BLOCK_SIZE);
+ sdio_release_host(priv->func);
+
+ kfree(buf);
+ return ret;
+}
+
+int iwmct_tx(struct iwmct_priv *priv, unsigned int addr,
+ void *src, int count)
+{
+ int ret;
+
+ sdio_claim_host(priv->func);
+ ret = sdio_memcpy_toio(priv->func, addr, src, count);
+ sdio_release_host(priv->func);
+
+ return ret;
+}
+
+static void iwmct_irq_read_worker(struct work_struct *ws)
+{
+ struct iwmct_priv *priv;
+ struct iwmct_work_struct *read_req;
+ __le32 *buf = NULL;
+ int ret;
+ int iosize;
+ u32 barker;
+ bool is_barker;
+
+ priv = container_of(ws, struct iwmct_priv, isr_worker);
+
+ LOG_INFO(priv, IRQ, "enter iwmct_irq_read_worker %p\n", ws);
+
+ /* --------------------- Handshake with device -------------------- */
+ sdio_claim_host(priv->func);
+
+ /* all list manipulations have to be protected by
+ * sdio_claim_host/sdio_release_host */
+ if (list_empty(&priv->read_req_list)) {
+ LOG_ERROR(priv, IRQ, "read_req_list empty in read worker\n");
+ goto exit_release;
+ }
+
+ read_req = list_entry(priv->read_req_list.next,
+ struct iwmct_work_struct, list);
+
+ list_del(&read_req->list);
+ iosize = read_req->iosize;
+ kfree(read_req);
+
+ buf = kzalloc(iosize, GFP_KERNEL);
+ if (!buf) {
+ LOG_ERROR(priv, IRQ, "kzalloc error, buf size %d\n", iosize);
+ goto exit_release;
+ }
+
+ LOG_INFO(priv, IRQ, "iosize=%d, buf=%p, func=%d\n",
+ iosize, buf, priv->func->num);
+
+ /* read from device */
+ ret = sdio_memcpy_fromio(priv->func, buf, IWMC_SDIO_DATA_ADDR, iosize);
+ if (ret) {
+ LOG_ERROR(priv, IRQ, "error %d reading buffer\n", ret);
+ goto exit_release;
+ }
+
+ LOG_HEXDUMP(IRQ, (u8 *)buf, iosize);
+
+ barker = le32_to_cpu(buf[0]);
+
+ /* Verify whether it's a barker and if not - treat as regular Rx */
+ if (barker == IWMC_BARKER_ACK ||
+ (barker & BARKER_DNLOAD_BARKER_MSK) == IWMC_BARKER_REBOOT) {
+
+ /* Valid Barker is equal on first 4 dwords */
+ is_barker = (buf[1] == buf[0]) &&
+ (buf[2] == buf[0]) &&
+ (buf[3] == buf[0]);
+
+ if (!is_barker) {
+ LOG_WARNING(priv, IRQ,
+ "Potentially inconsistent barker "
+ "%08X_%08X_%08X_%08X\n",
+ le32_to_cpu(buf[0]), le32_to_cpu(buf[1]),
+ le32_to_cpu(buf[2]), le32_to_cpu(buf[3]));
+ }
+ } else {
+ is_barker = false;
+ }
+
+ /* Handle Top CommHub message */
+ if (!is_barker) {
+ sdio_release_host(priv->func);
+ handle_top_message(priv, (u8 *)buf, iosize);
+ goto exit;
+ } else if (barker == IWMC_BARKER_ACK) { /* Handle barkers */
+ if (atomic_read(&priv->dev_sync) == 0) {
+ LOG_ERROR(priv, IRQ,
+ "ACK barker arrived out-of-sync\n");
+ goto exit_release;
+ }
+
+ /* Continuing to FW download (after Sync is completed)*/
+ atomic_set(&priv->dev_sync, 0);
+ LOG_INFO(priv, IRQ, "ACK barker arrived "
+ "- starting FW download\n");
+ } else { /* REBOOT barker */
+ LOG_INFO(priv, IRQ, "Recieved reboot barker: %x\n", barker);
+ priv->barker = barker;
+
+ if (barker & BARKER_DNLOAD_SYNC_MSK) {
+ /* Send the same barker back */
+ ret = sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR,
+ buf, iosize);
+ if (ret) {
+ LOG_ERROR(priv, IRQ,
+ "error %d echoing barker\n", ret);
+ goto exit_release;
+ }
+ LOG_INFO(priv, IRQ, "Echoing barker to device\n");
+ atomic_set(&priv->dev_sync, 1);
+ goto exit_release;
+ }
+
+ /* Continuing to FW download (without Sync) */
+ LOG_INFO(priv, IRQ, "No sync requested "
+ "- starting FW download\n");
+ }
+
+ sdio_release_host(priv->func);
+
+
+ LOG_INFO(priv, IRQ, "barker download request 0x%x is:\n", priv->barker);
+ LOG_INFO(priv, IRQ, "******* Top FW %s requested ********\n",
+ (priv->barker & BARKER_DNLOAD_TOP_MSK) ? "was" : "not");
+ LOG_INFO(priv, IRQ, "******* GPS FW %s requested ********\n",
+ (priv->barker & BARKER_DNLOAD_GPS_MSK) ? "was" : "not");
+ LOG_INFO(priv, IRQ, "******* BT FW %s requested ********\n",
+ (priv->barker & BARKER_DNLOAD_BT_MSK) ? "was" : "not");
+
+ if (priv->dbg.fw_download)
+ iwmct_fw_load(priv);
+ else
+ LOG_ERROR(priv, IRQ, "FW download not allowed\n");
+
+ goto exit;
+
+exit_release:
+ sdio_release_host(priv->func);
+exit:
+ kfree(buf);
+ LOG_INFO(priv, IRQ, "exit iwmct_irq_read_worker\n");
+}
+
+static void iwmct_irq(struct sdio_func *func)
+{
+ struct iwmct_priv *priv;
+ int val, ret;
+ int iosize;
+ int addr = IWMC_SDIO_INTR_GET_SIZE_ADDR;
+ struct iwmct_work_struct *read_req;
+
+ priv = sdio_get_drvdata(func);
+
+ LOG_INFO(priv, IRQ, "enter iwmct_irq\n");
+
+ /* read the function's status register */
+ val = sdio_readb(func, IWMC_SDIO_INTR_STATUS_ADDR, &ret);
+
+ LOG_INFO(priv, IRQ, "iir value = %d, ret=%d\n", val, ret);
+
+ if (!val) {
+ LOG_ERROR(priv, IRQ, "iir = 0, exiting ISR\n");
+ goto exit_clear_intr;
+ }
+
+
+ /*
+ * read 2 bytes of the transaction size
+ * IMPORTANT: sdio transaction size has to be read before clearing
+ * sdio interrupt!!!
+ */
+ val = sdio_readb(priv->func, addr++, &ret);
+ iosize = val;
+ val = sdio_readb(priv->func, addr++, &ret);
+ iosize += val << 8;
+
+ LOG_INFO(priv, IRQ, "READ size %d\n", iosize);
+
+ if (iosize == 0) {
+ LOG_ERROR(priv, IRQ, "READ size %d, exiting ISR\n", iosize);
+ goto exit_clear_intr;
+ }
+
+ /* allocate a work structure to pass iosize to the worker */
+ read_req = kzalloc(sizeof(struct iwmct_work_struct), GFP_KERNEL);
+ if (!read_req) {
+ LOG_ERROR(priv, IRQ, "failed to allocate read_req, exit ISR\n");
+ goto exit_clear_intr;
+ }
+
+ INIT_LIST_HEAD(&read_req->list);
+ read_req->iosize = iosize;
+
+ list_add_tail(&priv->read_req_list, &read_req->list);
+
+ /* clear the function's interrupt request bit (write 1 to clear) */
+ sdio_writeb(func, 1, IWMC_SDIO_INTR_CLEAR_ADDR, &ret);
+
+ queue_work(priv->wq, &priv->isr_worker);
+
+ LOG_INFO(priv, IRQ, "exit iwmct_irq\n");
+
+ return;
+
+exit_clear_intr:
+ /* clear the function's interrupt request bit (write 1 to clear) */
+ sdio_writeb(func, 1, IWMC_SDIO_INTR_CLEAR_ADDR, &ret);
+}
+
+
+static int blocks;
+module_param(blocks, int, 0604);
+MODULE_PARM_DESC(blocks, "max_blocks_to_send");
+
+static int dump;
+module_param(dump, bool, 0604);
+MODULE_PARM_DESC(dump, "dump_hex_content");
+
+static int jump = 1;
+module_param(jump, bool, 0604);
+
+static int direct = 1;
+module_param(direct, bool, 0604);
+
+static int checksum = 1;
+module_param(checksum, bool, 0604);
+
+static int fw_download = 1;
+module_param(fw_download, bool, 0604);
+
+static int block_size = IWMC_SDIO_BLK_SIZE;
+module_param(block_size, int, 0404);
+
+static int download_trans_blks = IWMC_DEFAULT_TR_BLK;
+module_param(download_trans_blks, int, 0604);
+
+static int rubbish_barker;
+module_param(rubbish_barker, bool, 0604);
+
+#ifdef CONFIG_IWMC3200TOP_DEBUG
+static int log_level[LOG_SRC_MAX];
+static unsigned int log_level_argc;
+module_param_array(log_level, int, &log_level_argc, 0604);
+MODULE_PARM_DESC(log_level, "log_level");
+
+static int log_level_fw[FW_LOG_SRC_MAX];
+static unsigned int log_level_fw_argc;
+module_param_array(log_level_fw, int, &log_level_fw_argc, 0604);
+MODULE_PARM_DESC(log_level_fw, "log_level_fw");
+#endif
+
+void iwmct_dbg_init_params(struct iwmct_priv *priv)
+{
+#ifdef CONFIG_IWMC3200TOP_DEBUG
+ int i;
+
+ for (i = 0; i < log_level_argc; i++) {
+ dev_notice(&priv->func->dev, "log_level[%d]=0x%X\n",
+ i, log_level[i]);
+ iwmct_log_set_filter((log_level[i] >> 8) & 0xFF,
+ log_level[i] & 0xFF);
+ }
+ for (i = 0; i < log_level_fw_argc; i++) {
+ dev_notice(&priv->func->dev, "log_level_fw[%d]=0x%X\n",
+ i, log_level_fw[i]);
+ iwmct_log_set_fw_filter((log_level_fw[i] >> 8) & 0xFF,
+ log_level_fw[i] & 0xFF);
+ }
+#endif
+
+ priv->dbg.blocks = blocks;
+ LOG_INFO(priv, INIT, "blocks=%d\n", blocks);
+ priv->dbg.dump = (bool)dump;
+ LOG_INFO(priv, INIT, "dump=%d\n", dump);
+ priv->dbg.jump = (bool)jump;
+ LOG_INFO(priv, INIT, "jump=%d\n", jump);
+ priv->dbg.direct = (bool)direct;
+ LOG_INFO(priv, INIT, "direct=%d\n", direct);
+ priv->dbg.checksum = (bool)checksum;
+ LOG_INFO(priv, INIT, "checksum=%d\n", checksum);
+ priv->dbg.fw_download = (bool)fw_download;
+ LOG_INFO(priv, INIT, "fw_download=%d\n", fw_download);
+ priv->dbg.block_size = block_size;
+ LOG_INFO(priv, INIT, "block_size=%d\n", block_size);
+ priv->dbg.download_trans_blks = download_trans_blks;
+ LOG_INFO(priv, INIT, "download_trans_blks=%d\n", download_trans_blks);
+}
+
+/*****************************************************************************
+ *
+ * sysfs attributes
+ *
+ *****************************************************************************/
+static ssize_t show_iwmct_fw_version(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct iwmct_priv *priv = dev_get_drvdata(d);
+ return sprintf(buf, "%s\n", priv->dbg.label_fw);
+}
+static DEVICE_ATTR(cc_label_fw, S_IRUGO, show_iwmct_fw_version, NULL);
+
+#ifdef CONFIG_IWMC3200TOP_DEBUG
+static DEVICE_ATTR(log_level, S_IWUSR | S_IRUGO,
+ show_iwmct_log_level, store_iwmct_log_level);
+static DEVICE_ATTR(log_level_fw, S_IWUSR | S_IRUGO,
+ show_iwmct_log_level_fw, store_iwmct_log_level_fw);
+#endif
+
+static struct attribute *iwmct_sysfs_entries[] = {
+ &dev_attr_cc_label_fw.attr,
+#ifdef CONFIG_IWMC3200TOP_DEBUG
+ &dev_attr_log_level.attr,
+ &dev_attr_log_level_fw.attr,
+#endif
+ NULL
+};
+
+static struct attribute_group iwmct_attribute_group = {
+ .name = NULL, /* put in device directory */
+ .attrs = iwmct_sysfs_entries,
+};
+
+
+static int iwmct_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ struct iwmct_priv *priv;
+ int ret;
+ int val = 1;
+ int addr = IWMC_SDIO_INTR_ENABLE_ADDR;
+
+ dev_dbg(&func->dev, "enter iwmct_probe\n");
+
+ dev_dbg(&func->dev, "IRQ polling period id %u msecs, HZ is %d\n",
+ jiffies_to_msecs(2147483647), HZ);
+
+ priv = kzalloc(sizeof(struct iwmct_priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&func->dev, "kzalloc error\n");
+ return -ENOMEM;
+ }
+ priv->func = func;
+ sdio_set_drvdata(func, priv);
+
+
+ /* create drivers work queue */
+ priv->wq = create_workqueue(DRV_NAME "_wq");
+ priv->bus_rescan_wq = create_workqueue(DRV_NAME "_rescan_wq");
+ INIT_WORK(&priv->bus_rescan_worker, iwmct_rescan_worker);
+ INIT_WORK(&priv->isr_worker, iwmct_irq_read_worker);
+
+ init_waitqueue_head(&priv->wait_q);
+
+ sdio_claim_host(func);
+ /* FIXME: Remove after it is fixed in the Boot ROM upgrade */
+ func->enable_timeout = 10;
+
+ /* In our HW, setting the block size also wakes up the boot rom. */
+ ret = sdio_set_block_size(func, priv->dbg.block_size);
+ if (ret) {
+ LOG_ERROR(priv, INIT,
+ "sdio_set_block_size() failure: %d\n", ret);
+ goto error_sdio_enable;
+ }
+
+ ret = sdio_enable_func(func);
+ if (ret) {
+ LOG_ERROR(priv, INIT, "sdio_enable_func() failure: %d\n", ret);
+ goto error_sdio_enable;
+ }
+
+ /* init reset and dev_sync states */
+ atomic_set(&priv->reset, 0);
+ atomic_set(&priv->dev_sync, 0);
+
+ /* init read req queue */
+ INIT_LIST_HEAD(&priv->read_req_list);
+
+ /* process configurable parameters */
+ iwmct_dbg_init_params(priv);
+ ret = sysfs_create_group(&func->dev.kobj, &iwmct_attribute_group);
+ if (ret) {
+ LOG_ERROR(priv, INIT, "Failed to register attributes and "
+ "initialize module_params\n");
+ goto error_dev_attrs;
+ }
+
+ iwmct_dbgfs_register(priv, DRV_NAME);
+
+ if (!priv->dbg.direct && priv->dbg.download_trans_blks > 8) {
+ LOG_INFO(priv, INIT,
+ "Reducing transaction to 8 blocks = 2K (from %d)\n",
+ priv->dbg.download_trans_blks);
+ priv->dbg.download_trans_blks = 8;
+ }
+ priv->trans_len = priv->dbg.download_trans_blks * priv->dbg.block_size;
+ LOG_INFO(priv, INIT, "Transaction length = %d\n", priv->trans_len);
+
+ ret = sdio_claim_irq(func, iwmct_irq);
+ if (ret) {
+ LOG_ERROR(priv, INIT, "sdio_claim_irq() failure: %d\n", ret);
+ goto error_claim_irq;
+ }
+
+
+ /* Enable function's interrupt */
+ sdio_writeb(priv->func, val, addr, &ret);
+ if (ret) {
+ LOG_ERROR(priv, INIT, "Failure writing to "
+ "Interrupt Enable Register (%d): %d\n", addr, ret);
+ goto error_enable_int;
+ }
+
+ sdio_release_host(func);
+
+ LOG_INFO(priv, INIT, "exit iwmct_probe\n");
+
+ return ret;
+
+error_enable_int:
+ sdio_release_irq(func);
+error_claim_irq:
+ sdio_disable_func(func);
+error_dev_attrs:
+ iwmct_dbgfs_unregister(priv->dbgfs);
+ sysfs_remove_group(&func->dev.kobj, &iwmct_attribute_group);
+error_sdio_enable:
+ sdio_release_host(func);
+ return ret;
+}
+
+static void iwmct_remove(struct sdio_func *func)
+{
+ struct iwmct_work_struct *read_req;
+ struct iwmct_priv *priv = sdio_get_drvdata(func);
+
+ priv = sdio_get_drvdata(func);
+
+ LOG_INFO(priv, INIT, "enter\n");
+
+ sdio_claim_host(func);
+ sdio_release_irq(func);
+ sdio_release_host(func);
+
+ /* Safely destroy osc workqueue */
+ destroy_workqueue(priv->bus_rescan_wq);
+ destroy_workqueue(priv->wq);
+
+ sdio_claim_host(func);
+ sdio_disable_func(func);
+ sysfs_remove_group(&func->dev.kobj, &iwmct_attribute_group);
+ iwmct_dbgfs_unregister(priv->dbgfs);
+ sdio_release_host(func);
+
+ /* free read requests */
+ while (!list_empty(&priv->read_req_list)) {
+ read_req = list_entry(priv->read_req_list.next,
+ struct iwmct_work_struct, list);
+
+ list_del(&read_req->list);
+ kfree(read_req);
+ }
+
+ kfree(priv);
+}
+
+
+static const struct sdio_device_id iwmct_ids[] = {
+ { SDIO_DEVICE(SDIO_INTEL_VENDOR_ID, SDIO_DEVICE_ID_INTEL_IWMC3200TOP)},
+ { /* end: all zeroes */ },
+};
+
+MODULE_DEVICE_TABLE(sdio, iwmct_ids);
+
+static struct sdio_driver iwmct_driver = {
+ .probe = iwmct_probe,
+ .remove = iwmct_remove,
+ .name = DRV_NAME,
+ .id_table = iwmct_ids,
+};
+
+static int __init iwmct_init(void)
+{
+ int rc;
+
+ /* Default log filter settings */
+ iwmct_log_set_filter(LOG_SRC_ALL, LOG_SEV_FILTER_RUNTIME);
+ iwmct_log_set_filter(LOG_SRC_FW_MSG, LOG_SEV_FILTER_ALL);
+ iwmct_log_set_fw_filter(LOG_SRC_ALL, FW_LOG_SEV_FILTER_RUNTIME);
+
+ rc = sdio_register_driver(&iwmct_driver);
+
+ return rc;
+}
+
+static void __exit iwmct_exit(void)
+{
+ sdio_unregister_driver(&iwmct_driver);
+}
+
+module_init(iwmct_init);
+module_exit(iwmct_exit);
+
--
1.6.0.6
---------------------------------------------------------------------
Intel Israel (74) Limited
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: MMC_CAP_SDIO_IRQ for omap 3430
From: Dirk Behme @ 2009-10-17 18:08 UTC (permalink / raw)
To: linux-omap
Cc: John Rigby, Madhusudhan, linux-mmc, Steve Sakoman,
Thompson, Bernard
In-Reply-To: <4b73d43f0910170812of187c94oc4931cd3330b88c0@mail.gmail.com>
Does anybody have access to omap_hsmmc driver of "the SDK 1.0.2
kernel"? Or has any hint where to get it from?
https://community.ti.com/forums/p/6290/23867.aspx#23873
talks about
"For the SDIO solution, the SDK 1.0.2 kernel supports SDIO out of the
box ..."
Thanks
Dirk
John Rigby wrote:
> First, answers to your questions:
>
> The CIRQ bit in the STAT register is on if the CIRQ is enabled in the
> IE register and clear when disabled in the IE. That is to say that
> the IE register appears to be working.
>
> Yes the card has no pending irqs.
>
> IBG is set really early when the card is discovered. First interrupt
> does not occur until much later when the libertas driver asks for
> interrupts.
>
> The lines have pull ups.
>
> Now a thought.
>
> Do we need to set DDIR in the CMD reg for CIRQ to work correctly?
> Right now it is set at the beginning of data read commands, cleared on
> data write commands and otherwise untouched. If DDIR is used
> unconditionally to set the direction of the data line buffers then it
> would make sense that we need to set the direction to in in order to
> monitor the DAT1 line. I will try this Monday when I get back to
> work.
>
> John
>
>
>
> On Sat, Oct 17, 2009 at 12:30 AM, Dirk Behme <dirk.behme@googlemail.com> wrote:
>> John Rigby wrote:
>>> It appears to never get cleared in the status register.
>> In the OMAP status register, correct? (just to get the correct
>> understanding)
>>
>>> I added some printks to sdio_irq.c to print the pending interrupts in
>>> the SDIO_CCCR_INTx register for the card and there are no pending
>>> interrupts so I don't think it is a card driver or card issue.
>> Ok, in other words, this does mean that the card has no interrupt asserted
>> any more (i.e. it is acknowledged by upper layers, e.g. libertas driver),
>> but OMAP thinks there is still an interrupt. Right? This would mean it is an
>> OMAP/omap_hsmmc.c issue. Right?
>>
>>> It would be funny if the TRM was wrong and the CIRQ bit is really
>>> cleared by writing 1 to it. I'll try that.
>> Have you checked if
>>
>> - IBG (and 4 bit mode) is correctly set before the first interrupt is fired
>> (just to make sure that we don't have a function calling order issue)?
>>
>> - your HW design has a pull up on DAT1 line (as required by the SD physical
>> spec)?
>>
>> Best regards
>>
>> Dirk
>>
>>> On Fri, Oct 16, 2009 at 3:14 PM, Madhusudhan <madhu.cr@ti.com> wrote:
>>>>> -----Original Message-----
>>>>> From: Dirk Behme [mailto:dirk.behme@googlemail.com]
>>>>> Sent: Friday, October 16, 2009 2:28 PM
>>>>> To: Madhusudhan Chikkature
>>>>> Cc: linux-mmc@vger.kernel.org; John Rigby; linux-omap@vger.kernel.org;
>>>>> Steve Sakoman
>>>>> Subject: Re: MMC_CAP_SDIO_IRQ for omap 3430
>>>>>
>>>>> Madhusudhan Chikkature wrote:
>>>>>> Hi Dirk,
>>>>>>
>>>>>> I am inlining the patch so that it helps review.
>>>>> ...
>>>>>> @@ -1165,8 +1178,15 @@ static void omap_hsmmc_set_ios(struct mm
>>>>>> break;
>>>>>> case MMC_BUS_WIDTH_4:
>>>>>> OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
>>>>>> - OMAP_HSMMC_WRITE(host->base, HCTL,
>>>>>> - OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
>>>>>> + if (mmc_card_sdio(host->mmc->card)) {
>>>>>>
>>>>>> I wish it could be moved to "enable_sdio_irq" so that we can avoid
>>>>> inclusion of
>>>>>> card.h and checking the type of card in the host controller driver.
>>>>> Yes, this would be the real clean way. But ...
>>>>>
>>>>>> But the
>>>>>> dependancy on 4-bit seems to be a problem here.
>>>>> ... most probably we have to find a workaround until (if ever?) above
>>>>> clean implementation is available.
>>>>>
>>>>> What we need is after SDIO mode and bus width is known, and before the
>>>>> first interrupt comes, to set IBG.
>>>>>
>>>>>> On the problems being discussed on testing is the interrupt source
>>>>> geting
>>>>>> cleared at the SDIO card level upon genaration of the CIRQ? If not it
>>>>> remains
>>>>>> asserted.
>>>>> Yes, this seems to be exactly the problem John reports in his follow
>>>>> up mail.
>>>>>
>>>>> Any hint how to clear SDIO interrupt?
>>>>>
>>>> On the controller side I guess it is cleared when you pass "disable" in
>>>> the
>>>> enable_sdio_irq" fn. This happens when you call mmc_signal_sdio_irq.
>>>>
>>>> I am not too sure about how it gets disabled from the card side. I see
>>>> that
>>>> SDIO core has a function "sdio_release_irq" which is used by the sdio
>>>> uart
>>>> driver. The usage of this could give a clue.
>>>>
>>>> Regards,
>>>> Madhu
>>>>
>>>>> Many thanks
>>>>>
>>>>> Dirk
>>>>>
>>>>>> + OMAP_HSMMC_WRITE(host->base, HCTL,
>>>>>> + OMAP_HSMMC_READ(host->base, HCTL)
>>>>>> + | IBG | FOUR_BIT);
>>>>>> + } else {
>>>>>> + OMAP_HSMMC_WRITE(host->base, HCTL,
>>>>>> + OMAP_HSMMC_READ(host->base, HCTL)
>>>>>> + | FOUR_BIT);
>>>>>> + }
>>>>>> break;
>>>>>> case MMC_BUS_WIDTH_1:
>>>>>> OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
>>>>>> @@ -1512,6 +1532,24 @@ static int omap_hsmmc_disable_fclk(struc
>>>>>> return 0;
>>>>>> }
>>>>>>
>>>>>> +static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int
>>>>> enable)
>>>>>> +{
>>>>>> + struct omap_hsmmc_host *host = mmc_priv(mmc);
>>>>>> + u32 ie, ise;
>>>>>> +
>>>>>> + ise = OMAP_HSMMC_READ(host->base, ISE);
>>>>>> + ie = OMAP_HSMMC_READ(host->base, IE);
>>>>>> +
>>>>>> + if (enable) {
>>>>>> + OMAP_HSMMC_WRITE(host->base, ISE, ise | CIRQ_ENABLE);
>>>>>> + OMAP_HSMMC_WRITE(host->base, IE, ie | CIRQ_ENABLE);
>>>>>> + } else {
>>>>>> + OMAP_HSMMC_WRITE(host->base, ISE, ise & ~CIRQ_ENABLE);
>>>>>> + OMAP_HSMMC_WRITE(host->base, IE, ie & ~CIRQ_ENABLE);
>>>>>> + }
>>>>>> +
>>>>>> +}
>>>>>> +
>>>>>> static const struct mmc_host_ops omap_hsmmc_ops = {
>>>>>> .enable = omap_hsmmc_enable_fclk,
>>>>>> .disable = omap_hsmmc_disable_fclk,
>>>>>> @@ -1519,7 +1557,7 @@ static const struct mmc_host_ops omap_hs
>>>>>> .set_ios = omap_hsmmc_set_ios,
>>>>>> .get_cd = omap_hsmmc_get_cd,
>>>>>> .get_ro = omap_hsmmc_get_ro,
>>>>>> - /* NYET -- enable_sdio_irq */
>>>>>> + .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
>>>>>> };
>>>>>>
>>>>>> static const struct mmc_host_ops omap_hsmmc_ps_ops = {
>>>>>> @@ -1529,7 +1567,7 @@ static const struct mmc_host_ops omap_hs
>>>>>> .set_ios = omap_hsmmc_set_ios,
>>>>>> .get_cd = omap_hsmmc_get_cd,
>>>>>> .get_ro = omap_hsmmc_get_ro,
>>>>>> - /* NYET -- enable_sdio_irq */
>>>>>> + .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
>>>>>> };
>>>>>>
>>>>>> #ifdef CONFIG_DEBUG_FS
>>>>>> @@ -1734,7 +1772,7 @@ static int __init omap_hsmmc_probe(struc
>>>>>> mmc->max_seg_size = mmc->max_req_size;
>>>>>>
>>>>>> mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
>>>>>> - MMC_CAP_WAIT_WHILE_BUSY;
>>>>>> + MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_SDIO_IRQ;
>>>>>>
>>>>>> if (mmc_slot(host).wires >= 8)
>>>>>> mmc->caps |= MMC_CAP_8_BIT_DATA;
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>> John Rigby wrote:
>>>>>>>> I have seen several discussions about lack of sdio irq support in the
>>>>>>>> hsmmc driver but no patches. Has anyone on this list implemented
>>>>>>>> this
>>>>>>>> and/or can anyone point me to patches?
>>>>>>> What a coincidence ;)
>>>>>>>
>>>>>>> I'm currently working on this. See attachment what I currently have.
>>>>>>> It is compile tested only against recent omap linux head. I don't have
>>>>>>> a board using SDIO at the moment, so no real testing possible :(
>>>>>>>
>>>>>>> Some background, maybe it helps people to step in:
>>>>>>>
>>>>>>> Gumstix OMAP3 based Overo air board connects Marvell 88W8686 wifi by
>>>>>>> MMC port 2 in 4 bit configuration [1]. The wifi performance is quite
>>>>>>> bad (~100kB/s). There is some rumor that this might be SDIO irq
>>>>>>> related [2]. There was an attempt to fix this [3] already, but this
>>>>>>> doesn't work [4]. Having this, I started to look into it.
>>>>>>>
>>>>>>> I used [3], the TI Davinci driver [5] (supporting SDIO irq), the SDIO
>>>>>>> Simplified Specification [6] and the OMAP35x TRM [7] as starting
>>>>> points.
>>>>>>> Unfortunately, the Davinci MMC registers and irqs are different
>>>>>>> (Davinci has a dedicated SDIO irq). But combining [3] and [5] helps to
>>>>>>> get an idea what has to be done.
>>>>>>>
>>>>>>> I think the main issues of [3] were that it doesn't enable IBG for 4
>>>>>>> bit mode ([6] chapter 8.1.2) and that mmc_omap_irq() doesn't reset the
>>>>>>> irq bits.
>>>>>>>
>>>>>>> Topics I still open:
>>>>>>>
>>>>>>> - Is it always necessary to deal with IE _and_ ISE register? I'm not
>>>>>>> totally clear what the difference between these two registers are ;)
>>>>>>> And in which order they have to be set.
>>>>>>>
>>>>>>> - Davinci driver [5] in line 1115 checks for data line to call
>>>>>>> mmc_signal_sdio_irq() for irq enable.
>>>>>>>
>>>>>>> - Davinci driver deals with SDIO in xfer_done() (line 873)
>>>>>>>
>>>>>>> - Davinci driver sets block size to 64 if SDIO in line 701
>>>>>>>
>>>>>>> It would be quite nice if anybody likes to comment on attachment and
>>>>>>> help testing.
>>>>>>>
>>>>>>> Many thanks and best regards
>>>>>>>
>>>>>>> Dirk
>>>>>>>
>>>>>>> [1] http://gumstix.net/wiki/index.php?title=Overo_Wifi
>>>>>>>
>>>>>>> [2] http://groups.google.com/group/beagleboard/msg/14e822778c5eeb56
>>>>>>>
>>>>>>> [3] http://groups.google.com/group/beagleboard/msg/d0eb69f4c20673be
>>>>>>>
>>>>>>> [4] http://groups.google.com/group/beagleboard/msg/5cdfe2a319531937
>>>>>>>
>>>>>>> [5]
>>>>>>> http://arago-project.org/git/projects/?p=linux-
>>>>>
>>>>> davinci.git;a=blob;f=drivers/mmc/host/davinci_mmc.c;h=1bf0587250614c6d8abf
>>>>> e02028b96e0e47148ac8;hb=HEAD
>>>>>>> [6] http://www.sdcard.org/developers/tech/sdio/sd_bluetooth_spec/
>>>>>>>
>>>>>>> [7] http://focus.ti.com/lit/ug/spruf98c/spruf98c.pdf
>>>>>>>
>>>>>>>
>>>>>>
>>>>
>>
>
^ permalink raw reply
* Re: MMC_CAP_SDIO_IRQ for omap 3430
From: Dirk Behme @ 2009-10-17 17:36 UTC (permalink / raw)
To: John Rigby, Madhusudhan; +Cc: linux-mmc, linux-omap, Steve Sakoman
In-Reply-To: <4b73d43f0910170812of187c94oc4931cd3330b88c0@mail.gmail.com>
John Rigby wrote:
> First, answers to your questions:
>
> The CIRQ bit in the STAT register is on if the CIRQ is enabled in the
> IE register and clear when disabled in the IE. That is to say that
> the IE register appears to be working.
>
> Yes the card has no pending irqs.
>
> IBG is set really early when the card is discovered. First interrupt
> does not occur until much later when the libertas driver asks for
> interrupts.
>
> The lines have pull ups.
Ok. This all sounds fine. Thanks for testing/checking all this!
> Now a thought.
>
> Do we need to set DDIR in the CMD reg for CIRQ to work correctly?
> Right now it is set at the beginning of data read commands, cleared on
> data write commands and otherwise untouched. If DDIR is used
> unconditionally to set the direction of the data line buffers then it
> would make sense that we need to set the direction to in in order to
> monitor the DAT1 line. I will try this Monday when I get back to
> work.
Sounds like it's time to re-read TRM again.
If somebody has additionally ideas, this would be really helpful!
Madhu: Do you think it would be possible to check inside TI if
somebody has SDIO working on OMAP3 and maybe can provide some example
code?
Many thanks and best regards
Dirk
> On Sat, Oct 17, 2009 at 12:30 AM, Dirk Behme <dirk.behme@googlemail.com> wrote:
>> John Rigby wrote:
>>> It appears to never get cleared in the status register.
>> In the OMAP status register, correct? (just to get the correct
>> understanding)
>>
>>> I added some printks to sdio_irq.c to print the pending interrupts in
>>> the SDIO_CCCR_INTx register for the card and there are no pending
>>> interrupts so I don't think it is a card driver or card issue.
>> Ok, in other words, this does mean that the card has no interrupt asserted
>> any more (i.e. it is acknowledged by upper layers, e.g. libertas driver),
>> but OMAP thinks there is still an interrupt. Right? This would mean it is an
>> OMAP/omap_hsmmc.c issue. Right?
>>
>>> It would be funny if the TRM was wrong and the CIRQ bit is really
>>> cleared by writing 1 to it. I'll try that.
>> Have you checked if
>>
>> - IBG (and 4 bit mode) is correctly set before the first interrupt is fired
>> (just to make sure that we don't have a function calling order issue)?
>>
>> - your HW design has a pull up on DAT1 line (as required by the SD physical
>> spec)?
>>
>> Best regards
>>
>> Dirk
>>
>>> On Fri, Oct 16, 2009 at 3:14 PM, Madhusudhan <madhu.cr@ti.com> wrote:
>>>>> -----Original Message-----
>>>>> From: Dirk Behme [mailto:dirk.behme@googlemail.com]
>>>>> Sent: Friday, October 16, 2009 2:28 PM
>>>>> To: Madhusudhan Chikkature
>>>>> Cc: linux-mmc@vger.kernel.org; John Rigby; linux-omap@vger.kernel.org;
>>>>> Steve Sakoman
>>>>> Subject: Re: MMC_CAP_SDIO_IRQ for omap 3430
>>>>>
>>>>> Madhusudhan Chikkature wrote:
>>>>>> Hi Dirk,
>>>>>>
>>>>>> I am inlining the patch so that it helps review.
>>>>> ...
>>>>>> @@ -1165,8 +1178,15 @@ static void omap_hsmmc_set_ios(struct mm
>>>>>> break;
>>>>>> case MMC_BUS_WIDTH_4:
>>>>>> OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
>>>>>> - OMAP_HSMMC_WRITE(host->base, HCTL,
>>>>>> - OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
>>>>>> + if (mmc_card_sdio(host->mmc->card)) {
>>>>>>
>>>>>> I wish it could be moved to "enable_sdio_irq" so that we can avoid
>>>>> inclusion of
>>>>>> card.h and checking the type of card in the host controller driver.
>>>>> Yes, this would be the real clean way. But ...
>>>>>
>>>>>> But the
>>>>>> dependancy on 4-bit seems to be a problem here.
>>>>> ... most probably we have to find a workaround until (if ever?) above
>>>>> clean implementation is available.
>>>>>
>>>>> What we need is after SDIO mode and bus width is known, and before the
>>>>> first interrupt comes, to set IBG.
>>>>>
>>>>>> On the problems being discussed on testing is the interrupt source
>>>>> geting
>>>>>> cleared at the SDIO card level upon genaration of the CIRQ? If not it
>>>>> remains
>>>>>> asserted.
>>>>> Yes, this seems to be exactly the problem John reports in his follow
>>>>> up mail.
>>>>>
>>>>> Any hint how to clear SDIO interrupt?
>>>>>
>>>> On the controller side I guess it is cleared when you pass "disable" in
>>>> the
>>>> enable_sdio_irq" fn. This happens when you call mmc_signal_sdio_irq.
>>>>
>>>> I am not too sure about how it gets disabled from the card side. I see
>>>> that
>>>> SDIO core has a function "sdio_release_irq" which is used by the sdio
>>>> uart
>>>> driver. The usage of this could give a clue.
>>>>
>>>> Regards,
>>>> Madhu
>>>>
>>>>> Many thanks
>>>>>
>>>>> Dirk
>>>>>
>>>>>> + OMAP_HSMMC_WRITE(host->base, HCTL,
>>>>>> + OMAP_HSMMC_READ(host->base, HCTL)
>>>>>> + | IBG | FOUR_BIT);
>>>>>> + } else {
>>>>>> + OMAP_HSMMC_WRITE(host->base, HCTL,
>>>>>> + OMAP_HSMMC_READ(host->base, HCTL)
>>>>>> + | FOUR_BIT);
>>>>>> + }
>>>>>> break;
>>>>>> case MMC_BUS_WIDTH_1:
>>>>>> OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
>>>>>> @@ -1512,6 +1532,24 @@ static int omap_hsmmc_disable_fclk(struc
>>>>>> return 0;
>>>>>> }
>>>>>>
>>>>>> +static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int
>>>>> enable)
>>>>>> +{
>>>>>> + struct omap_hsmmc_host *host = mmc_priv(mmc);
>>>>>> + u32 ie, ise;
>>>>>> +
>>>>>> + ise = OMAP_HSMMC_READ(host->base, ISE);
>>>>>> + ie = OMAP_HSMMC_READ(host->base, IE);
>>>>>> +
>>>>>> + if (enable) {
>>>>>> + OMAP_HSMMC_WRITE(host->base, ISE, ise | CIRQ_ENABLE);
>>>>>> + OMAP_HSMMC_WRITE(host->base, IE, ie | CIRQ_ENABLE);
>>>>>> + } else {
>>>>>> + OMAP_HSMMC_WRITE(host->base, ISE, ise & ~CIRQ_ENABLE);
>>>>>> + OMAP_HSMMC_WRITE(host->base, IE, ie & ~CIRQ_ENABLE);
>>>>>> + }
>>>>>> +
>>>>>> +}
>>>>>> +
>>>>>> static const struct mmc_host_ops omap_hsmmc_ops = {
>>>>>> .enable = omap_hsmmc_enable_fclk,
>>>>>> .disable = omap_hsmmc_disable_fclk,
>>>>>> @@ -1519,7 +1557,7 @@ static const struct mmc_host_ops omap_hs
>>>>>> .set_ios = omap_hsmmc_set_ios,
>>>>>> .get_cd = omap_hsmmc_get_cd,
>>>>>> .get_ro = omap_hsmmc_get_ro,
>>>>>> - /* NYET -- enable_sdio_irq */
>>>>>> + .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
>>>>>> };
>>>>>>
>>>>>> static const struct mmc_host_ops omap_hsmmc_ps_ops = {
>>>>>> @@ -1529,7 +1567,7 @@ static const struct mmc_host_ops omap_hs
>>>>>> .set_ios = omap_hsmmc_set_ios,
>>>>>> .get_cd = omap_hsmmc_get_cd,
>>>>>> .get_ro = omap_hsmmc_get_ro,
>>>>>> - /* NYET -- enable_sdio_irq */
>>>>>> + .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
>>>>>> };
>>>>>>
>>>>>> #ifdef CONFIG_DEBUG_FS
>>>>>> @@ -1734,7 +1772,7 @@ static int __init omap_hsmmc_probe(struc
>>>>>> mmc->max_seg_size = mmc->max_req_size;
>>>>>>
>>>>>> mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
>>>>>> - MMC_CAP_WAIT_WHILE_BUSY;
>>>>>> + MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_SDIO_IRQ;
>>>>>>
>>>>>> if (mmc_slot(host).wires >= 8)
>>>>>> mmc->caps |= MMC_CAP_8_BIT_DATA;
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>> John Rigby wrote:
>>>>>>>> I have seen several discussions about lack of sdio irq support in the
>>>>>>>> hsmmc driver but no patches. Has anyone on this list implemented
>>>>>>>> this
>>>>>>>> and/or can anyone point me to patches?
>>>>>>> What a coincidence ;)
>>>>>>>
>>>>>>> I'm currently working on this. See attachment what I currently have.
>>>>>>> It is compile tested only against recent omap linux head. I don't have
>>>>>>> a board using SDIO at the moment, so no real testing possible :(
>>>>>>>
>>>>>>> Some background, maybe it helps people to step in:
>>>>>>>
>>>>>>> Gumstix OMAP3 based Overo air board connects Marvell 88W8686 wifi by
>>>>>>> MMC port 2 in 4 bit configuration [1]. The wifi performance is quite
>>>>>>> bad (~100kB/s). There is some rumor that this might be SDIO irq
>>>>>>> related [2]. There was an attempt to fix this [3] already, but this
>>>>>>> doesn't work [4]. Having this, I started to look into it.
>>>>>>>
>>>>>>> I used [3], the TI Davinci driver [5] (supporting SDIO irq), the SDIO
>>>>>>> Simplified Specification [6] and the OMAP35x TRM [7] as starting
>>>>> points.
>>>>>>> Unfortunately, the Davinci MMC registers and irqs are different
>>>>>>> (Davinci has a dedicated SDIO irq). But combining [3] and [5] helps to
>>>>>>> get an idea what has to be done.
>>>>>>>
>>>>>>> I think the main issues of [3] were that it doesn't enable IBG for 4
>>>>>>> bit mode ([6] chapter 8.1.2) and that mmc_omap_irq() doesn't reset the
>>>>>>> irq bits.
>>>>>>>
>>>>>>> Topics I still open:
>>>>>>>
>>>>>>> - Is it always necessary to deal with IE _and_ ISE register? I'm not
>>>>>>> totally clear what the difference between these two registers are ;)
>>>>>>> And in which order they have to be set.
>>>>>>>
>>>>>>> - Davinci driver [5] in line 1115 checks for data line to call
>>>>>>> mmc_signal_sdio_irq() for irq enable.
>>>>>>>
>>>>>>> - Davinci driver deals with SDIO in xfer_done() (line 873)
>>>>>>>
>>>>>>> - Davinci driver sets block size to 64 if SDIO in line 701
>>>>>>>
>>>>>>> It would be quite nice if anybody likes to comment on attachment and
>>>>>>> help testing.
>>>>>>>
>>>>>>> Many thanks and best regards
>>>>>>>
>>>>>>> Dirk
>>>>>>>
>>>>>>> [1] http://gumstix.net/wiki/index.php?title=Overo_Wifi
>>>>>>>
>>>>>>> [2] http://groups.google.com/group/beagleboard/msg/14e822778c5eeb56
>>>>>>>
>>>>>>> [3] http://groups.google.com/group/beagleboard/msg/d0eb69f4c20673be
>>>>>>>
>>>>>>> [4] http://groups.google.com/group/beagleboard/msg/5cdfe2a319531937
>>>>>>>
>>>>>>> [5]
>>>>>>> http://arago-project.org/git/projects/?p=linux-
>>>>>
>>>>> davinci.git;a=blob;f=drivers/mmc/host/davinci_mmc.c;h=1bf0587250614c6d8abf
>>>>> e02028b96e0e47148ac8;hb=HEAD
>>>>>>> [6] http://www.sdcard.org/developers/tech/sdio/sd_bluetooth_spec/
>>>>>>>
>>>>>>> [7] http://focus.ti.com/lit/ug/spruf98c/spruf98c.pdf
>>>>>>>
>>>>>>>
>>>>>>
>>>>
>>
>
^ permalink raw reply
* Re: MMC_CAP_SDIO_IRQ for omap 3430
From: John Rigby @ 2009-10-17 15:12 UTC (permalink / raw)
To: Dirk Behme; +Cc: Madhusudhan, linux-mmc, linux-omap, Steve Sakoman
In-Reply-To: <4AD9647F.3010505@googlemail.com>
First, answers to your questions:
The CIRQ bit in the STAT register is on if the CIRQ is enabled in the
IE register and clear when disabled in the IE. That is to say that
the IE register appears to be working.
Yes the card has no pending irqs.
IBG is set really early when the card is discovered. First interrupt
does not occur until much later when the libertas driver asks for
interrupts.
The lines have pull ups.
Now a thought.
Do we need to set DDIR in the CMD reg for CIRQ to work correctly?
Right now it is set at the beginning of data read commands, cleared on
data write commands and otherwise untouched. If DDIR is used
unconditionally to set the direction of the data line buffers then it
would make sense that we need to set the direction to in in order to
monitor the DAT1 line. I will try this Monday when I get back to
work.
John
On Sat, Oct 17, 2009 at 12:30 AM, Dirk Behme <dirk.behme@googlemail.com> wrote:
> John Rigby wrote:
>>
>> It appears to never get cleared in the status register.
>
> In the OMAP status register, correct? (just to get the correct
> understanding)
>
>> I added some printks to sdio_irq.c to print the pending interrupts in
>> the SDIO_CCCR_INTx register for the card and there are no pending
>> interrupts so I don't think it is a card driver or card issue.
>
> Ok, in other words, this does mean that the card has no interrupt asserted
> any more (i.e. it is acknowledged by upper layers, e.g. libertas driver),
> but OMAP thinks there is still an interrupt. Right? This would mean it is an
> OMAP/omap_hsmmc.c issue. Right?
>
>> It would be funny if the TRM was wrong and the CIRQ bit is really
>> cleared by writing 1 to it. I'll try that.
>
> Have you checked if
>
> - IBG (and 4 bit mode) is correctly set before the first interrupt is fired
> (just to make sure that we don't have a function calling order issue)?
>
> - your HW design has a pull up on DAT1 line (as required by the SD physical
> spec)?
>
> Best regards
>
> Dirk
>
>> On Fri, Oct 16, 2009 at 3:14 PM, Madhusudhan <madhu.cr@ti.com> wrote:
>>>
>>>> -----Original Message-----
>>>> From: Dirk Behme [mailto:dirk.behme@googlemail.com]
>>>> Sent: Friday, October 16, 2009 2:28 PM
>>>> To: Madhusudhan Chikkature
>>>> Cc: linux-mmc@vger.kernel.org; John Rigby; linux-omap@vger.kernel.org;
>>>> Steve Sakoman
>>>> Subject: Re: MMC_CAP_SDIO_IRQ for omap 3430
>>>>
>>>> Madhusudhan Chikkature wrote:
>>>>>
>>>>> Hi Dirk,
>>>>>
>>>>> I am inlining the patch so that it helps review.
>>>>
>>>> ...
>>>>>
>>>>> @@ -1165,8 +1178,15 @@ static void omap_hsmmc_set_ios(struct mm
>>>>> break;
>>>>> case MMC_BUS_WIDTH_4:
>>>>> OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
>>>>> - OMAP_HSMMC_WRITE(host->base, HCTL,
>>>>> - OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
>>>>> + if (mmc_card_sdio(host->mmc->card)) {
>>>>>
>>>>> I wish it could be moved to "enable_sdio_irq" so that we can avoid
>>>>
>>>> inclusion of
>>>>>
>>>>> card.h and checking the type of card in the host controller driver.
>>>>
>>>> Yes, this would be the real clean way. But ...
>>>>
>>>>> But the
>>>>> dependancy on 4-bit seems to be a problem here.
>>>>
>>>> ... most probably we have to find a workaround until (if ever?) above
>>>> clean implementation is available.
>>>>
>>>> What we need is after SDIO mode and bus width is known, and before the
>>>> first interrupt comes, to set IBG.
>>>>
>>>>> On the problems being discussed on testing is the interrupt source
>>>>
>>>> geting
>>>>>
>>>>> cleared at the SDIO card level upon genaration of the CIRQ? If not it
>>>>
>>>> remains
>>>>>
>>>>> asserted.
>>>>
>>>> Yes, this seems to be exactly the problem John reports in his follow
>>>> up mail.
>>>>
>>>> Any hint how to clear SDIO interrupt?
>>>>
>>> On the controller side I guess it is cleared when you pass "disable" in
>>> the
>>> enable_sdio_irq" fn. This happens when you call mmc_signal_sdio_irq.
>>>
>>> I am not too sure about how it gets disabled from the card side. I see
>>> that
>>> SDIO core has a function "sdio_release_irq" which is used by the sdio
>>> uart
>>> driver. The usage of this could give a clue.
>>>
>>> Regards,
>>> Madhu
>>>
>>>> Many thanks
>>>>
>>>> Dirk
>>>>
>>>>> + OMAP_HSMMC_WRITE(host->base, HCTL,
>>>>> + OMAP_HSMMC_READ(host->base, HCTL)
>>>>> + | IBG | FOUR_BIT);
>>>>> + } else {
>>>>> + OMAP_HSMMC_WRITE(host->base, HCTL,
>>>>> + OMAP_HSMMC_READ(host->base, HCTL)
>>>>> + | FOUR_BIT);
>>>>> + }
>>>>> break;
>>>>> case MMC_BUS_WIDTH_1:
>>>>> OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
>>>>> @@ -1512,6 +1532,24 @@ static int omap_hsmmc_disable_fclk(struc
>>>>> return 0;
>>>>> }
>>>>>
>>>>> +static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int
>>>>
>>>> enable)
>>>>>
>>>>> +{
>>>>> + struct omap_hsmmc_host *host = mmc_priv(mmc);
>>>>> + u32 ie, ise;
>>>>> +
>>>>> + ise = OMAP_HSMMC_READ(host->base, ISE);
>>>>> + ie = OMAP_HSMMC_READ(host->base, IE);
>>>>> +
>>>>> + if (enable) {
>>>>> + OMAP_HSMMC_WRITE(host->base, ISE, ise | CIRQ_ENABLE);
>>>>> + OMAP_HSMMC_WRITE(host->base, IE, ie | CIRQ_ENABLE);
>>>>> + } else {
>>>>> + OMAP_HSMMC_WRITE(host->base, ISE, ise & ~CIRQ_ENABLE);
>>>>> + OMAP_HSMMC_WRITE(host->base, IE, ie & ~CIRQ_ENABLE);
>>>>> + }
>>>>> +
>>>>> +}
>>>>> +
>>>>> static const struct mmc_host_ops omap_hsmmc_ops = {
>>>>> .enable = omap_hsmmc_enable_fclk,
>>>>> .disable = omap_hsmmc_disable_fclk,
>>>>> @@ -1519,7 +1557,7 @@ static const struct mmc_host_ops omap_hs
>>>>> .set_ios = omap_hsmmc_set_ios,
>>>>> .get_cd = omap_hsmmc_get_cd,
>>>>> .get_ro = omap_hsmmc_get_ro,
>>>>> - /* NYET -- enable_sdio_irq */
>>>>> + .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
>>>>> };
>>>>>
>>>>> static const struct mmc_host_ops omap_hsmmc_ps_ops = {
>>>>> @@ -1529,7 +1567,7 @@ static const struct mmc_host_ops omap_hs
>>>>> .set_ios = omap_hsmmc_set_ios,
>>>>> .get_cd = omap_hsmmc_get_cd,
>>>>> .get_ro = omap_hsmmc_get_ro,
>>>>> - /* NYET -- enable_sdio_irq */
>>>>> + .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
>>>>> };
>>>>>
>>>>> #ifdef CONFIG_DEBUG_FS
>>>>> @@ -1734,7 +1772,7 @@ static int __init omap_hsmmc_probe(struc
>>>>> mmc->max_seg_size = mmc->max_req_size;
>>>>>
>>>>> mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
>>>>> - MMC_CAP_WAIT_WHILE_BUSY;
>>>>> + MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_SDIO_IRQ;
>>>>>
>>>>> if (mmc_slot(host).wires >= 8)
>>>>> mmc->caps |= MMC_CAP_8_BIT_DATA;
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>> John Rigby wrote:
>>>>>>>
>>>>>>> I have seen several discussions about lack of sdio irq support in the
>>>>>>> hsmmc driver but no patches. Has anyone on this list implemented
>>>>>>> this
>>>>>>> and/or can anyone point me to patches?
>>>>>>
>>>>>> What a coincidence ;)
>>>>>>
>>>>>> I'm currently working on this. See attachment what I currently have.
>>>>>> It is compile tested only against recent omap linux head. I don't have
>>>>>> a board using SDIO at the moment, so no real testing possible :(
>>>>>>
>>>>>> Some background, maybe it helps people to step in:
>>>>>>
>>>>>> Gumstix OMAP3 based Overo air board connects Marvell 88W8686 wifi by
>>>>>> MMC port 2 in 4 bit configuration [1]. The wifi performance is quite
>>>>>> bad (~100kB/s). There is some rumor that this might be SDIO irq
>>>>>> related [2]. There was an attempt to fix this [3] already, but this
>>>>>> doesn't work [4]. Having this, I started to look into it.
>>>>>>
>>>>>> I used [3], the TI Davinci driver [5] (supporting SDIO irq), the SDIO
>>>>>> Simplified Specification [6] and the OMAP35x TRM [7] as starting
>>>>
>>>> points.
>>>>>>
>>>>>> Unfortunately, the Davinci MMC registers and irqs are different
>>>>>> (Davinci has a dedicated SDIO irq). But combining [3] and [5] helps to
>>>>>> get an idea what has to be done.
>>>>>>
>>>>>> I think the main issues of [3] were that it doesn't enable IBG for 4
>>>>>> bit mode ([6] chapter 8.1.2) and that mmc_omap_irq() doesn't reset the
>>>>>> irq bits.
>>>>>>
>>>>>> Topics I still open:
>>>>>>
>>>>>> - Is it always necessary to deal with IE _and_ ISE register? I'm not
>>>>>> totally clear what the difference between these two registers are ;)
>>>>>> And in which order they have to be set.
>>>>>>
>>>>>> - Davinci driver [5] in line 1115 checks for data line to call
>>>>>> mmc_signal_sdio_irq() for irq enable.
>>>>>>
>>>>>> - Davinci driver deals with SDIO in xfer_done() (line 873)
>>>>>>
>>>>>> - Davinci driver sets block size to 64 if SDIO in line 701
>>>>>>
>>>>>> It would be quite nice if anybody likes to comment on attachment and
>>>>>> help testing.
>>>>>>
>>>>>> Many thanks and best regards
>>>>>>
>>>>>> Dirk
>>>>>>
>>>>>> [1] http://gumstix.net/wiki/index.php?title=Overo_Wifi
>>>>>>
>>>>>> [2] http://groups.google.com/group/beagleboard/msg/14e822778c5eeb56
>>>>>>
>>>>>> [3] http://groups.google.com/group/beagleboard/msg/d0eb69f4c20673be
>>>>>>
>>>>>> [4] http://groups.google.com/group/beagleboard/msg/5cdfe2a319531937
>>>>>>
>>>>>> [5]
>>>>>> http://arago-project.org/git/projects/?p=linux-
>>>>
>>>>
>>>> davinci.git;a=blob;f=drivers/mmc/host/davinci_mmc.c;h=1bf0587250614c6d8abf
>>>> e02028b96e0e47148ac8;hb=HEAD
>>>>>>
>>>>>> [6] http://www.sdcard.org/developers/tech/sdio/sd_bluetooth_spec/
>>>>>>
>>>>>> [7] http://focus.ti.com/lit/ug/spruf98c/spruf98c.pdf
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>
>>>
>>
>
>
^ permalink raw reply
* [PATCH 7/14] drivers/mmc: Move dereference after NULL test
From: Julia Lawall @ 2009-10-17 6:40 UTC (permalink / raw)
To: Ben Dooks, linux-arm-kernel, linux-mmc, linux-kernel,
kernel-janitors
From: Julia Lawall <julia@diku.dk>
If the NULL test on mrq is needed, then the derefernce should be after the
NULL test.
A simplified version of the semantic match that detects this problem is as
follows (http://coccinelle.lip6.fr/):
// <smpl>
@match exists@
expression x, E;
identifier fld;
@@
* x->fld
... when != \(x = E\|&x\)
* x == NULL
// </smpl>
Signed-off-by: Julia Lawall <julia@diku.dk>
---
drivers/mmc/host/s3cmci.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index 99b74a3..5be6962 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -820,7 +820,7 @@ fail_request:
static void finalize_request(struct s3cmci_host *host)
{
struct mmc_request *mrq = host->mrq;
- struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
+ struct mmc_command *cmd;
int debug_as_failure = 0;
if (host->complete_what != COMPLETION_FINALIZE)
@@ -828,6 +828,7 @@ static void finalize_request(struct s3cmci_host *host)
if (!mrq)
return;
+ cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
if (cmd->data && (cmd->error == 0) &&
(cmd->data->error == 0)) {
^ permalink raw reply related
* Re: MMC_CAP_SDIO_IRQ for omap 3430
From: Dirk Behme @ 2009-10-17 6:30 UTC (permalink / raw)
To: John Rigby; +Cc: Madhusudhan, linux-mmc, linux-omap, Steve Sakoman
In-Reply-To: <4b73d43f0910161426l7600f424w5b8345d16790dd21@mail.gmail.com>
John Rigby wrote:
> It appears to never get cleared in the status register.
In the OMAP status register, correct? (just to get the correct
understanding)
> I added some printks to sdio_irq.c to print the pending interrupts in
> the SDIO_CCCR_INTx register for the card and there are no pending
> interrupts so I don't think it is a card driver or card issue.
Ok, in other words, this does mean that the card has no interrupt
asserted any more (i.e. it is acknowledged by upper layers, e.g.
libertas driver), but OMAP thinks there is still an interrupt. Right?
This would mean it is an OMAP/omap_hsmmc.c issue. Right?
> It would be funny if the TRM was wrong and the CIRQ bit is really
> cleared by writing 1 to it. I'll try that.
Have you checked if
- IBG (and 4 bit mode) is correctly set before the first interrupt is
fired (just to make sure that we don't have a function calling order
issue)?
- your HW design has a pull up on DAT1 line (as required by the SD
physical spec)?
Best regards
Dirk
> On Fri, Oct 16, 2009 at 3:14 PM, Madhusudhan <madhu.cr@ti.com> wrote:
>>
>>> -----Original Message-----
>>> From: Dirk Behme [mailto:dirk.behme@googlemail.com]
>>> Sent: Friday, October 16, 2009 2:28 PM
>>> To: Madhusudhan Chikkature
>>> Cc: linux-mmc@vger.kernel.org; John Rigby; linux-omap@vger.kernel.org;
>>> Steve Sakoman
>>> Subject: Re: MMC_CAP_SDIO_IRQ for omap 3430
>>>
>>> Madhusudhan Chikkature wrote:
>>>> Hi Dirk,
>>>>
>>>> I am inlining the patch so that it helps review.
>>> ...
>>>> @@ -1165,8 +1178,15 @@ static void omap_hsmmc_set_ios(struct mm
>>>> break;
>>>> case MMC_BUS_WIDTH_4:
>>>> OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
>>>> - OMAP_HSMMC_WRITE(host->base, HCTL,
>>>> - OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
>>>> + if (mmc_card_sdio(host->mmc->card)) {
>>>>
>>>> I wish it could be moved to "enable_sdio_irq" so that we can avoid
>>> inclusion of
>>>> card.h and checking the type of card in the host controller driver.
>>> Yes, this would be the real clean way. But ...
>>>
>>>> But the
>>>> dependancy on 4-bit seems to be a problem here.
>>> ... most probably we have to find a workaround until (if ever?) above
>>> clean implementation is available.
>>>
>>> What we need is after SDIO mode and bus width is known, and before the
>>> first interrupt comes, to set IBG.
>>>
>>>> On the problems being discussed on testing is the interrupt source
>>> geting
>>>> cleared at the SDIO card level upon genaration of the CIRQ? If not it
>>> remains
>>>> asserted.
>>> Yes, this seems to be exactly the problem John reports in his follow
>>> up mail.
>>>
>>> Any hint how to clear SDIO interrupt?
>>>
>> On the controller side I guess it is cleared when you pass "disable" in the
>> enable_sdio_irq" fn. This happens when you call mmc_signal_sdio_irq.
>>
>> I am not too sure about how it gets disabled from the card side. I see that
>> SDIO core has a function "sdio_release_irq" which is used by the sdio uart
>> driver. The usage of this could give a clue.
>>
>> Regards,
>> Madhu
>>
>>> Many thanks
>>>
>>> Dirk
>>>
>>>> + OMAP_HSMMC_WRITE(host->base, HCTL,
>>>> + OMAP_HSMMC_READ(host->base, HCTL)
>>>> + | IBG | FOUR_BIT);
>>>> + } else {
>>>> + OMAP_HSMMC_WRITE(host->base, HCTL,
>>>> + OMAP_HSMMC_READ(host->base, HCTL)
>>>> + | FOUR_BIT);
>>>> + }
>>>> break;
>>>> case MMC_BUS_WIDTH_1:
>>>> OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
>>>> @@ -1512,6 +1532,24 @@ static int omap_hsmmc_disable_fclk(struc
>>>> return 0;
>>>> }
>>>>
>>>> +static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int
>>> enable)
>>>> +{
>>>> + struct omap_hsmmc_host *host = mmc_priv(mmc);
>>>> + u32 ie, ise;
>>>> +
>>>> + ise = OMAP_HSMMC_READ(host->base, ISE);
>>>> + ie = OMAP_HSMMC_READ(host->base, IE);
>>>> +
>>>> + if (enable) {
>>>> + OMAP_HSMMC_WRITE(host->base, ISE, ise | CIRQ_ENABLE);
>>>> + OMAP_HSMMC_WRITE(host->base, IE, ie | CIRQ_ENABLE);
>>>> + } else {
>>>> + OMAP_HSMMC_WRITE(host->base, ISE, ise & ~CIRQ_ENABLE);
>>>> + OMAP_HSMMC_WRITE(host->base, IE, ie & ~CIRQ_ENABLE);
>>>> + }
>>>> +
>>>> +}
>>>> +
>>>> static const struct mmc_host_ops omap_hsmmc_ops = {
>>>> .enable = omap_hsmmc_enable_fclk,
>>>> .disable = omap_hsmmc_disable_fclk,
>>>> @@ -1519,7 +1557,7 @@ static const struct mmc_host_ops omap_hs
>>>> .set_ios = omap_hsmmc_set_ios,
>>>> .get_cd = omap_hsmmc_get_cd,
>>>> .get_ro = omap_hsmmc_get_ro,
>>>> - /* NYET -- enable_sdio_irq */
>>>> + .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
>>>> };
>>>>
>>>> static const struct mmc_host_ops omap_hsmmc_ps_ops = {
>>>> @@ -1529,7 +1567,7 @@ static const struct mmc_host_ops omap_hs
>>>> .set_ios = omap_hsmmc_set_ios,
>>>> .get_cd = omap_hsmmc_get_cd,
>>>> .get_ro = omap_hsmmc_get_ro,
>>>> - /* NYET -- enable_sdio_irq */
>>>> + .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
>>>> };
>>>>
>>>> #ifdef CONFIG_DEBUG_FS
>>>> @@ -1734,7 +1772,7 @@ static int __init omap_hsmmc_probe(struc
>>>> mmc->max_seg_size = mmc->max_req_size;
>>>>
>>>> mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
>>>> - MMC_CAP_WAIT_WHILE_BUSY;
>>>> + MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_SDIO_IRQ;
>>>>
>>>> if (mmc_slot(host).wires >= 8)
>>>> mmc->caps |= MMC_CAP_8_BIT_DATA;
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>> John Rigby wrote:
>>>>>> I have seen several discussions about lack of sdio irq support in the
>>>>>> hsmmc driver but no patches. Has anyone on this list implemented this
>>>>>> and/or can anyone point me to patches?
>>>>> What a coincidence ;)
>>>>>
>>>>> I'm currently working on this. See attachment what I currently have.
>>>>> It is compile tested only against recent omap linux head. I don't have
>>>>> a board using SDIO at the moment, so no real testing possible :(
>>>>>
>>>>> Some background, maybe it helps people to step in:
>>>>>
>>>>> Gumstix OMAP3 based Overo air board connects Marvell 88W8686 wifi by
>>>>> MMC port 2 in 4 bit configuration [1]. The wifi performance is quite
>>>>> bad (~100kB/s). There is some rumor that this might be SDIO irq
>>>>> related [2]. There was an attempt to fix this [3] already, but this
>>>>> doesn't work [4]. Having this, I started to look into it.
>>>>>
>>>>> I used [3], the TI Davinci driver [5] (supporting SDIO irq), the SDIO
>>>>> Simplified Specification [6] and the OMAP35x TRM [7] as starting
>>> points.
>>>>> Unfortunately, the Davinci MMC registers and irqs are different
>>>>> (Davinci has a dedicated SDIO irq). But combining [3] and [5] helps to
>>>>> get an idea what has to be done.
>>>>>
>>>>> I think the main issues of [3] were that it doesn't enable IBG for 4
>>>>> bit mode ([6] chapter 8.1.2) and that mmc_omap_irq() doesn't reset the
>>>>> irq bits.
>>>>>
>>>>> Topics I still open:
>>>>>
>>>>> - Is it always necessary to deal with IE _and_ ISE register? I'm not
>>>>> totally clear what the difference between these two registers are ;)
>>>>> And in which order they have to be set.
>>>>>
>>>>> - Davinci driver [5] in line 1115 checks for data line to call
>>>>> mmc_signal_sdio_irq() for irq enable.
>>>>>
>>>>> - Davinci driver deals with SDIO in xfer_done() (line 873)
>>>>>
>>>>> - Davinci driver sets block size to 64 if SDIO in line 701
>>>>>
>>>>> It would be quite nice if anybody likes to comment on attachment and
>>>>> help testing.
>>>>>
>>>>> Many thanks and best regards
>>>>>
>>>>> Dirk
>>>>>
>>>>> [1] http://gumstix.net/wiki/index.php?title=Overo_Wifi
>>>>>
>>>>> [2] http://groups.google.com/group/beagleboard/msg/14e822778c5eeb56
>>>>>
>>>>> [3] http://groups.google.com/group/beagleboard/msg/d0eb69f4c20673be
>>>>>
>>>>> [4] http://groups.google.com/group/beagleboard/msg/5cdfe2a319531937
>>>>>
>>>>> [5]
>>>>> http://arago-project.org/git/projects/?p=linux-
>>> davinci.git;a=blob;f=drivers/mmc/host/davinci_mmc.c;h=1bf0587250614c6d8abf
>>> e02028b96e0e47148ac8;hb=HEAD
>>>>> [6] http://www.sdcard.org/developers/tech/sdio/sd_bluetooth_spec/
>>>>>
>>>>> [7] http://focus.ti.com/lit/ug/spruf98c/spruf98c.pdf
>>>>>
>>>>>
>>>>
>>>>
>>
>>
>
^ permalink raw reply
* + sdio-rework-cis-tuple-parsing.patch added to -mm tree
From: akpm @ 2009-10-16 22:21 UTC (permalink / raw)
To: mm-commits; +Cc: albert_herranz, linux-mmc, pierre
The patch titled
sdio: rework cis tuple parsing
has been added to the -mm tree. Its filename is
sdio-rework-cis-tuple-parsing.patch
Before you just go and hit "reply", please:
a) Consider who else should be cc'ed
b) Prefer to cc a suitable mailing list as well
c) Ideally: find the original patch on the mailing list and do a
reply-to-all to that, adding suitable additional cc's
*** Remember to use Documentation/SubmitChecklist when testing your code ***
See http://userweb.kernel.org/~akpm/stuff/added-to-mm.txt to find
out what to do about this
The current -mm tree may be found at http://userweb.kernel.org/~akpm/mmotm/
------------------------------------------------------
Subject: sdio: rework cis tuple parsing
From: Albert Herranz <albert_herranz@yahoo.es>
Rework the current CIS tuple parsing code, reusing the existing
infrastructure and providing an easy way to add new CISTPL_FUNCE parsers
by TPLFE_TYPE.
Valid known CIS tuples are now silently queued for the SDIO function
driver when not parsed/processed (-EILSEQ) by the SDIO core. Unknown CIS
tuples (-ENOENT) are queued too for the SDIO function driver without
aborting the initialization, but emit a warning in the kernel log.
CISTPL_FUNCE tuples can be "whitelisted" now by adding a matching entry to
the cis_tpl_funce_list table.
Signed-off-by: Albert Herranz <albert_herranz@yahoo.es>
Acked-by: Pierre Ossman <pierre@ossman.eu>
Cc: <linux-mmc@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
---
diff -puN drivers/mmc/core/sdio_cis.c~sdio-rework-cis-tuple-parsing drivers/mmc/core/sdio_cis.c
--- a/drivers/mmc/core/sdio_cis.c~sdio-rework-cis-tuple-parsing
+++ a/drivers/mmc/core/sdio_cis.c
@@ -97,26 +97,56 @@ static const unsigned char speed_val[16]
static const unsigned int speed_unit[8] =
{ 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 };
-/* FUNCE tuples with these types get passed to SDIO drivers */
-static const unsigned char funce_type_whitelist[] = {
- 4 /* CISTPL_FUNCE_LAN_NODE_ID used in Broadcom cards */
+
+typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *,
+ const unsigned char *, unsigned);
+
+struct cis_tpl {
+ unsigned char code;
+ unsigned char min_size;
+ tpl_parse_t *parse;
};
-static int cistpl_funce_whitelisted(unsigned char type)
+static int cis_tpl_parse(struct mmc_card *card, struct sdio_func *func,
+ const char *tpl_descr,
+ const struct cis_tpl *tpl, int tpl_count,
+ unsigned char code,
+ const unsigned char *buf, unsigned size)
{
- int i;
+ int i, ret;
- for (i = 0; i < ARRAY_SIZE(funce_type_whitelist); i++) {
- if (funce_type_whitelist[i] == type)
- return 1;
+ /* look for a matching code in the table */
+ for (i = 0; i < tpl_count; i++, tpl++) {
+ if (tpl->code == code)
+ break;
}
- return 0;
+ if (i < tpl_count) {
+ if (size >= tpl->min_size) {
+ if (tpl->parse)
+ ret = tpl->parse(card, func, buf, size);
+ else
+ ret = -EILSEQ; /* known tuple, not parsed */
+ } else {
+ /* invalid tuple */
+ ret = -EINVAL;
+ }
+ if (ret && ret != -EILSEQ && ret != -ENOENT) {
+ printk(KERN_ERR "%s: bad %s tuple 0x%02x (%u bytes)\n",
+ mmc_hostname(card->host), tpl_descr, code, size);
+ }
+ } else {
+ /* unknown tuple */
+ ret = -ENOENT;
+ }
+
+ return ret;
}
-static int cistpl_funce_common(struct mmc_card *card,
+static int cistpl_funce_common(struct mmc_card *card, struct sdio_func *func,
const unsigned char *buf, unsigned size)
{
- if (size < 0x04 || buf[0] != 0)
+ /* Only valid for the common CIS (function 0) */
+ if (func)
return -EINVAL;
/* TPLFE_FN0_BLK_SIZE */
@@ -129,20 +159,24 @@ static int cistpl_funce_common(struct mm
return 0;
}
-static int cistpl_funce_func(struct sdio_func *func,
+static int cistpl_funce_func(struct mmc_card *card, struct sdio_func *func,
const unsigned char *buf, unsigned size)
{
unsigned vsn;
unsigned min_size;
- /* let SDIO drivers take care of whitelisted FUNCE tuples */
- if (cistpl_funce_whitelisted(buf[0]))
- return -EILSEQ;
+ /* Only valid for the individual function's CIS (1-7) */
+ if (!func)
+ return -EINVAL;
+ /*
+ * This tuple has a different length depending on the SDIO spec
+ * version.
+ */
vsn = func->card->cccr.sdio_vsn;
min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42;
- if (size < min_size || buf[0] != 1)
+ if (size < min_size)
return -EINVAL;
/* TPLFE_MAX_BLK_SIZE */
@@ -157,39 +191,32 @@ static int cistpl_funce_func(struct sdio
return 0;
}
+/*
+ * Known TPLFE_TYPEs table for CISTPL_FUNCE tuples.
+ *
+ * Note that, unlike PCMCIA, CISTPL_FUNCE tuples are not parsed depending
+ * on the TPLFID_FUNCTION value of the previous CISTPL_FUNCID as on SDIO
+ * TPLFID_FUNCTION is always hardcoded to 0x0C.
+ */
+static const struct cis_tpl cis_tpl_funce_list[] = {
+ { 0x00, 4, cistpl_funce_common },
+ { 0x01, 0, cistpl_funce_func },
+ { 0x04, 1+1+6, /* CISTPL_FUNCE_LAN_NODE_ID */ },
+};
+
static int cistpl_funce(struct mmc_card *card, struct sdio_func *func,
const unsigned char *buf, unsigned size)
{
- int ret;
-
- /*
- * There should be two versions of the CISTPL_FUNCE tuple,
- * one for the common CIS (function 0) and a version used by
- * the individual function's CIS (1-7). Yet, the later has a
- * different length depending on the SDIO spec version.
- */
- if (func)
- ret = cistpl_funce_func(func, buf, size);
- else
- ret = cistpl_funce_common(card, buf, size);
-
- if (ret && ret != -EILSEQ) {
- printk(KERN_ERR "%s: bad CISTPL_FUNCE size %u "
- "type %u\n", mmc_hostname(card->host), size, buf[0]);
- }
+ if (size < 1)
+ return -EINVAL;
- return ret;
+ return cis_tpl_parse(card, func, "CISTPL_FUNCE",
+ cis_tpl_funce_list,
+ ARRAY_SIZE(cis_tpl_funce_list),
+ buf[0], buf, size);
}
-typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *,
- const unsigned char *, unsigned);
-
-struct cis_tpl {
- unsigned char code;
- unsigned char min_size;
- tpl_parse_t *parse;
-};
-
+/* Known TPL_CODEs table for CIS tuples */
static const struct cis_tpl cis_tpl_list[] = {
{ 0x15, 3, cistpl_vers_1 },
{ 0x20, 4, cistpl_manfid },
@@ -268,46 +295,38 @@ static int sdio_read_cis(struct mmc_card
break;
}
- for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++)
- if (cis_tpl_list[i].code == tpl_code)
- break;
- if (i < ARRAY_SIZE(cis_tpl_list)) {
- const struct cis_tpl *tpl = cis_tpl_list + i;
- if (tpl_link < tpl->min_size) {
- printk(KERN_ERR
- "%s: bad CIS tuple 0x%02x"
- " (length = %u, expected >= %u)\n",
- mmc_hostname(card->host),
- tpl_code, tpl_link, tpl->min_size);
- ret = -EINVAL;
- } else if (tpl->parse) {
- ret = tpl->parse(card, func,
- this->data, tpl_link);
- }
+ /* Try to parse the CIS tuple */
+ ret = cis_tpl_parse(card, func, "CIS",
+ cis_tpl_list, ARRAY_SIZE(cis_tpl_list),
+ tpl_code, this->data, tpl_link);
+ if (ret == -EILSEQ || ret == -ENOENT) {
/*
- * We don't need the tuple anymore if it was
- * successfully parsed by the SDIO core or if it is
- * not going to be parsed by SDIO drivers.
+ * The tuple is unknown or known but not parsed.
+ * Queue the tuple for the function driver.
*/
- if (!ret || ret != -EILSEQ)
- kfree(this);
- } else {
- /* unknown tuple */
- ret = -EILSEQ;
- }
-
- if (ret == -EILSEQ) {
- /* this tuple is unknown to the core or whitelisted */
this->next = NULL;
this->code = tpl_code;
this->size = tpl_link;
*prev = this;
prev = &this->next;
- printk(KERN_DEBUG
- "%s: queuing CIS tuple 0x%02x length %u\n",
- mmc_hostname(card->host), tpl_code, tpl_link);
+
+ if (ret == -ENOENT) {
+ /* warn about unknown tuples */
+ printk(KERN_WARNING "%s: queuing unknown"
+ " CIS tuple 0x%02x (%u bytes)\n",
+ mmc_hostname(card->host),
+ tpl_code, tpl_link);
+ }
+
/* keep on analyzing tuples */
ret = 0;
+ } else {
+ /*
+ * We don't need the tuple anymore if it was
+ * successfully parsed by the SDIO core or if it is
+ * not going to be queued for a driver.
+ */
+ kfree(this);
}
ptr += tpl_link;
_
Patches currently in -mm which might be from albert_herranz@yahoo.es are
sdio-rework-cis-tuple-parsing.patch
sdio-recognize-io-card-without-powercycle.patch
^ permalink raw reply
* Re: MMC_CAP_SDIO_IRQ for omap 3430
From: John Rigby @ 2009-10-16 21:26 UTC (permalink / raw)
To: Madhusudhan; +Cc: Dirk Behme, linux-mmc, linux-omap, Steve Sakoman
In-Reply-To: <004701ca4ea5$aeacefe0$544ff780@am.dhcp.ti.com>
It appears to never get cleared in the status register.
I added some printks to sdio_irq.c to print the pending interrupts in
the SDIO_CCCR_INTx register for the card and there are no pending
interrupts so I don't think it is a card driver or card issue.
It would be funny if the TRM was wrong and the CIRQ bit is really
cleared by writing 1 to it. I'll try that.
John
On Fri, Oct 16, 2009 at 3:14 PM, Madhusudhan <madhu.cr@ti.com> wrote:
>
>
>> -----Original Message-----
>> From: Dirk Behme [mailto:dirk.behme@googlemail.com]
>> Sent: Friday, October 16, 2009 2:28 PM
>> To: Madhusudhan Chikkature
>> Cc: linux-mmc@vger.kernel.org; John Rigby; linux-omap@vger.kernel.org;
>> Steve Sakoman
>> Subject: Re: MMC_CAP_SDIO_IRQ for omap 3430
>>
>> Madhusudhan Chikkature wrote:
>> > Hi Dirk,
>> >
>> > I am inlining the patch so that it helps review.
>> ...
>> > @@ -1165,8 +1178,15 @@ static void omap_hsmmc_set_ios(struct mm
>> > break;
>> > case MMC_BUS_WIDTH_4:
>> > OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
>> > - OMAP_HSMMC_WRITE(host->base, HCTL,
>> > - OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
>> > + if (mmc_card_sdio(host->mmc->card)) {
>> >
>> > I wish it could be moved to "enable_sdio_irq" so that we can avoid
>> inclusion of
>> > card.h and checking the type of card in the host controller driver.
>>
>> Yes, this would be the real clean way. But ...
>>
>> > But the
>> > dependancy on 4-bit seems to be a problem here.
>>
>> ... most probably we have to find a workaround until (if ever?) above
>> clean implementation is available.
>>
>> What we need is after SDIO mode and bus width is known, and before the
>> first interrupt comes, to set IBG.
>>
>> > On the problems being discussed on testing is the interrupt source
>> geting
>> > cleared at the SDIO card level upon genaration of the CIRQ? If not it
>> remains
>> > asserted.
>>
>> Yes, this seems to be exactly the problem John reports in his follow
>> up mail.
>>
>> Any hint how to clear SDIO interrupt?
>>
> On the controller side I guess it is cleared when you pass "disable" in the
> enable_sdio_irq" fn. This happens when you call mmc_signal_sdio_irq.
>
> I am not too sure about how it gets disabled from the card side. I see that
> SDIO core has a function "sdio_release_irq" which is used by the sdio uart
> driver. The usage of this could give a clue.
>
> Regards,
> Madhu
>
>> Many thanks
>>
>> Dirk
>>
>> > + OMAP_HSMMC_WRITE(host->base, HCTL,
>> > + OMAP_HSMMC_READ(host->base, HCTL)
>> > + | IBG | FOUR_BIT);
>> > + } else {
>> > + OMAP_HSMMC_WRITE(host->base, HCTL,
>> > + OMAP_HSMMC_READ(host->base, HCTL)
>> > + | FOUR_BIT);
>> > + }
>> > break;
>> > case MMC_BUS_WIDTH_1:
>> > OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
>> > @@ -1512,6 +1532,24 @@ static int omap_hsmmc_disable_fclk(struc
>> > return 0;
>> > }
>> >
>> > +static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int
>> enable)
>> > +{
>> > + struct omap_hsmmc_host *host = mmc_priv(mmc);
>> > + u32 ie, ise;
>> > +
>> > + ise = OMAP_HSMMC_READ(host->base, ISE);
>> > + ie = OMAP_HSMMC_READ(host->base, IE);
>> > +
>> > + if (enable) {
>> > + OMAP_HSMMC_WRITE(host->base, ISE, ise | CIRQ_ENABLE);
>> > + OMAP_HSMMC_WRITE(host->base, IE, ie | CIRQ_ENABLE);
>> > + } else {
>> > + OMAP_HSMMC_WRITE(host->base, ISE, ise & ~CIRQ_ENABLE);
>> > + OMAP_HSMMC_WRITE(host->base, IE, ie & ~CIRQ_ENABLE);
>> > + }
>> > +
>> > +}
>> > +
>> > static const struct mmc_host_ops omap_hsmmc_ops = {
>> > .enable = omap_hsmmc_enable_fclk,
>> > .disable = omap_hsmmc_disable_fclk,
>> > @@ -1519,7 +1557,7 @@ static const struct mmc_host_ops omap_hs
>> > .set_ios = omap_hsmmc_set_ios,
>> > .get_cd = omap_hsmmc_get_cd,
>> > .get_ro = omap_hsmmc_get_ro,
>> > - /* NYET -- enable_sdio_irq */
>> > + .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
>> > };
>> >
>> > static const struct mmc_host_ops omap_hsmmc_ps_ops = {
>> > @@ -1529,7 +1567,7 @@ static const struct mmc_host_ops omap_hs
>> > .set_ios = omap_hsmmc_set_ios,
>> > .get_cd = omap_hsmmc_get_cd,
>> > .get_ro = omap_hsmmc_get_ro,
>> > - /* NYET -- enable_sdio_irq */
>> > + .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
>> > };
>> >
>> > #ifdef CONFIG_DEBUG_FS
>> > @@ -1734,7 +1772,7 @@ static int __init omap_hsmmc_probe(struc
>> > mmc->max_seg_size = mmc->max_req_size;
>> >
>> > mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
>> > - MMC_CAP_WAIT_WHILE_BUSY;
>> > + MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_SDIO_IRQ;
>> >
>> > if (mmc_slot(host).wires >= 8)
>> > mmc->caps |= MMC_CAP_8_BIT_DATA;
>> >
>> >
>> >
>> >
>> >
>> >> John Rigby wrote:
>> >>> I have seen several discussions about lack of sdio irq support in the
>> >>> hsmmc driver but no patches. Has anyone on this list implemented this
>> >>> and/or can anyone point me to patches?
>> >> What a coincidence ;)
>> >>
>> >> I'm currently working on this. See attachment what I currently have.
>> >> It is compile tested only against recent omap linux head. I don't have
>> >> a board using SDIO at the moment, so no real testing possible :(
>> >>
>> >> Some background, maybe it helps people to step in:
>> >>
>> >> Gumstix OMAP3 based Overo air board connects Marvell 88W8686 wifi by
>> >> MMC port 2 in 4 bit configuration [1]. The wifi performance is quite
>> >> bad (~100kB/s). There is some rumor that this might be SDIO irq
>> >> related [2]. There was an attempt to fix this [3] already, but this
>> >> doesn't work [4]. Having this, I started to look into it.
>> >>
>> >> I used [3], the TI Davinci driver [5] (supporting SDIO irq), the SDIO
>> >> Simplified Specification [6] and the OMAP35x TRM [7] as starting
>> points.
>> >>
>> >> Unfortunately, the Davinci MMC registers and irqs are different
>> >> (Davinci has a dedicated SDIO irq). But combining [3] and [5] helps to
>> >> get an idea what has to be done.
>> >>
>> >> I think the main issues of [3] were that it doesn't enable IBG for 4
>> >> bit mode ([6] chapter 8.1.2) and that mmc_omap_irq() doesn't reset the
>> >> irq bits.
>> >>
>> >> Topics I still open:
>> >>
>> >> - Is it always necessary to deal with IE _and_ ISE register? I'm not
>> >> totally clear what the difference between these two registers are ;)
>> >> And in which order they have to be set.
>> >>
>> >> - Davinci driver [5] in line 1115 checks for data line to call
>> >> mmc_signal_sdio_irq() for irq enable.
>> >>
>> >> - Davinci driver deals with SDIO in xfer_done() (line 873)
>> >>
>> >> - Davinci driver sets block size to 64 if SDIO in line 701
>> >>
>> >> It would be quite nice if anybody likes to comment on attachment and
>> >> help testing.
>> >>
>> >> Many thanks and best regards
>> >>
>> >> Dirk
>> >>
>> >> [1] http://gumstix.net/wiki/index.php?title=Overo_Wifi
>> >>
>> >> [2] http://groups.google.com/group/beagleboard/msg/14e822778c5eeb56
>> >>
>> >> [3] http://groups.google.com/group/beagleboard/msg/d0eb69f4c20673be
>> >>
>> >> [4] http://groups.google.com/group/beagleboard/msg/5cdfe2a319531937
>> >>
>> >> [5]
>> >> http://arago-project.org/git/projects/?p=linux-
>> davinci.git;a=blob;f=drivers/mmc/host/davinci_mmc.c;h=1bf0587250614c6d8abf
>> e02028b96e0e47148ac8;hb=HEAD
>> >>
>> >> [6] http://www.sdcard.org/developers/tech/sdio/sd_bluetooth_spec/
>> >>
>> >> [7] http://focus.ti.com/lit/ug/spruf98c/spruf98c.pdf
>> >>
>> >>
>> >
>> >
>> >
>>
>
>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* RE: MMC_CAP_SDIO_IRQ for omap 3430
From: Madhusudhan @ 2009-10-16 21:14 UTC (permalink / raw)
To: 'Dirk Behme'
Cc: linux-mmc, 'John Rigby', linux-omap,
'Steve Sakoman'
In-Reply-To: <4AD8C959.1000004@googlemail.com>
> -----Original Message-----
> From: Dirk Behme [mailto:dirk.behme@googlemail.com]
> Sent: Friday, October 16, 2009 2:28 PM
> To: Madhusudhan Chikkature
> Cc: linux-mmc@vger.kernel.org; John Rigby; linux-omap@vger.kernel.org;
> Steve Sakoman
> Subject: Re: MMC_CAP_SDIO_IRQ for omap 3430
>
> Madhusudhan Chikkature wrote:
> > Hi Dirk,
> >
> > I am inlining the patch so that it helps review.
> ...
> > @@ -1165,8 +1178,15 @@ static void omap_hsmmc_set_ios(struct mm
> > break;
> > case MMC_BUS_WIDTH_4:
> > OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
> > - OMAP_HSMMC_WRITE(host->base, HCTL,
> > - OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
> > + if (mmc_card_sdio(host->mmc->card)) {
> >
> > I wish it could be moved to "enable_sdio_irq" so that we can avoid
> inclusion of
> > card.h and checking the type of card in the host controller driver.
>
> Yes, this would be the real clean way. But ...
>
> > But the
> > dependancy on 4-bit seems to be a problem here.
>
> ... most probably we have to find a workaround until (if ever?) above
> clean implementation is available.
>
> What we need is after SDIO mode and bus width is known, and before the
> first interrupt comes, to set IBG.
>
> > On the problems being discussed on testing is the interrupt source
> geting
> > cleared at the SDIO card level upon genaration of the CIRQ? If not it
> remains
> > asserted.
>
> Yes, this seems to be exactly the problem John reports in his follow
> up mail.
>
> Any hint how to clear SDIO interrupt?
>
On the controller side I guess it is cleared when you pass "disable" in the
enable_sdio_irq" fn. This happens when you call mmc_signal_sdio_irq.
I am not too sure about how it gets disabled from the card side. I see that
SDIO core has a function "sdio_release_irq" which is used by the sdio uart
driver. The usage of this could give a clue.
Regards,
Madhu
> Many thanks
>
> Dirk
>
> > + OMAP_HSMMC_WRITE(host->base, HCTL,
> > + OMAP_HSMMC_READ(host->base, HCTL)
> > + | IBG | FOUR_BIT);
> > + } else {
> > + OMAP_HSMMC_WRITE(host->base, HCTL,
> > + OMAP_HSMMC_READ(host->base, HCTL)
> > + | FOUR_BIT);
> > + }
> > break;
> > case MMC_BUS_WIDTH_1:
> > OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
> > @@ -1512,6 +1532,24 @@ static int omap_hsmmc_disable_fclk(struc
> > return 0;
> > }
> >
> > +static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int
> enable)
> > +{
> > + struct omap_hsmmc_host *host = mmc_priv(mmc);
> > + u32 ie, ise;
> > +
> > + ise = OMAP_HSMMC_READ(host->base, ISE);
> > + ie = OMAP_HSMMC_READ(host->base, IE);
> > +
> > + if (enable) {
> > + OMAP_HSMMC_WRITE(host->base, ISE, ise | CIRQ_ENABLE);
> > + OMAP_HSMMC_WRITE(host->base, IE, ie | CIRQ_ENABLE);
> > + } else {
> > + OMAP_HSMMC_WRITE(host->base, ISE, ise & ~CIRQ_ENABLE);
> > + OMAP_HSMMC_WRITE(host->base, IE, ie & ~CIRQ_ENABLE);
> > + }
> > +
> > +}
> > +
> > static const struct mmc_host_ops omap_hsmmc_ops = {
> > .enable = omap_hsmmc_enable_fclk,
> > .disable = omap_hsmmc_disable_fclk,
> > @@ -1519,7 +1557,7 @@ static const struct mmc_host_ops omap_hs
> > .set_ios = omap_hsmmc_set_ios,
> > .get_cd = omap_hsmmc_get_cd,
> > .get_ro = omap_hsmmc_get_ro,
> > - /* NYET -- enable_sdio_irq */
> > + .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
> > };
> >
> > static const struct mmc_host_ops omap_hsmmc_ps_ops = {
> > @@ -1529,7 +1567,7 @@ static const struct mmc_host_ops omap_hs
> > .set_ios = omap_hsmmc_set_ios,
> > .get_cd = omap_hsmmc_get_cd,
> > .get_ro = omap_hsmmc_get_ro,
> > - /* NYET -- enable_sdio_irq */
> > + .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
> > };
> >
> > #ifdef CONFIG_DEBUG_FS
> > @@ -1734,7 +1772,7 @@ static int __init omap_hsmmc_probe(struc
> > mmc->max_seg_size = mmc->max_req_size;
> >
> > mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
> > - MMC_CAP_WAIT_WHILE_BUSY;
> > + MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_SDIO_IRQ;
> >
> > if (mmc_slot(host).wires >= 8)
> > mmc->caps |= MMC_CAP_8_BIT_DATA;
> >
> >
> >
> >
> >
> >> John Rigby wrote:
> >>> I have seen several discussions about lack of sdio irq support in the
> >>> hsmmc driver but no patches. Has anyone on this list implemented this
> >>> and/or can anyone point me to patches?
> >> What a coincidence ;)
> >>
> >> I'm currently working on this. See attachment what I currently have.
> >> It is compile tested only against recent omap linux head. I don't have
> >> a board using SDIO at the moment, so no real testing possible :(
> >>
> >> Some background, maybe it helps people to step in:
> >>
> >> Gumstix OMAP3 based Overo air board connects Marvell 88W8686 wifi by
> >> MMC port 2 in 4 bit configuration [1]. The wifi performance is quite
> >> bad (~100kB/s). There is some rumor that this might be SDIO irq
> >> related [2]. There was an attempt to fix this [3] already, but this
> >> doesn't work [4]. Having this, I started to look into it.
> >>
> >> I used [3], the TI Davinci driver [5] (supporting SDIO irq), the SDIO
> >> Simplified Specification [6] and the OMAP35x TRM [7] as starting
> points.
> >>
> >> Unfortunately, the Davinci MMC registers and irqs are different
> >> (Davinci has a dedicated SDIO irq). But combining [3] and [5] helps to
> >> get an idea what has to be done.
> >>
> >> I think the main issues of [3] were that it doesn't enable IBG for 4
> >> bit mode ([6] chapter 8.1.2) and that mmc_omap_irq() doesn't reset the
> >> irq bits.
> >>
> >> Topics I still open:
> >>
> >> - Is it always necessary to deal with IE _and_ ISE register? I'm not
> >> totally clear what the difference between these two registers are ;)
> >> And in which order they have to be set.
> >>
> >> - Davinci driver [5] in line 1115 checks for data line to call
> >> mmc_signal_sdio_irq() for irq enable.
> >>
> >> - Davinci driver deals with SDIO in xfer_done() (line 873)
> >>
> >> - Davinci driver sets block size to 64 if SDIO in line 701
> >>
> >> It would be quite nice if anybody likes to comment on attachment and
> >> help testing.
> >>
> >> Many thanks and best regards
> >>
> >> Dirk
> >>
> >> [1] http://gumstix.net/wiki/index.php?title=Overo_Wifi
> >>
> >> [2] http://groups.google.com/group/beagleboard/msg/14e822778c5eeb56
> >>
> >> [3] http://groups.google.com/group/beagleboard/msg/d0eb69f4c20673be
> >>
> >> [4] http://groups.google.com/group/beagleboard/msg/5cdfe2a319531937
> >>
> >> [5]
> >> http://arago-project.org/git/projects/?p=linux-
> davinci.git;a=blob;f=drivers/mmc/host/davinci_mmc.c;h=1bf0587250614c6d8abf
> e02028b96e0e47148ac8;hb=HEAD
> >>
> >> [6] http://www.sdcard.org/developers/tech/sdio/sd_bluetooth_spec/
> >>
> >> [7] http://focus.ti.com/lit/ug/spruf98c/spruf98c.pdf
> >>
> >>
> >
> >
> >
>
^ permalink raw reply
* Re: MMC_CAP_SDIO_IRQ for omap 3430
From: Dirk Behme @ 2009-10-16 19:28 UTC (permalink / raw)
To: Madhusudhan Chikkature; +Cc: linux-mmc, John Rigby, linux-omap, Steve Sakoman
In-Reply-To: <42153.192.168.10.89.1255715021.squirrel@dbdmail.itg.ti.com>
Madhusudhan Chikkature wrote:
> Hi Dirk,
>
> I am inlining the patch so that it helps review.
...
> @@ -1165,8 +1178,15 @@ static void omap_hsmmc_set_ios(struct mm
> break;
> case MMC_BUS_WIDTH_4:
> OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
> - OMAP_HSMMC_WRITE(host->base, HCTL,
> - OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
> + if (mmc_card_sdio(host->mmc->card)) {
>
> I wish it could be moved to "enable_sdio_irq" so that we can avoid inclusion of
> card.h and checking the type of card in the host controller driver.
Yes, this would be the real clean way. But ...
> But the
> dependancy on 4-bit seems to be a problem here.
... most probably we have to find a workaround until (if ever?) above
clean implementation is available.
What we need is after SDIO mode and bus width is known, and before the
first interrupt comes, to set IBG.
> On the problems being discussed on testing is the interrupt source geting
> cleared at the SDIO card level upon genaration of the CIRQ? If not it remains
> asserted.
Yes, this seems to be exactly the problem John reports in his follow
up mail.
Any hint how to clear SDIO interrupt?
Many thanks
Dirk
> + OMAP_HSMMC_WRITE(host->base, HCTL,
> + OMAP_HSMMC_READ(host->base, HCTL)
> + | IBG | FOUR_BIT);
> + } else {
> + OMAP_HSMMC_WRITE(host->base, HCTL,
> + OMAP_HSMMC_READ(host->base, HCTL)
> + | FOUR_BIT);
> + }
> break;
> case MMC_BUS_WIDTH_1:
> OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
> @@ -1512,6 +1532,24 @@ static int omap_hsmmc_disable_fclk(struc
> return 0;
> }
>
> +static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
> +{
> + struct omap_hsmmc_host *host = mmc_priv(mmc);
> + u32 ie, ise;
> +
> + ise = OMAP_HSMMC_READ(host->base, ISE);
> + ie = OMAP_HSMMC_READ(host->base, IE);
> +
> + if (enable) {
> + OMAP_HSMMC_WRITE(host->base, ISE, ise | CIRQ_ENABLE);
> + OMAP_HSMMC_WRITE(host->base, IE, ie | CIRQ_ENABLE);
> + } else {
> + OMAP_HSMMC_WRITE(host->base, ISE, ise & ~CIRQ_ENABLE);
> + OMAP_HSMMC_WRITE(host->base, IE, ie & ~CIRQ_ENABLE);
> + }
> +
> +}
> +
> static const struct mmc_host_ops omap_hsmmc_ops = {
> .enable = omap_hsmmc_enable_fclk,
> .disable = omap_hsmmc_disable_fclk,
> @@ -1519,7 +1557,7 @@ static const struct mmc_host_ops omap_hs
> .set_ios = omap_hsmmc_set_ios,
> .get_cd = omap_hsmmc_get_cd,
> .get_ro = omap_hsmmc_get_ro,
> - /* NYET -- enable_sdio_irq */
> + .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
> };
>
> static const struct mmc_host_ops omap_hsmmc_ps_ops = {
> @@ -1529,7 +1567,7 @@ static const struct mmc_host_ops omap_hs
> .set_ios = omap_hsmmc_set_ios,
> .get_cd = omap_hsmmc_get_cd,
> .get_ro = omap_hsmmc_get_ro,
> - /* NYET -- enable_sdio_irq */
> + .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
> };
>
> #ifdef CONFIG_DEBUG_FS
> @@ -1734,7 +1772,7 @@ static int __init omap_hsmmc_probe(struc
> mmc->max_seg_size = mmc->max_req_size;
>
> mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
> - MMC_CAP_WAIT_WHILE_BUSY;
> + MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_SDIO_IRQ;
>
> if (mmc_slot(host).wires >= 8)
> mmc->caps |= MMC_CAP_8_BIT_DATA;
>
>
>
>
>
>> John Rigby wrote:
>>> I have seen several discussions about lack of sdio irq support in the
>>> hsmmc driver but no patches. Has anyone on this list implemented this
>>> and/or can anyone point me to patches?
>> What a coincidence ;)
>>
>> I'm currently working on this. See attachment what I currently have.
>> It is compile tested only against recent omap linux head. I don't have
>> a board using SDIO at the moment, so no real testing possible :(
>>
>> Some background, maybe it helps people to step in:
>>
>> Gumstix OMAP3 based Overo air board connects Marvell 88W8686 wifi by
>> MMC port 2 in 4 bit configuration [1]. The wifi performance is quite
>> bad (~100kB/s). There is some rumor that this might be SDIO irq
>> related [2]. There was an attempt to fix this [3] already, but this
>> doesn't work [4]. Having this, I started to look into it.
>>
>> I used [3], the TI Davinci driver [5] (supporting SDIO irq), the SDIO
>> Simplified Specification [6] and the OMAP35x TRM [7] as starting points.
>>
>> Unfortunately, the Davinci MMC registers and irqs are different
>> (Davinci has a dedicated SDIO irq). But combining [3] and [5] helps to
>> get an idea what has to be done.
>>
>> I think the main issues of [3] were that it doesn't enable IBG for 4
>> bit mode ([6] chapter 8.1.2) and that mmc_omap_irq() doesn't reset the
>> irq bits.
>>
>> Topics I still open:
>>
>> - Is it always necessary to deal with IE _and_ ISE register? I'm not
>> totally clear what the difference between these two registers are ;)
>> And in which order they have to be set.
>>
>> - Davinci driver [5] in line 1115 checks for data line to call
>> mmc_signal_sdio_irq() for irq enable.
>>
>> - Davinci driver deals with SDIO in xfer_done() (line 873)
>>
>> - Davinci driver sets block size to 64 if SDIO in line 701
>>
>> It would be quite nice if anybody likes to comment on attachment and
>> help testing.
>>
>> Many thanks and best regards
>>
>> Dirk
>>
>> [1] http://gumstix.net/wiki/index.php?title=Overo_Wifi
>>
>> [2] http://groups.google.com/group/beagleboard/msg/14e822778c5eeb56
>>
>> [3] http://groups.google.com/group/beagleboard/msg/d0eb69f4c20673be
>>
>> [4] http://groups.google.com/group/beagleboard/msg/5cdfe2a319531937
>>
>> [5]
>> http://arago-project.org/git/projects/?p=linux-davinci.git;a=blob;f=drivers/mmc/host/davinci_mmc.c;h=1bf0587250614c6d8abfe02028b96e0e47148ac8;hb=HEAD
>>
>> [6] http://www.sdcard.org/developers/tech/sdio/sd_bluetooth_spec/
>>
>> [7] http://focus.ti.com/lit/ug/spruf98c/spruf98c.pdf
>>
>>
>
>
>
^ permalink raw reply
* Re: [PATCH 1/8] trivial: coding style fixes
From: Jiri Kosina @ 2009-10-16 17:47 UTC (permalink / raw)
To: Stefan Richter
Cc: Thadeu Lima de Souza Cascardo, akpm, adobriyan, linux-mmc,
linux-kernel
In-Reply-To: <4AD88729.3050209@s5r6.in-berlin.de>
On Fri, 16 Oct 2009, Stefan Richter wrote:
> Plus, I hope Jiri enhanced your changelog a bit before he committed it.
> (I only looked at this patch because I was wondering what subsystem it
> changed --- the patch title doesn't say it.)
Yup, I have put it into header (I always do if it is not clear).
--
Jiri Kosina
SUSE Labs, Novell Inc.
^ permalink raw reply
* Re: MMC_CAP_SDIO_IRQ for omap 3430
From: Madhusudhan Chikkature @ 2009-10-16 17:43 UTC (permalink / raw)
To: Dirk Behme, linux-mmc; +Cc: John Rigby, linux-omap, Steve Sakoman
In-Reply-To: <4AD81DC2.4080607@googlemail.com>
Hi Dirk,
I am inlining the patch so that it helps review.
Subject: [PATCH][RFC] OMAP HSMMC: Add SDIO interrupt support
Form: Dirk Behme <dirk.behme@googlemail.com>
At the moment, OMAP HSMMC driver supports only SDIO polling, resulting in poor
performance. Add support for SDIO interrupt handling.
Signed-off-by: Dirk Behme <dirk.behme@googlemail.com>
---
Patch against recent omap-linux head "Linux omap got rebuilt from scratch"
274c94b29ee7c53609a756acca974e4742c59559
Compile tested only. Please comment and help testing.
drivers/mmc/host/omap_hsmmc.c | 48 +++++++++++++++++++++++++++++++++++++-----
1 file changed, 43 insertions(+), 5 deletions(-)
Index: linux-beagle/drivers/mmc/host/omap_hsmmc.c
===================================================================
--- linux-beagle.orig/drivers/mmc/host/omap_hsmmc.c
+++ linux-beagle/drivers/mmc/host/omap_hsmmc.c
@@ -27,6 +27,7 @@
#include <linux/timer.h>
#include <linux/clk.h>
#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
#include <linux/mmc/core.h>
#include <linux/io.h>
#include <linux/semaphore.h>
@@ -65,6 +66,7 @@
#define SDVSDET 0x00000400
#define AUTOIDLE 0x1
#define SDBP (1 << 8)
+#define IBG (1 << 19)
#define DTO 0xe
#define ICE 0x1
#define ICS 0x2
@@ -76,6 +78,7 @@
#define INT_EN_MASK 0x307F0033
#define BWR_ENABLE (1 << 4)
#define BRR_ENABLE (1 << 5)
+#define CIRQ_ENABLE (1 << 8)
#define INIT_STREAM (1 << 1)
#define DP_SELECT (1 << 21)
#define DDIR (1 << 4)
@@ -87,6 +90,7 @@
#define CC 0x1
#define TC 0x02
#define OD 0x1
+#define CIRQ (1 << 8)
#define ERR (1 << 15)
#define CMD_TIMEOUT (1 << 16)
#define DATA_TIMEOUT (1 << 20)
@@ -653,6 +657,15 @@ static irqreturn_t omap_hsmmc_irq(int ir
status = OMAP_HSMMC_READ(host->base, STAT);
dev_dbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
+ if (status & CIRQ) {
+ dev_dbg(mmc_dev(host->mmc), "SDIO interrupt");
+ OMAP_HSMMC_WRITE(host->base, IE, OMAP_HSMMC_READ(host->base, IE)
+ & ~(CIRQ_ENABLE));
+ mmc_signal_sdio_irq(host->mmc);
+ spin_unlock(&host->irq_lock);
+ return IRQ_HANDLED;
+ }
+
if (status & ERR) {
#ifdef CONFIG_MMC_DEBUG
omap_hsmmc_report_irq(host, status);
@@ -1165,8 +1178,15 @@ static void omap_hsmmc_set_ios(struct mm
break;
case MMC_BUS_WIDTH_4:
OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
+ if (mmc_card_sdio(host->mmc->card)) {
I wish it could be moved to "enable_sdio_irq" so that we can avoid inclusion of
card.h and checking the type of card in the host controller driver. But the
dependancy on 4-bit seems to be a problem here.
On the problems being discussed on testing is the interrupt source geting
cleared at the SDIO card level upon genaration of the CIRQ? If not it remains
asserted.
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL)
+ | IBG | FOUR_BIT);
+ } else {
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL)
+ | FOUR_BIT);
+ }
break;
case MMC_BUS_WIDTH_1:
OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
@@ -1512,6 +1532,24 @@ static int omap_hsmmc_disable_fclk(struc
return 0;
}
+static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ struct omap_hsmmc_host *host = mmc_priv(mmc);
+ u32 ie, ise;
+
+ ise = OMAP_HSMMC_READ(host->base, ISE);
+ ie = OMAP_HSMMC_READ(host->base, IE);
+
+ if (enable) {
+ OMAP_HSMMC_WRITE(host->base, ISE, ise | CIRQ_ENABLE);
+ OMAP_HSMMC_WRITE(host->base, IE, ie | CIRQ_ENABLE);
+ } else {
+ OMAP_HSMMC_WRITE(host->base, ISE, ise & ~CIRQ_ENABLE);
+ OMAP_HSMMC_WRITE(host->base, IE, ie & ~CIRQ_ENABLE);
+ }
+
+}
+
static const struct mmc_host_ops omap_hsmmc_ops = {
.enable = omap_hsmmc_enable_fclk,
.disable = omap_hsmmc_disable_fclk,
@@ -1519,7 +1557,7 @@ static const struct mmc_host_ops omap_hs
.set_ios = omap_hsmmc_set_ios,
.get_cd = omap_hsmmc_get_cd,
.get_ro = omap_hsmmc_get_ro,
- /* NYET -- enable_sdio_irq */
+ .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
};
static const struct mmc_host_ops omap_hsmmc_ps_ops = {
@@ -1529,7 +1567,7 @@ static const struct mmc_host_ops omap_hs
.set_ios = omap_hsmmc_set_ios,
.get_cd = omap_hsmmc_get_cd,
.get_ro = omap_hsmmc_get_ro,
- /* NYET -- enable_sdio_irq */
+ .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
};
#ifdef CONFIG_DEBUG_FS
@@ -1734,7 +1772,7 @@ static int __init omap_hsmmc_probe(struc
mmc->max_seg_size = mmc->max_req_size;
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
- MMC_CAP_WAIT_WHILE_BUSY;
+ MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_SDIO_IRQ;
if (mmc_slot(host).wires >= 8)
mmc->caps |= MMC_CAP_8_BIT_DATA;
> John Rigby wrote:
>> I have seen several discussions about lack of sdio irq support in the
>> hsmmc driver but no patches. Has anyone on this list implemented this
>> and/or can anyone point me to patches?
>
> What a coincidence ;)
>
> I'm currently working on this. See attachment what I currently have.
> It is compile tested only against recent omap linux head. I don't have
> a board using SDIO at the moment, so no real testing possible :(
>
> Some background, maybe it helps people to step in:
>
> Gumstix OMAP3 based Overo air board connects Marvell 88W8686 wifi by
> MMC port 2 in 4 bit configuration [1]. The wifi performance is quite
> bad (~100kB/s). There is some rumor that this might be SDIO irq
> related [2]. There was an attempt to fix this [3] already, but this
> doesn't work [4]. Having this, I started to look into it.
>
> I used [3], the TI Davinci driver [5] (supporting SDIO irq), the SDIO
> Simplified Specification [6] and the OMAP35x TRM [7] as starting points.
>
> Unfortunately, the Davinci MMC registers and irqs are different
> (Davinci has a dedicated SDIO irq). But combining [3] and [5] helps to
> get an idea what has to be done.
>
> I think the main issues of [3] were that it doesn't enable IBG for 4
> bit mode ([6] chapter 8.1.2) and that mmc_omap_irq() doesn't reset the
> irq bits.
>
> Topics I still open:
>
> - Is it always necessary to deal with IE _and_ ISE register? I'm not
> totally clear what the difference between these two registers are ;)
> And in which order they have to be set.
>
> - Davinci driver [5] in line 1115 checks for data line to call
> mmc_signal_sdio_irq() for irq enable.
>
> - Davinci driver deals with SDIO in xfer_done() (line 873)
>
> - Davinci driver sets block size to 64 if SDIO in line 701
>
> It would be quite nice if anybody likes to comment on attachment and
> help testing.
>
> Many thanks and best regards
>
> Dirk
>
> [1] http://gumstix.net/wiki/index.php?title=Overo_Wifi
>
> [2] http://groups.google.com/group/beagleboard/msg/14e822778c5eeb56
>
> [3] http://groups.google.com/group/beagleboard/msg/d0eb69f4c20673be
>
> [4] http://groups.google.com/group/beagleboard/msg/5cdfe2a319531937
>
> [5]
> http://arago-project.org/git/projects/?p=linux-davinci.git;a=blob;f=drivers/mmc/host/davinci_mmc.c;h=1bf0587250614c6d8abfe02028b96e0e47148ac8;hb=HEAD
>
> [6] http://www.sdcard.org/developers/tech/sdio/sd_bluetooth_spec/
>
> [7] http://focus.ti.com/lit/ug/spruf98c/spruf98c.pdf
>
>
^ permalink raw reply
* Re: [PATCH 1/8] trivial: coding style fixes
From: Stefan Richter @ 2009-10-16 14:46 UTC (permalink / raw)
To: Thadeu Lima de Souza Cascardo
Cc: trivial, akpm, adobriyan, linux-mmc, linux-kernel, Jiri Kosina
In-Reply-To: <1255635840-5691-1-git-send-email-cascardo@holoscopio.com>
Thadeu Lima de Souza Cascardo wrote:
> --- a/drivers/mmc/card/sdio_uart.c
> +++ b/drivers/mmc/card/sdio_uart.c
[...]
> @@ -946,31 +948,31 @@ static int sdio_uart_proc_show(struct seq_file *m, void *v)
> seq_printf(m, "%d: uart:SDIO", i);
> if(capable(CAP_SYS_ADMIN)) {
> seq_printf(m, " tx:%d rx:%d",
> - port->icount.tx, port->icount.rx);
> + port->icount.tx, port->icount.rx);
> if (port->icount.frame)
> seq_printf(m, " fe:%d",
> - port->icount.frame);
> + port->icount.frame);
> if (port->icount.parity)
> seq_printf(m, " pe:%d",
> - port->icount.parity);
> + port->icount.parity);
> if (port->icount.brk)
> seq_printf(m, " brk:%d",
> - port->icount.brk);
> + port->icount.brk);
> if (port->icount.overrun)
> seq_printf(m, " oe:%d",
> - port->icount.overrun);
> + port->icount.overrun);
> if (port->icount.cts)
[...]
The style problem of the original code was not indentation, but depth of
indentation levels. Why not factor this cascade out to a
show_icount_fields() function?
Plus, I hope Jiri enhanced your changelog a bit before he committed it.
(I only looked at this patch because I was wondering what subsystem it
changed --- the patch title doesn't say it.)
--
Stefan Richter
-=====-==--= =-=- =----
http://arcgraph.de/sr/
^ permalink raw reply
* Re: [PATCH 1/8] trivial: coding style fixes
From: Jiri Kosina @ 2009-10-16 13:31 UTC (permalink / raw)
To: Thadeu Lima de Souza Cascardo; +Cc: akpm, adobriyan, linux-mmc, linux-kernel
In-Reply-To: <1255635840-5691-1-git-send-email-cascardo@holoscopio.com>
On Thu, 15 Oct 2009, Thadeu Lima de Souza Cascardo wrote:
> Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
> ---
> drivers/mmc/card/sdio_uart.c | 40 +++++++++++++++++++++-------------------
> 1 files changed, 21 insertions(+), 19 deletions(-)
Applied to trivial queue.
--
Jiri Kosina
SUSE Labs, Novell Inc.
^ permalink raw reply
* [PATCH 1/8] trivial: coding style fixes
From: Thadeu Lima de Souza Cascardo @ 2009-10-15 19:44 UTC (permalink / raw)
To: trivial
Cc: akpm, adobriyan, linux-mmc, linux-kernel,
Thadeu Lima de Souza Cascardo
Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
---
drivers/mmc/card/sdio_uart.c | 40 +++++++++++++++++++++-------------------
1 files changed, 21 insertions(+), 19 deletions(-)
diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index 36a8d53..b8e7c5a 100644
--- a/drivers/mmc/card/sdio_uart.c
+++ b/drivers/mmc/card/sdio_uart.c
@@ -231,7 +231,8 @@ static unsigned int sdio_uart_get_mctrl(struct sdio_uart_port *port)
return ret;
}
-static void sdio_uart_write_mctrl(struct sdio_uart_port *port, unsigned int mctrl)
+static void sdio_uart_write_mctrl(struct sdio_uart_port *port,
+ unsigned int mctrl)
{
unsigned char mcr = 0;
@@ -387,7 +388,8 @@ static void sdio_uart_stop_rx(struct sdio_uart_port *port)
sdio_out(port, UART_IER, port->ier);
}
-static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *status)
+static void sdio_uart_receive_chars(struct sdio_uart_port *port,
+ unsigned int *status)
{
struct tty_struct *tty = port->tty;
unsigned int ch, flag;
@@ -399,7 +401,7 @@ static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *s
port->icount.rx++;
if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
- UART_LSR_FE | UART_LSR_OE))) {
+ UART_LSR_FE | UART_LSR_OE))) {
/*
* For statistics only
*/
@@ -417,9 +419,9 @@ static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *s
* Mask off conditions which should be ignored.
*/
*status &= port->read_status_mask;
- if (*status & UART_LSR_BI) {
+ if (*status & UART_LSR_BI)
flag = TTY_BREAK;
- } else if (*status & UART_LSR_PE)
+ else if (*status & UART_LSR_PE)
flag = TTY_PARITY;
else if (*status & UART_LSR_FE)
flag = TTY_FRAME;
@@ -574,7 +576,7 @@ static int sdio_uart_startup(struct sdio_uart_port *port)
*/
sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO);
sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO |
- UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
sdio_out(port, UART_FCR, 0);
/*
@@ -635,7 +637,7 @@ static void sdio_uart_shutdown(struct sdio_uart_port *port)
if (port->tty->termios->c_cflag & HUPCL)
sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
- /* Disable interrupts from this port */
+ /* Disable interrupts from this port */
sdio_release_irq(port->func);
port->ier = 0;
sdio_out(port, UART_IER, 0);
@@ -659,7 +661,7 @@ skip:
free_page((unsigned long)port->xmit.buf);
}
-static int sdio_uart_open (struct tty_struct *tty, struct file * filp)
+static int sdio_uart_open(struct tty_struct *tty, struct file *filp)
{
struct sdio_uart_port *port;
int ret;
@@ -846,7 +848,7 @@ static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_t
struct sdio_uart_port *port = tty->driver_data;
unsigned int cflag = tty->termios->c_cflag;
-#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
if ((cflag ^ old_termios->c_cflag) == 0 &&
RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)
@@ -925,7 +927,7 @@ static int sdio_uart_tiocmset(struct tty_struct *tty, struct file *file,
struct sdio_uart_port *port = tty->driver_data;
int result;
- result =sdio_uart_claim_func(port);
+ result = sdio_uart_claim_func(port);
if(!result) {
sdio_uart_update_mctrl(port, set, clear);
sdio_uart_release_func(port);
@@ -946,31 +948,31 @@ static int sdio_uart_proc_show(struct seq_file *m, void *v)
seq_printf(m, "%d: uart:SDIO", i);
if(capable(CAP_SYS_ADMIN)) {
seq_printf(m, " tx:%d rx:%d",
- port->icount.tx, port->icount.rx);
+ port->icount.tx, port->icount.rx);
if (port->icount.frame)
seq_printf(m, " fe:%d",
- port->icount.frame);
+ port->icount.frame);
if (port->icount.parity)
seq_printf(m, " pe:%d",
- port->icount.parity);
+ port->icount.parity);
if (port->icount.brk)
seq_printf(m, " brk:%d",
- port->icount.brk);
+ port->icount.brk);
if (port->icount.overrun)
seq_printf(m, " oe:%d",
- port->icount.overrun);
+ port->icount.overrun);
if (port->icount.cts)
seq_printf(m, " cts:%d",
- port->icount.cts);
+ port->icount.cts);
if (port->icount.dsr)
seq_printf(m, " dsr:%d",
- port->icount.dsr);
+ port->icount.dsr);
if (port->icount.rng)
seq_printf(m, " rng:%d",
- port->icount.rng);
+ port->icount.rng);
if (port->icount.dcd)
seq_printf(m, " dcd:%d",
- port->icount.dcd);
+ port->icount.dcd);
}
sdio_uart_port_put(port);
seq_putc(m, '\n');
--
1.6.3.3
^ permalink raw reply related
* + davinci-mmc-modify-data-types-of-edma-related-variables.patch added to -mm tree
From: akpm @ 2009-10-15 19:27 UTC (permalink / raw)
To: mm-commits; +Cc: sudhakar.raj, dbrownell, linux-mmc, purushotam, vipin.bhandari
The patch titled
mmc: davinci: modify data types of EDMA related variables
has been added to the -mm tree. Its filename is
davinci-mmc-modify-data-types-of-edma-related-variables.patch
Before you just go and hit "reply", please:
a) Consider who else should be cc'ed
b) Prefer to cc a suitable mailing list as well
c) Ideally: find the original patch on the mailing list and do a
reply-to-all to that, adding suitable additional cc's
*** Remember to use Documentation/SubmitChecklist when testing your code ***
See http://userweb.kernel.org/~akpm/stuff/added-to-mm.txt to find
out what to do about this
The current -mm tree may be found at http://userweb.kernel.org/~akpm/mmotm/
------------------------------------------------------
Subject: mmc: davinci: modify data types of EDMA related variables
From: Sudhakar Rajashekhara <sudhakar.raj@ti.com>
Currently DaVinci EDMA driver supports multiple EDMA channel
controller instances. edma_alloc_channel() api returns a 32
bit value which has the channel controller number in MSB and
the EDMA channel number in LSB. The variables which store the
value returned by edma_alloc_channel() have to be 32 bit wide
now.
Signed-off-by: Sudhakar Rajashekhara <sudhakar.raj@ti.com>
Acked-by: Vipin Bhandari <vipin.bhandari@ti.com>
Cc: Purshotam Kumar <purushotam@ti.com>
Acked-by: David Brownell <dbrownell@users.sourceforge.net>
Cc: <linux-mmc@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
---
diff -puN drivers/mmc/host/davinci_mmc.c~davinci-mmc-modify-data-types-of-edma-related-variables drivers/mmc/host/davinci_mmc.c
--- a/drivers/mmc/host/davinci_mmc.c~davinci-mmc-modify-data-types-of-edma-related-variables
+++ a/drivers/mmc/host/davinci_mmc.c
@@ -178,7 +178,7 @@ struct mmc_davinci_host {
u32 buffer_bytes_left;
u32 bytes_left;
- u8 rxdma, txdma;
+ u32 rxdma, txdma;
bool use_dma;
bool do_dma;
@@ -190,7 +190,7 @@ struct mmc_davinci_host {
struct edmacc_param tx_template;
struct edmacc_param rx_template;
unsigned n_link;
- u8 links[NR_SG - 1];
+ u32 links[NR_SG - 1];
/* For PIO we walk scatterlists one segment at a time. */
unsigned int sg_len;
_
Patches currently in -mm which might be from sudhakar.raj@ti.com are
linux-next.patch
mtdpart-memory-accessor-interface-for-mtd-layer.patch
davinci-mmc-modify-data-types-of-edma-related-variables.patch
^ permalink raw reply
* Re: [PATCH] sdio: add MMC_CAP_VDD_165_195 host capability
From: Pierre Ossman @ 2009-10-14 11:05 UTC (permalink / raw)
To: David Vrabel
Cc: Ohad Ben-Cohen, Philip Langdale, akpm, ian, matt,
roberto.foglietta, linux-mmc
In-Reply-To: <4AD5A942.3000207@csr.com>
[-- Attachment #1: Type: text/plain, Size: 800 bytes --]
On Wed, 14 Oct 2009 11:34:42 +0100
David Vrabel <david.vrabel@csr.com> wrote:
>
> It can be done per-card, the switch to the lower voltage just needs to
> be deferred. Initially set the voltage to a standard one that's
> supported by the card and host. After the card is fully initialized and
> enumerated, have a hook for per-card fixups. For the particular
> non-standard card in question, this would then reduce the voltage to 1.8V.
>
In theory. But it would require a bit of redesign of the code as it
would have to restart the whole init again.
Rgds
--
-- Pierre Ossman
WARNING: This correspondence is being monitored by the
Swedish government. Make sure your server uses encryption
for SMTP traffic and consider using PGP for end-to-end
encryption.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* Re: [PATCH] sdio: add MMC_CAP_VDD_165_195 host capability
From: David Vrabel @ 2009-10-14 10:34 UTC (permalink / raw)
To: Pierre Ossman
Cc: Ohad Ben-Cohen, Philip Langdale, akpm, ian, matt,
roberto.foglietta, linux-mmc
In-Reply-To: <20091014104849.3c441249@mjolnir.ossman.eu>
Pierre Ossman wrote:
> On Wed, 14 Oct 2009 09:56:28 +0200
> Ohad Ben-Cohen <ohad@wizery.com> wrote:
>
>> On Tue, Oct 13, 2009 at 4:39 AM, Philip Langdale <philipl@overt.org> wrote:
>>> Interesting. But that means that Ohad's patch doesn't make much sense;
>>> his uses the MMC low voltage OCR bit in an SDIO context. So either, the
>>> patch is wrong, or he's dealing with out-of-spec hardware.
>> Yes, the hardware is out-of-spec. It uses the undefined low voltage
>> OCR bit to achieve 1.8V SDIO voltage. By removing the MMC_VDD_165_195
>> restriction, commit 27cce39f555def6f5ebe7f03d69ccc44ab25f0b2 makes it
>> possible for the hardware to work with unpatched kernels.
>>
>> Philip, David, Pierre - would you like to remove the MMC_VDD_165_195
>> restriction differently ? maybe to revive the MMC_CAP_VDD_165_195 host
>> capability patch in some way (see
>> http://www.mail-archive.com/linux-mmc@vger.kernel.org/msg00386.html) ?
>>
>
> Since this is out-of-spec and therefore possibly dangerous behaviour,
> I'd like it to be opt-in for the user. And since it's so early in the
> init process, we can't automate it based on card id.
It can be done per-card, the switch to the lower voltage just needs to
be deferred. Initially set the voltage to a standard one that's
supported by the card and host. After the card is fully initialized and
enumerated, have a hook for per-card fixups. For the particular
non-standard card in question, this would then reduce the voltage to 1.8V.
Alternatively, if this is for a chip hardwired to the controller then
some board-specific data for the SD controller can be used to always set
the voltage correctly. e.g., always run at 1.8V regardless of what the
stack says. Obviously, this strategy won't work with removable cards.
David
--
David Vrabel, Senior Software Engineer, Drivers
CSR, Churchill House, Cambridge Business Park, Tel: +44 (0)1223 692562
Cowley Road, Cambridge, CB4 0WZ http://www.csr.com/
Member of the CSR plc group of companies. CSR plc registered in England and Wales, registered number 4187346, registered office Churchill House, Cambridge Business Park, Cowley Road, Cambridge, CB4 0WZ, United Kingdom
^ permalink raw reply
* Re: [PATCH] sdio: add MMC_CAP_VDD_165_195 host capability
From: Pierre Ossman @ 2009-10-14 8:48 UTC (permalink / raw)
To: Ohad Ben-Cohen
Cc: Philip Langdale, David Vrabel, akpm, ian, matt, roberto.foglietta,
linux-mmc
In-Reply-To: <da15981b0910140056j9a62fccvdb256b851455c686@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1350 bytes --]
On Wed, 14 Oct 2009 09:56:28 +0200
Ohad Ben-Cohen <ohad@wizery.com> wrote:
> On Tue, Oct 13, 2009 at 4:39 AM, Philip Langdale <philipl@overt.org> wrote:
> > Interesting. But that means that Ohad's patch doesn't make much sense;
> > his uses the MMC low voltage OCR bit in an SDIO context. So either, the
> > patch is wrong, or he's dealing with out-of-spec hardware.
>
> Yes, the hardware is out-of-spec. It uses the undefined low voltage
> OCR bit to achieve 1.8V SDIO voltage. By removing the MMC_VDD_165_195
> restriction, commit 27cce39f555def6f5ebe7f03d69ccc44ab25f0b2 makes it
> possible for the hardware to work with unpatched kernels.
>
> Philip, David, Pierre - would you like to remove the MMC_VDD_165_195
> restriction differently ? maybe to revive the MMC_CAP_VDD_165_195 host
> capability patch in some way (see
> http://www.mail-archive.com/linux-mmc@vger.kernel.org/msg00386.html) ?
>
Since this is out-of-spec and therefore possibly dangerous behaviour,
I'd like it to be opt-in for the user. And since it's so early in the
init process, we can't automate it based on card id.
Rgds
--
-- Pierre Ossman
WARNING: This correspondence is being monitored by the
Swedish government. Make sure your server uses encryption
for SMTP traffic and consider using PGP for end-to-end
encryption.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* Re: [PATCH] sdio: add MMC_CAP_VDD_165_195 host capability
From: Ohad Ben-Cohen @ 2009-10-14 7:56 UTC (permalink / raw)
To: Philip Langdale, David Vrabel, Pierre Ossman
Cc: akpm, ian, matt, roberto.foglietta, linux-mmc
In-Reply-To: <20091012193952.1482e322@fido2.homeip.net>
On Tue, Oct 13, 2009 at 4:39 AM, Philip Langdale <philipl@overt.org> wrote:
> Interesting. But that means that Ohad's patch doesn't make much sense;
> his uses the MMC low voltage OCR bit in an SDIO context. So either, the
> patch is wrong, or he's dealing with out-of-spec hardware.
Yes, the hardware is out-of-spec. It uses the undefined low voltage
OCR bit to achieve 1.8V SDIO voltage. By removing the MMC_VDD_165_195
restriction, commit 27cce39f555def6f5ebe7f03d69ccc44ab25f0b2 makes it
possible for the hardware to work with unpatched kernels.
Philip, David, Pierre - would you like to remove the MMC_VDD_165_195
restriction differently ? maybe to revive the MMC_CAP_VDD_165_195 host
capability patch in some way (see
http://www.mail-archive.com/linux-mmc@vger.kernel.org/msg00386.html) ?
Thank you for the comments and help,
Ohad.
^ permalink raw reply
* [PATCH] mmc: au1xmmc: allow platforms to disable host capabilities
From: Manuel Lauss @ 2009-10-14 7:38 UTC (permalink / raw)
To: linux-mmc; +Cc: Manuel Lauss, Linux-MIPS
Although the hardware supports a 4/8bit SD interface and the driver
unconditionally advertises all hardware caps to the MMC core, not all
datalines may actually be wired up. This patch introduces another
field to au1xmmc platform data allowing platforms to disable certain
advanced host controller features.
Signed-off-by: Manuel Lauss <manuel.lauss@gmail.com>
---
arch/mips/include/asm/mach-au1x00/au1100_mmc.h | 1 +
drivers/mmc/host/au1xmmc.c | 4 ++++
2 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/arch/mips/include/asm/mach-au1x00/au1100_mmc.h b/arch/mips/include/asm/mach-au1x00/au1100_mmc.h
index c35e209..a674643 100644
--- a/arch/mips/include/asm/mach-au1x00/au1100_mmc.h
+++ b/arch/mips/include/asm/mach-au1x00/au1100_mmc.h
@@ -46,6 +46,7 @@ struct au1xmmc_platform_data {
int(*card_readonly)(void *mmc_host);
void(*set_power)(void *mmc_host, int state);
struct led_classdev *led;
+ unsigned long mask_host_caps;
};
#define SD0_BASE 0xB0600000
diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
index 7534726..e2caf5a 100644
--- a/drivers/mmc/host/au1xmmc.c
+++ b/drivers/mmc/host/au1xmmc.c
@@ -1025,6 +1025,10 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
} else
mmc->caps |= MMC_CAP_NEEDS_POLL;
+ /* platform may not be able to use all advertised caps */
+ if (host->platdata)
+ mmc->caps &= ~(host->platdata->mask_host_caps);
+
tasklet_init(&host->data_task, au1xmmc_tasklet_data,
(unsigned long)host);
--
1.6.5
^ permalink raw reply related
* + mxcmmc-fix-error-path-in-mxcmci_probe.patch added to -mm tree
From: akpm @ 2009-10-14 2:45 UTC (permalink / raw)
To: mm-commits; +Cc: u.kleine-koenig, drzeus, linux-mmc, mfuzzey, pierre, s.hauer
The patch titled
mxcmmc: fix error path in mxcmci_probe
has been added to the -mm tree. Its filename is
mxcmmc-fix-error-path-in-mxcmci_probe.patch
Before you just go and hit "reply", please:
a) Consider who else should be cc'ed
b) Prefer to cc a suitable mailing list as well
c) Ideally: find the original patch on the mailing list and do a
reply-to-all to that, adding suitable additional cc's
*** Remember to use Documentation/SubmitChecklist when testing your code ***
See http://userweb.kernel.org/~akpm/stuff/added-to-mm.txt to find
out what to do about this
The current -mm tree may be found at http://userweb.kernel.org/~akpm/mmotm/
------------------------------------------------------
Subject: mxcmmc: fix error path in mxcmci_probe
From: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
After a failing allocation of mmc or a failed ioremap in mxcmci_probe host was
used uninitialized.
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Acked-by: Sascha Hauer <s.hauer@pengutronix.de>
Cc: Pierre Ossman <pierre@ossman.eu>
Cc: Martin Fuzzey <mfuzzey@gmail.com>
Cc: Pierre Ossman <drzeus@drzeus.cx>
Cc: <linux-mmc@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
---
drivers/mmc/host/mxcmmc.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff -puN drivers/mmc/host/mxcmmc.c~mxcmmc-fix-error-path-in-mxcmci_probe drivers/mmc/host/mxcmmc.c
--- a/drivers/mmc/host/mxcmmc.c~mxcmmc-fix-error-path-in-mxcmci_probe
+++ a/drivers/mmc/host/mxcmmc.c
@@ -679,17 +679,17 @@ static int mxcmci_probe(struct platform_
{
struct mmc_host *mmc;
struct mxcmci_host *host = NULL;
- struct resource *r;
+ struct resource *iores, *r;
int ret = 0, irq;
printk(KERN_INFO "i.MX SDHC driver\n");
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
- if (!r || irq < 0)
+ if (!iores || irq < 0)
return -EINVAL;
- r = request_mem_region(r->start, resource_size(r), pdev->name);
+ r = request_mem_region(iores->start, resource_size(iores), pdev->name);
if (!r)
return -EBUSY;
@@ -809,7 +809,7 @@ out_iounmap:
out_free:
mmc_free_host(mmc);
out_release_mem:
- release_mem_region(host->res->start, resource_size(host->res));
+ release_mem_region(iores->start, resource_size(iores));
return ret;
}
_
Patches currently in -mm which might be from u.kleine-koenig@pengutronix.de are
linux-next.patch
mmc-at91_mci-dont-include-asm-mach-mmch.patch
atmel_serial-fix-bad-build_bug_on-usage.patch
mxcmmc-fix-error-path-in-mxcmci_probe.patch
^ permalink raw reply
* RE: RFC: Shared eMMC Controller
From: Johnson, Charles F @ 2009-10-13 21:32 UTC (permalink / raw)
To: Ian Molton; +Cc: linux-mmc@vger.kernel.org
In-Reply-To: <c09aa50a0910131428t744c8334ia814a6a365ab51a7@mail.gmail.com>
While they will share the controller, they will not share partitions. So there is no file system level sharing.
Just the controller. Also the granularity would be at the command level. We won't cut short any command in progress.
-----Original Message-----
From: spyro2@gmail.com [mailto:spyro2@gmail.com] On Behalf Of Ian Molton
Sent: Tuesday, October 13, 2009 2:29 PM
To: Johnson, Charles F
Subject: Re: RFC: Shared eMMC Controller
I cant see that being a problem. I wonder how fine grained the access
would be though - having mounted filesystems would mean that the other
systtem would risk getting vorrupt data and I doubt your mutexes could
be that low level.
As an alternative, why dont you probe / remove the driver when not used?
2009/10/13 Johnson, Charles F <charles.f.johnson@intel.com>:
> I'm working on a system where we have a need to share the eMMC host controller with both the main CPU and a micro-controller. So to coodinate access we need to define a mutex mechanism between the two. This is possible since there is some shared non-cachable static ram that both have access to. My question for this email list is, if we limit the mmc driver changes to use the chipset specific quirk mechanism, would that be acceptable ??
>
> As soon as we have the code, we'll post it, but I am interested in any comments.
>
> Thanks.
>
> Charles Johnson
> Intel Corp.
> charles.f.johnson@intel.com
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
Ian Molton
Linux, Automotive, and other hacking:
http://www.mnementh.co.uk/
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox