linux/drivers/nfc/nfcmrvl/fw_dnld.c
<<
>>
Prefs
   1/*
   2 * Marvell NFC driver: Firmware downloader
   3 *
   4 * Copyright (C) 2015, Marvell International Ltd.
   5 *
   6 * This software file (the "File") is distributed by Marvell International
   7 * Ltd. under the terms of the GNU General Public License Version 2, June 1991
   8 * (the "License").  You may use, redistribute and/or modify this File in
   9 * accordance with the terms and conditions of the License, a copy of which
  10 * is available on the worldwide web at
  11 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
  12 *
  13 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
  14 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
  15 * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
  16 * this warranty disclaimer.
  17 */
  18
  19#include <linux/module.h>
  20#include <asm/unaligned.h>
  21#include <linux/firmware.h>
  22#include <linux/nfc.h>
  23#include <net/nfc/nci.h>
  24#include <net/nfc/nci_core.h>
  25#include "nfcmrvl.h"
  26
  27#define FW_DNLD_TIMEOUT                 15000
  28
  29#define NCI_OP_PROPRIETARY_BOOT_CMD     nci_opcode_pack(NCI_GID_PROPRIETARY, \
  30                                                        NCI_OP_PROP_BOOT_CMD)
  31
  32/* FW download states */
  33
  34enum {
  35        STATE_RESET = 0,
  36        STATE_INIT,
  37        STATE_SET_REF_CLOCK,
  38        STATE_SET_HI_CONFIG,
  39        STATE_OPEN_LC,
  40        STATE_FW_DNLD,
  41        STATE_CLOSE_LC,
  42        STATE_BOOT
  43};
  44
  45enum {
  46        SUBSTATE_WAIT_COMMAND = 0,
  47        SUBSTATE_WAIT_ACK_CREDIT,
  48        SUBSTATE_WAIT_NACK_CREDIT,
  49        SUBSTATE_WAIT_DATA_CREDIT,
  50};
  51
  52/*
  53** Patterns for responses
  54*/
  55
  56static const uint8_t nci_pattern_core_reset_ntf[] = {
  57        0x60, 0x00, 0x02, 0xA0, 0x01
  58};
  59
  60static const uint8_t nci_pattern_core_init_rsp[] = {
  61        0x40, 0x01, 0x11
  62};
  63
  64static const uint8_t nci_pattern_core_set_config_rsp[] = {
  65        0x40, 0x02, 0x02, 0x00, 0x00
  66};
  67
  68static const uint8_t nci_pattern_core_conn_create_rsp[] = {
  69        0x40, 0x04, 0x04, 0x00
  70};
  71
  72static const uint8_t nci_pattern_core_conn_close_rsp[] = {
  73        0x40, 0x05, 0x01, 0x00
  74};
  75
  76static const uint8_t nci_pattern_core_conn_credits_ntf[] = {
  77        0x60, 0x06, 0x03, 0x01, NCI_CORE_LC_CONNID_PROP_FW_DL, 0x01
  78};
  79
  80static const uint8_t nci_pattern_proprietary_boot_rsp[] = {
  81        0x4F, 0x3A, 0x01, 0x00
  82};
  83
  84static struct sk_buff *alloc_lc_skb(struct nfcmrvl_private *priv, uint8_t plen)
  85{
  86        struct sk_buff *skb;
  87        struct nci_data_hdr *hdr;
  88
  89        skb = nci_skb_alloc(priv->ndev, (NCI_DATA_HDR_SIZE + plen), GFP_KERNEL);
  90        if (!skb) {
  91                pr_err("no memory for data\n");
  92                return NULL;
  93        }
  94
  95        hdr = skb_put(skb, NCI_DATA_HDR_SIZE);
  96        hdr->conn_id = NCI_CORE_LC_CONNID_PROP_FW_DL;
  97        hdr->rfu = 0;
  98        hdr->plen = plen;
  99
 100        nci_mt_set((__u8 *)hdr, NCI_MT_DATA_PKT);
 101        nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST);
 102
 103        return skb;
 104}
 105
 106static void fw_dnld_over(struct nfcmrvl_private *priv, u32 error)
 107{
 108        if (priv->fw_dnld.fw) {
 109                release_firmware(priv->fw_dnld.fw);
 110                priv->fw_dnld.fw = NULL;
 111                priv->fw_dnld.header = NULL;
 112                priv->fw_dnld.binary_config = NULL;
 113        }
 114
 115        atomic_set(&priv->ndev->cmd_cnt, 0);
 116
 117        if (timer_pending(&priv->ndev->cmd_timer))
 118                del_timer_sync(&priv->ndev->cmd_timer);
 119
 120        if (timer_pending(&priv->fw_dnld.timer))
 121                del_timer_sync(&priv->fw_dnld.timer);
 122
 123        nfc_info(priv->dev, "FW loading over (%d)]\n", error);
 124
 125        if (error != 0) {
 126                /* failed, halt the chip to avoid power consumption */
 127                nfcmrvl_chip_halt(priv);
 128        }
 129
 130        nfc_fw_download_done(priv->ndev->nfc_dev, priv->fw_dnld.name, error);
 131}
 132
 133static void fw_dnld_timeout(struct timer_list *t)
 134{
 135        struct nfcmrvl_private *priv = from_timer(priv, t, fw_dnld.timer);
 136
 137        nfc_err(priv->dev, "FW loading timeout");
 138        priv->fw_dnld.state = STATE_RESET;
 139        fw_dnld_over(priv, -ETIMEDOUT);
 140}
 141
 142static int process_state_reset(struct nfcmrvl_private *priv,
 143                               struct sk_buff *skb)
 144{
 145        if (sizeof(nci_pattern_core_reset_ntf) != skb->len ||
 146            memcmp(skb->data, nci_pattern_core_reset_ntf,
 147                   sizeof(nci_pattern_core_reset_ntf)))
 148                return -EINVAL;
 149
 150        nfc_info(priv->dev, "BootROM reset, start fw download\n");
 151
 152        /* Start FW download state machine */
 153        priv->fw_dnld.state = STATE_INIT;
 154        nci_send_cmd(priv->ndev, NCI_OP_CORE_INIT_CMD, 0, NULL);
 155
 156        return 0;
 157}
 158
 159static int process_state_init(struct nfcmrvl_private *priv, struct sk_buff *skb)
 160{
 161        struct nci_core_set_config_cmd cmd;
 162
 163        if (sizeof(nci_pattern_core_init_rsp) >= skb->len ||
 164            memcmp(skb->data, nci_pattern_core_init_rsp,
 165                   sizeof(nci_pattern_core_init_rsp)))
 166                return -EINVAL;
 167
 168        cmd.num_params = 1;
 169        cmd.param.id = NFCMRVL_PROP_REF_CLOCK;
 170        cmd.param.len = 4;
 171        memcpy(cmd.param.val, &priv->fw_dnld.header->ref_clock, 4);
 172
 173        nci_send_cmd(priv->ndev, NCI_OP_CORE_SET_CONFIG_CMD, 3 + cmd.param.len,
 174                     &cmd);
 175
 176        priv->fw_dnld.state = STATE_SET_REF_CLOCK;
 177        return 0;
 178}
 179
 180static void create_lc(struct nfcmrvl_private *priv)
 181{
 182        uint8_t param[2] = { NCI_CORE_LC_PROP_FW_DL, 0x0 };
 183
 184        priv->fw_dnld.state = STATE_OPEN_LC;
 185        nci_send_cmd(priv->ndev, NCI_OP_CORE_CONN_CREATE_CMD, 2, param);
 186}
 187
 188static int process_state_set_ref_clock(struct nfcmrvl_private *priv,
 189                                       struct sk_buff *skb)
 190{
 191        struct nci_core_set_config_cmd cmd;
 192
 193        if (sizeof(nci_pattern_core_set_config_rsp) != skb->len ||
 194            memcmp(skb->data, nci_pattern_core_set_config_rsp, skb->len))
 195                return -EINVAL;
 196
 197        cmd.num_params = 1;
 198        cmd.param.id = NFCMRVL_PROP_SET_HI_CONFIG;
 199
 200        switch (priv->phy) {
 201        case NFCMRVL_PHY_UART:
 202                cmd.param.len = 5;
 203                memcpy(cmd.param.val,
 204                       &priv->fw_dnld.binary_config->uart.baudrate,
 205                       4);
 206                cmd.param.val[4] =
 207                        priv->fw_dnld.binary_config->uart.flow_control;
 208                break;
 209        case NFCMRVL_PHY_I2C:
 210                cmd.param.len = 5;
 211                memcpy(cmd.param.val,
 212                       &priv->fw_dnld.binary_config->i2c.clk,
 213                       4);
 214                cmd.param.val[4] = 0;
 215                break;
 216        case NFCMRVL_PHY_SPI:
 217                cmd.param.len = 5;
 218                memcpy(cmd.param.val,
 219                       &priv->fw_dnld.binary_config->spi.clk,
 220                       4);
 221                cmd.param.val[4] = 0;
 222                break;
 223        default:
 224                create_lc(priv);
 225                return 0;
 226        }
 227
 228        priv->fw_dnld.state = STATE_SET_HI_CONFIG;
 229        nci_send_cmd(priv->ndev, NCI_OP_CORE_SET_CONFIG_CMD, 3 + cmd.param.len,
 230                     &cmd);
 231        return 0;
 232}
 233
 234static int process_state_set_hi_config(struct nfcmrvl_private *priv,
 235                                       struct sk_buff *skb)
 236{
 237        if (sizeof(nci_pattern_core_set_config_rsp) != skb->len ||
 238            memcmp(skb->data, nci_pattern_core_set_config_rsp, skb->len))
 239                return -EINVAL;
 240
 241        create_lc(priv);
 242        return 0;
 243}
 244
 245static int process_state_open_lc(struct nfcmrvl_private *priv,
 246                                 struct sk_buff *skb)
 247{
 248        if (sizeof(nci_pattern_core_conn_create_rsp) >= skb->len ||
 249            memcmp(skb->data, nci_pattern_core_conn_create_rsp,
 250                   sizeof(nci_pattern_core_conn_create_rsp)))
 251                return -EINVAL;
 252
 253        priv->fw_dnld.state = STATE_FW_DNLD;
 254        priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
 255        priv->fw_dnld.offset = priv->fw_dnld.binary_config->offset;
 256        return 0;
 257}
 258
 259static int process_state_fw_dnld(struct nfcmrvl_private *priv,
 260                                 struct sk_buff *skb)
 261{
 262        uint16_t len;
 263        uint16_t comp_len;
 264        struct sk_buff *out_skb;
 265
 266        switch (priv->fw_dnld.substate) {
 267        case SUBSTATE_WAIT_COMMAND:
 268                /*
 269                 * Command format:
 270                 * B0..2: NCI header
 271                 * B3   : Helper command (0xA5)
 272                 * B4..5: le16 data size
 273                 * B6..7: le16 data size complement (~)
 274                 * B8..N: payload
 275                 */
 276
 277                /* Remove NCI HDR */
 278                skb_pull(skb, 3);
 279                if (skb->data[0] != HELPER_CMD_PACKET_FORMAT || skb->len != 5) {
 280                        nfc_err(priv->dev, "bad command");
 281                        return -EINVAL;
 282                }
 283                skb_pull(skb, 1);
 284                len = get_unaligned_le16(skb->data);
 285                skb_pull(skb, 2);
 286                comp_len = get_unaligned_le16(skb->data);
 287                memcpy(&comp_len, skb->data, 2);
 288                skb_pull(skb, 2);
 289                if (((~len) & 0xFFFF) != comp_len) {
 290                        nfc_err(priv->dev, "bad len complement: %x %x %x",
 291                                len, comp_len, (~len & 0xFFFF));
 292                        out_skb = alloc_lc_skb(priv, 1);
 293                        if (!out_skb)
 294                                return -ENOMEM;
 295                        skb_put_u8(out_skb, 0xBF);
 296                        nci_send_frame(priv->ndev, out_skb);
 297                        priv->fw_dnld.substate = SUBSTATE_WAIT_NACK_CREDIT;
 298                        return 0;
 299                }
 300                priv->fw_dnld.chunk_len = len;
 301                out_skb = alloc_lc_skb(priv, 1);
 302                if (!out_skb)
 303                        return -ENOMEM;
 304                skb_put_u8(out_skb, HELPER_ACK_PACKET_FORMAT);
 305                nci_send_frame(priv->ndev, out_skb);
 306                priv->fw_dnld.substate = SUBSTATE_WAIT_ACK_CREDIT;
 307                break;
 308
 309        case SUBSTATE_WAIT_ACK_CREDIT:
 310                if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
 311                    memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
 312                           skb->len)) {
 313                        nfc_err(priv->dev, "bad packet: waiting for credit");
 314                        return -EINVAL;
 315                }
 316                if (priv->fw_dnld.chunk_len == 0) {
 317                        /* FW Loading is done */
 318                        uint8_t conn_id = NCI_CORE_LC_CONNID_PROP_FW_DL;
 319
 320                        priv->fw_dnld.state = STATE_CLOSE_LC;
 321                        nci_send_cmd(priv->ndev, NCI_OP_CORE_CONN_CLOSE_CMD,
 322                                     1, &conn_id);
 323                } else {
 324                        out_skb = alloc_lc_skb(priv, priv->fw_dnld.chunk_len);
 325                        if (!out_skb)
 326                                return -ENOMEM;
 327                        skb_put_data(out_skb,
 328                                     ((uint8_t *)priv->fw_dnld.fw->data) + priv->fw_dnld.offset,
 329                                     priv->fw_dnld.chunk_len);
 330                        nci_send_frame(priv->ndev, out_skb);
 331                        priv->fw_dnld.substate = SUBSTATE_WAIT_DATA_CREDIT;
 332                }
 333                break;
 334
 335        case SUBSTATE_WAIT_DATA_CREDIT:
 336                if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
 337                    memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
 338                            skb->len)) {
 339                        nfc_err(priv->dev, "bad packet: waiting for credit");
 340                        return -EINVAL;
 341                }
 342                priv->fw_dnld.offset += priv->fw_dnld.chunk_len;
 343                priv->fw_dnld.chunk_len = 0;
 344                priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
 345                break;
 346
 347        case SUBSTATE_WAIT_NACK_CREDIT:
 348                if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
 349                    memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
 350                            skb->len)) {
 351                        nfc_err(priv->dev, "bad packet: waiting for credit");
 352                        return -EINVAL;
 353                }
 354                priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
 355                break;
 356        }
 357        return 0;
 358}
 359
 360static int process_state_close_lc(struct nfcmrvl_private *priv,
 361                                  struct sk_buff *skb)
 362{
 363        if (sizeof(nci_pattern_core_conn_close_rsp) != skb->len ||
 364            memcmp(skb->data, nci_pattern_core_conn_close_rsp, skb->len))
 365                return -EINVAL;
 366
 367        priv->fw_dnld.state = STATE_BOOT;
 368        nci_send_cmd(priv->ndev, NCI_OP_PROPRIETARY_BOOT_CMD, 0, NULL);
 369        return 0;
 370}
 371
 372static int process_state_boot(struct nfcmrvl_private *priv, struct sk_buff *skb)
 373{
 374        if (sizeof(nci_pattern_proprietary_boot_rsp) != skb->len ||
 375            memcmp(skb->data, nci_pattern_proprietary_boot_rsp, skb->len))
 376                return -EINVAL;
 377
 378        /*
 379         * Update HI config to use the right configuration for the next
 380         * data exchanges.
 381         */
 382        priv->if_ops->nci_update_config(priv,
 383                                        &priv->fw_dnld.binary_config->config);
 384
 385        if (priv->fw_dnld.binary_config == &priv->fw_dnld.header->helper) {
 386                /*
 387                 * This is the case where an helper was needed and we have
 388                 * uploaded it. Now we have to wait the next RESET NTF to start
 389                 * FW download.
 390                 */
 391                priv->fw_dnld.state = STATE_RESET;
 392                priv->fw_dnld.binary_config = &priv->fw_dnld.header->firmware;
 393                nfc_info(priv->dev, "FW loading: helper loaded");
 394        } else {
 395                nfc_info(priv->dev, "FW loading: firmware loaded");
 396                fw_dnld_over(priv, 0);
 397        }
 398        return 0;
 399}
 400
 401static void fw_dnld_rx_work(struct work_struct *work)
 402{
 403        int ret;
 404        struct sk_buff *skb;
 405        struct nfcmrvl_fw_dnld *fw_dnld = container_of(work,
 406                                                       struct nfcmrvl_fw_dnld,
 407                                                       rx_work);
 408        struct nfcmrvl_private *priv = container_of(fw_dnld,
 409                                                    struct nfcmrvl_private,
 410                                                    fw_dnld);
 411
 412        while ((skb = skb_dequeue(&fw_dnld->rx_q))) {
 413                nfc_send_to_raw_sock(priv->ndev->nfc_dev, skb,
 414                                     RAW_PAYLOAD_NCI, NFC_DIRECTION_RX);
 415                switch (fw_dnld->state) {
 416                case STATE_RESET:
 417                        ret = process_state_reset(priv, skb);
 418                        break;
 419                case STATE_INIT:
 420                        ret = process_state_init(priv, skb);
 421                        break;
 422                case STATE_SET_REF_CLOCK:
 423                        ret = process_state_set_ref_clock(priv, skb);
 424                        break;
 425                case STATE_SET_HI_CONFIG:
 426                        ret = process_state_set_hi_config(priv, skb);
 427                        break;
 428                case STATE_OPEN_LC:
 429                        ret = process_state_open_lc(priv, skb);
 430                        break;
 431                case STATE_FW_DNLD:
 432                        ret = process_state_fw_dnld(priv, skb);
 433                        break;
 434                case STATE_CLOSE_LC:
 435                        ret = process_state_close_lc(priv, skb);
 436                        break;
 437                case STATE_BOOT:
 438                        ret = process_state_boot(priv, skb);
 439                        break;
 440                default:
 441                        ret = -EFAULT;
 442                }
 443
 444                kfree_skb(skb);
 445
 446                if (ret != 0) {
 447                        nfc_err(priv->dev, "FW loading error");
 448                        fw_dnld_over(priv, ret);
 449                        break;
 450                }
 451        }
 452}
 453
 454int     nfcmrvl_fw_dnld_init(struct nfcmrvl_private *priv)
 455{
 456        char name[32];
 457
 458        INIT_WORK(&priv->fw_dnld.rx_work, fw_dnld_rx_work);
 459        snprintf(name, sizeof(name), "%s_nfcmrvl_fw_dnld_rx_wq",
 460                 dev_name(&priv->ndev->nfc_dev->dev));
 461        priv->fw_dnld.rx_wq = create_singlethread_workqueue(name);
 462        if (!priv->fw_dnld.rx_wq)
 463                return -ENOMEM;
 464        skb_queue_head_init(&priv->fw_dnld.rx_q);
 465        return 0;
 466}
 467
 468void    nfcmrvl_fw_dnld_deinit(struct nfcmrvl_private *priv)
 469{
 470        destroy_workqueue(priv->fw_dnld.rx_wq);
 471}
 472
 473void    nfcmrvl_fw_dnld_recv_frame(struct nfcmrvl_private *priv,
 474                                   struct sk_buff *skb)
 475{
 476        /* Discard command timer */
 477        if (timer_pending(&priv->ndev->cmd_timer))
 478                del_timer_sync(&priv->ndev->cmd_timer);
 479
 480        /* Allow next command */
 481        atomic_set(&priv->ndev->cmd_cnt, 1);
 482
 483        /* Queue and trigger rx work */
 484        skb_queue_tail(&priv->fw_dnld.rx_q, skb);
 485        queue_work(priv->fw_dnld.rx_wq, &priv->fw_dnld.rx_work);
 486}
 487
 488void nfcmrvl_fw_dnld_abort(struct nfcmrvl_private *priv)
 489{
 490        fw_dnld_over(priv, -EHOSTDOWN);
 491}
 492
 493int nfcmrvl_fw_dnld_start(struct nci_dev *ndev, const char *firmware_name)
 494{
 495        struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
 496        struct nfcmrvl_fw_dnld *fw_dnld = &priv->fw_dnld;
 497        int res;
 498
 499        if (!priv->support_fw_dnld)
 500                return -ENOTSUPP;
 501
 502        if (!firmware_name || !firmware_name[0])
 503                return -EINVAL;
 504
 505        strcpy(fw_dnld->name, firmware_name);
 506
 507        /*
 508         * Retrieve FW binary file and parse it to initialize FW download
 509         * state machine.
 510         */
 511
 512        /* Retrieve FW binary */
 513        res = request_firmware(&fw_dnld->fw, firmware_name,
 514                               &ndev->nfc_dev->dev);
 515        if (res < 0) {
 516                nfc_err(priv->dev, "failed to retrieve FW %s", firmware_name);
 517                return -ENOENT;
 518        }
 519
 520        fw_dnld->header = (const struct nfcmrvl_fw *) priv->fw_dnld.fw->data;
 521
 522        if (fw_dnld->header->magic != NFCMRVL_FW_MAGIC ||
 523            fw_dnld->header->phy != priv->phy) {
 524                nfc_err(priv->dev, "bad firmware binary %s magic=0x%x phy=%d",
 525                        firmware_name, fw_dnld->header->magic,
 526                        fw_dnld->header->phy);
 527                release_firmware(fw_dnld->fw);
 528                fw_dnld->header = NULL;
 529                return -EINVAL;
 530        }
 531
 532        if (fw_dnld->header->helper.offset != 0) {
 533                nfc_info(priv->dev, "loading helper");
 534                fw_dnld->binary_config = &fw_dnld->header->helper;
 535        } else {
 536                nfc_info(priv->dev, "loading firmware");
 537                fw_dnld->binary_config = &fw_dnld->header->firmware;
 538        }
 539
 540        /* Configure a timer for timeout */
 541        timer_setup(&priv->fw_dnld.timer, fw_dnld_timeout, 0);
 542        mod_timer(&priv->fw_dnld.timer,
 543                  jiffies + msecs_to_jiffies(FW_DNLD_TIMEOUT));
 544
 545        /* Ronfigure HI to be sure that it is the bootrom values */
 546        priv->if_ops->nci_update_config(priv,
 547                                        &fw_dnld->header->bootrom.config);
 548
 549        /* Allow first command */
 550        atomic_set(&priv->ndev->cmd_cnt, 1);
 551
 552        /* First, reset the chip */
 553        priv->fw_dnld.state = STATE_RESET;
 554        nfcmrvl_chip_reset(priv);
 555
 556        /* Now wait for CORE_RESET_NTF or timeout */
 557
 558        return 0;
 559}
 560