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 <linux/unaligned/access_ok.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 = (struct nci_data_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(unsigned long arg)
 134{
 135        struct nfcmrvl_private *priv = (struct nfcmrvl_private *) arg;
 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                memcpy(&len, skb->data, 2);
 285                skb_pull(skb, 2);
 286                memcpy(&comp_len, skb->data, 2);
 287                skb_pull(skb, 2);
 288                len = get_unaligned_le16(&len);
 289                comp_len = get_unaligned_le16(&comp_len);
 290                if (((~len) & 0xFFFF) != comp_len) {
 291                        nfc_err(priv->dev, "bad len complement: %x %x %x",
 292                                len, comp_len, (~len & 0xFFFF));
 293                        out_skb = alloc_lc_skb(priv, 1);
 294                        if (!out_skb)
 295                                return -ENOMEM;
 296                        *skb_put(out_skb, 1) = 0xBF;
 297                        nci_send_frame(priv->ndev, out_skb);
 298                        priv->fw_dnld.substate = SUBSTATE_WAIT_NACK_CREDIT;
 299                        return 0;
 300                }
 301                priv->fw_dnld.chunk_len = len;
 302                out_skb = alloc_lc_skb(priv, 1);
 303                if (!out_skb)
 304                        return -ENOMEM;
 305                *skb_put(out_skb, 1) = HELPER_ACK_PACKET_FORMAT;
 306                nci_send_frame(priv->ndev, out_skb);
 307                priv->fw_dnld.substate = SUBSTATE_WAIT_ACK_CREDIT;
 308                break;
 309
 310        case SUBSTATE_WAIT_ACK_CREDIT:
 311                if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
 312                    memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
 313                           skb->len)) {
 314                        nfc_err(priv->dev, "bad packet: waiting for credit");
 315                        return -EINVAL;
 316                }
 317                if (priv->fw_dnld.chunk_len == 0) {
 318                        /* FW Loading is done */
 319                        uint8_t conn_id = NCI_CORE_LC_CONNID_PROP_FW_DL;
 320
 321                        priv->fw_dnld.state = STATE_CLOSE_LC;
 322                        nci_send_cmd(priv->ndev, NCI_OP_CORE_CONN_CLOSE_CMD,
 323                                     1, &conn_id);
 324                } else {
 325                        out_skb = alloc_lc_skb(priv, priv->fw_dnld.chunk_len);
 326                        if (!out_skb)
 327                                return -ENOMEM;
 328                        memcpy(skb_put(out_skb, priv->fw_dnld.chunk_len),
 329                               ((uint8_t *)priv->fw_dnld.fw->data) +
 330                               priv->fw_dnld.offset,
 331                               priv->fw_dnld.chunk_len);
 332                        nci_send_frame(priv->ndev, out_skb);
 333                        priv->fw_dnld.substate = SUBSTATE_WAIT_DATA_CREDIT;
 334                }
 335                break;
 336
 337        case SUBSTATE_WAIT_DATA_CREDIT:
 338                if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
 339                    memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
 340                            skb->len)) {
 341                        nfc_err(priv->dev, "bad packet: waiting for credit");
 342                        return -EINVAL;
 343                }
 344                priv->fw_dnld.offset += priv->fw_dnld.chunk_len;
 345                priv->fw_dnld.chunk_len = 0;
 346                priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
 347                break;
 348
 349        case SUBSTATE_WAIT_NACK_CREDIT:
 350                if (sizeof(nci_pattern_core_conn_credits_ntf) != skb->len ||
 351                    memcmp(nci_pattern_core_conn_credits_ntf, skb->data,
 352                            skb->len)) {
 353                        nfc_err(priv->dev, "bad packet: waiting for credit");
 354                        return -EINVAL;
 355                }
 356                priv->fw_dnld.substate = SUBSTATE_WAIT_COMMAND;
 357                break;
 358        }
 359        return 0;
 360}
 361
 362static int process_state_close_lc(struct nfcmrvl_private *priv,
 363                                  struct sk_buff *skb)
 364{
 365        if (sizeof(nci_pattern_core_conn_close_rsp) != skb->len ||
 366            memcmp(skb->data, nci_pattern_core_conn_close_rsp, skb->len))
 367                return -EINVAL;
 368
 369        priv->fw_dnld.state = STATE_BOOT;
 370        nci_send_cmd(priv->ndev, NCI_OP_PROPRIETARY_BOOT_CMD, 0, NULL);
 371        return 0;
 372}
 373
 374static int process_state_boot(struct nfcmrvl_private *priv, struct sk_buff *skb)
 375{
 376        if (sizeof(nci_pattern_proprietary_boot_rsp) != skb->len ||
 377            memcmp(skb->data, nci_pattern_proprietary_boot_rsp, skb->len))
 378                return -EINVAL;
 379
 380        /*
 381         * Update HI config to use the right configuration for the next
 382         * data exchanges.
 383         */
 384        priv->if_ops->nci_update_config(priv,
 385                                        &priv->fw_dnld.binary_config->config);
 386
 387        if (priv->fw_dnld.binary_config == &priv->fw_dnld.header->helper) {
 388                /*
 389                 * This is the case where an helper was needed and we have
 390                 * uploaded it. Now we have to wait the next RESET NTF to start
 391                 * FW download.
 392                 */
 393                priv->fw_dnld.state = STATE_RESET;
 394                priv->fw_dnld.binary_config = &priv->fw_dnld.header->firmware;
 395                nfc_info(priv->dev, "FW loading: helper loaded");
 396        } else {
 397                nfc_info(priv->dev, "FW loading: firmware loaded");
 398                fw_dnld_over(priv, 0);
 399        }
 400        return 0;
 401}
 402
 403static void fw_dnld_rx_work(struct work_struct *work)
 404{
 405        int ret;
 406        struct sk_buff *skb;
 407        struct nfcmrvl_fw_dnld *fw_dnld = container_of(work,
 408                                                       struct nfcmrvl_fw_dnld,
 409                                                       rx_work);
 410        struct nfcmrvl_private *priv = container_of(fw_dnld,
 411                                                    struct nfcmrvl_private,
 412                                                    fw_dnld);
 413
 414        while ((skb = skb_dequeue(&fw_dnld->rx_q))) {
 415                nfc_send_to_raw_sock(priv->ndev->nfc_dev, skb,
 416                                     RAW_PAYLOAD_NCI, NFC_DIRECTION_RX);
 417                switch (fw_dnld->state) {
 418                case STATE_RESET:
 419                        ret = process_state_reset(priv, skb);
 420                        break;
 421                case STATE_INIT:
 422                        ret = process_state_init(priv, skb);
 423                        break;
 424                case STATE_SET_REF_CLOCK:
 425                        ret = process_state_set_ref_clock(priv, skb);
 426                        break;
 427                case STATE_SET_HI_CONFIG:
 428                        ret = process_state_set_hi_config(priv, skb);
 429                        break;
 430                case STATE_OPEN_LC:
 431                        ret = process_state_open_lc(priv, skb);
 432                        break;
 433                case STATE_FW_DNLD:
 434                        ret = process_state_fw_dnld(priv, skb);
 435                        break;
 436                case STATE_CLOSE_LC:
 437                        ret = process_state_close_lc(priv, skb);
 438                        break;
 439                case STATE_BOOT:
 440                        ret = process_state_boot(priv, skb);
 441                        break;
 442                default:
 443                        ret = -EFAULT;
 444                }
 445
 446                kfree_skb(skb);
 447
 448                if (ret != 0) {
 449                        nfc_err(priv->dev, "FW loading error");
 450                        fw_dnld_over(priv, ret);
 451                        break;
 452                }
 453        }
 454}
 455
 456int     nfcmrvl_fw_dnld_init(struct nfcmrvl_private *priv)
 457{
 458        char name[32];
 459
 460        INIT_WORK(&priv->fw_dnld.rx_work, fw_dnld_rx_work);
 461        snprintf(name, sizeof(name), "%s_nfcmrvl_fw_dnld_rx_wq",
 462                 dev_name(priv->dev));
 463        priv->fw_dnld.rx_wq = create_singlethread_workqueue(name);
 464        if (!priv->fw_dnld.rx_wq)
 465                return -ENOMEM;
 466        skb_queue_head_init(&priv->fw_dnld.rx_q);
 467        return 0;
 468}
 469
 470void    nfcmrvl_fw_dnld_deinit(struct nfcmrvl_private *priv)
 471{
 472        destroy_workqueue(priv->fw_dnld.rx_wq);
 473}
 474
 475void    nfcmrvl_fw_dnld_recv_frame(struct nfcmrvl_private *priv,
 476                                   struct sk_buff *skb)
 477{
 478        /* Discard command timer */
 479        if (timer_pending(&priv->ndev->cmd_timer))
 480                del_timer_sync(&priv->ndev->cmd_timer);
 481
 482        /* Allow next command */
 483        atomic_set(&priv->ndev->cmd_cnt, 1);
 484
 485        /* Queue and trigger rx work */
 486        skb_queue_tail(&priv->fw_dnld.rx_q, skb);
 487        queue_work(priv->fw_dnld.rx_wq, &priv->fw_dnld.rx_work);
 488}
 489
 490void nfcmrvl_fw_dnld_abort(struct nfcmrvl_private *priv)
 491{
 492        fw_dnld_over(priv, -EHOSTDOWN);
 493}
 494
 495int nfcmrvl_fw_dnld_start(struct nci_dev *ndev, const char *firmware_name)
 496{
 497        struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
 498        struct nfcmrvl_fw_dnld *fw_dnld = &priv->fw_dnld;
 499
 500        if (!priv->support_fw_dnld)
 501                return -ENOTSUPP;
 502
 503        if (!firmware_name || !firmware_name[0])
 504                return -EINVAL;
 505
 506        strcpy(fw_dnld->name, firmware_name);
 507
 508        /*
 509         * Retrieve FW binary file and parse it to initialize FW download
 510         * state machine.
 511         */
 512
 513        /* Retrieve FW binary */
 514        if (request_firmware(&fw_dnld->fw, firmware_name, priv->dev) < 0) {
 515                nfc_err(priv->dev, "failed to retrieve FW %s", firmware_name);
 516                return -ENOENT;
 517        }
 518
 519        fw_dnld->header = (const struct nfcmrvl_fw *) priv->fw_dnld.fw->data;
 520
 521        if (fw_dnld->header->magic != NFCMRVL_FW_MAGIC ||
 522            fw_dnld->header->phy != priv->phy) {
 523                nfc_err(priv->dev, "bad firmware binary %s magic=0x%x phy=%d",
 524                        firmware_name, fw_dnld->header->magic,
 525                        fw_dnld->header->phy);
 526                release_firmware(fw_dnld->fw);
 527                fw_dnld->header = NULL;
 528                return -EINVAL;
 529        }
 530
 531        if (fw_dnld->header->helper.offset != 0) {
 532                nfc_info(priv->dev, "loading helper");
 533                fw_dnld->binary_config = &fw_dnld->header->helper;
 534        } else {
 535                nfc_info(priv->dev, "loading firmware");
 536                fw_dnld->binary_config = &fw_dnld->header->firmware;
 537        }
 538
 539        /* Configure a timer for timeout */
 540        setup_timer(&priv->fw_dnld.timer, fw_dnld_timeout,
 541                    (unsigned long) priv);
 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