linux/drivers/net/wireless/ath/ath11k/htc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: BSD-3-Clause-Clear
   2/*
   3 * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
   4 */
   5#include <linux/skbuff.h>
   6#include <linux/ctype.h>
   7
   8#include "debug.h"
   9#include "hif.h"
  10
  11struct sk_buff *ath11k_htc_alloc_skb(struct ath11k_base *ab, int size)
  12{
  13        struct sk_buff *skb;
  14
  15        skb = dev_alloc_skb(size + sizeof(struct ath11k_htc_hdr));
  16        if (!skb)
  17                return NULL;
  18
  19        skb_reserve(skb, sizeof(struct ath11k_htc_hdr));
  20
  21        /* FW/HTC requires 4-byte aligned streams */
  22        if (!IS_ALIGNED((unsigned long)skb->data, 4))
  23                ath11k_warn(ab, "Unaligned HTC tx skb\n");
  24
  25        return skb;
  26}
  27
  28static void ath11k_htc_control_tx_complete(struct ath11k_base *ab,
  29                                           struct sk_buff *skb)
  30{
  31        kfree_skb(skb);
  32}
  33
  34static struct sk_buff *ath11k_htc_build_tx_ctrl_skb(void *ab)
  35{
  36        struct sk_buff *skb;
  37        struct ath11k_skb_cb *skb_cb;
  38
  39        skb = dev_alloc_skb(ATH11K_HTC_CONTROL_BUFFER_SIZE);
  40        if (!skb)
  41                return NULL;
  42
  43        skb_reserve(skb, sizeof(struct ath11k_htc_hdr));
  44        WARN_ON_ONCE(!IS_ALIGNED((unsigned long)skb->data, 4));
  45
  46        skb_cb = ATH11K_SKB_CB(skb);
  47        memset(skb_cb, 0, sizeof(*skb_cb));
  48
  49        ath11k_dbg(ab, ATH11K_DBG_HTC, "%s: skb %pK\n", __func__, skb);
  50        return skb;
  51}
  52
  53static void ath11k_htc_prepare_tx_skb(struct ath11k_htc_ep *ep,
  54                                      struct sk_buff *skb)
  55{
  56        struct ath11k_htc_hdr *hdr;
  57
  58        hdr = (struct ath11k_htc_hdr *)skb->data;
  59
  60        memset(hdr, 0, sizeof(*hdr));
  61        hdr->htc_info = FIELD_PREP(HTC_HDR_ENDPOINTID, ep->eid) |
  62                        FIELD_PREP(HTC_HDR_PAYLOADLEN,
  63                                   (skb->len - sizeof(*hdr)));
  64
  65        if (ep->tx_credit_flow_enabled)
  66                hdr->htc_info |= FIELD_PREP(HTC_HDR_FLAGS,
  67                                            ATH11K_HTC_FLAG_NEED_CREDIT_UPDATE);
  68
  69        spin_lock_bh(&ep->htc->tx_lock);
  70        hdr->ctrl_info = FIELD_PREP(HTC_HDR_CONTROLBYTES1, ep->seq_no++);
  71        spin_unlock_bh(&ep->htc->tx_lock);
  72}
  73
  74int ath11k_htc_send(struct ath11k_htc *htc,
  75                    enum ath11k_htc_ep_id eid,
  76                    struct sk_buff *skb)
  77{
  78        struct ath11k_htc_ep *ep = &htc->endpoint[eid];
  79        struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB(skb);
  80        struct device *dev = htc->ab->dev;
  81        struct ath11k_base *ab = htc->ab;
  82        int credits = 0;
  83        int ret;
  84
  85        if (eid >= ATH11K_HTC_EP_COUNT) {
  86                ath11k_warn(ab, "Invalid endpoint id: %d\n", eid);
  87                return -ENOENT;
  88        }
  89
  90        skb_push(skb, sizeof(struct ath11k_htc_hdr));
  91
  92        if (ep->tx_credit_flow_enabled) {
  93                credits = DIV_ROUND_UP(skb->len, htc->target_credit_size);
  94                spin_lock_bh(&htc->tx_lock);
  95                if (ep->tx_credits < credits) {
  96                        ath11k_dbg(ab, ATH11K_DBG_HTC,
  97                                   "htc insufficient credits ep %d required %d available %d\n",
  98                                   eid, credits, ep->tx_credits);
  99                        spin_unlock_bh(&htc->tx_lock);
 100                        ret = -EAGAIN;
 101                        goto err_pull;
 102                }
 103                ep->tx_credits -= credits;
 104                ath11k_dbg(ab, ATH11K_DBG_HTC,
 105                           "htc ep %d consumed %d credits (total %d)\n",
 106                           eid, credits, ep->tx_credits);
 107                spin_unlock_bh(&htc->tx_lock);
 108        }
 109
 110        ath11k_htc_prepare_tx_skb(ep, skb);
 111
 112        skb_cb->eid = eid;
 113        skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE);
 114        ret = dma_mapping_error(dev, skb_cb->paddr);
 115        if (ret) {
 116                ret = -EIO;
 117                goto err_credits;
 118        }
 119
 120        ret = ath11k_ce_send(htc->ab, skb, ep->ul_pipe_id, ep->eid);
 121        if (ret)
 122                goto err_unmap;
 123
 124        return 0;
 125
 126err_unmap:
 127        dma_unmap_single(dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE);
 128err_credits:
 129        if (ep->tx_credit_flow_enabled) {
 130                spin_lock_bh(&htc->tx_lock);
 131                ep->tx_credits += credits;
 132                ath11k_dbg(ab, ATH11K_DBG_HTC,
 133                           "htc ep %d reverted %d credits back (total %d)\n",
 134                           eid, credits, ep->tx_credits);
 135                spin_unlock_bh(&htc->tx_lock);
 136
 137                if (ep->ep_ops.ep_tx_credits)
 138                        ep->ep_ops.ep_tx_credits(htc->ab);
 139        }
 140err_pull:
 141        skb_pull(skb, sizeof(struct ath11k_htc_hdr));
 142        return ret;
 143}
 144
 145static void
 146ath11k_htc_process_credit_report(struct ath11k_htc *htc,
 147                                 const struct ath11k_htc_credit_report *report,
 148                                 int len,
 149                                 enum ath11k_htc_ep_id eid)
 150{
 151        struct ath11k_base *ab = htc->ab;
 152        struct ath11k_htc_ep *ep;
 153        int i, n_reports;
 154
 155        if (len % sizeof(*report))
 156                ath11k_warn(ab, "Uneven credit report len %d", len);
 157
 158        n_reports = len / sizeof(*report);
 159
 160        spin_lock_bh(&htc->tx_lock);
 161        for (i = 0; i < n_reports; i++, report++) {
 162                if (report->eid >= ATH11K_HTC_EP_COUNT)
 163                        break;
 164
 165                ep = &htc->endpoint[report->eid];
 166                ep->tx_credits += report->credits;
 167
 168                ath11k_dbg(ab, ATH11K_DBG_HTC, "htc ep %d got %d credits (total %d)\n",
 169                           report->eid, report->credits, ep->tx_credits);
 170
 171                if (ep->ep_ops.ep_tx_credits) {
 172                        spin_unlock_bh(&htc->tx_lock);
 173                        ep->ep_ops.ep_tx_credits(htc->ab);
 174                        spin_lock_bh(&htc->tx_lock);
 175                }
 176        }
 177        spin_unlock_bh(&htc->tx_lock);
 178}
 179
 180static int ath11k_htc_process_trailer(struct ath11k_htc *htc,
 181                                      u8 *buffer,
 182                                      int length,
 183                                      enum ath11k_htc_ep_id src_eid)
 184{
 185        struct ath11k_base *ab = htc->ab;
 186        int status = 0;
 187        struct ath11k_htc_record *record;
 188        size_t len;
 189
 190        while (length > 0) {
 191                record = (struct ath11k_htc_record *)buffer;
 192
 193                if (length < sizeof(record->hdr)) {
 194                        status = -EINVAL;
 195                        break;
 196                }
 197
 198                if (record->hdr.len > length) {
 199                        /* no room left in buffer for record */
 200                        ath11k_warn(ab, "Invalid record length: %d\n",
 201                                    record->hdr.len);
 202                        status = -EINVAL;
 203                        break;
 204                }
 205
 206                switch (record->hdr.id) {
 207                case ATH11K_HTC_RECORD_CREDITS:
 208                        len = sizeof(struct ath11k_htc_credit_report);
 209                        if (record->hdr.len < len) {
 210                                ath11k_warn(ab, "Credit report too long\n");
 211                                status = -EINVAL;
 212                                break;
 213                        }
 214                        ath11k_htc_process_credit_report(htc,
 215                                                         record->credit_report,
 216                                                         record->hdr.len,
 217                                                         src_eid);
 218                        break;
 219                default:
 220                        ath11k_warn(ab, "Unhandled record: id:%d length:%d\n",
 221                                    record->hdr.id, record->hdr.len);
 222                        break;
 223                }
 224
 225                if (status)
 226                        break;
 227
 228                /* multiple records may be present in a trailer */
 229                buffer += sizeof(record->hdr) + record->hdr.len;
 230                length -= sizeof(record->hdr) + record->hdr.len;
 231        }
 232
 233        return status;
 234}
 235
 236static void ath11k_htc_suspend_complete(struct ath11k_base *ab, bool ack)
 237{
 238        ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot suspend complete %d\n", ack);
 239
 240        if (ack)
 241                set_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags);
 242        else
 243                clear_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags);
 244
 245        complete(&ab->htc_suspend);
 246}
 247
 248void ath11k_htc_rx_completion_handler(struct ath11k_base *ab,
 249                                      struct sk_buff *skb)
 250{
 251        int status = 0;
 252        struct ath11k_htc *htc = &ab->htc;
 253        struct ath11k_htc_hdr *hdr;
 254        struct ath11k_htc_ep *ep;
 255        u16 payload_len;
 256        u32 trailer_len = 0;
 257        size_t min_len;
 258        u8 eid;
 259        bool trailer_present;
 260
 261        hdr = (struct ath11k_htc_hdr *)skb->data;
 262        skb_pull(skb, sizeof(*hdr));
 263
 264        eid = FIELD_GET(HTC_HDR_ENDPOINTID, hdr->htc_info);
 265
 266        if (eid >= ATH11K_HTC_EP_COUNT) {
 267                ath11k_warn(ab, "HTC Rx: invalid eid %d\n", eid);
 268                goto out;
 269        }
 270
 271        ep = &htc->endpoint[eid];
 272
 273        payload_len = FIELD_GET(HTC_HDR_PAYLOADLEN, hdr->htc_info);
 274
 275        if (payload_len + sizeof(*hdr) > ATH11K_HTC_MAX_LEN) {
 276                ath11k_warn(ab, "HTC rx frame too long, len: %zu\n",
 277                            payload_len + sizeof(*hdr));
 278                goto out;
 279        }
 280
 281        if (skb->len < payload_len) {
 282                ath11k_warn(ab, "HTC Rx: insufficient length, got %d, expected %d\n",
 283                            skb->len, payload_len);
 284                goto out;
 285        }
 286
 287        /* get flags to check for trailer */
 288        trailer_present = (FIELD_GET(HTC_HDR_FLAGS, hdr->htc_info)) &
 289                          ATH11K_HTC_FLAG_TRAILER_PRESENT;
 290
 291        if (trailer_present) {
 292                u8 *trailer;
 293
 294                trailer_len = FIELD_GET(HTC_HDR_CONTROLBYTES0, hdr->ctrl_info);
 295                min_len = sizeof(struct ath11k_htc_record_hdr);
 296
 297                if ((trailer_len < min_len) ||
 298                    (trailer_len > payload_len)) {
 299                        ath11k_warn(ab, "Invalid trailer length: %d\n",
 300                                    trailer_len);
 301                        goto out;
 302                }
 303
 304                trailer = (u8 *)hdr;
 305                trailer += sizeof(*hdr);
 306                trailer += payload_len;
 307                trailer -= trailer_len;
 308                status = ath11k_htc_process_trailer(htc, trailer,
 309                                                    trailer_len, eid);
 310                if (status)
 311                        goto out;
 312
 313                skb_trim(skb, skb->len - trailer_len);
 314        }
 315
 316        if (trailer_len >= payload_len)
 317                /* zero length packet with trailer data, just drop these */
 318                goto out;
 319
 320        if (eid == ATH11K_HTC_EP_0) {
 321                struct ath11k_htc_msg *msg = (struct ath11k_htc_msg *)skb->data;
 322
 323                switch (FIELD_GET(HTC_MSG_MESSAGEID, msg->msg_svc_id)) {
 324                case ATH11K_HTC_MSG_READY_ID:
 325                case ATH11K_HTC_MSG_CONNECT_SERVICE_RESP_ID:
 326                        /* handle HTC control message */
 327                        if (completion_done(&htc->ctl_resp)) {
 328                                /* this is a fatal error, target should not be
 329                                 * sending unsolicited messages on the ep 0
 330                                 */
 331                                ath11k_warn(ab, "HTC rx ctrl still processing\n");
 332                                complete(&htc->ctl_resp);
 333                                goto out;
 334                        }
 335
 336                        htc->control_resp_len =
 337                                min_t(int, skb->len,
 338                                      ATH11K_HTC_MAX_CTRL_MSG_LEN);
 339
 340                        memcpy(htc->control_resp_buffer, skb->data,
 341                               htc->control_resp_len);
 342
 343                        complete(&htc->ctl_resp);
 344                        break;
 345                case ATH11K_HTC_MSG_SEND_SUSPEND_COMPLETE:
 346                        ath11k_htc_suspend_complete(ab, true);
 347                        break;
 348                case ATH11K_HTC_MSG_NACK_SUSPEND:
 349                        ath11k_htc_suspend_complete(ab, false);
 350                        break;
 351                case ATH11K_HTC_MSG_WAKEUP_FROM_SUSPEND_ID:
 352                        break;
 353                default:
 354                        ath11k_warn(ab, "ignoring unsolicited htc ep0 event %ld\n",
 355                                    FIELD_GET(HTC_MSG_MESSAGEID, msg->msg_svc_id));
 356                        break;
 357                }
 358                goto out;
 359        }
 360
 361        ath11k_dbg(ab, ATH11K_DBG_HTC, "htc rx completion ep %d skb %pK\n",
 362                   eid, skb);
 363        ep->ep_ops.ep_rx_complete(ab, skb);
 364
 365        /* poll tx completion for interrupt disabled CE's */
 366        ath11k_ce_poll_send_completed(ab, ep->ul_pipe_id);
 367
 368        /* skb is now owned by the rx completion handler */
 369        skb = NULL;
 370out:
 371        kfree_skb(skb);
 372}
 373
 374static void ath11k_htc_control_rx_complete(struct ath11k_base *ab,
 375                                           struct sk_buff *skb)
 376{
 377        /* This is unexpected. FW is not supposed to send regular rx on this
 378         * endpoint.
 379         */
 380        ath11k_warn(ab, "unexpected htc rx\n");
 381        kfree_skb(skb);
 382}
 383
 384static const char *htc_service_name(enum ath11k_htc_svc_id id)
 385{
 386        switch (id) {
 387        case ATH11K_HTC_SVC_ID_RESERVED:
 388                return "Reserved";
 389        case ATH11K_HTC_SVC_ID_RSVD_CTRL:
 390                return "Control";
 391        case ATH11K_HTC_SVC_ID_WMI_CONTROL:
 392                return "WMI";
 393        case ATH11K_HTC_SVC_ID_WMI_DATA_BE:
 394                return "DATA BE";
 395        case ATH11K_HTC_SVC_ID_WMI_DATA_BK:
 396                return "DATA BK";
 397        case ATH11K_HTC_SVC_ID_WMI_DATA_VI:
 398                return "DATA VI";
 399        case ATH11K_HTC_SVC_ID_WMI_DATA_VO:
 400                return "DATA VO";
 401        case ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1:
 402                return "WMI MAC1";
 403        case ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2:
 404                return "WMI MAC2";
 405        case ATH11K_HTC_SVC_ID_NMI_CONTROL:
 406                return "NMI Control";
 407        case ATH11K_HTC_SVC_ID_NMI_DATA:
 408                return "NMI Data";
 409        case ATH11K_HTC_SVC_ID_HTT_DATA_MSG:
 410                return "HTT Data";
 411        case ATH11K_HTC_SVC_ID_TEST_RAW_STREAMS:
 412                return "RAW";
 413        case ATH11K_HTC_SVC_ID_IPA_TX:
 414                return "IPA TX";
 415        case ATH11K_HTC_SVC_ID_PKT_LOG:
 416                return "PKT LOG";
 417        }
 418
 419        return "Unknown";
 420}
 421
 422static void ath11k_htc_reset_endpoint_states(struct ath11k_htc *htc)
 423{
 424        struct ath11k_htc_ep *ep;
 425        int i;
 426
 427        for (i = ATH11K_HTC_EP_0; i < ATH11K_HTC_EP_COUNT; i++) {
 428                ep = &htc->endpoint[i];
 429                ep->service_id = ATH11K_HTC_SVC_ID_UNUSED;
 430                ep->max_ep_message_len = 0;
 431                ep->max_tx_queue_depth = 0;
 432                ep->eid = i;
 433                ep->htc = htc;
 434                ep->tx_credit_flow_enabled = true;
 435        }
 436}
 437
 438static u8 ath11k_htc_get_credit_allocation(struct ath11k_htc *htc,
 439                                           u16 service_id)
 440{
 441        u8 i, allocation = 0;
 442
 443        for (i = 0; i < ATH11K_HTC_MAX_SERVICE_ALLOC_ENTRIES; i++) {
 444                if (htc->service_alloc_table[i].service_id == service_id) {
 445                        allocation =
 446                                htc->service_alloc_table[i].credit_allocation;
 447                }
 448        }
 449
 450        return allocation;
 451}
 452
 453static int ath11k_htc_setup_target_buffer_assignments(struct ath11k_htc *htc)
 454{
 455        struct ath11k_htc_svc_tx_credits *serv_entry;
 456        u32 svc_id[] = {
 457                ATH11K_HTC_SVC_ID_WMI_CONTROL,
 458                ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1,
 459                ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2,
 460        };
 461        int i, credits;
 462
 463        credits =  htc->total_transmit_credits;
 464        serv_entry = htc->service_alloc_table;
 465
 466        if ((htc->wmi_ep_count == 0) ||
 467            (htc->wmi_ep_count > ARRAY_SIZE(svc_id)))
 468                return -EINVAL;
 469
 470        /* Divide credits among number of endpoints for WMI */
 471        credits = credits / htc->wmi_ep_count;
 472        for (i = 0; i < htc->wmi_ep_count; i++) {
 473                serv_entry[i].service_id = svc_id[i];
 474                serv_entry[i].credit_allocation = credits;
 475        }
 476
 477        return 0;
 478}
 479
 480int ath11k_htc_wait_target(struct ath11k_htc *htc)
 481{
 482        int i, status = 0;
 483        struct ath11k_base *ab = htc->ab;
 484        unsigned long time_left;
 485        struct ath11k_htc_ready *ready;
 486        u16 message_id;
 487        u16 credit_count;
 488        u16 credit_size;
 489
 490        time_left = wait_for_completion_timeout(&htc->ctl_resp,
 491                                                ATH11K_HTC_WAIT_TIMEOUT_HZ);
 492        if (!time_left) {
 493                ath11k_warn(ab, "failed to receive control response completion, polling..\n");
 494
 495                for (i = 0; i < ab->hw_params.ce_count; i++)
 496                        ath11k_ce_per_engine_service(htc->ab, i);
 497
 498                time_left =
 499                        wait_for_completion_timeout(&htc->ctl_resp,
 500                                                    ATH11K_HTC_WAIT_TIMEOUT_HZ);
 501
 502                if (!time_left)
 503                        status = -ETIMEDOUT;
 504        }
 505
 506        if (status < 0) {
 507                ath11k_warn(ab, "ctl_resp never came in (%d)\n", status);
 508                return status;
 509        }
 510
 511        if (htc->control_resp_len < sizeof(*ready)) {
 512                ath11k_warn(ab, "Invalid HTC ready msg len:%d\n",
 513                            htc->control_resp_len);
 514                return -ECOMM;
 515        }
 516
 517        ready = (struct ath11k_htc_ready *)htc->control_resp_buffer;
 518        message_id   = FIELD_GET(HTC_MSG_MESSAGEID, ready->id_credit_count);
 519        credit_count = FIELD_GET(HTC_READY_MSG_CREDITCOUNT,
 520                                 ready->id_credit_count);
 521        credit_size  = FIELD_GET(HTC_READY_MSG_CREDITSIZE, ready->size_ep);
 522
 523        if (message_id != ATH11K_HTC_MSG_READY_ID) {
 524                ath11k_warn(ab, "Invalid HTC ready msg: 0x%x\n", message_id);
 525                return -ECOMM;
 526        }
 527
 528        htc->total_transmit_credits = credit_count;
 529        htc->target_credit_size = credit_size;
 530
 531        ath11k_dbg(ab, ATH11K_DBG_HTC,
 532                   "Target ready! transmit resources: %d size:%d\n",
 533                   htc->total_transmit_credits, htc->target_credit_size);
 534
 535        if ((htc->total_transmit_credits == 0) ||
 536            (htc->target_credit_size == 0)) {
 537                ath11k_warn(ab, "Invalid credit size received\n");
 538                return -ECOMM;
 539        }
 540
 541        /* For QCA6390, wmi endpoint uses 1 credit to avoid
 542         * back-to-back write.
 543         */
 544        if (ab->hw_params.supports_shadow_regs)
 545                htc->total_transmit_credits = 1;
 546
 547        ath11k_htc_setup_target_buffer_assignments(htc);
 548
 549        return 0;
 550}
 551
 552int ath11k_htc_connect_service(struct ath11k_htc *htc,
 553                               struct ath11k_htc_svc_conn_req *conn_req,
 554                               struct ath11k_htc_svc_conn_resp *conn_resp)
 555{
 556        struct ath11k_base *ab = htc->ab;
 557        struct ath11k_htc_conn_svc *req_msg;
 558        struct ath11k_htc_conn_svc_resp resp_msg_dummy;
 559        struct ath11k_htc_conn_svc_resp *resp_msg = &resp_msg_dummy;
 560        enum ath11k_htc_ep_id assigned_eid = ATH11K_HTC_EP_COUNT;
 561        struct ath11k_htc_ep *ep;
 562        struct sk_buff *skb;
 563        unsigned int max_msg_size = 0;
 564        int length, status;
 565        unsigned long time_left;
 566        bool disable_credit_flow_ctrl = false;
 567        u16 message_id, service_id, flags = 0;
 568        u8 tx_alloc = 0;
 569
 570        /* special case for HTC pseudo control service */
 571        if (conn_req->service_id == ATH11K_HTC_SVC_ID_RSVD_CTRL) {
 572                disable_credit_flow_ctrl = true;
 573                assigned_eid = ATH11K_HTC_EP_0;
 574                max_msg_size = ATH11K_HTC_MAX_CTRL_MSG_LEN;
 575                memset(&resp_msg_dummy, 0, sizeof(resp_msg_dummy));
 576                goto setup;
 577        }
 578
 579        tx_alloc = ath11k_htc_get_credit_allocation(htc,
 580                                                    conn_req->service_id);
 581        if (!tx_alloc)
 582                ath11k_dbg(ab, ATH11K_DBG_BOOT,
 583                           "boot htc service %s does not allocate target credits\n",
 584                           htc_service_name(conn_req->service_id));
 585
 586        skb = ath11k_htc_build_tx_ctrl_skb(htc->ab);
 587        if (!skb) {
 588                ath11k_warn(ab, "Failed to allocate HTC packet\n");
 589                return -ENOMEM;
 590        }
 591
 592        length = sizeof(*req_msg);
 593        skb_put(skb, length);
 594        memset(skb->data, 0, length);
 595
 596        req_msg = (struct ath11k_htc_conn_svc *)skb->data;
 597        req_msg->msg_svc_id = FIELD_PREP(HTC_MSG_MESSAGEID,
 598                                         ATH11K_HTC_MSG_CONNECT_SERVICE_ID);
 599
 600        flags |= FIELD_PREP(ATH11K_HTC_CONN_FLAGS_RECV_ALLOC, tx_alloc);
 601
 602        /* Only enable credit flow control for WMI ctrl service */
 603        if (!(conn_req->service_id == ATH11K_HTC_SVC_ID_WMI_CONTROL ||
 604              conn_req->service_id == ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1 ||
 605              conn_req->service_id == ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2)) {
 606                flags |= ATH11K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL;
 607                disable_credit_flow_ctrl = true;
 608        }
 609
 610        req_msg->flags_len = FIELD_PREP(HTC_SVC_MSG_CONNECTIONFLAGS, flags);
 611        req_msg->msg_svc_id |= FIELD_PREP(HTC_SVC_MSG_SERVICE_ID,
 612                                          conn_req->service_id);
 613
 614        reinit_completion(&htc->ctl_resp);
 615
 616        status = ath11k_htc_send(htc, ATH11K_HTC_EP_0, skb);
 617        if (status) {
 618                kfree_skb(skb);
 619                return status;
 620        }
 621
 622        /* wait for response */
 623        time_left = wait_for_completion_timeout(&htc->ctl_resp,
 624                                                ATH11K_HTC_CONN_SVC_TIMEOUT_HZ);
 625        if (!time_left) {
 626                ath11k_err(ab, "Service connect timeout\n");
 627                return -ETIMEDOUT;
 628        }
 629
 630        /* we controlled the buffer creation, it's aligned */
 631        resp_msg = (struct ath11k_htc_conn_svc_resp *)htc->control_resp_buffer;
 632        message_id = FIELD_GET(HTC_MSG_MESSAGEID, resp_msg->msg_svc_id);
 633        service_id = FIELD_GET(HTC_SVC_RESP_MSG_SERVICEID,
 634                               resp_msg->msg_svc_id);
 635
 636        if ((message_id != ATH11K_HTC_MSG_CONNECT_SERVICE_RESP_ID) ||
 637            (htc->control_resp_len < sizeof(*resp_msg))) {
 638                ath11k_err(ab, "Invalid resp message ID 0x%x", message_id);
 639                return -EPROTO;
 640        }
 641
 642        ath11k_dbg(ab, ATH11K_DBG_HTC,
 643                   "HTC Service %s connect response: status: 0x%lx, assigned ep: 0x%lx\n",
 644                   htc_service_name(service_id),
 645                   FIELD_GET(HTC_SVC_RESP_MSG_STATUS, resp_msg->flags_len),
 646                   FIELD_GET(HTC_SVC_RESP_MSG_ENDPOINTID, resp_msg->flags_len));
 647
 648        conn_resp->connect_resp_code = FIELD_GET(HTC_SVC_RESP_MSG_STATUS,
 649                                                 resp_msg->flags_len);
 650
 651        /* check response status */
 652        if (conn_resp->connect_resp_code != ATH11K_HTC_CONN_SVC_STATUS_SUCCESS) {
 653                ath11k_err(ab, "HTC Service %s connect request failed: 0x%x)\n",
 654                           htc_service_name(service_id),
 655                       conn_resp->connect_resp_code);
 656                return -EPROTO;
 657        }
 658
 659        assigned_eid = (enum ath11k_htc_ep_id)FIELD_GET(
 660                                                HTC_SVC_RESP_MSG_ENDPOINTID,
 661                                                resp_msg->flags_len);
 662
 663        max_msg_size = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE,
 664                                 resp_msg->flags_len);
 665
 666setup:
 667
 668        if (assigned_eid >= ATH11K_HTC_EP_COUNT)
 669                return -EPROTO;
 670
 671        if (max_msg_size == 0)
 672                return -EPROTO;
 673
 674        ep = &htc->endpoint[assigned_eid];
 675        ep->eid = assigned_eid;
 676
 677        if (ep->service_id != ATH11K_HTC_SVC_ID_UNUSED)
 678                return -EPROTO;
 679
 680        /* return assigned endpoint to caller */
 681        conn_resp->eid = assigned_eid;
 682        conn_resp->max_msg_len = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE,
 683                                           resp_msg->flags_len);
 684
 685        /* setup the endpoint */
 686        ep->service_id = conn_req->service_id;
 687        ep->max_tx_queue_depth = conn_req->max_send_queue_depth;
 688        ep->max_ep_message_len = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE,
 689                                           resp_msg->flags_len);
 690        ep->tx_credits = tx_alloc;
 691
 692        /* copy all the callbacks */
 693        ep->ep_ops = conn_req->ep_ops;
 694
 695        status = ath11k_hif_map_service_to_pipe(htc->ab,
 696                                                ep->service_id,
 697                                                &ep->ul_pipe_id,
 698                                                &ep->dl_pipe_id);
 699        if (status)
 700                return status;
 701
 702        ath11k_dbg(ab, ATH11K_DBG_BOOT,
 703                   "boot htc service '%s' ul pipe %d dl pipe %d eid %d ready\n",
 704                   htc_service_name(ep->service_id), ep->ul_pipe_id,
 705                   ep->dl_pipe_id, ep->eid);
 706
 707        if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) {
 708                ep->tx_credit_flow_enabled = false;
 709                ath11k_dbg(ab, ATH11K_DBG_BOOT,
 710                           "boot htc service '%s' eid %d TX flow control disabled\n",
 711                           htc_service_name(ep->service_id), assigned_eid);
 712        }
 713
 714        return status;
 715}
 716
 717int ath11k_htc_start(struct ath11k_htc *htc)
 718{
 719        struct sk_buff *skb;
 720        int status = 0;
 721        struct ath11k_base *ab = htc->ab;
 722        struct ath11k_htc_setup_complete_extended *msg;
 723
 724        skb = ath11k_htc_build_tx_ctrl_skb(htc->ab);
 725        if (!skb)
 726                return -ENOMEM;
 727
 728        skb_put(skb, sizeof(*msg));
 729        memset(skb->data, 0, skb->len);
 730
 731        msg = (struct ath11k_htc_setup_complete_extended *)skb->data;
 732        msg->msg_id = FIELD_PREP(HTC_MSG_MESSAGEID,
 733                                 ATH11K_HTC_MSG_SETUP_COMPLETE_EX_ID);
 734
 735        ath11k_dbg(ab, ATH11K_DBG_HTC, "HTC is using TX credit flow control\n");
 736
 737        status = ath11k_htc_send(htc, ATH11K_HTC_EP_0, skb);
 738        if (status) {
 739                kfree_skb(skb);
 740                return status;
 741        }
 742
 743        return 0;
 744}
 745
 746int ath11k_htc_init(struct ath11k_base *ab)
 747{
 748        struct ath11k_htc *htc = &ab->htc;
 749        struct ath11k_htc_svc_conn_req conn_req;
 750        struct ath11k_htc_svc_conn_resp conn_resp;
 751        int ret;
 752
 753        spin_lock_init(&htc->tx_lock);
 754
 755        ath11k_htc_reset_endpoint_states(htc);
 756
 757        htc->ab = ab;
 758
 759        switch (ab->wmi_ab.preferred_hw_mode) {
 760        case WMI_HOST_HW_MODE_SINGLE:
 761                htc->wmi_ep_count = 1;
 762                break;
 763        case WMI_HOST_HW_MODE_DBS:
 764        case WMI_HOST_HW_MODE_DBS_OR_SBS:
 765                htc->wmi_ep_count = 2;
 766                break;
 767        case WMI_HOST_HW_MODE_DBS_SBS:
 768                htc->wmi_ep_count = 3;
 769                break;
 770        default:
 771                htc->wmi_ep_count = ab->hw_params.max_radios;
 772                break;
 773        }
 774
 775        /* setup our pseudo HTC control endpoint connection */
 776        memset(&conn_req, 0, sizeof(conn_req));
 777        memset(&conn_resp, 0, sizeof(conn_resp));
 778        conn_req.ep_ops.ep_tx_complete = ath11k_htc_control_tx_complete;
 779        conn_req.ep_ops.ep_rx_complete = ath11k_htc_control_rx_complete;
 780        conn_req.max_send_queue_depth = ATH11K_NUM_CONTROL_TX_BUFFERS;
 781        conn_req.service_id = ATH11K_HTC_SVC_ID_RSVD_CTRL;
 782
 783        /* connect fake service */
 784        ret = ath11k_htc_connect_service(htc, &conn_req, &conn_resp);
 785        if (ret) {
 786                ath11k_err(ab, "could not connect to htc service (%d)\n", ret);
 787                return ret;
 788        }
 789
 790        init_completion(&htc->ctl_resp);
 791
 792        return 0;
 793}
 794