Bluetooth: btmrvl: add calibration data download support
authorAmitkumar Karwar <akarwar@marvell.com>
Tue, 1 Oct 2013 19:19:15 +0000 (12:19 -0700)
committerMarcel Holtmann <marcel@holtmann.org>
Wed, 2 Oct 2013 07:36:16 +0000 (00:36 -0700)
A text file containing calibration data in hex format can
be provided at following path:

/lib/firmware/mrvl/sd8797_caldata.conf

The data will be downloaded to firmware during initialization.

Reviewed-by: Mike Frysinger <vapier@chromium.org>
Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Signed-off-by: Bing Zhao <bzhao@marvell.com>
Signed-off-by: Hyuckjoo Lee <hyuckjoo.lee@samsung.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
drivers/bluetooth/btmrvl_drv.h
drivers/bluetooth/btmrvl_main.c
drivers/bluetooth/btmrvl_sdio.c
drivers/bluetooth/btmrvl_sdio.h

index 42f7028d3890a697e692b482005d7042aa6ae98f..f9d183387f4585b37e0642aef9e269e17c6be1a4 100644 (file)
@@ -23,6 +23,8 @@
 #include <linux/bitops.h>
 #include <linux/slab.h>
 #include <net/bluetooth/bluetooth.h>
+#include <linux/ctype.h>
+#include <linux/firmware.h>
 
 #define BTM_HEADER_LEN                 4
 #define BTM_UPLD_SIZE                  2312
@@ -41,6 +43,8 @@ struct btmrvl_thread {
 struct btmrvl_device {
        void *card;
        struct hci_dev *hcidev;
+       struct device *dev;
+       const char *cal_data;
 
        u8 dev_type;
 
@@ -91,6 +95,7 @@ struct btmrvl_private {
 #define BT_CMD_HOST_SLEEP_CONFIG       0x59
 #define BT_CMD_HOST_SLEEP_ENABLE       0x5A
 #define BT_CMD_MODULE_CFG_REQ          0x5B
+#define BT_CMD_LOAD_CONFIG_DATA                0x61
 
 /* Sub-commands: Module Bringup/Shutdown Request/Response */
 #define MODULE_BRINGUP_REQ             0xF1
@@ -116,6 +121,9 @@ struct btmrvl_private {
 #define PS_SLEEP                       0x01
 #define PS_AWAKE                       0x00
 
+#define BT_CMD_DATA_SIZE               32
+#define BT_CAL_DATA_SIZE               28
+
 struct btmrvl_event {
        u8 ec;          /* event counter */
        u8 length;
index e0ae1f4ea4062eb53f8d891c61d79c59c5a2eaf4..6e7bd4e4adbb29f5b3d43ea0f4fcacf2a67a3c3f 100644 (file)
@@ -432,12 +432,128 @@ static int btmrvl_open(struct hci_dev *hdev)
        return 0;
 }
 
+/*
+ * This function parses provided calibration data input. It should contain
+ * hex bytes separated by space or new line character. Here is an example.
+ * 00 1C 01 37 FF FF FF FF 02 04 7F 01
+ * CE BA 00 00 00 2D C6 C0 00 00 00 00
+ * 00 F0 00 00
+ */
+static int btmrvl_parse_cal_cfg(const u8 *src, u32 len, u8 *dst, u32 dst_size)
+{
+       const u8 *s = src;
+       u8 *d = dst;
+       int ret;
+       u8 tmp[3];
+
+       tmp[2] = '\0';
+       while ((s - src) <= len - 2) {
+               if (isspace(*s)) {
+                       s++;
+                       continue;
+               }
+
+               if (isxdigit(*s)) {
+                       if ((d - dst) >= dst_size) {
+                               BT_ERR("calibration data file too big!!!");
+                               return -EINVAL;
+                       }
+
+                       memcpy(tmp, s, 2);
+
+                       ret = kstrtou8(tmp, 16, d++);
+                       if (ret < 0)
+                               return ret;
+
+                       s += 2;
+               } else {
+                       return -EINVAL;
+               }
+       }
+       if (d == dst)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int btmrvl_load_cal_data(struct btmrvl_private *priv,
+                               u8 *config_data)
+{
+       int i, ret;
+       u8 data[BT_CMD_DATA_SIZE];
+
+       data[0] = 0x00;
+       data[1] = 0x00;
+       data[2] = 0x00;
+       data[3] = BT_CMD_DATA_SIZE - 4;
+
+       /* Swap cal-data bytes. Each four bytes are swapped. Considering 4
+        * byte SDIO header offset, mapping of input and output bytes will be
+        * {3, 2, 1, 0} -> {0+4, 1+4, 2+4, 3+4},
+        * {7, 6, 5, 4} -> {4+4, 5+4, 6+4, 7+4} */
+       for (i = 4; i < BT_CMD_DATA_SIZE; i++)
+               data[i] = config_data[(i / 4) * 8 - 1 - i];
+
+       print_hex_dump_bytes("Calibration data: ",
+                            DUMP_PREFIX_OFFSET, data, BT_CMD_DATA_SIZE);
+
+       ret = btmrvl_send_sync_cmd(priv, BT_CMD_LOAD_CONFIG_DATA, data,
+                                  BT_CMD_DATA_SIZE);
+       if (ret)
+               BT_ERR("Failed to download caibration data\n");
+
+       return 0;
+}
+
+static int
+btmrvl_process_cal_cfg(struct btmrvl_private *priv, u8 *data, u32 size)
+{
+       u8 cal_data[BT_CAL_DATA_SIZE];
+       int ret;
+
+       ret = btmrvl_parse_cal_cfg(data, size, cal_data, sizeof(cal_data));
+       if (ret)
+               return ret;
+
+       ret = btmrvl_load_cal_data(priv, cal_data);
+       if (ret) {
+               BT_ERR("Fail to load calibrate data");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int btmrvl_cal_data_config(struct btmrvl_private *priv)
+{
+       const struct firmware *cfg;
+       int ret;
+       const char *cal_data = priv->btmrvl_dev.cal_data;
+
+       if (!cal_data)
+               return 0;
+
+       ret = request_firmware(&cfg, cal_data, priv->btmrvl_dev.dev);
+       if (ret < 0) {
+               BT_DBG("Failed to get %s file, skipping cal data download",
+                      cal_data);
+               return 0;
+       }
+
+       ret = btmrvl_process_cal_cfg(priv, (u8 *)cfg->data, cfg->size);
+       release_firmware(cfg);
+       return ret;
+}
+
 static int btmrvl_setup(struct hci_dev *hdev)
 {
        struct btmrvl_private *priv = hci_get_drvdata(hdev);
 
        btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
 
+       if (btmrvl_cal_data_config(priv))
+               BT_ERR("Set cal data failed");
+
        priv->btmrvl_dev.psmode = 1;
        btmrvl_enable_ps(priv);
 
index 5b70bcb38a5ec81e1cab96029e24222e85c7a704..332475e400cff2f2234c401fb4f1d1a50ba66e0c 100644 (file)
@@ -18,7 +18,6 @@
  * this warranty disclaimer.
  **/
 
-#include <linux/firmware.h>
 #include <linux/slab.h>
 
 #include <linux/mmc/sdio_ids.h>
@@ -102,6 +101,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_88xx = {
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
        .helper         = "mrvl/sd8688_helper.bin",
        .firmware       = "mrvl/sd8688.bin",
+       .cal_data       = NULL,
        .reg            = &btmrvl_reg_8688,
        .sd_blksz_fw_dl = 64,
 };
@@ -109,6 +109,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
        .helper         = NULL,
        .firmware       = "mrvl/sd8787_uapsta.bin",
+       .cal_data       = NULL,
        .reg            = &btmrvl_reg_87xx,
        .sd_blksz_fw_dl = 256,
 };
@@ -116,6 +117,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
        .helper         = NULL,
        .firmware       = "mrvl/sd8797_uapsta.bin",
+       .cal_data       = "mrvl/sd8797_caldata.conf",
        .reg            = &btmrvl_reg_87xx,
        .sd_blksz_fw_dl = 256,
 };
@@ -123,6 +125,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
        .helper         = NULL,
        .firmware       = "mrvl/sd8897_uapsta.bin",
+       .cal_data       = NULL,
        .reg            = &btmrvl_reg_88xx,
        .sd_blksz_fw_dl = 256,
 };
@@ -1006,6 +1009,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
                struct btmrvl_sdio_device *data = (void *) id->driver_data;
                card->helper = data->helper;
                card->firmware = data->firmware;
+               card->cal_data = data->cal_data;
                card->reg = data->reg;
                card->sd_blksz_fw_dl = data->sd_blksz_fw_dl;
        }
@@ -1034,6 +1038,8 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
        }
 
        card->priv = priv;
+       priv->btmrvl_dev.dev = &card->func->dev;
+       priv->btmrvl_dev.cal_data = card->cal_data;
 
        /* Initialize the interface specific function pointers */
        priv->hw_host_to_card = btmrvl_sdio_host_to_card;
@@ -1216,4 +1222,5 @@ MODULE_FIRMWARE("mrvl/sd8688_helper.bin");
 MODULE_FIRMWARE("mrvl/sd8688.bin");
 MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
 MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
+MODULE_FIRMWARE("mrvl/sd8797_caldata.conf");
 MODULE_FIRMWARE("mrvl/sd8897_uapsta.bin");
index 43d35a609ca9a94795afb731d230fa88ca109bef..6872d9ecac074ba04792c22776b3baee3971c9de 100644 (file)
@@ -85,6 +85,7 @@ struct btmrvl_sdio_card {
        u32 ioport;
        const char *helper;
        const char *firmware;
+       const char *cal_data;
        const struct btmrvl_sdio_card_reg *reg;
        u16 sd_blksz_fw_dl;
        u8 rx_unit;
@@ -94,6 +95,7 @@ struct btmrvl_sdio_card {
 struct btmrvl_sdio_device {
        const char *helper;
        const char *firmware;
+       const char *cal_data;
        const struct btmrvl_sdio_card_reg *reg;
        u16 sd_blksz_fw_dl;
 };