linux/drivers/net/wireless/ath/ath10k/bmi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: ISC
   2/*
   3 * Copyright (c) 2005-2011 Atheros Communications Inc.
   4 * Copyright (c) 2011-2014,2016-2017 Qualcomm Atheros, Inc.
   5 */
   6
   7#include "bmi.h"
   8#include "hif.h"
   9#include "debug.h"
  10#include "htc.h"
  11#include "hw.h"
  12
  13void ath10k_bmi_start(struct ath10k *ar)
  14{
  15        int ret;
  16
  17        ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi start\n");
  18
  19        ar->bmi.done_sent = false;
  20
  21        /* Enable hardware clock to speed up firmware download */
  22        if (ar->hw_params.hw_ops->enable_pll_clk) {
  23                ret = ar->hw_params.hw_ops->enable_pll_clk(ar);
  24                ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi enable pll ret %d\n", ret);
  25        }
  26}
  27
  28int ath10k_bmi_done(struct ath10k *ar)
  29{
  30        struct bmi_cmd cmd;
  31        u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done);
  32        int ret;
  33
  34        ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi done\n");
  35
  36        if (ar->bmi.done_sent) {
  37                ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi skipped\n");
  38                return 0;
  39        }
  40
  41        ar->bmi.done_sent = true;
  42        cmd.id = __cpu_to_le32(BMI_DONE);
  43
  44        ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
  45        if (ret) {
  46                ath10k_warn(ar, "unable to write to the device: %d\n", ret);
  47                return ret;
  48        }
  49
  50        return 0;
  51}
  52
  53int ath10k_bmi_get_target_info(struct ath10k *ar,
  54                               struct bmi_target_info *target_info)
  55{
  56        struct bmi_cmd cmd;
  57        union bmi_resp resp;
  58        u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);
  59        u32 resplen = sizeof(resp.get_target_info);
  60        int ret;
  61
  62        ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi get target info\n");
  63
  64        if (ar->bmi.done_sent) {
  65                ath10k_warn(ar, "BMI Get Target Info Command disallowed\n");
  66                return -EBUSY;
  67        }
  68
  69        cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);
  70
  71        ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
  72        if (ret) {
  73                ath10k_warn(ar, "unable to get target info from device\n");
  74                return ret;
  75        }
  76
  77        if (resplen < sizeof(resp.get_target_info)) {
  78                ath10k_warn(ar, "invalid get_target_info response length (%d)\n",
  79                            resplen);
  80                return -EIO;
  81        }
  82
  83        target_info->version = __le32_to_cpu(resp.get_target_info.version);
  84        target_info->type    = __le32_to_cpu(resp.get_target_info.type);
  85
  86        return 0;
  87}
  88
  89#define TARGET_VERSION_SENTINAL 0xffffffffu
  90
  91int ath10k_bmi_get_target_info_sdio(struct ath10k *ar,
  92                                    struct bmi_target_info *target_info)
  93{
  94        struct bmi_cmd cmd;
  95        union bmi_resp resp;
  96        u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);
  97        u32 resplen, ver_len;
  98        __le32 tmp;
  99        int ret;
 100
 101        ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi get target info SDIO\n");
 102
 103        if (ar->bmi.done_sent) {
 104                ath10k_warn(ar, "BMI Get Target Info Command disallowed\n");
 105                return -EBUSY;
 106        }
 107
 108        cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);
 109
 110        /* Step 1: Read 4 bytes of the target info and check if it is
 111         * the special sentinal version word or the first word in the
 112         * version response.
 113         */
 114        resplen = sizeof(u32);
 115        ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &tmp, &resplen);
 116        if (ret) {
 117                ath10k_warn(ar, "unable to read from device\n");
 118                return ret;
 119        }
 120
 121        /* Some SDIO boards have a special sentinal byte before the real
 122         * version response.
 123         */
 124        if (__le32_to_cpu(tmp) == TARGET_VERSION_SENTINAL) {
 125                /* Step 1b: Read the version length */
 126                resplen = sizeof(u32);
 127                ret = ath10k_hif_exchange_bmi_msg(ar, NULL, 0, &tmp,
 128                                                  &resplen);
 129                if (ret) {
 130                        ath10k_warn(ar, "unable to read from device\n");
 131                        return ret;
 132                }
 133        }
 134
 135        ver_len = __le32_to_cpu(tmp);
 136
 137        /* Step 2: Check the target info length */
 138        if (ver_len != sizeof(resp.get_target_info)) {
 139                ath10k_warn(ar, "Unexpected target info len: %u. Expected: %zu\n",
 140                            ver_len, sizeof(resp.get_target_info));
 141                return -EINVAL;
 142        }
 143
 144        /* Step 3: Read the rest of the version response */
 145        resplen = sizeof(resp.get_target_info) - sizeof(u32);
 146        ret = ath10k_hif_exchange_bmi_msg(ar, NULL, 0,
 147                                          &resp.get_target_info.version,
 148                                          &resplen);
 149        if (ret) {
 150                ath10k_warn(ar, "unable to read from device\n");
 151                return ret;
 152        }
 153
 154        target_info->version = __le32_to_cpu(resp.get_target_info.version);
 155        target_info->type    = __le32_to_cpu(resp.get_target_info.type);
 156
 157        return 0;
 158}
 159
 160int ath10k_bmi_read_memory(struct ath10k *ar,
 161                           u32 address, void *buffer, u32 length)
 162{
 163        struct bmi_cmd cmd;
 164        union bmi_resp resp;
 165        u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_mem);
 166        u32 rxlen;
 167        int ret;
 168
 169        ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n",
 170                   address, length);
 171
 172        if (ar->bmi.done_sent) {
 173                ath10k_warn(ar, "command disallowed\n");
 174                return -EBUSY;
 175        }
 176
 177        while (length) {
 178                rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE);
 179
 180                cmd.id            = __cpu_to_le32(BMI_READ_MEMORY);
 181                cmd.read_mem.addr = __cpu_to_le32(address);
 182                cmd.read_mem.len  = __cpu_to_le32(rxlen);
 183
 184                ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,
 185                                                  &resp, &rxlen);
 186                if (ret) {
 187                        ath10k_warn(ar, "unable to read from the device (%d)\n",
 188                                    ret);
 189                        return ret;
 190                }
 191
 192                memcpy(buffer, resp.read_mem.payload, rxlen);
 193                address += rxlen;
 194                buffer  += rxlen;
 195                length  -= rxlen;
 196        }
 197
 198        return 0;
 199}
 200
 201int ath10k_bmi_write_soc_reg(struct ath10k *ar, u32 address, u32 reg_val)
 202{
 203        struct bmi_cmd cmd;
 204        u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.write_soc_reg);
 205        int ret;
 206
 207        ath10k_dbg(ar, ATH10K_DBG_BMI,
 208                   "bmi write soc register 0x%08x val 0x%08x\n",
 209                   address, reg_val);
 210
 211        if (ar->bmi.done_sent) {
 212                ath10k_warn(ar, "bmi write soc register command in progress\n");
 213                return -EBUSY;
 214        }
 215
 216        cmd.id = __cpu_to_le32(BMI_WRITE_SOC_REGISTER);
 217        cmd.write_soc_reg.addr = __cpu_to_le32(address);
 218        cmd.write_soc_reg.value = __cpu_to_le32(reg_val);
 219
 220        ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
 221        if (ret) {
 222                ath10k_warn(ar, "Unable to write soc register to device: %d\n",
 223                            ret);
 224                return ret;
 225        }
 226
 227        return 0;
 228}
 229
 230int ath10k_bmi_read_soc_reg(struct ath10k *ar, u32 address, u32 *reg_val)
 231{
 232        struct bmi_cmd cmd;
 233        union bmi_resp resp;
 234        u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_soc_reg);
 235        u32 resplen = sizeof(resp.read_soc_reg);
 236        int ret;
 237
 238        ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read soc register 0x%08x\n",
 239                   address);
 240
 241        if (ar->bmi.done_sent) {
 242                ath10k_warn(ar, "bmi read soc register command in progress\n");
 243                return -EBUSY;
 244        }
 245
 246        cmd.id = __cpu_to_le32(BMI_READ_SOC_REGISTER);
 247        cmd.read_soc_reg.addr = __cpu_to_le32(address);
 248
 249        ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
 250        if (ret) {
 251                ath10k_warn(ar, "Unable to read soc register from device: %d\n",
 252                            ret);
 253                return ret;
 254        }
 255
 256        *reg_val = __le32_to_cpu(resp.read_soc_reg.value);
 257
 258        ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read soc register value 0x%08x\n",
 259                   *reg_val);
 260
 261        return 0;
 262}
 263
 264int ath10k_bmi_write_memory(struct ath10k *ar,
 265                            u32 address, const void *buffer, u32 length)
 266{
 267        struct bmi_cmd cmd;
 268        u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.write_mem);
 269        u32 txlen;
 270        int ret;
 271
 272        ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n",
 273                   address, length);
 274
 275        if (ar->bmi.done_sent) {
 276                ath10k_warn(ar, "command disallowed\n");
 277                return -EBUSY;
 278        }
 279
 280        while (length) {
 281                txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
 282
 283                /* copy before roundup to avoid reading beyond buffer*/
 284                memcpy(cmd.write_mem.payload, buffer, txlen);
 285                txlen = roundup(txlen, 4);
 286
 287                cmd.id             = __cpu_to_le32(BMI_WRITE_MEMORY);
 288                cmd.write_mem.addr = __cpu_to_le32(address);
 289                cmd.write_mem.len  = __cpu_to_le32(txlen);
 290
 291                ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
 292                                                  NULL, NULL);
 293                if (ret) {
 294                        ath10k_warn(ar, "unable to write to the device (%d)\n",
 295                                    ret);
 296                        return ret;
 297                }
 298
 299                /* fixup roundup() so `length` zeroes out for last chunk */
 300                txlen = min(txlen, length);
 301
 302                address += txlen;
 303                buffer  += txlen;
 304                length  -= txlen;
 305        }
 306
 307        return 0;
 308}
 309
 310int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result)
 311{
 312        struct bmi_cmd cmd;
 313        union bmi_resp resp;
 314        u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.execute);
 315        u32 resplen = sizeof(resp.execute);
 316        int ret;
 317
 318        ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n",
 319                   address, param);
 320
 321        if (ar->bmi.done_sent) {
 322                ath10k_warn(ar, "command disallowed\n");
 323                return -EBUSY;
 324        }
 325
 326        cmd.id            = __cpu_to_le32(BMI_EXECUTE);
 327        cmd.execute.addr  = __cpu_to_le32(address);
 328        cmd.execute.param = __cpu_to_le32(param);
 329
 330        ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
 331        if (ret) {
 332                ath10k_warn(ar, "unable to read from the device\n");
 333                return ret;
 334        }
 335
 336        if (resplen < sizeof(resp.execute)) {
 337                ath10k_warn(ar, "invalid execute response length (%d)\n",
 338                            resplen);
 339                return -EIO;
 340        }
 341
 342        *result = __le32_to_cpu(resp.execute.result);
 343
 344        ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute result 0x%x\n", *result);
 345
 346        return 0;
 347}
 348
 349int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length)
 350{
 351        struct bmi_cmd cmd;
 352        u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.lz_data);
 353        u32 txlen;
 354        int ret;
 355
 356        ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz data buffer 0x%pK length %d\n",
 357                   buffer, length);
 358
 359        if (ar->bmi.done_sent) {
 360                ath10k_warn(ar, "command disallowed\n");
 361                return -EBUSY;
 362        }
 363
 364        while (length) {
 365                txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
 366
 367                WARN_ON_ONCE(txlen & 3);
 368
 369                cmd.id          = __cpu_to_le32(BMI_LZ_DATA);
 370                cmd.lz_data.len = __cpu_to_le32(txlen);
 371                memcpy(cmd.lz_data.payload, buffer, txlen);
 372
 373                ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
 374                                                  NULL, NULL);
 375                if (ret) {
 376                        ath10k_warn(ar, "unable to write to the device\n");
 377                        return ret;
 378                }
 379
 380                buffer += txlen;
 381                length -= txlen;
 382        }
 383
 384        return 0;
 385}
 386
 387int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address)
 388{
 389        struct bmi_cmd cmd;
 390        u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start);
 391        int ret;
 392
 393        ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n",
 394                   address);
 395
 396        if (ar->bmi.done_sent) {
 397                ath10k_warn(ar, "command disallowed\n");
 398                return -EBUSY;
 399        }
 400
 401        cmd.id            = __cpu_to_le32(BMI_LZ_STREAM_START);
 402        cmd.lz_start.addr = __cpu_to_le32(address);
 403
 404        ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
 405        if (ret) {
 406                ath10k_warn(ar, "unable to Start LZ Stream to the device\n");
 407                return ret;
 408        }
 409
 410        return 0;
 411}
 412
 413int ath10k_bmi_fast_download(struct ath10k *ar,
 414                             u32 address, const void *buffer, u32 length)
 415{
 416        u8 trailer[4] = {};
 417        u32 head_len = rounddown(length, 4);
 418        u32 trailer_len = length - head_len;
 419        int ret;
 420
 421        ath10k_dbg(ar, ATH10K_DBG_BMI,
 422                   "bmi fast download address 0x%x buffer 0x%pK length %d\n",
 423                   address, buffer, length);
 424
 425        ret = ath10k_bmi_lz_stream_start(ar, address);
 426        if (ret)
 427                return ret;
 428
 429        /* copy the last word into a zero padded buffer */
 430        if (trailer_len > 0)
 431                memcpy(trailer, buffer + head_len, trailer_len);
 432
 433        ret = ath10k_bmi_lz_data(ar, buffer, head_len);
 434        if (ret)
 435                return ret;
 436
 437        if (trailer_len > 0)
 438                ret = ath10k_bmi_lz_data(ar, trailer, 4);
 439
 440        if (ret != 0)
 441                return ret;
 442
 443        /*
 444         * Close compressed stream and open a new (fake) one.
 445         * This serves mainly to flush Target caches.
 446         */
 447        ret = ath10k_bmi_lz_stream_start(ar, 0x00);
 448
 449        return ret;
 450}
 451
 452int ath10k_bmi_set_start(struct ath10k *ar, u32 address)
 453{
 454        struct bmi_cmd cmd;
 455        u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.set_app_start);
 456        int ret;
 457
 458        if (ar->bmi.done_sent) {
 459                ath10k_warn(ar, "bmi set start command disallowed\n");
 460                return -EBUSY;
 461        }
 462
 463        cmd.id = __cpu_to_le32(BMI_SET_APP_START);
 464        cmd.set_app_start.addr = __cpu_to_le32(address);
 465
 466        ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
 467        if (ret) {
 468                ath10k_warn(ar, "unable to set start to the device:%d\n", ret);
 469                return ret;
 470        }
 471
 472        return 0;
 473}
 474