staging: wfx: setup initial chip configuration
A few tasks remain to be done in order to finish chip initial configuration: - configure chip to use multi-tx confirmation (speed up data transfer) - configure chip to use wake-up feature (save power consumption during runtime) - set hardware configuration (clocks, RF, pinout, etc...) using a Platform Data Set (PDS) file On release, driver completely shutdown the chip to save power consumption. Documentation about PDS and PDS data for sample boards are available here[1]. One day, PDS data may find a place in device tree but, currently, PDS is too much linked with firmware to allowing that. This patch also add "send_pds" file in debugfs to be able to dynamically change PDS (only for debug, of course). [1]: https://github.com/SiliconLabs/wfx-firmware/tree/master/PDS Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com> Link: https://lore.kernel.org/r/20190919142527.31797-15-Jerome.Pouiller@silabs.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Родитель
846239f641
Коммит
c7ff39dd8b
|
@ -19,6 +19,7 @@
|
|||
|
||||
static const struct wfx_platform_data wfx_sdio_pdata = {
|
||||
.file_fw = "wfm_wf200",
|
||||
.file_pds = "wf200.pds",
|
||||
};
|
||||
|
||||
struct wfx_sdio_priv {
|
||||
|
|
|
@ -29,6 +29,7 @@ MODULE_PARM_DESC(gpio_reset, "gpio number for reset. -1 for none.");
|
|||
|
||||
static const struct wfx_platform_data wfx_spi_pdata = {
|
||||
.file_fw = "wfm_wf200",
|
||||
.file_pds = "wf200.pds",
|
||||
.use_rising_clk = true,
|
||||
};
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "debug.h"
|
||||
#include "wfx.h"
|
||||
#include "main.h"
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "traces.h"
|
||||
|
@ -54,6 +55,33 @@ const char *get_reg_name(unsigned long id)
|
|||
return get_symbol(id, wfx_reg_print_map);
|
||||
}
|
||||
|
||||
static ssize_t wfx_send_pds_write(struct file *file, const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct wfx_dev *wdev = file->private_data;
|
||||
char *buf;
|
||||
int ret;
|
||||
|
||||
if (*ppos != 0) {
|
||||
dev_dbg(wdev->dev, "PDS data must be written in one transaction");
|
||||
return -EBUSY;
|
||||
}
|
||||
buf = memdup_user(user_buf, count);
|
||||
if (IS_ERR(buf))
|
||||
return PTR_ERR(buf);
|
||||
*ppos = *ppos + count;
|
||||
ret = wfx_send_pds(wdev, buf, count);
|
||||
kfree(buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations wfx_send_pds_fops = {
|
||||
.open = simple_open,
|
||||
.write = wfx_send_pds_write,
|
||||
};
|
||||
|
||||
static ssize_t wfx_burn_slk_key_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
|
@ -162,6 +190,7 @@ int wfx_debug_init(struct wfx_dev *wdev)
|
|||
struct dentry *d;
|
||||
|
||||
d = debugfs_create_dir("wfx", wdev->hw->wiphy->debugfsdir);
|
||||
debugfs_create_file("send_pds", 0200, d, wdev, &wfx_send_pds_fops);
|
||||
debugfs_create_file("burn_slk_key", 0200, d, wdev, &wfx_burn_slk_key_fops);
|
||||
debugfs_create_file("send_hif_msg", 0600, d, wdev, &wfx_send_hif_msg_fops);
|
||||
|
||||
|
|
|
@ -71,6 +71,16 @@ static int hif_startup_indication(struct wfx_dev *wdev, struct hif_msg *hif, voi
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int hif_wakeup_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
|
||||
{
|
||||
if (!wdev->pdata.gpio_wakeup
|
||||
|| !gpiod_get_value(wdev->pdata.gpio_wakeup)) {
|
||||
dev_warn(wdev->dev, "unexpected wake-up indication\n");
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hif_keys_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
|
||||
{
|
||||
struct hif_ind_sl_exchange_pub_keys *body = buf;
|
||||
|
@ -89,6 +99,7 @@ static const struct {
|
|||
int (*handler)(struct wfx_dev *wdev, struct hif_msg *hif, void *buf);
|
||||
} hif_handlers[] = {
|
||||
{ HIF_IND_ID_STARTUP, hif_startup_indication },
|
||||
{ HIF_IND_ID_WAKEUP, hif_wakeup_indication },
|
||||
{ HIF_IND_ID_SL_EXCHANGE_PUB_KEYS, hif_keys_indication },
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/mmc/sdio_func.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "wfx.h"
|
||||
|
@ -28,9 +29,12 @@
|
|||
#include "sta.h"
|
||||
#include "debug.h"
|
||||
#include "secure_link.h"
|
||||
#include "hif_tx_mib.h"
|
||||
#include "hif_api_cmd.h"
|
||||
#include "wfx_version.h"
|
||||
|
||||
#define WFX_PDS_MAX_SIZE 1500
|
||||
|
||||
MODULE_DESCRIPTION("Silicon Labs 802.11 Wireless LAN driver for WFx");
|
||||
MODULE_AUTHOR("Jérôme Pouiller <jerome.pouiller@silabs.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -112,6 +116,69 @@ static void wfx_fill_sl_key(struct device *dev, struct wfx_platform_data *pdata)
|
|||
dev_err(dev, "secure link is not supported by this driver, ignoring provided key\n");
|
||||
}
|
||||
|
||||
/* NOTE: wfx_send_pds() destroy buf */
|
||||
int wfx_send_pds(struct wfx_dev *wdev, unsigned char *buf, size_t len)
|
||||
{
|
||||
int ret;
|
||||
int start, brace_level, i;
|
||||
|
||||
start = 0;
|
||||
brace_level = 0;
|
||||
if (buf[0] != '{') {
|
||||
dev_err(wdev->dev, "valid PDS start with '{'. Did you forget to compress it?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 1; i < len - 1; i++) {
|
||||
if (buf[i] == '{')
|
||||
brace_level++;
|
||||
if (buf[i] == '}')
|
||||
brace_level--;
|
||||
if (buf[i] == '}' && !brace_level) {
|
||||
i++;
|
||||
if (i - start + 1 > WFX_PDS_MAX_SIZE)
|
||||
return -EFBIG;
|
||||
buf[start] = '{';
|
||||
buf[i] = 0;
|
||||
dev_dbg(wdev->dev, "send PDS '%s}'\n", buf + start);
|
||||
buf[i] = '}';
|
||||
ret = hif_configuration(wdev, buf + start, i - start + 1);
|
||||
if (ret == HIF_STATUS_FAILURE) {
|
||||
dev_err(wdev->dev, "PDS bytes %d to %d: invalid data (unsupported options?)\n", start, i);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (ret == -ETIMEDOUT) {
|
||||
dev_err(wdev->dev, "PDS bytes %d to %d: chip didn't reply (corrupted file?)\n", start, i);
|
||||
return ret;
|
||||
}
|
||||
if (ret) {
|
||||
dev_err(wdev->dev, "PDS bytes %d to %d: chip returned an unknown error\n", start, i);
|
||||
return -EIO;
|
||||
}
|
||||
buf[i] = ',';
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wfx_send_pdata_pds(struct wfx_dev *wdev)
|
||||
{
|
||||
int ret = 0;
|
||||
const struct firmware *pds;
|
||||
unsigned char *tmp_buf;
|
||||
|
||||
ret = request_firmware(&pds, wdev->pdata.file_pds, wdev->dev);
|
||||
if (ret) {
|
||||
dev_err(wdev->dev, "can't load PDS file %s\n", wdev->pdata.file_pds);
|
||||
return ret;
|
||||
}
|
||||
tmp_buf = kmemdup(pds->data, pds->size, GFP_KERNEL);
|
||||
ret = wfx_send_pds(wdev, tmp_buf, pds->size);
|
||||
kfree(tmp_buf);
|
||||
release_firmware(pds);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct wfx_dev *wfx_init_common(struct device *dev,
|
||||
const struct wfx_platform_data *pdata,
|
||||
const struct hwbus_ops *hwbus_ops,
|
||||
|
@ -141,6 +208,8 @@ struct wfx_dev *wfx_init_common(struct device *dev,
|
|||
wdev->hwbus_ops = hwbus_ops;
|
||||
wdev->hwbus_priv = hwbus_priv;
|
||||
memcpy(&wdev->pdata, pdata, sizeof(*pdata));
|
||||
of_property_read_string(dev->of_node, "config-file", &wdev->pdata.file_pds);
|
||||
wdev->pdata.gpio_wakeup = wfx_get_gpio(dev, gpio_wakeup, "wakeup");
|
||||
wfx_fill_sl_key(dev, &wdev->pdata);
|
||||
|
||||
init_completion(&wdev->firmware_ready);
|
||||
|
@ -159,6 +228,12 @@ int wfx_probe(struct wfx_dev *wdev)
|
|||
int i;
|
||||
int err;
|
||||
const void *macaddr;
|
||||
struct gpio_desc *gpio_saved;
|
||||
|
||||
// During first part of boot, gpio_wakeup cannot yet been used. So
|
||||
// prevent bh() to touch it.
|
||||
gpio_saved = wdev->pdata.gpio_wakeup;
|
||||
wdev->pdata.gpio_wakeup = NULL;
|
||||
|
||||
wfx_bh_register(wdev);
|
||||
|
||||
|
@ -202,6 +277,24 @@ int wfx_probe(struct wfx_dev *wdev)
|
|||
goto err1;
|
||||
}
|
||||
|
||||
dev_dbg(wdev->dev, "sending configuration file %s\n", wdev->pdata.file_pds);
|
||||
err = wfx_send_pdata_pds(wdev);
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
|
||||
wdev->pdata.gpio_wakeup = gpio_saved;
|
||||
if (wdev->pdata.gpio_wakeup) {
|
||||
dev_dbg(wdev->dev, "enable 'quiescent' power mode with gpio %d and PDS file %s\n",
|
||||
desc_to_gpio(wdev->pdata.gpio_wakeup), wdev->pdata.file_pds);
|
||||
gpiod_set_value(wdev->pdata.gpio_wakeup, 1);
|
||||
control_reg_write(wdev, 0);
|
||||
hif_set_operational_mode(wdev, HIF_OP_POWER_MODE_QUIESCENT);
|
||||
} else {
|
||||
hif_set_operational_mode(wdev, HIF_OP_POWER_MODE_DOZE);
|
||||
}
|
||||
|
||||
hif_use_multi_tx_conf(wdev, true);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wdev->addresses); i++) {
|
||||
eth_zero_addr(wdev->addresses[i].addr);
|
||||
macaddr = of_get_mac_address(wdev->dev->of_node);
|
||||
|
@ -232,6 +325,7 @@ err1:
|
|||
|
||||
void wfx_release(struct wfx_dev *wdev)
|
||||
{
|
||||
hif_shutdown(wdev);
|
||||
wfx_bh_unregister(wdev);
|
||||
wfx_sl_deinit(wdev);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ struct wfx_dev;
|
|||
struct wfx_platform_data {
|
||||
/* Keyset and ".sec" extention will appended to this string */
|
||||
const char *file_fw;
|
||||
const char *file_pds;
|
||||
unsigned char slk_key[API_KEY_VALUE_SIZE];
|
||||
struct gpio_desc *gpio_wakeup;
|
||||
/*
|
||||
|
@ -42,5 +43,6 @@ void wfx_release(struct wfx_dev *wdev);
|
|||
struct gpio_desc *wfx_get_gpio(struct device *dev, int override,
|
||||
const char *label);
|
||||
bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor);
|
||||
int wfx_send_pds(struct wfx_dev *wdev, unsigned char *buf, size_t len);
|
||||
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче