diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-08 07:55:01 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-08 07:55:01 -0800 |
commit | d7fc02c7bae7b1cf69269992cf880a43a350cdaa (patch) | |
tree | a43d56fa72913a1cc98a0bbebe054d08581b3a7c /drivers/misc | |
parent | ee1262dbc65ce0b6234a915d8432171e8d77f518 (diff) | |
parent | 28b4d5cc17c20786848cdc07b7ea237a309776bb (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6: (1815 commits)
mac80211: fix reorder buffer release
iwmc3200wifi: Enable wimax core through module parameter
iwmc3200wifi: Add wifi-wimax coexistence mode as a module parameter
iwmc3200wifi: Coex table command does not expect a response
iwmc3200wifi: Update wiwi priority table
iwlwifi: driver version track kernel version
iwlwifi: indicate uCode type when fail dump error/event log
iwl3945: remove duplicated event logging code
b43: fix two warnings
ipw2100: fix rebooting hang with driver loaded
cfg80211: indent regulatory messages with spaces
iwmc3200wifi: fix NULL pointer dereference in pmkid update
mac80211: Fix TX status reporting for injected data frames
ath9k: enable 2GHz band only if the device supports it
airo: Fix integer overflow warning
rt2x00: Fix padding bug on L2PAD devices.
WE: Fix set events not propagated
b43legacy: avoid PPC fault during resume
b43: avoid PPC fault during resume
tcp: fix a timewait refcnt race
...
Fix up conflicts due to sysctl cleanups (dead sysctl_check code and
CTL_UNNUMBERED removed) in
kernel/sysctl_check.c
net/ipv4/sysctl_net_ipv4.c
net/ipv6/addrconf.c
net/sctp/sysctl.c
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/Kconfig | 1 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/iwmc3200top/Kconfig | 20 | ||||
-rw-r--r-- | drivers/misc/iwmc3200top/Makefile | 29 | ||||
-rw-r--r-- | drivers/misc/iwmc3200top/debugfs.c | 133 | ||||
-rw-r--r-- | drivers/misc/iwmc3200top/debugfs.h | 58 | ||||
-rw-r--r-- | drivers/misc/iwmc3200top/fw-download.c | 355 | ||||
-rw-r--r-- | drivers/misc/iwmc3200top/fw-msg.h | 113 | ||||
-rw-r--r-- | drivers/misc/iwmc3200top/iwmc3200top.h | 209 | ||||
-rw-r--r-- | drivers/misc/iwmc3200top/log.c | 347 | ||||
-rw-r--r-- | drivers/misc/iwmc3200top/log.h | 158 | ||||
-rw-r--r-- | drivers/misc/iwmc3200top/main.c | 678 |
12 files changed, 2102 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index df1f86b5c83..a2ea383105a 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 f982d2ecfde..e311267a355 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 00000000000..9e4b88fb57f --- /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 00000000000..fbf53fb4634 --- /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@intel.com> +# - +# +# + +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 00000000000..0c8ea0a1c8a --- /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@intel.com> + * - + * + */ + +#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 00000000000..71d45759b40 --- /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@intel.com> + * - + * + */ + +#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 00000000000..50d431e469f --- /dev/null +++ b/drivers/misc/iwmc3200top/fw-download.c @@ -0,0 +1,355 @@ +/* + * 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@intel.com> + * - + * + */ + +#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 u8 *fw_name = FW_NAME(FW_API_VER); + const struct firmware *raw; + const u8 *pdata; + size_t len; + __le32 addr; + int ret; + + /* clear parser struct */ + memset(&priv->parser, 0, sizeof(struct iwmct_parser)); + + /* get the firmware */ + ret = request_firmware(&raw, fw_name, &priv->func->dev); + if (ret < 0) { + LOG_ERROR(priv, FW_DOWNLOAD, "%s request_firmware failed %d\n", + fw_name, ret); + goto exit; + } + + if (raw->size < sizeof(struct iwmct_fw_sec_hdr)) { + LOG_ERROR(priv, FW_DOWNLOAD, "%s smaller then (%zd) (%zd)\n", + fw_name, sizeof(struct iwmct_fw_sec_hdr), raw->size); + goto exit; + } + + LOG_INFO(priv, FW_DOWNLOAD, "Read firmware '%s'\n", fw_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", fw_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 00000000000..9e26b75bd48 --- /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@intel.com> + * - + * + */ + +#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 00000000000..43bd510e187 --- /dev/null +++ b/drivers/misc/iwmc3200top/iwmc3200top.h @@ -0,0 +1,209 @@ +/* + * 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@intel.com> + * - + * + */ + +#ifndef __IWMC3200TOP_H__ +#define __IWMC3200TOP_H__ + +#include <linux/workqueue.h> + +#define DRV_NAME "iwmc3200top" +#define FW_API_VER 1 +#define _FW_NAME(api) DRV_NAME "." #api ".fw" +#define FW_NAME(api) _FW_NAME(api) + +#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 00000000000..d569279698f --- /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, |