aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorTomas Winkler <tomas.winkler@intel.com>2009-10-17 05:09:34 -0400
committerDavid S. Miller <davem@davemloft.net>2009-10-20 02:22:50 -0400
commitab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3bae (patch)
treed5ed0f7d99c516c2feba03db5c70afbf79f20a48 /drivers
parent7e75f93eda027d9f9e0203ee6ffd210ea92e98f3 (diff)
iwmc3200top: Add Intel Wireless MultiCom 3200 top driver.
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@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/misc/Kconfig1
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/iwmc3200top/Kconfig20
-rw-r--r--drivers/misc/iwmc3200top/Makefile29
-rw-r--r--drivers/misc/iwmc3200top/debugfs.c133
-rw-r--r--drivers/misc/iwmc3200top/debugfs.h58
-rw-r--r--drivers/misc/iwmc3200top/fw-download.c359
-rw-r--r--drivers/misc/iwmc3200top/fw-msg.h113
-rw-r--r--drivers/misc/iwmc3200top/iwmc3200top.h206
-rw-r--r--drivers/misc/iwmc3200top/log.c347
-rw-r--r--drivers/misc/iwmc3200top/log.h158
-rw-r--r--drivers/misc/iwmc3200top/main.c699
12 files changed, 2124 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index df1f86b5c83e..a2ea383105a6 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -249,5 +249,6 @@ config EP93XX_PWM
249source "drivers/misc/c2port/Kconfig" 249source "drivers/misc/c2port/Kconfig"
250source "drivers/misc/eeprom/Kconfig" 250source "drivers/misc/eeprom/Kconfig"
251source "drivers/misc/cb710/Kconfig" 251source "drivers/misc/cb710/Kconfig"
252source "drivers/misc/iwmc3200top/Kconfig"
252 253
253endif # MISC_DEVICES 254endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index f982d2ecfde7..e311267a355f 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -21,5 +21,6 @@ obj-$(CONFIG_HP_ILO) += hpilo.o
21obj-$(CONFIG_ISL29003) += isl29003.o 21obj-$(CONFIG_ISL29003) += isl29003.o
22obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o 22obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o
23obj-$(CONFIG_C2PORT) += c2port/ 23obj-$(CONFIG_C2PORT) += c2port/
24obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/
24obj-y += eeprom/ 25obj-y += eeprom/
25obj-y += cb710/ 26obj-y += cb710/
diff --git a/drivers/misc/iwmc3200top/Kconfig b/drivers/misc/iwmc3200top/Kconfig
new file mode 100644
index 000000000000..9e4b88fb57f1
--- /dev/null
+++ b/drivers/misc/iwmc3200top/Kconfig
@@ -0,0 +1,20 @@
1config IWMC3200TOP
2 tristate "Intel Wireless MultiCom Top Driver"
3 depends on MMC && EXPERIMENTAL
4 select FW_LOADER
5 ---help---
6 Intel Wireless MultiCom 3200 Top driver is responsible for
7 for firmware load and enabled coms enumeration
8
9config IWMC3200TOP_DEBUG
10 bool "Enable full debug output of iwmc3200top Driver"
11 depends on IWMC3200TOP
12 ---help---
13 Enable full debug output of iwmc3200top Driver
14
15config IWMC3200TOP_DEBUGFS
16 bool "Enable Debugfs debugging interface for iwmc3200top"
17 depends on IWMC3200TOP
18 ---help---
19 Enable creation of debugfs files for iwmc3200top
20
diff --git a/drivers/misc/iwmc3200top/Makefile b/drivers/misc/iwmc3200top/Makefile
new file mode 100644
index 000000000000..fbf53fb4634e
--- /dev/null
+++ b/drivers/misc/iwmc3200top/Makefile
@@ -0,0 +1,29 @@
1# iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
2# drivers/misc/iwmc3200top/Makefile
3#
4# Copyright (C) 2009 Intel Corporation. All rights reserved.
5#
6# This program is free software; you can redistribute it and/or
7# modify it under the terms of the GNU General Public License version
8# 2 as published by the Free Software Foundation.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18# 02110-1301, USA.
19#
20#
21# Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
22# -
23#
24#
25
26obj-$(CONFIG_IWMC3200TOP) += iwmc3200top.o
27iwmc3200top-objs := main.o fw-download.o
28iwmc3200top-$(CONFIG_IWMC3200TOP_DEBUG) += log.o
29iwmc3200top-$(CONFIG_IWMC3200TOP_DEBUGFS) += debugfs.o
diff --git a/drivers/misc/iwmc3200top/debugfs.c b/drivers/misc/iwmc3200top/debugfs.c
new file mode 100644
index 000000000000..0c8ea0a1c8a3
--- /dev/null
+++ b/drivers/misc/iwmc3200top/debugfs.c
@@ -0,0 +1,133 @@
1/*
2 * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
3 * drivers/misc/iwmc3200top/debufs.c
4 *
5 * Copyright (C) 2009 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 *
21 *
22 * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
23 * -
24 *
25 */
26
27#include <linux/kernel.h>
28#include <linux/string.h>
29#include <linux/ctype.h>
30#include <linux/mmc/sdio_func.h>
31#include <linux/mmc/sdio.h>
32#include <linux/debugfs.h>
33
34#include "iwmc3200top.h"
35#include "fw-msg.h"
36#include "log.h"
37#include "debugfs.h"
38
39
40
41/* Constants definition */
42#define HEXADECIMAL_RADIX 16
43
44/* Functions definition */
45
46
47#define DEBUGFS_ADD(name, parent) do { \
48 dbgfs->dbgfs_##parent##_files.file_##name = \
49 debugfs_create_file(#name, 0644, dbgfs->dir_##parent, priv, \
50 &iwmct_dbgfs_##name##_ops); \
51} while (0)
52
53#define DEBUGFS_RM(name) do { \
54 debugfs_remove(name); \
55 name = NULL; \
56} while (0)
57
58#define DEBUGFS_READ_FUNC(name) \
59ssize_t iwmct_dbgfs_##name##_read(struct file *file, \
60 char __user *user_buf, \
61 size_t count, loff_t *ppos);
62
63#define DEBUGFS_WRITE_FUNC(name) \
64ssize_t iwmct_dbgfs_##name##_write(struct file *file, \
65 const char __user *user_buf, \
66 size_t count, loff_t *ppos);
67
68#define DEBUGFS_READ_FILE_OPS(name) \
69 DEBUGFS_READ_FUNC(name) \
70 static const struct file_operations iwmct_dbgfs_##name##_ops = { \
71 .read = iwmct_dbgfs_##name##_read, \
72 .open = iwmct_dbgfs_open_file_generic, \
73 };
74
75#define DEBUGFS_WRITE_FILE_OPS(name) \
76 DEBUGFS_WRITE_FUNC(name) \
77 static const struct file_operations iwmct_dbgfs_##name##_ops = { \
78 .write = iwmct_dbgfs_##name##_write, \
79 .open = iwmct_dbgfs_open_file_generic, \
80 };
81
82#define DEBUGFS_READ_WRITE_FILE_OPS(name) \
83 DEBUGFS_READ_FUNC(name) \
84 DEBUGFS_WRITE_FUNC(name) \
85 static const struct file_operations iwmct_dbgfs_##name##_ops = {\
86 .write = iwmct_dbgfs_##name##_write, \
87 .read = iwmct_dbgfs_##name##_read, \
88 .open = iwmct_dbgfs_open_file_generic, \
89 };
90
91
92/* Debugfs file ops definitions */
93
94/*
95 * Create the debugfs files and directories
96 *
97 */
98void iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name)
99{
100 struct iwmct_debugfs *dbgfs;
101
102 dbgfs = kzalloc(sizeof(struct iwmct_debugfs), GFP_KERNEL);
103 if (!dbgfs) {
104 LOG_ERROR(priv, DEBUGFS, "failed to allocate %zd bytes\n",
105 sizeof(struct iwmct_debugfs));
106 return;
107 }
108
109 priv->dbgfs = dbgfs;
110 dbgfs->name = name;
111 dbgfs->dir_drv = debugfs_create_dir(name, NULL);
112 if (!dbgfs->dir_drv) {
113 LOG_ERROR(priv, DEBUGFS, "failed to create debugfs dir\n");
114 return;
115 }
116
117 return;
118}
119
120/**
121 * Remove the debugfs files and directories
122 *
123 */
124void iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs)
125{
126 if (!dbgfs)
127 return;
128
129 DEBUGFS_RM(dbgfs->dir_drv);
130 kfree(dbgfs);
131 dbgfs = NULL;
132}
133
diff --git a/drivers/misc/iwmc3200top/debugfs.h b/drivers/misc/iwmc3200top/debugfs.h
new file mode 100644
index 000000000000..71d45759b40f
--- /dev/null
+++ b/drivers/misc/iwmc3200top/debugfs.h
@@ -0,0 +1,58 @@
1/*
2 * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
3 * drivers/misc/iwmc3200top/debufs.h
4 *
5 * Copyright (C) 2009 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 *
21 *
22 * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
23 * -
24 *
25 */
26
27#ifndef __DEBUGFS_H__
28#define __DEBUGFS_H__
29
30
31#ifdef CONFIG_IWMC3200TOP_DEBUGFS
32
33struct iwmct_debugfs {
34 const char *name;
35 struct dentry *dir_drv;
36 struct dir_drv_files {
37 } dbgfs_drv_files;
38};
39
40void iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name);
41void iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs);
42
43#else /* CONFIG_IWMC3200TOP_DEBUGFS */
44
45struct iwmct_debugfs;
46
47static inline void
48iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name)
49{}
50
51static inline void
52iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs)
53{}
54
55#endif /* CONFIG_IWMC3200TOP_DEBUGFS */
56
57#endif /* __DEBUGFS_H__ */
58
diff --git a/drivers/misc/iwmc3200top/fw-download.c b/drivers/misc/iwmc3200top/fw-download.c
new file mode 100644
index 000000000000..33cb693dd37c
--- /dev/null
+++ b/drivers/misc/iwmc3200top/fw-download.c
@@ -0,0 +1,359 @@
1/*
2 * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
3 * drivers/misc/iwmc3200top/fw-download.c
4 *
5 * Copyright (C) 2009 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 *
21 *
22 * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
23 * -
24 *
25 */
26
27#include <linux/firmware.h>
28#include <linux/mmc/sdio_func.h>
29#include <asm/unaligned.h>
30
31#include "iwmc3200top.h"
32#include "log.h"
33#include "fw-msg.h"
34
35#define CHECKSUM_BYTES_NUM sizeof(u32)
36
37/**
38 init parser struct with file
39 */
40static int iwmct_fw_parser_init(struct iwmct_priv *priv, const u8 *file,
41 size_t file_size, size_t block_size)
42{
43 struct iwmct_parser *parser = &priv->parser;
44 struct iwmct_fw_hdr *fw_hdr = &parser->versions;
45
46 LOG_INFOEX(priv, INIT, "-->\n");
47
48 LOG_INFO(priv, FW_DOWNLOAD, "file_size=%zd\n", file_size);
49
50 parser->file = file;
51 parser->file_size = file_size;
52 parser->cur_pos = 0;
53 parser->buf = NULL;
54
55 parser->buf = kzalloc(block_size, GFP_KERNEL);
56 if (!parser->buf) {
57 LOG_ERROR(priv, FW_DOWNLOAD, "kzalloc error\n");
58 return -ENOMEM;
59 }
60 parser->buf_size = block_size;
61
62 /* extract fw versions */
63 memcpy(fw_hdr, parser->file, sizeof(struct iwmct_fw_hdr));
64 LOG_INFO(priv, FW_DOWNLOAD, "fw versions are:\n"
65 "top %u.%u.%u gps %u.%u.%u bt %u.%u.%u tic %s\n",
66 fw_hdr->top_major, fw_hdr->top_minor, fw_hdr->top_revision,
67 fw_hdr->gps_major, fw_hdr->gps_minor, fw_hdr->gps_revision,
68 fw_hdr->bt_major, fw_hdr->bt_minor, fw_hdr->bt_revision,
69 fw_hdr->tic_name);
70
71 parser->cur_pos += sizeof(struct iwmct_fw_hdr);
72
73 LOG_INFOEX(priv, INIT, "<--\n");
74 return 0;
75}
76
77static bool iwmct_checksum(struct iwmct_priv *priv)
78{
79 struct iwmct_parser *parser = &priv->parser;
80 __le32 *file = (__le32 *)parser->file;
81 int i, pad, steps;
82 u32 accum = 0;
83 u32 checksum;
84 u32 mask = 0xffffffff;
85
86 pad = (parser->file_size - CHECKSUM_BYTES_NUM) % 4;
87 steps = (parser->file_size - CHECKSUM_BYTES_NUM) / 4;
88
89 LOG_INFO(priv, FW_DOWNLOAD, "pad=%d steps=%d\n", pad, steps);
90
91 for (i = 0; i < steps; i++)
92 accum += le32_to_cpu(file[i]);
93
94 if (pad) {
95 mask <<= 8 * (4 - pad);
96 accum += le32_to_cpu(file[steps]) & mask;
97 }
98
99 checksum = get_unaligned_le32((__le32 *)(parser->file +
100 parser->file_size - CHECKSUM_BYTES_NUM));
101
102 LOG_INFO(priv, FW_DOWNLOAD,
103 "compare checksum accum=0x%x to checksum=0x%x\n",
104 accum, checksum);
105
106 return checksum == accum;
107}
108
109static int iwmct_parse_next_section(struct iwmct_priv *priv, const u8 **p_sec,
110 size_t *sec_size, __le32 *sec_addr)
111{
112 struct iwmct_parser *parser = &priv->parser;
113 struct iwmct_dbg *dbg = &priv->dbg;
114 struct iwmct_fw_sec_hdr *sec_hdr;
115
116 LOG_INFOEX(priv, INIT, "-->\n");
117
118 while (parser->cur_pos + sizeof(struct iwmct_fw_sec_hdr)
119 <= parser->file_size) {
120
121 sec_hdr = (struct iwmct_fw_sec_hdr *)
122 (parser->file + parser->cur_pos);
123 parser->cur_pos += sizeof(struct iwmct_fw_sec_hdr);
124
125 LOG_INFO(priv, FW_DOWNLOAD,
126 "sec hdr: type=%s addr=0x%x size=%d\n",
127 sec_hdr->type, sec_hdr->target_addr,
128 sec_hdr->data_size);
129
130 if (strcmp(sec_hdr->type, "ENT") == 0)
131 parser->entry_point = le32_to_cpu(sec_hdr->target_addr);
132 else if (strcmp(sec_hdr->type, "LBL") == 0)
133 strcpy(dbg->label_fw, parser->file + parser->cur_pos);
134 else if (((strcmp(sec_hdr->type, "TOP") == 0) &&
135 (priv->barker & BARKER_DNLOAD_TOP_MSK)) ||
136 ((strcmp(sec_hdr->type, "GPS") == 0) &&
137 (priv->barker & BARKER_DNLOAD_GPS_MSK)) ||
138 ((strcmp(sec_hdr->type, "BTH") == 0) &&
139 (priv->barker & BARKER_DNLOAD_BT_MSK))) {
140 *sec_addr = sec_hdr->target_addr;
141 *sec_size = le32_to_cpu(sec_hdr->data_size);
142 *p_sec = parser->file + parser->cur_pos;
143 parser->cur_pos += le32_to_cpu(sec_hdr->data_size);
144 return 1;
145 } else if (strcmp(sec_hdr->type, "LOG") != 0)
146 LOG_WARNING(priv, FW_DOWNLOAD,
147 "skipping section type %s\n",
148 sec_hdr->type);
149
150 parser->cur_pos += le32_to_cpu(sec_hdr->data_size);
151 LOG_INFO(priv, FW_DOWNLOAD,
152 "finished with section cur_pos=%zd\n", parser->cur_pos);
153 }
154
155 LOG_INFOEX(priv, INIT, "<--\n");
156 return 0;
157}
158
159static int iwmct_download_section(struct iwmct_priv *priv, const u8 *p_sec,
160 size_t sec_size, __le32 addr)
161{
162 struct iwmct_parser *parser = &priv->parser;
163 struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf;
164 const u8 *cur_block = p_sec;
165 size_t sent = 0;
166 int cnt = 0;
167 int ret = 0;
168 u32 cmd = 0;
169
170 LOG_INFOEX(priv, INIT, "-->\n");
171 LOG_INFO(priv, FW_DOWNLOAD, "Download address 0x%x size 0x%zx\n",
172 addr, sec_size);
173
174 while (sent < sec_size) {
175 int i;
176 u32 chksm = 0;
177 u32 reset = atomic_read(&priv->reset);
178 /* actual FW data */
179 u32 data_size = min(parser->buf_size - sizeof(*hdr),
180 sec_size - sent);
181 /* Pad to block size */
182 u32 trans_size = (data_size + sizeof(*hdr) +
183 IWMC_SDIO_BLK_SIZE - 1) &
184 ~(IWMC_SDIO_BLK_SIZE - 1);
185 ++cnt;
186
187 /* in case of reset, interrupt FW DOWNLAOD */
188 if (reset) {
189 LOG_INFO(priv, FW_DOWNLOAD,
190 "Reset detected. Abort FW download!!!");
191 ret = -ECANCELED;
192 goto exit;
193 }
194
195 memset(parser->buf, 0, parser->buf_size);
196 cmd |= IWMC_OPCODE_WRITE << CMD_HDR_OPCODE_POS;
197 cmd |= IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS;
198 cmd |= (priv->dbg.direct ? 1 : 0) << CMD_HDR_DIRECT_ACCESS_POS;
199 cmd |= (priv->dbg.checksum ? 1 : 0) << CMD_HDR_USE_CHECKSUM_POS;
200 hdr->data_size = cpu_to_le32(data_size);
201 hdr->target_addr = addr;
202
203 /* checksum is allowed for sizes divisible by 4 */
204 if (data_size & 0x3)
205 cmd &= ~CMD_HDR_USE_CHECKSUM_MSK;
206
207 memcpy(hdr->data, cur_block, data_size);
208
209
210 if (cmd & CMD_HDR_USE_CHECKSUM_MSK) {
211
212 chksm = data_size + le32_to_cpu(addr) + cmd;
213 for (i = 0; i < data_size >> 2; i++)
214 chksm += ((u32 *)cur_block)[i];
215
216 hdr->block_chksm = cpu_to_le32(chksm);
217 LOG_INFO(priv, FW_DOWNLOAD, "Checksum = 0x%X\n",
218 hdr->block_chksm);
219 }
220
221 LOG_INFO(priv, FW_DOWNLOAD, "trans#%d, len=%d, sent=%zd, "
222 "sec_size=%zd, startAddress 0x%X\n",
223 cnt, trans_size, sent, sec_size, addr);
224
225 if (priv->dbg.dump)
226 LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, trans_size);
227
228
229 hdr->cmd = cpu_to_le32(cmd);
230 /* send it down */
231 /* TODO: add more proper sending and error checking */
232 ret = iwmct_tx(priv, 0, parser->buf, trans_size);
233 if (ret != 0) {
234 LOG_INFO(priv, FW_DOWNLOAD,
235 "iwmct_tx returned %d\n", ret);
236 goto exit;
237 }
238
239 addr = cpu_to_le32(le32_to_cpu(addr) + data_size);
240 sent += data_size;
241 cur_block = p_sec + sent;
242
243 if (priv->dbg.blocks && (cnt + 1) >= priv->dbg.blocks) {
244 LOG_INFO(priv, FW_DOWNLOAD,
245 "Block number limit is reached [%d]\n",
246 priv->dbg.blocks);
247 break;
248 }
249 }
250
251 if (sent < sec_size)
252 ret = -EINVAL;
253exit:
254 LOG_INFOEX(priv, INIT, "<--\n");
255 return ret;
256}
257
258static int iwmct_kick_fw(struct iwmct_priv *priv, bool jump)
259{
260 struct iwmct_parser *parser = &priv->parser;
261 struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf;
262 int ret;
263 u32 cmd;
264
265 LOG_INFOEX(priv, INIT, "-->\n");
266
267 memset(parser->buf, 0, parser->buf_size);
268 cmd = IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS;
269 if (jump) {
270 cmd |= IWMC_OPCODE_JUMP << CMD_HDR_OPCODE_POS;
271 hdr->target_addr = cpu_to_le32(parser->entry_point);
272 LOG_INFO(priv, FW_DOWNLOAD, "jump address 0x%x\n",
273 parser->entry_point);
274 } else {
275 cmd |= IWMC_OPCODE_LAST_COMMAND << CMD_HDR_OPCODE_POS;
276 LOG_INFO(priv, FW_DOWNLOAD, "last command\n");
277 }
278
279 hdr->cmd = cpu_to_le32(cmd);
280
281 LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, sizeof(*hdr));
282 /* send it down */
283 /* TODO: add more proper sending and error checking */
284 ret = iwmct_tx(priv, 0, parser->buf, IWMC_SDIO_BLK_SIZE);
285 if (ret)
286 LOG_INFO(priv, FW_DOWNLOAD, "iwmct_tx returned %d", ret);
287
288 LOG_INFOEX(priv, INIT, "<--\n");
289 return 0;
290}
291
292int iwmct_fw_load(struct iwmct_priv *priv)
293{
294 const struct firmware *raw = NULL;
295 __le32 addr;
296 size_t len;
297 const u8 *pdata;
298 const u8 *name = "iwmc3200top.1.fw";
299 int ret = 0;
300
301 /* clear parser struct */
302 memset(&priv->parser, 0, sizeof(struct iwmct_parser));
303 if (!name) {
304 ret = -EINVAL;
305 goto exit;
306 }
307
308 /* get the firmware */
309 ret = request_firmware(&raw, name, &priv->func->dev);
310 if (ret < 0) {
311 LOG_ERROR(priv, FW_DOWNLOAD, "%s request_firmware failed %d\n",
312 name, ret);
313 goto exit;
314 }
315
316 if (raw->size < sizeof(struct iwmct_fw_sec_hdr)) {
317 LOG_ERROR(priv, FW_DOWNLOAD, "%s smaller then (%zd) (%zd)\n",
318 name, sizeof(struct iwmct_fw_sec_hdr), raw->size);
319 goto exit;
320 }
321
322 LOG_INFO(priv, FW_DOWNLOAD, "Read firmware '%s'\n", name);
323
324 ret = iwmct_fw_parser_init(priv, raw->data, raw->size, priv->trans_len);
325 if (ret < 0) {
326 LOG_ERROR(priv, FW_DOWNLOAD,
327 "iwmct_parser_init failed: Reason %d\n", ret);
328 goto exit;
329 }
330
331 /* checksum */
332 if (!iwmct_checksum(priv)) {
333 LOG_ERROR(priv, FW_DOWNLOAD, "checksum error\n");
334 ret = -EINVAL;
335 goto exit;
336 }
337
338 /* download firmware to device */
339 while (iwmct_parse_next_section(priv, &pdata, &len, &addr)) {
340 if (iwmct_download_section(priv, pdata, len, addr)) {
341 LOG_ERROR(priv, FW_DOWNLOAD,
342 "%s download section failed\n", name);
343 ret = -EIO;
344 goto exit;
345 }
346 }
347
348 iwmct_kick_fw(priv, !!(priv->barker & BARKER_DNLOAD_JUMP_MSK));
349
350exit:
351 kfree(priv->parser.buf);
352
353 if (raw)
354 release_firmware(raw);
355
356 raw = NULL;
357
358 return ret;
359}
diff --git a/drivers/misc/iwmc3200top/fw-msg.h b/drivers/misc/iwmc3200top/fw-msg.h
new file mode 100644
index 000000000000..9e26b75bd482
--- /dev/null
+++ b/drivers/misc/iwmc3200top/fw-msg.h
@@ -0,0 +1,113 @@
1/*
2 * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
3 * drivers/misc/iwmc3200top/fw-msg.h
4 *
5 * Copyright (C) 2009 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 *
21 *
22 * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
23 * -
24 *
25 */
26
27#ifndef __FWMSG_H__
28#define __FWMSG_H__
29
30#define COMM_TYPE_D2H 0xFF
31#define COMM_TYPE_H2D 0xEE
32
33#define COMM_CATEGORY_OPERATIONAL 0x00
34#define COMM_CATEGORY_DEBUG 0x01
35#define COMM_CATEGORY_TESTABILITY 0x02
36#define COMM_CATEGORY_DIAGNOSTICS 0x03
37
38#define OP_DBG_ZSTR_MSG cpu_to_le16(0x1A)
39
40#define FW_LOG_SRC_MAX 32
41#define FW_LOG_SRC_ALL 255
42
43#define FW_STRING_TABLE_ADDR cpu_to_le32(0x0C000000)
44
45#define CMD_DBG_LOG_LEVEL cpu_to_le16(0x0001)
46#define CMD_TST_DEV_RESET cpu_to_le16(0x0060)
47#define CMD_TST_FUNC_RESET cpu_to_le16(0x0062)
48#define CMD_TST_IFACE_RESET cpu_to_le16(0x0064)
49#define CMD_TST_CPU_UTILIZATION cpu_to_le16(0x0065)
50#define CMD_TST_TOP_DEEP_SLEEP cpu_to_le16(0x0080)
51#define CMD_TST_WAKEUP cpu_to_le16(0x0081)
52#define CMD_TST_FUNC_WAKEUP cpu_to_le16(0x0082)
53#define CMD_TST_FUNC_DEEP_SLEEP_REQUEST cpu_to_le16(0x0083)
54#define CMD_TST_GET_MEM_DUMP cpu_to_le16(0x0096)
55
56#define OP_OPR_ALIVE cpu_to_le16(0x0010)
57#define OP_OPR_CMD_ACK cpu_to_le16(0x001F)
58#define OP_OPR_CMD_NACK cpu_to_le16(0x0020)
59#define OP_TST_MEM_DUMP cpu_to_le16(0x0043)
60
61#define CMD_FLAG_PADDING_256 0x80
62
63#define FW_HCMD_BLOCK_SIZE 256
64
65struct msg_hdr {
66 u8 type;
67 u8 category;
68 __le16 opcode;
69 u8 seqnum;
70 u8 flags;
71 __le16 length;
72} __attribute__((__packed__));
73
74struct log_hdr {
75 __le32 timestamp;
76 u8 severity;
77 u8 logsource;
78 __le16 reserved;
79} __attribute__((__packed__));
80
81struct mdump_hdr {
82 u8 dmpid;
83 u8 frag;
84 __le16 size;
85 __le32 addr;
86} __attribute__((__packed__));
87
88struct top_msg {
89 struct msg_hdr hdr;
90 union {
91 /* D2H messages */
92 struct {
93 struct log_hdr log_hdr;
94 u8 data[1];
95 } __attribute__((__packed__)) log;
96
97 struct {
98 struct log_hdr log_hdr;
99 struct mdump_hdr md_hdr;
100 u8 data[1];
101 } __attribute__((__packed__)) mdump;
102
103 /* H2D messages */
104 struct {
105 u8 logsource;
106 u8 sevmask;
107 } __attribute__((__packed__)) logdefs[FW_LOG_SRC_MAX];
108 struct mdump_hdr mdump_req;
109 } u;
110} __attribute__((__packed__));
111
112
113#endif /* __FWMSG_H__ */
diff --git a/drivers/misc/iwmc3200top/iwmc3200top.h b/drivers/misc/iwmc3200top/iwmc3200top.h
new file mode 100644
index 000000000000..f572fcf177a1
--- /dev/null
+++ b/drivers/misc/iwmc3200top/iwmc3200top.h
@@ -0,0 +1,206 @@
1/*
2 * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
3 * drivers/misc/iwmc3200top/iwmc3200top.h
4 *
5 * Copyright (C) 2009 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 *
21 *
22 * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
23 * -
24 *
25 */
26
27#ifndef __IWMC3200TOP_H__
28#define __IWMC3200TOP_H__
29
30#include <linux/workqueue.h>
31
32#define DRV_NAME "iwmc3200top"
33
34#define IWMC_SDIO_BLK_SIZE 256
35#define IWMC_DEFAULT_TR_BLK 64
36#define IWMC_SDIO_DATA_ADDR 0x0
37#define IWMC_SDIO_INTR_ENABLE_ADDR 0x14
38#define IWMC_SDIO_INTR_STATUS_ADDR 0x13
39#define IWMC_SDIO_INTR_CLEAR_ADDR 0x13
40#define IWMC_SDIO_INTR_GET_SIZE_ADDR 0x2C
41
42#define COMM_HUB_HEADER_LENGTH 16
43#define LOGGER_HEADER_LENGTH 10
44
45
46#define BARKER_DNLOAD_BT_POS 0
47#define BARKER_DNLOAD_BT_MSK BIT(BARKER_DNLOAD_BT_POS)
48#define BARKER_DNLOAD_GPS_POS 1
49#define BARKER_DNLOAD_GPS_MSK BIT(BARKER_DNLOAD_GPS_POS)
50#define BARKER_DNLOAD_TOP_POS 2
51#define BARKER_DNLOAD_TOP_MSK BIT(BARKER_DNLOAD_TOP_POS)
52#define BARKER_DNLOAD_RESERVED1_POS 3
53#define BARKER_DNLOAD_RESERVED1_MSK BIT(BARKER_DNLOAD_RESERVED1_POS)
54#define BARKER_DNLOAD_JUMP_POS 4
55#define BARKER_DNLOAD_JUMP_MSK BIT(BARKER_DNLOAD_JUMP_POS)
56#define BARKER_DNLOAD_SYNC_POS 5
57#define BARKER_DNLOAD_SYNC_MSK BIT(BARKER_DNLOAD_SYNC_POS)
58#define BARKER_DNLOAD_RESERVED2_POS 6
59#define BARKER_DNLOAD_RESERVED2_MSK (0x3 << BARKER_DNLOAD_RESERVED2_POS)
60#define BARKER_DNLOAD_BARKER_POS 8
61#define BARKER_DNLOAD_BARKER_MSK (0xffffff << BARKER_DNLOAD_BARKER_POS)
62
63#define IWMC_BARKER_REBOOT (0xdeadbe << BARKER_DNLOAD_BARKER_POS)
64/* whole field barker */
65#define IWMC_BARKER_ACK 0xfeedbabe
66
67#define IWMC_CMD_SIGNATURE 0xcbbc
68
69#define CMD_HDR_OPCODE_POS 0
70#define CMD_HDR_OPCODE_MSK_MSK (0xf << CMD_HDR_OPCODE_MSK_POS)
71#define CMD_HDR_RESPONSE_CODE_POS 4
72#define CMD_HDR_RESPONSE_CODE_MSK (0xf << CMD_HDR_RESPONSE_CODE_POS)
73#define CMD_HDR_USE_CHECKSUM_POS 8
74#define CMD_HDR_USE_CHECKSUM_MSK BIT(CMD_HDR_USE_CHECKSUM_POS)
75#define CMD_HDR_RESPONSE_REQUIRED_POS 9
76#define CMD_HDR_RESPONSE_REQUIRED_MSK BIT(CMD_HDR_RESPONSE_REQUIRED_POS)
77#define CMD_HDR_DIRECT_ACCESS_POS 10
78#define CMD_HDR_DIRECT_ACCESS_MSK BIT(CMD_HDR_DIRECT_ACCESS_POS)
79#define CMD_HDR_RESERVED_POS 11
80#define CMD_HDR_RESERVED_MSK BIT(0x1f << CMD_HDR_RESERVED_POS)
81#define CMD_HDR_SIGNATURE_POS 16
82#define CMD_HDR_SIGNATURE_MSK BIT(0xffff << CMD_HDR_SIGNATURE_POS)
83
84enum {
85 IWMC_OPCODE_PING = 0,
86 IWMC_OPCODE_READ = 1,
87 IWMC_OPCODE_WRITE = 2,
88 IWMC_OPCODE_JUMP = 3,
89 IWMC_OPCODE_REBOOT = 4,
90 IWMC_OPCODE_PERSISTENT_WRITE = 5,
91 IWMC_OPCODE_PERSISTENT_READ = 6,
92 IWMC_OPCODE_READ_MODIFY_WRITE = 7,
93 IWMC_OPCODE_LAST_COMMAND = 15
94};
95
96struct iwmct_fw_load_hdr {
97 __le32 cmd;
98 __le32 target_addr;
99 __le32 data_size;
100 __le32 block_chksm;
101 u8 data[0];
102};
103
104/**
105 * struct iwmct_fw_hdr
106 * holds all sw components versions
107 */
108struct iwmct_fw_hdr {
109 u8 top_major;
110 u8 top_minor;
111 u8 top_revision;
112 u8 gps_major;
113 u8 gps_minor;
114 u8 gps_revision;
115 u8 bt_major;
116 u8 bt_minor;
117 u8 bt_revision;
118 u8 tic_name[31];
119};
120
121/**
122 * struct iwmct_fw_sec_hdr
123 * @type: function type
124 * @data_size: section's data size
125 * @target_addr: download address
126 */
127struct iwmct_fw_sec_hdr {
128 u8 type[4];
129 __le32 data_size;
130 __le32 target_addr;
131};
132
133/**
134 * struct iwmct_parser
135 * @file: fw image
136 * @file_size: fw size
137 * @cur_pos: position in file
138 * @buf: temp buf for download
139 * @buf_size: size of buf
140 * @entry_point: address to jump in fw kick-off
141 */
142struct iwmct_parser {
143 const u8 *file;
144 size_t file_size;
145 size_t cur_pos;
146 u8 *buf;
147 size_t buf_size;
148 u32 entry_point;
149 struct iwmct_fw_hdr versions;
150};
151
152
153struct iwmct_work_struct {
154 struct list_head list;
155 ssize_t iosize;
156};
157
158struct iwmct_dbg {
159 int blocks;
160 bool dump;
161 bool jump;
162 bool direct;
163 bool checksum;
164 bool fw_download;
165 int block_size;
166 int download_trans_blks;
167
168 char label_fw[256];
169};
170
171struct iwmct_debugfs;
172
173struct iwmct_priv {
174 struct sdio_func *func;
175 struct iwmct_debugfs *dbgfs;
176 struct iwmct_parser parser;
177 atomic_t reset;
178 atomic_t dev_sync;
179 u32 trans_len;
180 u32 barker;
181 struct iwmct_dbg dbg;
182
183 /* drivers work queue */
184 struct workqueue_struct *wq;
185 struct workqueue_struct *bus_rescan_wq;
186 struct work_struct bus_rescan_worker;
187 struct work_struct isr_worker;
188
189 /* drivers wait queue */
190 wait_queue_head_t wait_q;
191
192 /* rx request list */
193 struct list_head read_req_list;
194};
195
196extern int iwmct_tx(struct iwmct_priv *priv, unsigned int addr,
197 void *src, int count);
198
199extern int iwmct_fw_load(struct iwmct_priv *priv);
200
201extern void iwmct_dbg_init_params(struct iwmct_priv *drv);
202extern void iwmct_dbg_init_drv_attrs(struct device_driver *drv);
203extern void iwmct_dbg_remove_drv_attrs(struct device_driver *drv);
204extern int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len);
205
206#endif /* __IWMC3200TOP_H__ */
diff --git a/drivers/misc/iwmc3200top/log.c b/drivers/misc/iwmc3200top/log.c
new file mode 100644
index 000000000000..d569279698f6
--- /dev/null
+++ b/drivers/misc/iwmc3200top/log.c
@@ -0,0 +1,347 @@
1/*
2 * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
3 * drivers/misc/iwmc3200top/log.c
4 *
5 * Copyright (C) 2009 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 *
21 *
22 * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
23 * -
24 *
25 */
26
27#include <linux/kernel.h>
28#include <linux/mmc/sdio_func.h>
29#include <linux/ctype.h>
30#include "fw-msg.h"
31#include "iwmc3200top.h"
32#include "log.h"
33
34/* Maximal hexadecimal string size of the FW memdump message */
35#define LOG_MSG_SIZE_MAX 12400
36
37/* iwmct_logdefs is a global used by log macros */
38u8 iwmct_logdefs[LOG_SRC_MAX];
39static u8 iwmct_fw_logdefs[FW_LOG_SRC_MAX];
40
41
42static int _log_set_log_filter(u8 *logdefs, int size, u8 src, u8 logmask)
43{
44 int i;
45
46 if (src < size)
47 logdefs[src] = logmask;
48 else if (src == LOG_SRC_ALL)
49 for (i = 0; i < size; i++)
50 logdefs[i] = logmask;
51 else
52 return -1;
53
54 return 0;
55}
56
57
58int iwmct_log_set_filter(u8 src, u8 logmask)
59{
60 return _log_set_log_filter(iwmct_logdefs, LOG_SRC_MAX, src, logmask);
61}
62
63
64int iwmct_log_set_fw_filter(u8 src, u8 logmask)
65{
66 return _log_set_log_filter(iwmct_fw_logdefs,
67 FW_LOG_SRC_MAX, src, logmask);
68}
69
70
71static int log_msg_format_hex(char *str, int slen, u8 *ibuf,
72 int ilen, char *pref)
73{
74 int pos = 0;
75 int i;
76 int len;
77
78 for (pos = 0, i = 0; pos < slen - 2 && pref[i] != '\0'; i++, pos++)
79 str[pos] = pref[i];
80
81 for (i = 0; pos < slen - 2 && i < ilen; pos += len, i++)
82 len = snprintf(&str[pos], slen - pos - 1, " %2.2X", ibuf[i]);
83
84 if (i < ilen)
85 return -1;
86
87 return 0;
88}
89
90/* NOTE: This function is not thread safe.
91 Currently it's called only from sdio rx worker - no race there
92*/
93void iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len)
94{
95 struct top_msg *msg;
96 static char logbuf[LOG_MSG_SIZE_MAX];
97
98 msg = (struct top_msg *)buf;
99
100 if (len < sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr)) {
101 LOG_ERROR(priv, FW_MSG, "Log message from TOP "
102 "is too short %d (expected %zd)\n",
103 len, sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr));
104 return;
105 }
106
107 if (!(iwmct_fw_logdefs[msg->u.log.log_hdr.logsource] &
108 BIT(msg->u.log.log_hdr.severity)) ||
109 !(iwmct_logdefs[LOG_SRC_FW_MSG] & BIT(msg->u.log.log_hdr.severity)))
110 return;
111
112 switch (msg->hdr.category) {
113 case COMM_CATEGORY_TESTABILITY:
114 if (!(iwmct_logdefs[LOG_SRC_TST] &
115 BIT(msg->u.log.log_hdr.severity)))
116 return;
117 if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf,
118 le16_to_cpu(msg->hdr.length) +
119 sizeof(msg->hdr), "<TST>"))
120 LOG_WARNING(priv, TST,
121 "TOP TST message is too long, truncating...");
122 LOG_WARNING(priv, TST, "%s\n", logbuf);
123 break;
124 case COMM_CATEGORY_DEBUG:
125 if (msg->hdr.opcode == OP_DBG_ZSTR_MSG)
126 LOG_INFO(priv, FW_MSG, "%s %s", "<DBG>",
127 ((u8 *)msg) + sizeof(msg->hdr)
128 + sizeof(msg->u.log.log_hdr));
129 else {
130 if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf,
131 le16_to_cpu(msg->hdr.length)
132 + sizeof(msg->hdr),
133 "<DBG>"))
134 LOG_WARNING(priv, FW_MSG,
135 "TOP DBG message is too long,"
136 "truncating...");
137 LOG_WARNING(priv, FW_MSG, "%s\n", logbuf);
138 }
139 break;
140 default:
141 break;
142 }
143}
144
145static int _log_get_filter_str(u8 *logdefs, int logdefsz, char *buf, int size)
146{
147 int i, pos, len;
148 for (i = 0, pos = 0; (pos < size-1) && (i < logdefsz); i++) {
149 len = snprintf(&buf[pos], size - pos - 1, "0x%02X%02X,",
150 i, logdefs[i]);
151 pos += len;
152 }
153 buf[pos-1] = '\n';
154 buf[pos] = '\0';
155
156 if (i < logdefsz)
157 return -1;
158 return 0;
159}
160
161int log_get_filter_str(char *buf, int size)
162{
163 return _log_get_filter_str(iwmct_logdefs, LOG_SRC_MAX, buf, size);
164}
165
166int log_get_fw_filter_str(char *buf, int size)
167{
168 return _log_get_filter_str(iwmct_fw_logdefs, FW_LOG_SRC_MAX, buf, size);
169}
170
171#define HEXADECIMAL_RADIX 16
172#define LOG_SRC_FORMAT 7 /* log level is in format of "0xXXXX," */
173
174ssize_t show_iwmct_log_level(struct device *d,
175 struct device_attribute *attr, char *buf)
176{
177 struct iwmct_priv *priv = dev_get_drvdata(d);
178 char *str_buf;
179 int buf_size;
180 ssize_t ret;
181
182 buf_size = (LOG_SRC_FORMAT * LOG_SRC_MAX) + 1;
183 str_buf = kzalloc(buf_size, GFP_KERNEL);
184 if (!str_buf) {
185 LOG_ERROR(priv, DEBUGFS,
186 "failed to allocate %d bytes\n", buf_size);
187 ret = -ENOMEM;
188 goto exit;
189 }
190
191 if (log_get_filter_str(str_buf, buf_size) < 0) {
192 ret = -EINVAL;
193 goto exit;
194 }
195
196 ret = sprintf(buf, "%s", str_buf);
197
198exit:
199 kfree(str_buf);
200 return ret;
201}
202
203ssize_t store_iwmct_log_level(struct device *d,
204 struct device_attribute *attr,
205 const char *buf, size_t count)
206{
207 struct iwmct_priv *priv = dev_get_drvdata(d);
208 char *token, *str_buf = NULL;
209 long val;
210 ssize_t ret = count;
211 u8 src, mask;
212
213 if (!count)
214 goto exit;
215
216 str_buf = kzalloc(count, GFP_KERNEL);
217 if (!str_buf) {
218 LOG_ERROR(priv, DEBUGFS,
219 "failed to allocate %zd bytes\n", count);
220 ret = -ENOMEM;
221 goto exit;
222 }
223
224 memcpy(str_buf, buf, count);
225
226 while ((token = strsep(&str_buf, ",")) != NULL) {
227 while (isspace(*token))
228 ++token;
229 if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) {
230 LOG_ERROR(priv, DEBUGFS,
231 "failed to convert string to long %s\n",
232 token);
233 ret = -EINVAL;
234 goto exit;
235 }
236
237 mask = val & 0xFF;
238 src = (val & 0XFF00) >> 8;
239 iwmct_log_set_filter(src, mask);
240 }
241
242exit:
243 kfree(str_buf);
244 return ret;
245}
246
247ssize_t show_iwmct_log_level_fw(struct device *d,
248 struct device_attribute *attr, char *buf)
249{
250 struct iwmct_priv *priv = dev_get_drvdata(d);
251 char *str_buf;
252 int buf_size;
253 ssize_t ret;
254
255 buf_size = (LOG_SRC_FORMAT * FW_LOG_SRC_MAX) + 2;
256
257 str_buf = kzalloc(buf_size, GFP_KERNEL);
258 if (!str_buf) {
259 LOG_ERROR(priv, DEBUGFS,
260 "failed to allocate %d bytes\n", buf_size);
261 ret = -ENOMEM;
262 goto exit;
263 }
264
265 if (log_get_fw_filter_str(str_buf, buf_size) < 0) {
266 ret = -EINVAL;
267 goto exit;
268 }
269
270 ret = sprintf(buf, "%s", str_buf);
271
272exit:
273 kfree(str_buf);
274 return ret;
275}
276
277ssize_t store_iwmct_log_level_fw(struct device *d,
278 struct device_attribute *attr,
279 const char *buf, size_t count)
280{
281 struct iwmct_priv *priv = dev_get_drvdata(d);
282 struct top_msg cmd;
283 char *token, *str_buf = NULL;
284 ssize_t ret = count;
285 u16 cmdlen = 0;
286 int i;
287 long val;
288 u8 src, mask;
289
290 if (!count)
291 goto exit;
292
293 str_buf = kzalloc(count, GFP_KERNEL);
294 if (!str_buf) {
295 LOG_ERROR(priv, DEBUGFS,
296 "failed to allocate %zd bytes\n", count);
297 ret = -ENOMEM;
298 goto exit;
299 }
300
301 memcpy(str_buf, buf, count);
302
303 cmd.hdr.type = COMM_TYPE_H2D;
304 cmd.hdr.category = COMM_CATEGORY_DEBUG;
305 cmd.hdr.opcode = CMD_DBG_LOG_LEVEL;
306
307 for (i = 0; ((token = strsep(&str_buf, ",")) != NULL) &&
308 (i < FW_LOG_SRC_MAX); i++) {
309
310 while (isspace(*token))
311 ++token;
312
313 if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) {
314 LOG_ERROR(priv, DEBUGFS,
315 "failed to convert string to long %s\n",
316 token);
317 ret = -EINVAL;
318 goto exit;
319 }
320
321 mask = val & 0xFF; /* LSB */
322 src = (val & 0XFF00) >> 8; /* 2nd least significant byte. */
323 iwmct_log_set_fw_filter(src, mask);
324
325 cmd.u.logdefs[i].logsource = src;
326 cmd.u.logdefs[i].sevmask = mask;
327 }
328
329 cmd.hdr.length = cpu_to_le16(i * sizeof(cmd.u.logdefs[0]));
330 cmdlen = (i * sizeof(cmd.u.logdefs[0]) + sizeof(cmd.hdr));
331
332 ret = iwmct_send_hcmd(priv, (u8 *)&cmd, cmdlen);
333 if (ret) {
334 LOG_ERROR(priv, DEBUGFS,
335 "Failed to send %d bytes of fwcmd, ret=%zd\n",
336 cmdlen, ret);
337 goto exit;
338 } else
339 LOG_INFO(priv, DEBUGFS, "fwcmd sent (%d bytes)\n", cmdlen);
340
341 ret = count;
342
343exit:
344 kfree(str_buf);
345 return ret;
346}
347
diff --git a/drivers/misc/iwmc3200top/log.h b/drivers/misc/iwmc3200top/log.h
new file mode 100644
index 000000000000..aba8121f978c
--- /dev/null
+++ b/drivers/misc/iwmc3200top/log.h
@@ -0,0 +1,158 @@
1/*
2 * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
3 * drivers/misc/iwmc3200top/log.h
4 *
5 * Copyright (C) 2009 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 *
21 *
22 * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
23 * -
24 *
25 */
26
27#ifndef __LOG_H__
28#define __LOG_H__
29
30
31/* log severity:
32 * The log levels here match FW log levels
33 * so values need to stay as is */
34#define LOG_SEV_CRITICAL 0
35#define LOG_SEV_ERROR 1
36#define LOG_SEV_WARNING 2
37#define LOG_SEV_INFO 3
38#define LOG_SEV_INFOEX 4
39
40#define LOG_SEV_FILTER_ALL \
41 (BIT(LOG_SEV_CRITICAL) | \
42 BIT(LOG_SEV_ERROR) | \
43 BIT(LOG_SEV_WARNING) | \
44 BIT(LOG_SEV_INFO) | \
45 BIT(LOG_SEV_INFOEX))
46
47/* log source */
48#define LOG_SRC_INIT 0
49#define LOG_SRC_DEBUGFS 1
50#define LOG_SRC_FW_DOWNLOAD 2
51#define LOG_SRC_FW_MSG 3
52#define LOG_SRC_TST 4
53#define LOG_SRC_IRQ 5
54
55#define LOG_SRC_MAX 6
56#define LOG_SRC_ALL 0xFF
57
58/**
59 * Default intitialization runtime log level
60 */
61#ifndef LOG_SEV_FILTER_RUNTIME
62#define LOG_SEV_FILTER_RUNTIME \
63 (BIT(LOG_SEV_CRITICAL) | \
64 BIT(LOG_SEV_ERROR) | \
65 BIT(LOG_SEV_WARNING))
66#endif
67
68#ifndef FW_LOG_SEV_FILTER_RUNTIME
69#define FW_LOG_SEV_FILTER_RUNTIME LOG_SEV_FILTER_ALL
70#endif
71
72#ifdef CONFIG_IWMC3200TOP_DEBUG
73/**
74 * Log macros
75 */
76
77#define priv2dev(priv) (&(priv->func)->dev)
78
79#define LOG_CRITICAL(priv, src, fmt, args...) \
80do { \
81 if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_CRITICAL)) \
82 dev_crit(priv2dev(priv), "%s %d: " fmt, \
83 __func__, __LINE__, ##args); \
84} while (0)
85
86#define LOG_ERROR(priv, src, fmt, args...) \
87do { \
88 if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_ERROR)) \
89 dev_err(priv2dev(priv), "%s %d: " fmt, \
90 __func__, __LINE__, ##args); \
91} while (0)
92
93#define LOG_WARNING(priv, src, fmt, args...) \
94do { \
95 if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_WARNING)) \
96 dev_warn(priv2dev(priv), "%s %d: " fmt, \
97 __func__, __LINE__, ##args); \
98} while (0)
99
100#define LOG_INFO(priv, src, fmt, args...) \
101do { \
102 if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFO)) \
103 dev_info(priv2dev(priv), "%s %d: " fmt, \
104 __func__, __LINE__, ##args); \
105} while (0)
106
107#define LOG_INFOEX(priv, src, fmt, args...) \
108do { \
109 if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFOEX)) \
110 dev_dbg(priv2dev(priv), "%s %d: " fmt, \
111 __func__, __LINE__, ##args); \
112} while (0)
113
114#define LOG_HEXDUMP(src, ptr, len) \
115do { \
116 if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFOEX)) \
117 print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, \
118 16, 1, ptr, len, false); \
119} while (0)
120
121void iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len);
122
123extern u8 iwmct_logdefs[];
124
125int iwmct_log_set_filter(u8 src, u8 logmask);
126int iwmct_log_set_fw_filter(u8 src, u8 logmask);
127
128ssize_t show_iwmct_log_level(struct device *d,
129 struct device_attribute *attr, char *buf);
130ssize_t store_iwmct_log_level(struct device *d,
131 struct device_attribute *attr,
132 const char *buf, size_t count);
133ssize_t show_iwmct_log_level_fw(struct device *d,
134 struct device_attribute *attr, char *buf);
135ssize_t store_iwmct_log_level_fw(struct device *d,
136 struct device_attribute *attr,
137 const char *buf, size_t count);
138
139#else
140
141#define LOG_CRITICAL(priv, src, fmt, args...)
142#define LOG_ERROR(priv, src, fmt, args...)
143#define LOG_WARNING(priv, src, fmt, args...)
144#define LOG_INFO(priv, src, fmt, args...)
145#define LOG_INFOEX(priv, src, fmt, args...)
146#define LOG_HEXDUMP(src, ptr, len)
147
148static inline void iwmct_log_top_message(struct iwmct_priv *priv,
149 u8 *buf, int len) {}
150static inline int iwmct_log_set_filter(u8 src, u8 logmask) { return 0; }
151static inline int iwmct_log_set_fw_filter(u8 src, u8 logmask) { return 0; }
152
153#endif /* CONFIG_IWMC3200TOP_DEBUG */
154
155int log_get_filter_str(char *buf, int size);
156int log_get_fw_filter_str(char *buf, int size);
157
158#endif /* __LOG_H__ */
diff --git a/drivers/misc/iwmc3200top/main.c b/drivers/misc/iwmc3200top/main.c
new file mode 100644
index 000000000000..6e4e49113ab4
--- /dev/null
+++ b/drivers/misc/iwmc3200top/main.c
@@ -0,0 +1,699 @@
1/*
2 * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
3 * drivers/misc/iwmc3200top/main.c
4 *
5 * Copyright (C) 2009 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 *
21 *
22 * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
23 * -
24 *
25 */
26
27#include <linux/module.h>
28#include <linux/init.h>
29#include <linux/kernel.h>
30#include <linux/debugfs.h>
31#include <linux/mmc/sdio_ids.h>
32#include <linux/mmc/sdio_func.h>
33#include <linux/mmc/sdio.h>
34
35#include "iwmc3200top.h"
36#include "log.h"
37#include "fw-msg.h"
38#include "debugfs.h"
39
40
41#define DRIVER_DESCRIPTION "Intel(R) IWMC 3200 Top Driver"
42#define DRIVER_COPYRIGHT "Copyright (c) 2008 Intel Corporation."
43
44#define IWMCT_VERSION "0.1.62"
45
46#ifdef REPOSITORY_LABEL
47#define RL REPOSITORY_LABEL
48#else
49#define RL local
50#endif
51
52#ifdef CONFIG_IWMC3200TOP_DEBUG
53#define VD "-d"
54#else
55#define VD
56#endif
57
58#define DRIVER_VERSION IWMCT_VERSION "-" __stringify(RL) VD
59
60MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
61MODULE_VERSION(DRIVER_VERSION);
62MODULE_LICENSE("GPL");
63MODULE_AUTHOR(DRIVER_COPYRIGHT);
64
65
66/* FIXME: These can be found in sdio_ids.h in newer kernels */
67#ifndef SDIO_INTEL_VENDOR_ID
68#define SDIO_INTEL_VENDOR_ID 0x0089
69#endif
70#ifndef SDIO_DEVICE_ID_INTEL_IWMC3200TOP
71#define SDIO_DEVICE_ID_INTEL_IWMC3200TOP 0x1404
72#endif
73
74/*
75 * This workers main task is to wait for OP_OPR_ALIVE
76 * from TOP FW until ALIVE_MSG_TIMOUT timeout is elapsed.
77 * When OP_OPR_ALIVE received it will issue
78 * a call to "bus_rescan_devices".
79 */
80static void iwmct_rescan_worker(struct work_struct *ws)
81{
82 struct iwmct_priv *priv;
83 int ret;
84
85 priv = container_of(ws, struct iwmct_priv, bus_rescan_worker);
86
87 LOG_INFO(priv, FW_MSG, "Calling bus_rescan\n");
88
89 ret = bus_rescan_devices(priv->func->dev.bus);
90 if (ret < 0)
91 LOG_INFO(priv, FW_DOWNLOAD, "bus_rescan_devices FAILED!!!\n");
92}
93
94static void op_top_message(struct iwmct_priv *priv, struct top_msg *msg)
95{
96 switch (msg->hdr.opcode) {
97 case OP_OPR_ALIVE:
98 LOG_INFO(priv, FW_MSG, "Got ALIVE from device, wake rescan\n");
99 queue_work(priv->bus_rescan_wq, &priv->bus_rescan_worker);
100 break;
101 default:
102 LOG_INFO(priv, FW_MSG, "Received msg opcode 0x%X\n",
103 msg->hdr.opcode);
104 break;
105 }
106}
107
108
109static void handle_top_message(struct iwmct_priv *priv, u8 *buf, int len)
110{
111 struct top_msg *msg;
112
113 msg = (struct top_msg *)buf;
114
115 if (msg->hdr.type != COMM_TYPE_D2H) {
116 LOG_ERROR(priv, FW_MSG,
117 "Message from TOP with invalid message type 0x%X\n",
118 msg->hdr.type);
119 return;
120 }
121
122 if (len < sizeof(msg->hdr)) {
123 LOG_ERROR(priv, FW_MSG,
124 "Message from TOP is too short for message header "
125 "received %d bytes, expected at least %zd bytes\n",
126 len, sizeof(msg->hdr));
127 return;
128 }
129
130 if (len < le16_to_cpu(msg->hdr.length) + sizeof(msg->hdr)) {
131 LOG_ERROR(priv, FW_MSG,
132 "Message length (%d bytes) is shorter than "
133 "in header (%d bytes)\n",
134 len, le16_to_cpu(msg->hdr.length));
135 return;
136 }
137
138 switch (msg->hdr.category) {
139 case COMM_CATEGORY_OPERATIONAL:
140 op_top_message(priv, (struct top_msg *)buf);
141 break;
142
143 case COMM_CATEGORY_DEBUG:
144 case COMM_CATEGORY_TESTABILITY:
145 case COMM_CATEGORY_DIAGNOSTICS:
146 iwmct_log_top_message(priv, buf, len);
147 break;
148
149 default:
150 LOG_ERROR(priv, FW_MSG,
151 "Message from TOP with unknown category 0x%X\n",
152 msg->hdr.category);
153 break;
154 }
155}
156
157int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len)
158{
159 int ret;
160 u8 *buf;
161
162 LOG_INFOEX(priv, FW_MSG, "Sending hcmd:\n");
163
164 /* add padding to 256 for IWMC */
165 ((struct top_msg *)cmd)->hdr.flags |= CMD_FLAG_PADDING_256;
166
167 LOG_HEXDUMP(FW_MSG, cmd, len);
168
169 if (len > FW_HCMD_BLOCK_SIZE) {
170 LOG_ERROR(priv, FW_MSG, "size %d exceeded hcmd max size %d\n",
171 len, FW_HCMD_BLOCK_SIZE);
172 return -1;
173 }
174
175 buf = kzalloc(FW_HCMD_BLOCK_SIZE, GFP_KERNEL);
176 if (!buf) {
177 LOG_ERROR(priv, FW_MSG, "kzalloc error, buf size %d\n",
178 FW_HCMD_BLOCK_SIZE);
179 return -1;
180 }
181
182 memcpy(buf, cmd, len);
183
184 sdio_claim_host(priv->func);
185 ret = sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR, buf,
186 FW_HCMD_BLOCK_SIZE);
187 sdio_release_host(priv->func);
188
189 kfree(buf);
190 return ret;
191}
192
193int iwmct_tx(struct iwmct_priv *priv, unsigned int addr,
194 void *src, int count)
195{
196 int ret;
197
198 sdio_claim_host(priv->func);
199 ret = sdio_memcpy_toio(priv->func, addr, src, count);
200 sdio_release_host(priv->func);
201
202 return ret;
203}
204
205static void iwmct_irq_read_worker(struct work_struct *ws)
206{
207 struct iwmct_priv *priv;
208 struct iwmct_work_struct *read_req;
209 __le32 *buf = NULL;
210 int ret;
211 int iosize;
212 u32 barker;
213 bool is_barker;
214
215 priv = container_of(ws, struct iwmct_priv, isr_worker);
216
217 LOG_INFO(priv, IRQ, "enter iwmct_irq_read_worker %p\n", ws);
218
219 /* --------------------- Handshake with device -------------------- */
220 sdio_claim_host(priv->func);
221
222 /* all list manipulations have to be protected by
223 * sdio_claim_host/sdio_release_host */
224 if (list_empty(&priv->read_req_list)) {
225 LOG_ERROR(priv, IRQ, "read_req_list empty in read worker\n");
226 goto exit_release;
227 }
228
229 read_req = list_entry(priv->read_req_list.next,
230 struct iwmct_work_struct, list);
231
232 list_del(&read_req->list);
233 iosize = read_req->iosize;
234 kfree(read_req);
235
236 buf = kzalloc(iosize, GFP_KERNEL);
237 if (!buf) {
238 LOG_ERROR(priv, IRQ, "kzalloc error, buf size %d\n", iosize);
239 goto exit_release;
240 }
241
242 LOG_INFO(priv, IRQ, "iosize=%d, buf=%p, func=%d\n",
243 iosize, buf, priv->func->num);
244
245 /* read from device */
246 ret = sdio_memcpy_fromio(priv->func, buf, IWMC_SDIO_DATA_ADDR, iosize);
247 if (ret) {
248 LOG_ERROR(priv, IRQ, "error %d reading buffer\n", ret);
249 goto exit_release;
250 }
251
252 LOG_HEXDUMP(IRQ, (u8 *)buf, iosize);
253
254 barker = le32_to_cpu(buf[0]);
255
256 /* Verify whether it's a barker and if not - treat as regular Rx */
257 if (barker == IWMC_BARKER_ACK ||
258 (barker & BARKER_DNLOAD_BARKER_MSK) == IWMC_BARKER_REBOOT) {
259
260 /* Valid Barker is equal on first 4 dwords */
261 is_barker = (buf[1] == buf[0]) &&
262 (buf[2] == buf[0]) &&
263 (buf[3] == buf[0]);
264
265 if (!is_barker) {
266 LOG_WARNING(priv, IRQ,
267 "Potentially inconsistent barker "
268 "%08X_%08X_%08X_%08X\n",
269 le32_to_cpu(buf[0]), le32_to_cpu(buf[1]),
270 le32_to_cpu(buf[2]), le32_to_cpu(buf[3]));
271 }
272 } else {
273 is_barker = false;
274 }
275
276 /* Handle Top CommHub message */
277 if (!is_barker) {
278 sdio_release_host(priv->func);
279 handle_top_message(priv, (u8 *)buf, iosize);
280 goto exit;
281 } else if (barker == IWMC_BARKER_ACK) { /* Handle barkers */
282 if (atomic_read(&priv->dev_sync) == 0) {
283 LOG_ERROR(priv, IRQ,
284 "ACK barker arrived out-of-sync\n");
285 goto exit_release;
286 }
287
288 /* Continuing to FW download (after Sync is completed)*/
289 atomic_set(&priv->dev_sync, 0);
290 LOG_INFO(priv, IRQ, "ACK barker arrived "
291 "- starting FW download\n");
292 } else { /* REBOOT barker */
293 LOG_INFO(priv, IRQ, "Recieved reboot barker: %x\n", barker);
294 priv->barker = barker;
295
296 if (barker & BARKER_DNLOAD_SYNC_MSK) {
297 /* Send the same barker back */
298 ret = sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR,
299 buf, iosize);
300 if (ret) {
301 LOG_ERROR(priv, IRQ,
302 "error %d echoing barker\n", ret);
303 goto exit_release;
304 }
305 LOG_INFO(priv, IRQ, "Echoing barker to device\n");
306 atomic_set(&priv->dev_sync, 1);
307 goto exit_release;
308 }
309
310 /* Continuing to FW download (without Sync) */
311 LOG_INFO(priv, IRQ, "No sync requested "
312 "- starting FW download\n");
313 }
314
315 sdio_release_host(priv->func);
316
317
318 LOG_INFO(priv, IRQ, "barker download request 0x%x is:\n", priv->barker);
319 LOG_INFO(priv, IRQ, "******* Top FW %s requested ********\n",
320 (priv->barker & BARKER_DNLOAD_TOP_MSK) ? "was" : "not");
321 LOG_INFO(priv, IRQ, "******* GPS FW %s requested ********\n",
322 (priv->barker & BARKER_DNLOAD_GPS_MSK) ? "was" : "not");
323 LOG_INFO(priv, IRQ, "******* BT FW %s requested ********\n",
324 (priv->barker & BARKER_DNLOAD_BT_MSK) ? "was" : "not");
325
326 if (priv->dbg.fw_download)
327 iwmct_fw_load(priv);
328 else
329 LOG_ERROR(priv, IRQ, "FW download not allowed\n");
330
331 goto exit;
332
333exit_release:
334 sdio_release_host(priv->func);
335exit:
336 kfree(buf);
337 LOG_INFO(priv, IRQ, "exit iwmct_irq_read_worker\n");
338}
339
340static void iwmct_irq(struct sdio_func *func)
341{
342 struct iwmct_priv *priv;
343 int val, ret;
344 int iosize;
345 int addr = IWMC_SDIO_INTR_GET_SIZE_ADDR;
346 struct iwmct_work_struct *read_req;
347
348 priv = sdio_get_drvdata(func);
349
350 LOG_INFO(priv, IRQ, "enter iwmct_irq\n");
351
352 /* read the function's status register */
353 val = sdio_readb(func, IWMC_SDIO_INTR_STATUS_ADDR, &ret);
354
355 LOG_INFO(priv, IRQ, "iir value = %d, ret=%d\n", val, ret);
356
357 if (!val) {
358 LOG_ERROR(priv, IRQ, "iir = 0, exiting ISR\n");
359 goto exit_clear_intr;
360 }
361
362
363 /*
364 * read 2 bytes of the transaction size
365 * IMPORTANT: sdio transaction size has to be read before clearing
366 * sdio interrupt!!!
367 */
368 val = sdio_readb(priv->func, addr++, &ret);
369 iosize = val;
370 val = sdio_readb(priv->func, addr++, &ret);
371 iosize += val << 8;
372
373 LOG_INFO(priv, IRQ, "READ size %d\n", iosize);
374
375 if (iosize == 0) {
376 LOG_ERROR(priv, IRQ, "READ size %d, exiting ISR\n", iosize);
377 goto exit_clear_intr;
378 }
379
380 /* allocate a work structure to pass iosize to the worker */
381 read_req = kzalloc(sizeof(struct iwmct_work_struct), GFP_KERNEL);
382 if (!read_req) {
383 LOG_ERROR(priv, IRQ, "failed to allocate read_req, exit ISR\n");
384 goto exit_clear_intr;
385 }
386
387 INIT_LIST_HEAD(&read_req->list);
388 read_req->iosize = iosize;
389
390 list_add_tail(&priv->read_req_list, &read_req->list);
391
392 /* clear the function's interrupt request bit (write 1 to clear) */
393 sdio_writeb(func, 1, IWMC_SDIO_INTR_CLEAR_ADDR, &ret);
394
395 queue_work(priv->wq, &priv->isr_worker);
396
397 LOG_INFO(priv, IRQ, "exit iwmct_irq\n");
398
399 return;
400
401exit_clear_intr:
402 /* clear the function's interrupt request bit (write 1 to clear) */
403 sdio_writeb(func, 1, IWMC_SDIO_INTR_CLEAR_ADDR, &ret);
404}
405
406
407static int blocks;
408module_param(blocks, int, 0604);
409MODULE_PARM_DESC(blocks, "max_blocks_to_send");
410
411static int dump;
412module_param(dump, bool, 0604);
413MODULE_PARM_DESC(dump, "dump_hex_content");
414
415static int jump = 1;
416module_param(jump, bool, 0604);
417
418static int direct = 1;
419module_param(direct, bool, 0604);
420
421static int checksum = 1;
422module_param(checksum, bool, 0604);
423
424static int fw_download = 1;
425module_param(fw_download, bool, 0604);
426
427static int block_size = IWMC_SDIO_BLK_SIZE;
428module_param(block_size, int, 0404);
429
430static int download_trans_blks = IWMC_DEFAULT_TR_BLK;
431module_param(download_trans_blks, int, 0604);
432
433static int rubbish_barker;
434module_param(rubbish_barker, bool, 0604);
435
436#ifdef CONFIG_IWMC3200TOP_DEBUG
437static int log_level[LOG_SRC_MAX];
438static unsigned int log_level_argc;
439module_param_array(log_level, int, &log_level_argc, 0604);
440MODULE_PARM_DESC(log_level, "log_level");
441
442static int log_level_fw[FW_LOG_SRC_MAX];
443static unsigned int log_level_fw_argc;
444module_param_array(log_level_fw, int, &log_level_fw_argc, 0604);
445MODULE_PARM_DESC(log_level_fw, "log_level_fw");
446#endif
447
448void iwmct_dbg_init_params(struct iwmct_priv *priv)
449{
450#ifdef CONFIG_IWMC3200TOP_DEBUG
451 int i;
452
453 for (i = 0; i < log_level_argc; i++) {
454 dev_notice(&priv->func->dev, "log_level[%d]=0x%X\n",
455 i, log_level[i]);
456 iwmct_log_set_filter((log_level[i] >> 8) & 0xFF,
457 log_level[i] & 0xFF);
458 }
459 for (i = 0; i < log_level_fw_argc; i++) {
460 dev_notice(&priv->func->dev, "log_level_fw[%d]=0x%X\n",
461 i, log_level_fw[i]);
462 iwmct_log_set_fw_filter((log_level_fw[i] >> 8) & 0xFF,
463 log_level_fw[i] & 0xFF);
464 }
465#endif
466
467 priv->dbg.blocks = blocks;
468 LOG_INFO(priv, INIT, "blocks=%d\n", blocks);
469 priv->dbg.dump = (bool)dump;
470 LOG_INFO(priv, INIT, "dump=%d\n", dump);
471 priv->dbg.jump = (bool)jump;
472 LOG_INFO(priv, INIT, "jump=%d\n", jump);
473 priv->dbg.direct = (bool)direct;
474 LOG_INFO(priv, INIT, "direct=%d\n", direct);
475 priv->dbg.checksum = (bool)checksum;
476 LOG_INFO(priv, INIT, "checksum=%d\n", checksum);
477 priv->dbg.fw_download = (bool)fw_download;
478 LOG_INFO(priv, INIT, "fw_download=%d\n", fw_download);
479 priv->dbg.block_size = block_size;
480 LOG_INFO(priv, INIT, "block_size=%d\n", block_size);
481 priv->dbg.download_trans_blks = download_trans_blks;
482 LOG_INFO(priv, INIT, "download_trans_blks=%d\n", download_trans_blks);
483}
484
485/*****************************************************************************
486 *
487 * sysfs attributes
488 *
489 *****************************************************************************/
490static ssize_t show_iwmct_fw_version(struct device *d,
491 struct device_attribute *attr, char *buf)
492{
493 struct iwmct_priv *priv = dev_get_drvdata(d);
494 return sprintf(buf, "%s\n", priv->dbg.label_fw);
495}
496static DEVICE_ATTR(cc_label_fw, S_IRUGO, show_iwmct_fw_version, NULL);
497
498#ifdef CONFIG_IWMC3200TOP_DEBUG
499static DEVICE_ATTR(log_level, S_IWUSR | S_IRUGO,
500 show_iwmct_log_level, store_iwmct_log_level);
501static DEVICE_ATTR(log_level_fw, S_IWUSR | S_IRUGO,
502 show_iwmct_log_level_fw, store_iwmct_log_level_fw);
503#endif
504
505static struct attribute *iwmct_sysfs_entries[] = {
506 &dev_attr_cc_label_fw.attr,
507#ifdef CONFIG_IWMC3200TOP_DEBUG
508 &dev_attr_log_level.attr,
509 &dev_attr_log_level_fw.attr,
510#endif
511 NULL
512};
513
514static struct attribute_group iwmct_attribute_group = {
515 .name = NULL, /* put in device directory */
516 .attrs = iwmct_sysfs_entries,
517};
518
519
520static int iwmct_probe(struct sdio_func *func,
521 const struct sdio_device_id *id)
522{
523 struct iwmct_priv *priv;
524 int ret;
525 int val = 1;
526 int addr = IWMC_SDIO_INTR_ENABLE_ADDR;
527
528 dev_dbg(&func->dev, "enter iwmct_probe\n");
529
530 dev_dbg(&func->dev, "IRQ polling period id %u msecs, HZ is %d\n",
531 jiffies_to_msecs(2147483647), HZ);
532
533 priv = kzalloc(sizeof(struct iwmct_priv), GFP_KERNEL);
534 if (!priv) {
535 dev_err(&func->dev, "kzalloc error\n");
536 return -ENOMEM;
537 }
538 priv->func = func;
539 sdio_set_drvdata(func, priv);
540
541
542 /* create drivers work queue */
543 priv->wq = create_workqueue(DRV_NAME "_wq");
544 priv->bus_rescan_wq = create_workqueue(DRV_NAME "_rescan_wq");
545 INIT_WORK(&priv->bus_rescan_worker, iwmct_rescan_worker);
546 INIT_WORK(&priv->isr_worker, iwmct_irq_read_worker);
547
548 init_waitqueue_head(&priv->wait_q);
549
550 sdio_claim_host(func);
551 /* FIXME: Remove after it is fixed in the Boot ROM upgrade */
552 func->enable_timeout = 10;
553
554 /* In our HW, setting the block size also wakes up the boot rom. */
555 ret = sdio_set_block_size(func, priv->dbg.block_size);
556 if (ret) {
557 LOG_ERROR(priv, INIT,
558 "sdio_set_block_size() failure: %d\n", ret);
559 goto error_sdio_enable;
560 }
561
562 ret = sdio_enable_func(func);
563 if (ret) {
564 LOG_ERROR(priv, INIT, "sdio_enable_func() failure: %d\n", ret);
565 goto error_sdio_enable;
566 }
567
568 /* init reset and dev_sync states */
569 atomic_set(&priv->reset, 0);
570 atomic_set(&priv->dev_sync, 0);
571
572 /* init read req queue */
573 INIT_LIST_HEAD(&priv->read_req_list);
574
575 /* process configurable parameters */
576 iwmct_dbg_init_params(priv);
577 ret = sysfs_create_group(&func->dev.kobj, &iwmct_attribute_group);
578 if (ret) {
579 LOG_ERROR(priv, INIT, "Failed to register attributes and "
580 "initialize module_params\n");
581 goto error_dev_attrs;
582 }
583
584 iwmct_dbgfs_register(priv, DRV_NAME);
585
586 if (!priv->dbg.direct && priv->dbg.download_trans_blks > 8) {
587 LOG_INFO(priv, INIT,
588 "Reducing transaction to 8 blocks = 2K (from %d)\n",
589 priv->dbg.download_trans_blks);
590 priv->dbg.download_trans_blks = 8;
591 }
592 priv->trans_len = priv->dbg.download_trans_blks * priv->dbg.block_size;
593 LOG_INFO(priv, INIT, "Transaction length = %d\n", priv->trans_len);
594
595 ret = sdio_claim_irq(func, iwmct_irq);
596 if (ret) {
597 LOG_ERROR(priv, INIT, "sdio_claim_irq() failure: %d\n", ret);
598 goto error_claim_irq;
599 }
600
601
602 /* Enable function's interrupt */
603 sdio_writeb(priv->func, val, addr, &ret);
604 if (ret) {
605 LOG_ERROR(priv, INIT, "Failure writing to "
606 "Interrupt Enable Register (%d): %d\n", addr, ret);
607 goto error_enable_int;
608 }
609
610 sdio_release_host(func);
611
612 LOG_INFO(priv, INIT, "exit iwmct_probe\n");
613
614 return ret;
615
616error_enable_int:
617 sdio_release_irq(func);
618error_claim_irq:
619 sdio_disable_func(func);
620error_dev_attrs:
621 iwmct_dbgfs_unregister(priv->dbgfs);
622 sysfs_remove_group(&func->dev.kobj, &iwmct_attribute_group);
623error_sdio_enable:
624 sdio_release_host(func);
625 return ret;
626}
627
628static void iwmct_remove(struct sdio_func *func)
629{
630 struct iwmct_work_struct *read_req;
631 struct iwmct_priv *priv = sdio_get_drvdata(func);
632
633 priv = sdio_get_drvdata(func);
634
635 LOG_INFO(priv, INIT, "enter\n");
636
637 sdio_claim_host(func);
638 sdio_release_irq(func);
639 sdio_release_host(func);
640
641 /* Safely destroy osc workqueue */
642 destroy_workqueue(priv->bus_rescan_wq);
643 destroy_workqueue(priv->wq);
644
645 sdio_claim_host(func);
646 sdio_disable_func(func);
647 sysfs_remove_group(&func->dev.kobj, &iwmct_attribute_group);
648 iwmct_dbgfs_unregister(priv->dbgfs);
649 sdio_release_host(func);
650
651 /* free read requests */
652 while (!list_empty(&priv->read_req_list)) {
653 read_req = list_entry(priv->read_req_list.next,
654 struct iwmct_work_struct, list);
655
656 list_del(&read_req->list);
657 kfree(read_req);
658 }
659
660 kfree(priv);
661}
662
663
664static const struct sdio_device_id iwmct_ids[] = {
665 { SDIO_DEVICE(SDIO_INTEL_VENDOR_ID, SDIO_DEVICE_ID_INTEL_IWMC3200TOP)},
666 { /* end: all zeroes */ },
667};
668
669MODULE_DEVICE_TABLE(sdio, iwmct_ids);
670
671static struct sdio_driver iwmct_driver = {
672 .probe = iwmct_probe,
673 .remove = iwmct_remove,
674 .name = DRV_NAME,
675 .id_table = iwmct_ids,
676};
677
678static int __init iwmct_init(void)
679{
680 int rc;
681
682 /* Default log filter settings */
683 iwmct_log_set_filter(LOG_SRC_ALL, LOG_SEV_FILTER_RUNTIME);
684 iwmct_log_set_filter(LOG_SRC_FW_MSG, LOG_SEV_FILTER_ALL);
685 iwmct_log_set_fw_filter(LOG_SRC_ALL, FW_LOG_SEV_FILTER_RUNTIME);
686
687 rc = sdio_register_driver(&iwmct_driver);
688
689 return rc;
690}
691
692static void __exit iwmct_exit(void)
693{
694 sdio_unregister_driver(&iwmct_driver);
695}
696
697module_init(iwmct_init);
698module_exit(iwmct_exit);
699