mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
uwb: add the i1480 DFU driver
Add the driver for downloading the firmware to an Intel i1480 device. Signed-off-by: David Vrabel <david.vrabel@csr.com>
This commit is contained in:
committed by
David Vrabel
parent
3b0c5a3818
commit
1ba47da527
@@ -64,4 +64,16 @@ config UWB_WLP
|
||||
This is a common library for drivers that implement
|
||||
networking over UWB.
|
||||
|
||||
config UWB_I1480U
|
||||
tristate "Support for Intel Wireless UWB Link 1480 HWA"
|
||||
depends on UWB_HWA
|
||||
select FW_LOADER
|
||||
help
|
||||
This driver enables support for the i1480 when connected via
|
||||
USB. It consists of a firmware uploader that will enable it
|
||||
to behave as an HWA device.
|
||||
|
||||
To compile this driver select Y (built in) or M (module). It
|
||||
is safe to select any even if you do not have the hardware.
|
||||
|
||||
endif # UWB
|
||||
|
||||
@@ -2,6 +2,7 @@ obj-$(CONFIG_UWB) += uwb.o
|
||||
obj-$(CONFIG_UWB_WLP) += wlp/
|
||||
obj-$(CONFIG_UWB_WHCI) += umc.o whci.o whc-rc.o
|
||||
obj-$(CONFIG_UWB_HWA) += hwa-rc.o
|
||||
obj-$(CONFIG_UWB_I1480U) += i1480/
|
||||
|
||||
uwb-objs := \
|
||||
address.o \
|
||||
|
||||
1
drivers/uwb/i1480/Makefile
Normal file
1
drivers/uwb/i1480/Makefile
Normal file
@@ -0,0 +1 @@
|
||||
obj-$(CONFIG_UWB_I1480U) += dfu/ i1480-est.o
|
||||
9
drivers/uwb/i1480/dfu/Makefile
Normal file
9
drivers/uwb/i1480/dfu/Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
obj-$(CONFIG_UWB_I1480U) += i1480-dfu-usb.o
|
||||
|
||||
i1480-dfu-usb-objs := \
|
||||
dfu.o \
|
||||
mac.o \
|
||||
phy.o \
|
||||
usb.o
|
||||
|
||||
|
||||
281
drivers/uwb/i1480/dfu/dfu.c
Normal file
281
drivers/uwb/i1480/dfu/dfu.c
Normal file
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* Intel Wireless UWB Link 1480
|
||||
* Main driver
|
||||
*
|
||||
* Copyright (C) 2005-2006 Intel Corporation
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* Common code for firmware upload used by the USB and PCI version;
|
||||
* i1480_fw_upload() takes a device descriptor and uses the function
|
||||
* pointers it provides to upload firmware and prepare the PHY.
|
||||
*
|
||||
* As well, provides common functions used by the rest of the code.
|
||||
*/
|
||||
#include "i1480-dfu.h"
|
||||
#include <linux/errno.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/uwb.h>
|
||||
#include <linux/random.h>
|
||||
|
||||
#define D_LOCAL 0
|
||||
#include <linux/uwb/debug.h>
|
||||
|
||||
/** @return 0 if If @evt is a valid reply event; otherwise complain */
|
||||
int i1480_rceb_check(const struct i1480 *i1480, const struct uwb_rceb *rceb,
|
||||
const char *cmd, u8 context,
|
||||
unsigned expected_type, unsigned expected_event)
|
||||
{
|
||||
int result = 0;
|
||||
struct device *dev = i1480->dev;
|
||||
if (rceb->bEventContext != context) {
|
||||
dev_err(dev, "%s: "
|
||||
"unexpected context id 0x%02x (expected 0x%02x)\n",
|
||||
cmd, rceb->bEventContext, context);
|
||||
result = -EINVAL;
|
||||
}
|
||||
if (rceb->bEventType != expected_type) {
|
||||
dev_err(dev, "%s: "
|
||||
"unexpected event type 0x%02x (expected 0x%02x)\n",
|
||||
cmd, rceb->bEventType, expected_type);
|
||||
result = -EINVAL;
|
||||
}
|
||||
if (le16_to_cpu(rceb->wEvent) != expected_event) {
|
||||
dev_err(dev, "%s: "
|
||||
"unexpected event 0x%04x (expected 0x%04x)\n",
|
||||
cmd, le16_to_cpu(rceb->wEvent), expected_event);
|
||||
result = -EINVAL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i1480_rceb_check);
|
||||
|
||||
|
||||
/**
|
||||
* Execute a Radio Control Command
|
||||
*
|
||||
* Command data has to be in i1480->cmd_buf.
|
||||
*
|
||||
* @returns size of the reply data filled in i1480->evt_buf or < 0 errno
|
||||
* code on error.
|
||||
*/
|
||||
ssize_t i1480_cmd(struct i1480 *i1480, const char *cmd_name, size_t cmd_size,
|
||||
size_t reply_size)
|
||||
{
|
||||
ssize_t result;
|
||||
struct uwb_rceb *reply = i1480->evt_buf;
|
||||
struct uwb_rccb *cmd = i1480->cmd_buf;
|
||||
u16 expected_event = reply->wEvent;
|
||||
u8 expected_type = reply->bEventType;
|
||||
u8 context;
|
||||
|
||||
d_fnstart(3, i1480->dev, "(%p, %s, %zu)\n", i1480, cmd_name, cmd_size);
|
||||
init_completion(&i1480->evt_complete);
|
||||
i1480->evt_result = -EINPROGRESS;
|
||||
do {
|
||||
get_random_bytes(&context, 1);
|
||||
} while (context == 0x00 || context == 0xff);
|
||||
cmd->bCommandContext = context;
|
||||
result = i1480->cmd(i1480, cmd_name, cmd_size);
|
||||
if (result < 0)
|
||||
goto error;
|
||||
/* wait for the callback to report a event was received */
|
||||
result = wait_for_completion_interruptible_timeout(
|
||||
&i1480->evt_complete, HZ);
|
||||
if (result == 0) {
|
||||
result = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
if (result < 0)
|
||||
goto error;
|
||||
result = i1480->evt_result;
|
||||
if (result < 0) {
|
||||
dev_err(i1480->dev, "%s: command reply reception failed: %zd\n",
|
||||
cmd_name, result);
|
||||
goto error;
|
||||
}
|
||||
if (result != reply_size) {
|
||||
dev_err(i1480->dev, "%s returned only %zu bytes, %zu expected\n",
|
||||
cmd_name, result, reply_size);
|
||||
result = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
/* Verify we got the right event in response */
|
||||
result = i1480_rceb_check(i1480, i1480->evt_buf, cmd_name, context,
|
||||
expected_type, expected_event);
|
||||
error:
|
||||
d_fnend(3, i1480->dev, "(%p, %s, %zu) = %zd\n",
|
||||
i1480, cmd_name, cmd_size, result);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i1480_cmd);
|
||||
|
||||
|
||||
/**
|
||||
* Get information about the MAC and PHY
|
||||
*
|
||||
* @wa: Wired adaptor
|
||||
* @neh: Notification/event handler
|
||||
* @reply: Pointer to the reply event buffer
|
||||
* @returns: 0 if ok, < 0 errno code on error.
|
||||
*/
|
||||
static
|
||||
int i1480_cmd_get_mac_phy_info(struct i1480 *i1480)
|
||||
{
|
||||
int result;
|
||||
struct uwb_rccb *cmd = i1480->cmd_buf;
|
||||
struct i1480_evt_confirm_GMPI *reply = i1480->evt_buf;
|
||||
|
||||
cmd->bCommandType = i1480_CET_VS1;
|
||||
cmd->wCommand = cpu_to_le16(i1480_CMD_GET_MAC_PHY_INFO);
|
||||
reply->rceb.bEventType = i1480_CET_VS1;
|
||||
reply->rceb.wEvent = i1480_EVT_GET_MAC_PHY_INFO;
|
||||
result = i1480_cmd(i1480, "GET_MAC_PHY_INFO", sizeof(*cmd),
|
||||
sizeof(*reply));
|
||||
if (result < 0)
|
||||
goto out;
|
||||
if (le16_to_cpu(reply->status) != 0x00) {
|
||||
dev_err(i1480->dev,
|
||||
"GET_MAC_PHY_INFO: command execution failed: %d\n",
|
||||
reply->status);
|
||||
result = -EIO;
|
||||
}
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get i1480's info and print it
|
||||
*
|
||||
* @wa: Wire Adapter
|
||||
* @neh: Notification/event handler
|
||||
* @returns: 0 if ok, < 0 errno code on error.
|
||||
*/
|
||||
static
|
||||
int i1480_check_info(struct i1480 *i1480)
|
||||
{
|
||||
struct i1480_evt_confirm_GMPI *reply = i1480->evt_buf;
|
||||
int result;
|
||||
unsigned mac_fw_rev;
|
||||
#if i1480_FW <= 0x00000302
|
||||
unsigned phy_fw_rev;
|
||||
#endif
|
||||
if (i1480->quirk_no_check_info) {
|
||||
dev_err(i1480->dev, "firmware info check disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
result = i1480_cmd_get_mac_phy_info(i1480);
|
||||
if (result < 0) {
|
||||
dev_err(i1480->dev, "Cannot get MAC & PHY information: %d\n",
|
||||
result);
|
||||
goto out;
|
||||
}
|
||||
mac_fw_rev = le16_to_cpu(reply->mac_fw_rev);
|
||||
#if i1480_FW > 0x00000302
|
||||
dev_info(i1480->dev,
|
||||
"HW v%02hx "
|
||||
"MAC FW v%02hx.%02hx caps %04hx "
|
||||
"PHY type %02hx v%02hx caps %02hx %02hx %02hx\n",
|
||||
reply->hw_rev, mac_fw_rev >> 8, mac_fw_rev & 0xff,
|
||||
le16_to_cpu(reply->mac_caps),
|
||||
reply->phy_vendor, reply->phy_rev,
|
||||
reply->phy_caps[0], reply->phy_caps[1], reply->phy_caps[2]);
|
||||
#else
|
||||
phy_fw_rev = le16_to_cpu(reply->phy_fw_rev);
|
||||
dev_info(i1480->dev, "MAC FW v%02hx.%02hx caps %04hx "
|
||||
" PHY FW v%02hx.%02hx caps %04hx\n",
|
||||
mac_fw_rev >> 8, mac_fw_rev & 0xff,
|
||||
le16_to_cpu(reply->mac_caps),
|
||||
phy_fw_rev >> 8, phy_fw_rev & 0xff,
|
||||
le16_to_cpu(reply->phy_caps));
|
||||
#endif
|
||||
dev_dbg(i1480->dev,
|
||||
"key-stores:%hu mcast-addr-stores:%hu sec-modes:%hu\n",
|
||||
(unsigned short) reply->key_stores,
|
||||
le16_to_cpu(reply->mcast_addr_stores),
|
||||
(unsigned short) reply->sec_mode_supported);
|
||||
/* FIXME: complain if fw version too low -- pending for
|
||||
* numbering to stabilize */
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
int i1480_print_state(struct i1480 *i1480)
|
||||
{
|
||||
int result;
|
||||
u32 *buf = (u32 *) i1480->cmd_buf;
|
||||
|
||||
result = i1480->read(i1480, 0x80080000, 2 * sizeof(*buf));
|
||||
if (result < 0) {
|
||||
dev_err(i1480->dev, "cannot read U & L states: %d\n", result);
|
||||
goto error;
|
||||
}
|
||||
dev_info(i1480->dev, "state U 0x%08x, L 0x%08x\n", buf[0], buf[1]);
|
||||
error:
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PCI probe, firmware uploader
|
||||
*
|
||||
* _mac_fw_upload() will call rc_setup(), which needs an rc_release().
|
||||
*/
|
||||
int i1480_fw_upload(struct i1480 *i1480)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = i1480_pre_fw_upload(i1480); /* PHY pre fw */
|
||||
if (result < 0 && result != -ENOENT) {
|
||||
i1480_print_state(i1480);
|
||||
goto error;
|
||||
}
|
||||
result = i1480_mac_fw_upload(i1480); /* MAC fw */
|
||||
if (result < 0) {
|
||||
if (result == -ENOENT)
|
||||
dev_err(i1480->dev, "Cannot locate MAC FW file '%s'\n",
|
||||
i1480->mac_fw_name);
|
||||
else
|
||||
i1480_print_state(i1480);
|
||||
goto error;
|
||||
}
|
||||
result = i1480_phy_fw_upload(i1480); /* PHY fw */
|
||||
if (result < 0 && result != -ENOENT) {
|
||||
i1480_print_state(i1480);
|
||||
goto error_rc_release;
|
||||
}
|
||||
result = i1480_check_info(i1480);
|
||||
if (result < 0) {
|
||||
dev_warn(i1480->dev, "Warning! Cannot check firmware info: %d\n",
|
||||
result);
|
||||
result = 0;
|
||||
}
|
||||
dev_info(i1480->dev, "firmware uploaded successfully\n");
|
||||
error_rc_release:
|
||||
if (i1480->rc_release)
|
||||
i1480->rc_release(i1480);
|
||||
result = 0;
|
||||
error:
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i1480_fw_upload);
|
||||
263
drivers/uwb/i1480/dfu/i1480-dfu.h
Normal file
263
drivers/uwb/i1480/dfu/i1480-dfu.h
Normal file
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* i1480 Device Firmware Upload
|
||||
*
|
||||
* Copyright (C) 2005-2006 Intel Corporation
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* This driver is the firmware uploader for the Intel Wireless UWB
|
||||
* Link 1480 device (both in the USB and PCI incarnations).
|
||||
*
|
||||
* The process is quite simple: we stop the device, write the firmware
|
||||
* to its memory and then restart it. Wait for the device to let us
|
||||
* know it is done booting firmware. Ready.
|
||||
*
|
||||
* We might have to upload before or after a phy firmware (which might
|
||||
* be done in two methods, using a normal firmware image or through
|
||||
* the MPI port).
|
||||
*
|
||||
* Because USB and PCI use common methods, we just make ops out of the
|
||||
* common operations (read, write, wait_init_done and cmd) and
|
||||
* implement them in usb.c and pci.c.
|
||||
*
|
||||
* The flow is (some parts omitted):
|
||||
*
|
||||
* i1480_{usb,pci}_probe() On enumerate/discovery
|
||||
* i1480_fw_upload()
|
||||
* i1480_pre_fw_upload()
|
||||
* __mac_fw_upload()
|
||||
* fw_hdrs_load()
|
||||
* mac_fw_hdrs_push()
|
||||
* i1480->write() [i1480_{usb,pci}_write()]
|
||||
* i1480_fw_cmp()
|
||||
* i1480->read() [i1480_{usb,pci}_read()]
|
||||
* i1480_mac_fw_upload()
|
||||
* __mac_fw_upload()
|
||||
* i1480->setup(()
|
||||
* i1480->wait_init_done()
|
||||
* i1480_cmd_reset()
|
||||
* i1480->cmd() [i1480_{usb,pci}_cmd()]
|
||||
* ...
|
||||
* i1480_phy_fw_upload()
|
||||
* request_firmware()
|
||||
* i1480_mpi_write()
|
||||
* i1480->cmd() [i1480_{usb,pci}_cmd()]
|
||||
* i1480_check_info()
|
||||
*
|
||||
* Once the probe function enumerates the device and uploads the
|
||||
* firmware, we just exit with -ENODEV, as we don't really want to
|
||||
* attach to the device.
|
||||
*/
|
||||
#ifndef __i1480_DFU_H__
|
||||
#define __i1480_DFU_H__
|
||||
|
||||
#include <linux/uwb/spec.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/completion.h>
|
||||
|
||||
#define i1480_FW_UPLOAD_MODE_MASK (cpu_to_le32(0x00000018))
|
||||
|
||||
#if i1480_FW > 0x00000302
|
||||
#define i1480_RCEB_EXTENDED
|
||||
#endif
|
||||
|
||||
struct uwb_rccb;
|
||||
struct uwb_rceb;
|
||||
|
||||
/*
|
||||
* Common firmware upload handlers
|
||||
*
|
||||
* Normally you embed this struct in another one specific to your hw.
|
||||
*
|
||||
* @write Write to device's memory from buffer.
|
||||
* @read Read from device's memory to i1480->evt_buf.
|
||||
* @setup Setup device after basic firmware is uploaded
|
||||
* @wait_init_done
|
||||
* Wait for the device to send a notification saying init
|
||||
* is done.
|
||||
* @cmd FOP for issuing the command to the hardware. The
|
||||
* command data is contained in i1480->cmd_buf and the size
|
||||
* is supplied as an argument. The command replied is put
|
||||
* in i1480->evt_buf and the size in i1480->evt_result (or if
|
||||
* an error, a < 0 errno code).
|
||||
*
|
||||
* @cmd_buf Memory buffer used to send commands to the device.
|
||||
* Allocated by the upper layers i1480_fw_upload().
|
||||
* Size has to be @buf_size.
|
||||
* @evt_buf Memory buffer used to place the async notifications
|
||||
* received by the hw. Allocated by the upper layers
|
||||
* i1480_fw_upload().
|
||||
* Size has to be @buf_size.
|
||||
* @cmd_complete
|
||||
* Low level driver uses this to notify code waiting afor
|
||||
* an event that the event has arrived and data is in
|
||||
* i1480->evt_buf (and size/result in i1480->evt_result).
|
||||
* @hw_rev
|
||||
* Use this value to activate dfu code to support new revisions
|
||||
* of hardware. i1480_init() sets this to a default value.
|
||||
* It should be updated by the USB and PCI code.
|
||||
*/
|
||||
struct i1480 {
|
||||
struct device *dev;
|
||||
|
||||
int (*write)(struct i1480 *, u32 addr, const void *, size_t);
|
||||
int (*read)(struct i1480 *, u32 addr, size_t);
|
||||
int (*rc_setup)(struct i1480 *);
|
||||
void (*rc_release)(struct i1480 *);
|
||||
int (*wait_init_done)(struct i1480 *);
|
||||
int (*cmd)(struct i1480 *, const char *cmd_name, size_t cmd_size);
|
||||
const char *pre_fw_name;
|
||||
const char *mac_fw_name;
|
||||
const char *mac_fw_name_deprecate; /* FIXME: Will go away */
|
||||
const char *phy_fw_name;
|
||||
u8 hw_rev;
|
||||
|
||||
size_t buf_size; /* size of both evt_buf and cmd_buf */
|
||||
void *evt_buf, *cmd_buf;
|
||||
ssize_t evt_result;
|
||||
struct completion evt_complete;
|
||||
|
||||
u8 quirk_no_check_info:1;
|
||||
};
|
||||
|
||||
static inline
|
||||
void i1480_init(struct i1480 *i1480)
|
||||
{
|
||||
i1480->hw_rev = 1;
|
||||
init_completion(&i1480->evt_complete);
|
||||
}
|
||||
|
||||
extern int i1480_fw_upload(struct i1480 *);
|
||||
extern int i1480_pre_fw_upload(struct i1480 *);
|
||||
extern int i1480_mac_fw_upload(struct i1480 *);
|
||||
extern int i1480_phy_fw_upload(struct i1480 *);
|
||||
extern ssize_t i1480_cmd(struct i1480 *, const char *, size_t, size_t);
|
||||
extern int i1480_rceb_check(const struct i1480 *,
|
||||
const struct uwb_rceb *, const char *, u8,
|
||||
unsigned, unsigned);
|
||||
|
||||
enum {
|
||||
/* Vendor specific command type */
|
||||
i1480_CET_VS1 = 0xfd,
|
||||
/* i1480 commands */
|
||||
i1480_CMD_SET_IP_MAS = 0x000e,
|
||||
i1480_CMD_GET_MAC_PHY_INFO = 0x0003,
|
||||
i1480_CMD_MPI_WRITE = 0x000f,
|
||||
i1480_CMD_MPI_READ = 0x0010,
|
||||
/* i1480 events */
|
||||
#if i1480_FW > 0x00000302
|
||||
i1480_EVT_CONFIRM = 0x0002,
|
||||
i1480_EVT_RM_INIT_DONE = 0x0101,
|
||||
i1480_EVT_DEV_ADD = 0x0103,
|
||||
i1480_EVT_DEV_RM = 0x0104,
|
||||
i1480_EVT_DEV_ID_CHANGE = 0x0105,
|
||||
i1480_EVT_GET_MAC_PHY_INFO = i1480_CMD_GET_MAC_PHY_INFO,
|
||||
#else
|
||||
i1480_EVT_CONFIRM = 0x0002,
|
||||
i1480_EVT_RM_INIT_DONE = 0x0101,
|
||||
i1480_EVT_DEV_ADD = 0x0103,
|
||||
i1480_EVT_DEV_RM = 0x0104,
|
||||
i1480_EVT_DEV_ID_CHANGE = 0x0105,
|
||||
i1480_EVT_GET_MAC_PHY_INFO = i1480_EVT_CONFIRM,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
struct i1480_evt_confirm {
|
||||
struct uwb_rceb rceb;
|
||||
#ifdef i1480_RCEB_EXTENDED
|
||||
__le16 wParamLength;
|
||||
#endif
|
||||
u8 bResultCode;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
struct i1480_rceb {
|
||||
struct uwb_rceb rceb;
|
||||
#ifdef i1480_RCEB_EXTENDED
|
||||
__le16 wParamLength;
|
||||
#endif
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
/**
|
||||
* Get MAC & PHY Information confirm event structure
|
||||
*
|
||||
* Confirm event returned by the command.
|
||||
*/
|
||||
struct i1480_evt_confirm_GMPI {
|
||||
#if i1480_FW > 0x00000302
|
||||
struct uwb_rceb rceb;
|
||||
__le16 wParamLength;
|
||||
__le16 status;
|
||||
u8 mac_addr[6]; /* EUI-64 bit IEEE address [still 8 bytes?] */
|
||||
u8 dev_addr[2];
|
||||
__le16 mac_fw_rev; /* major = v >> 8; minor = v & 0xff */
|
||||
u8 hw_rev;
|
||||
u8 phy_vendor;
|
||||
u8 phy_rev; /* major v = >> 8; minor = v & 0xff */
|
||||
__le16 mac_caps;
|
||||
u8 phy_caps[3];
|
||||
u8 key_stores;
|
||||
__le16 mcast_addr_stores;
|
||||
u8 sec_mode_supported;
|
||||
#else
|
||||
struct uwb_rceb rceb;
|
||||
u8 status;
|
||||
u8 mac_addr[8]; /* EUI-64 bit IEEE address [still 8 bytes?] */
|
||||
u8 dev_addr[2];
|
||||
__le16 mac_fw_rev; /* major = v >> 8; minor = v & 0xff */
|
||||
__le16 phy_fw_rev; /* major v = >> 8; minor = v & 0xff */
|
||||
__le16 mac_caps;
|
||||
u8 phy_caps;
|
||||
u8 key_stores;
|
||||
__le16 mcast_addr_stores;
|
||||
u8 sec_mode_supported;
|
||||
#endif
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
struct i1480_cmd_mpi_write {
|
||||
struct uwb_rccb rccb;
|
||||
__le16 size;
|
||||
u8 data[];
|
||||
};
|
||||
|
||||
|
||||
struct i1480_cmd_mpi_read {
|
||||
struct uwb_rccb rccb;
|
||||
__le16 size;
|
||||
struct {
|
||||
u8 page, offset;
|
||||
} __attribute__((packed)) data[];
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
struct i1480_evt_mpi_read {
|
||||
struct uwb_rceb rceb;
|
||||
#ifdef i1480_RCEB_EXTENDED
|
||||
__le16 wParamLength;
|
||||
#endif
|
||||
u8 bResultCode;
|
||||
__le16 size;
|
||||
struct {
|
||||
u8 page, offset, value;
|
||||
} __attribute__((packed)) data[];
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
#endif /* #ifndef __i1480_DFU_H__ */
|
||||
529
drivers/uwb/i1480/dfu/mac.c
Normal file
529
drivers/uwb/i1480/dfu/mac.c
Normal file
File diff suppressed because it is too large
Load Diff
203
drivers/uwb/i1480/dfu/phy.c
Normal file
203
drivers/uwb/i1480/dfu/phy.c
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Intel Wireless UWB Link 1480
|
||||
* PHY parameters upload
|
||||
*
|
||||
* Copyright (C) 2005-2006 Intel Corporation
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* Code for uploading the PHY parameters to the PHY through the UWB
|
||||
* Radio Control interface.
|
||||
*
|
||||
* We just send the data through the MPI interface using HWA-like
|
||||
* commands and then reset the PHY to make sure it is ok.
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/usb/wusb.h>
|
||||
#include "i1480-dfu.h"
|
||||
|
||||
|
||||
/**
|
||||
* Write a value array to an address of the MPI interface
|
||||
*
|
||||
* @i1480: Device descriptor
|
||||
* @data: Data array to write
|
||||
* @size: Size of the data array
|
||||
* @returns: 0 if ok, < 0 errno code on error.
|
||||
*
|
||||
* The data array is organized into pairs:
|
||||
*
|
||||
* ADDRESS VALUE
|
||||
*
|
||||
* ADDRESS is BE 16 bit unsigned, VALUE 8 bit unsigned. Size thus has
|
||||
* to be a multiple of three.
|
||||
*/
|
||||
static
|
||||
int i1480_mpi_write(struct i1480 *i1480, const void *data, size_t size)
|
||||
{
|
||||
int result;
|
||||
struct i1480_cmd_mpi_write *cmd = i1480->cmd_buf;
|
||||
struct i1480_evt_confirm *reply = i1480->evt_buf;
|
||||
|
||||
BUG_ON(size > 480);
|
||||
result = -ENOMEM;
|
||||
cmd->rccb.bCommandType = i1480_CET_VS1;
|
||||
cmd->rccb.wCommand = cpu_to_le16(i1480_CMD_MPI_WRITE);
|
||||
cmd->size = cpu_to_le16(size);
|
||||
memcpy(cmd->data, data, size);
|
||||
reply->rceb.bEventType = i1480_CET_VS1;
|
||||
reply->rceb.wEvent = i1480_CMD_MPI_WRITE;
|
||||
result = i1480_cmd(i1480, "MPI-WRITE", sizeof(*cmd) + size, sizeof(*reply));
|
||||
if (result < 0)
|
||||
goto out;
|
||||
if (reply->bResultCode != UWB_RC_RES_SUCCESS) {
|
||||
dev_err(i1480->dev, "MPI-WRITE: command execution failed: %d\n",
|
||||
reply->bResultCode);
|
||||
result = -EIO;
|
||||
}
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read a value array to from an address of the MPI interface
|
||||
*
|
||||
* @i1480: Device descriptor
|
||||
* @data: where to place the read array
|
||||
* @srcaddr: Where to read from
|
||||
* @size: Size of the data read array
|
||||
* @returns: 0 if ok, < 0 errno code on error.
|
||||
*
|
||||
* The command data array is organized into pairs ADDR0 ADDR1..., and
|
||||
* the returned data in ADDR0 VALUE0 ADDR1 VALUE1...
|
||||
*
|
||||
* We generate the command array to be a sequential read and then
|
||||
* rearrange the result.
|
||||
*
|
||||
* We use the i1480->cmd_buf for the command, i1480->evt_buf for the reply.
|
||||
*
|
||||
* As the reply has to fit in 512 bytes (i1480->evt_buffer), the max amount
|
||||
* of values we can read is (512 - sizeof(*reply)) / 3
|
||||
*/
|
||||
static
|
||||
int i1480_mpi_read(struct i1480 *i1480, u8 *data, u16 srcaddr, size_t size)
|
||||
{
|
||||
int result;
|
||||
struct i1480_cmd_mpi_read *cmd = i1480->cmd_buf;
|
||||
struct i1480_evt_mpi_read *reply = i1480->evt_buf;
|
||||
unsigned cnt;
|
||||
|
||||
memset(i1480->cmd_buf, 0x69, 512);
|
||||
memset(i1480->evt_buf, 0x69, 512);
|
||||
|
||||
BUG_ON(size > (i1480->buf_size - sizeof(*reply)) / 3);
|
||||
result = -ENOMEM;
|
||||
cmd->rccb.bCommandType = i1480_CET_VS1;
|
||||
cmd->rccb.wCommand = cpu_to_le16(i1480_CMD_MPI_READ);
|
||||
cmd->size = cpu_to_le16(3*size);
|
||||
for (cnt = 0; cnt < size; cnt++) {
|
||||
cmd->data[cnt].page = (srcaddr + cnt) >> 8;
|
||||
cmd->data[cnt].offset = (srcaddr + cnt) & 0xff;
|
||||
}
|
||||
reply->rceb.bEventType = i1480_CET_VS1;
|
||||
reply->rceb.wEvent = i1480_CMD_MPI_READ;
|
||||
result = i1480_cmd(i1480, "MPI-READ", sizeof(*cmd) + 2*size,
|
||||
sizeof(*reply) + 3*size);
|
||||
if (result < 0)
|
||||
goto out;
|
||||
if (reply->bResultCode != UWB_RC_RES_SUCCESS) {
|
||||
dev_err(i1480->dev, "MPI-READ: command execution failed: %d\n",
|
||||
reply->bResultCode);
|
||||
result = -EIO;
|
||||
}
|
||||
for (cnt = 0; cnt < size; cnt++) {
|
||||
if (reply->data[cnt].page != (srcaddr + cnt) >> 8)
|
||||
dev_err(i1480->dev, "MPI-READ: page inconsistency at "
|
||||
"index %u: expected 0x%02x, got 0x%02x\n", cnt,
|
||||
(srcaddr + cnt) >> 8, reply->data[cnt].page);
|
||||
if (reply->data[cnt].offset != ((srcaddr + cnt) & 0x00ff))
|
||||
dev_err(i1480->dev, "MPI-READ: offset inconsistency at "
|
||||
"index %u: expected 0x%02x, got 0x%02x\n", cnt,
|
||||
(srcaddr + cnt) & 0x00ff,
|
||||
reply->data[cnt].offset);
|
||||
data[cnt] = reply->data[cnt].value;
|
||||
}
|
||||
result = 0;
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Upload a PHY firmware, wait for it to start
|
||||
*
|
||||
* @i1480: Device instance
|
||||
* @fw_name: Name of the file that contains the firmware
|
||||
*
|
||||
* We assume the MAC fw is up and running. This means we can use the
|
||||
* MPI interface to write the PHY firmware. Once done, we issue an
|
||||
* MBOA Reset, which will force the MAC to reset and reinitialize the
|
||||
* PHY. If that works, we are ready to go.
|
||||
*
|
||||
* Max packet size for the MPI write is 512, so the max buffer is 480
|
||||
* (which gives us 160 byte triads of MSB, LSB and VAL for the data).
|
||||
*/
|
||||
int i1480_phy_fw_upload(struct i1480 *i1480)
|
||||
{
|
||||
int result;
|
||||
const struct firmware *fw;
|
||||
const char *data_itr, *data_top;
|
||||
const size_t MAX_BLK_SIZE = 480; /* 160 triads */
|
||||
size_t data_size;
|
||||
u8 phy_stat;
|
||||
|
||||
result = request_firmware(&fw, i1480->phy_fw_name, i1480->dev);
|
||||
if (result < 0)
|
||||
goto out;
|
||||
/* Loop writing data in chunks as big as possible until done. */
|
||||
for (data_itr = fw->data, data_top = data_itr + fw->size;
|
||||
data_itr < data_top; data_itr += MAX_BLK_SIZE) {
|
||||
data_size = min(MAX_BLK_SIZE, (size_t) (data_top - data_itr));
|
||||
result = i1480_mpi_write(i1480, data_itr, data_size);
|
||||
if (result < 0)
|
||||
goto error_mpi_write;
|
||||
}
|
||||
/* Read MPI page 0, offset 6; if 0, PHY was initialized correctly. */
|
||||
result = i1480_mpi_read(i1480, &phy_stat, 0x0006, 1);
|
||||
if (result < 0) {
|
||||
dev_err(i1480->dev, "PHY: can't get status: %d\n", result);
|
||||
goto error_mpi_status;
|
||||
}
|
||||
if (phy_stat != 0) {
|
||||
result = -ENODEV;
|
||||
dev_info(i1480->dev, "error, PHY not ready: %u\n", phy_stat);
|
||||
goto error_phy_status;
|
||||
}
|
||||
dev_info(i1480->dev, "PHY fw '%s': uploaded\n", i1480->phy_fw_name);
|
||||
error_phy_status:
|
||||
error_mpi_status:
|
||||
error_mpi_write:
|
||||
release_firmware(fw);
|
||||
if (result < 0)
|
||||
dev_err(i1480->dev, "PHY fw '%s': failed to upload (%d), "
|
||||
"power cycle device\n", i1480->phy_fw_name, result);
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
500
drivers/uwb/i1480/dfu/usb.c
Normal file
500
drivers/uwb/i1480/dfu/usb.c
Normal file
File diff suppressed because it is too large
Load Diff
99
drivers/uwb/i1480/i1480-est.c
Normal file
99
drivers/uwb/i1480/i1480-est.c
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Intel Wireless UWB Link 1480
|
||||
* Event Size tables for Wired Adaptors
|
||||
*
|
||||
* Copyright (C) 2005-2006 Intel Corporation
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* FIXME: docs
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/uwb.h>
|
||||
#include "dfu/i1480-dfu.h"
|
||||
|
||||
|
||||
/** Event size table for wEvents 0x00XX */
|
||||
static struct uwb_est_entry i1480_est_fd00[] = {
|
||||
/* Anybody expecting this response has to use
|
||||
* neh->extra_size to specify the real size that will
|
||||
* come back. */
|
||||
[i1480_EVT_CONFIRM] = { .size = sizeof(struct i1480_evt_confirm) },
|
||||
[i1480_CMD_SET_IP_MAS] = { .size = sizeof(struct i1480_evt_confirm) },
|
||||
#ifdef i1480_RCEB_EXTENDED
|
||||
[0x09] = {
|
||||
.size = sizeof(struct i1480_rceb),
|
||||
.offset = 1 + offsetof(struct i1480_rceb, wParamLength),
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
/** Event size table for wEvents 0x01XX */
|
||||
static struct uwb_est_entry i1480_est_fd01[] = {
|
||||
[0xff & i1480_EVT_RM_INIT_DONE] = { .size = sizeof(struct i1480_rceb) },
|
||||
[0xff & i1480_EVT_DEV_ADD] = { .size = sizeof(struct i1480_rceb) + 9 },
|
||||
[0xff & i1480_EVT_DEV_RM] = { .size = sizeof(struct i1480_rceb) + 9 },
|
||||
[0xff & i1480_EVT_DEV_ID_CHANGE] = {
|
||||
.size = sizeof(struct i1480_rceb) + 2 },
|
||||
};
|
||||
|
||||
static int i1480_est_init(void)
|
||||
{
|
||||
int result = uwb_est_register(i1480_CET_VS1, 0x00, 0x8086, 0x0c3b,
|
||||
i1480_est_fd00,
|
||||
ARRAY_SIZE(i1480_est_fd00));
|
||||
if (result < 0) {
|
||||
printk(KERN_ERR "Can't register EST table fd00: %d\n", result);
|
||||
return result;
|
||||
}
|
||||
result = uwb_est_register(i1480_CET_VS1, 0x01, 0x8086, 0x0c3b,
|
||||
i1480_est_fd01, ARRAY_SIZE(i1480_est_fd01));
|
||||
if (result < 0) {
|
||||
printk(KERN_ERR "Can't register EST table fd01: %d\n", result);
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
module_init(i1480_est_init);
|
||||
|
||||
static void i1480_est_exit(void)
|
||||
{
|
||||
uwb_est_unregister(i1480_CET_VS1, 0x00, 0x8086, 0x0c3b,
|
||||
i1480_est_fd00, ARRAY_SIZE(i1480_est_fd00));
|
||||
uwb_est_unregister(i1480_CET_VS1, 0x01, 0x8086, 0x0c3b,
|
||||
i1480_est_fd01, ARRAY_SIZE(i1480_est_fd01));
|
||||
}
|
||||
module_exit(i1480_est_exit);
|
||||
|
||||
MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
|
||||
MODULE_DESCRIPTION("i1480's Vendor Specific Event Size Tables");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/**
|
||||
* USB device ID's that we handle
|
||||
*
|
||||
* [so we are loaded when this kind device is connected]
|
||||
*/
|
||||
static struct usb_device_id i1480_est_id_table[] = {
|
||||
{ USB_DEVICE(0x8086, 0xdf3b), },
|
||||
{ USB_DEVICE(0x8086, 0x0c3b), },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, i1480_est_id_table);
|
||||
Reference in New Issue
Block a user