linux/drivers/soc/qcom/wcnss_ctrl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2016, Linaro Ltd.
   4 * Copyright (c) 2015, Sony Mobile Communications Inc.
   5 */
   6#include <linux/firmware.h>
   7#include <linux/module.h>
   8#include <linux/slab.h>
   9#include <linux/io.h>
  10#include <linux/of_platform.h>
  11#include <linux/platform_device.h>
  12#include <linux/rpmsg.h>
  13#include <linux/soc/qcom/wcnss_ctrl.h>
  14
  15#define WCNSS_REQUEST_TIMEOUT   (5 * HZ)
  16#define WCNSS_CBC_TIMEOUT       (10 * HZ)
  17
  18#define WCNSS_ACK_DONE_BOOTING  1
  19#define WCNSS_ACK_COLD_BOOTING  2
  20
  21#define NV_FRAGMENT_SIZE        3072
  22#define NVBIN_FILE              "wlan/prima/WCNSS_qcom_wlan_nv.bin"
  23
  24/**
  25 * struct wcnss_ctrl - driver context
  26 * @dev:        device handle
  27 * @channel:    SMD channel handle
  28 * @ack:        completion for outstanding requests
  29 * @cbc:        completion for cbc complete indication
  30 * @ack_status: status of the outstanding request
  31 * @probe_work: worker for uploading nv binary
  32 */
  33struct wcnss_ctrl {
  34        struct device *dev;
  35        struct rpmsg_endpoint *channel;
  36
  37        struct completion ack;
  38        struct completion cbc;
  39        int ack_status;
  40
  41        struct work_struct probe_work;
  42};
  43
  44/* message types */
  45enum {
  46        WCNSS_VERSION_REQ = 0x01000000,
  47        WCNSS_VERSION_RESP,
  48        WCNSS_DOWNLOAD_NV_REQ,
  49        WCNSS_DOWNLOAD_NV_RESP,
  50        WCNSS_UPLOAD_CAL_REQ,
  51        WCNSS_UPLOAD_CAL_RESP,
  52        WCNSS_DOWNLOAD_CAL_REQ,
  53        WCNSS_DOWNLOAD_CAL_RESP,
  54        WCNSS_VBAT_LEVEL_IND,
  55        WCNSS_BUILD_VERSION_REQ,
  56        WCNSS_BUILD_VERSION_RESP,
  57        WCNSS_PM_CONFIG_REQ,
  58        WCNSS_CBC_COMPLETE_IND,
  59};
  60
  61/**
  62 * struct wcnss_msg_hdr - common packet header for requests and responses
  63 * @type:       packet message type
  64 * @len:        total length of the packet, including this header
  65 */
  66struct wcnss_msg_hdr {
  67        u32 type;
  68        u32 len;
  69} __packed;
  70
  71/*
  72 * struct wcnss_version_resp - version request response
  73 */
  74struct wcnss_version_resp {
  75        struct wcnss_msg_hdr hdr;
  76        u8 major;
  77        u8 minor;
  78        u8 version;
  79        u8 revision;
  80} __packed;
  81
  82/**
  83 * struct wcnss_download_nv_req - firmware fragment request
  84 * @hdr:        common packet wcnss_msg_hdr header
  85 * @seq:        sequence number of this fragment
  86 * @last:       boolean indicator of this being the last fragment of the binary
  87 * @frag_size:  length of this fragment
  88 * @fragment:   fragment data
  89 */
  90struct wcnss_download_nv_req {
  91        struct wcnss_msg_hdr hdr;
  92        u16 seq;
  93        u16 last;
  94        u32 frag_size;
  95        u8 fragment[];
  96} __packed;
  97
  98/**
  99 * struct wcnss_download_nv_resp - firmware download response
 100 * @hdr:        common packet wcnss_msg_hdr header
 101 * @status:     boolean to indicate success of the download
 102 */
 103struct wcnss_download_nv_resp {
 104        struct wcnss_msg_hdr hdr;
 105        u8 status;
 106} __packed;
 107
 108/**
 109 * wcnss_ctrl_smd_callback() - handler from SMD responses
 110 * @rpdev:      remote processor message device pointer
 111 * @data:       pointer to the incoming data packet
 112 * @count:      size of the incoming data packet
 113 * @priv:       unused
 114 * @addr:       unused
 115 *
 116 * Handles any incoming packets from the remote WCNSS_CTRL service.
 117 */
 118static int wcnss_ctrl_smd_callback(struct rpmsg_device *rpdev,
 119                                   void *data,
 120                                   int count,
 121                                   void *priv,
 122                                   u32 addr)
 123{
 124        struct wcnss_ctrl *wcnss = dev_get_drvdata(&rpdev->dev);
 125        const struct wcnss_download_nv_resp *nvresp;
 126        const struct wcnss_version_resp *version;
 127        const struct wcnss_msg_hdr *hdr = data;
 128
 129        switch (hdr->type) {
 130        case WCNSS_VERSION_RESP:
 131                if (count != sizeof(*version)) {
 132                        dev_err(wcnss->dev,
 133                                "invalid size of version response\n");
 134                        break;
 135                }
 136
 137                version = data;
 138                dev_info(wcnss->dev, "WCNSS Version %d.%d %d.%d\n",
 139                         version->major, version->minor,
 140                         version->version, version->revision);
 141
 142                complete(&wcnss->ack);
 143                break;
 144        case WCNSS_DOWNLOAD_NV_RESP:
 145                if (count != sizeof(*nvresp)) {
 146                        dev_err(wcnss->dev,
 147                                "invalid size of download response\n");
 148                        break;
 149                }
 150
 151                nvresp = data;
 152                wcnss->ack_status = nvresp->status;
 153                complete(&wcnss->ack);
 154                break;
 155        case WCNSS_CBC_COMPLETE_IND:
 156                dev_dbg(wcnss->dev, "cold boot complete\n");
 157                complete(&wcnss->cbc);
 158                break;
 159        default:
 160                dev_info(wcnss->dev, "unknown message type %d\n", hdr->type);
 161                break;
 162        }
 163
 164        return 0;
 165}
 166
 167/**
 168 * wcnss_request_version() - send a version request to WCNSS
 169 * @wcnss:      wcnss ctrl driver context
 170 */
 171static int wcnss_request_version(struct wcnss_ctrl *wcnss)
 172{
 173        struct wcnss_msg_hdr msg;
 174        int ret;
 175
 176        msg.type = WCNSS_VERSION_REQ;
 177        msg.len = sizeof(msg);
 178        ret = rpmsg_send(wcnss->channel, &msg, sizeof(msg));
 179        if (ret < 0)
 180                return ret;
 181
 182        ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_CBC_TIMEOUT);
 183        if (!ret) {
 184                dev_err(wcnss->dev, "timeout waiting for version response\n");
 185                return -ETIMEDOUT;
 186        }
 187
 188        return 0;
 189}
 190
 191/**
 192 * wcnss_download_nv() - send nv binary to WCNSS
 193 * @wcnss:      wcnss_ctrl state handle
 194 * @expect_cbc: indicator to caller that an cbc event is expected
 195 *
 196 * Returns 0 on success. Negative errno on failure.
 197 */
 198static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc)
 199{
 200        struct wcnss_download_nv_req *req;
 201        const struct firmware *fw;
 202        const void *data;
 203        ssize_t left;
 204        int ret;
 205
 206        req = kzalloc(sizeof(*req) + NV_FRAGMENT_SIZE, GFP_KERNEL);
 207        if (!req)
 208                return -ENOMEM;
 209
 210        ret = request_firmware(&fw, NVBIN_FILE, wcnss->dev);
 211        if (ret < 0) {
 212                dev_err(wcnss->dev, "Failed to load nv file %s: %d\n",
 213                        NVBIN_FILE, ret);
 214                goto free_req;
 215        }
 216
 217        data = fw->data;
 218        left = fw->size;
 219
 220        req->hdr.type = WCNSS_DOWNLOAD_NV_REQ;
 221        req->hdr.len = sizeof(*req) + NV_FRAGMENT_SIZE;
 222
 223        req->last = 0;
 224        req->frag_size = NV_FRAGMENT_SIZE;
 225
 226        req->seq = 0;
 227        do {
 228                if (left <= NV_FRAGMENT_SIZE) {
 229                        req->last = 1;
 230                        req->frag_size = left;
 231                        req->hdr.len = sizeof(*req) + left;
 232                }
 233
 234                memcpy(req->fragment, data, req->frag_size);
 235
 236                ret = rpmsg_send(wcnss->channel, req, req->hdr.len);
 237                if (ret < 0) {
 238                        dev_err(wcnss->dev, "failed to send smd packet\n");
 239                        goto release_fw;
 240                }
 241
 242                /* Increment for next fragment */
 243                req->seq++;
 244
 245                data += NV_FRAGMENT_SIZE;
 246                left -= NV_FRAGMENT_SIZE;
 247        } while (left > 0);
 248
 249        ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_REQUEST_TIMEOUT);
 250        if (!ret) {
 251                dev_err(wcnss->dev, "timeout waiting for nv upload ack\n");
 252                ret = -ETIMEDOUT;
 253        } else {
 254                *expect_cbc = wcnss->ack_status == WCNSS_ACK_COLD_BOOTING;
 255                ret = 0;
 256        }
 257
 258release_fw:
 259        release_firmware(fw);
 260free_req:
 261        kfree(req);
 262
 263        return ret;
 264}
 265
 266/**
 267 * qcom_wcnss_open_channel() - open additional SMD channel to WCNSS
 268 * @wcnss:      wcnss handle, retrieved from drvdata
 269 * @name:       SMD channel name
 270 * @cb:         callback to handle incoming data on the channel
 271 * @priv:       private data for use in the call-back
 272 */
 273struct rpmsg_endpoint *qcom_wcnss_open_channel(void *wcnss, const char *name, rpmsg_rx_cb_t cb, void *priv)
 274{
 275        struct rpmsg_channel_info chinfo;
 276        struct wcnss_ctrl *_wcnss = wcnss;
 277
 278        strscpy(chinfo.name, name, sizeof(chinfo.name));
 279        chinfo.src = RPMSG_ADDR_ANY;
 280        chinfo.dst = RPMSG_ADDR_ANY;
 281
 282        return rpmsg_create_ept(_wcnss->channel->rpdev, cb, priv, chinfo);
 283}
 284EXPORT_SYMBOL(qcom_wcnss_open_channel);
 285
 286static void wcnss_async_probe(struct work_struct *work)
 287{
 288        struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, probe_work);
 289        bool expect_cbc;
 290        int ret;
 291
 292        ret = wcnss_request_version(wcnss);
 293        if (ret < 0)
 294                return;
 295
 296        ret = wcnss_download_nv(wcnss, &expect_cbc);
 297        if (ret < 0)
 298                return;
 299
 300        /* Wait for pending cold boot completion if indicated by the nv downloader */
 301        if (expect_cbc) {
 302                ret = wait_for_completion_timeout(&wcnss->cbc, WCNSS_REQUEST_TIMEOUT);
 303                if (!ret)
 304                        dev_err(wcnss->dev, "expected cold boot completion\n");
 305        }
 306
 307        of_platform_populate(wcnss->dev->of_node, NULL, NULL, wcnss->dev);
 308}
 309
 310static int wcnss_ctrl_probe(struct rpmsg_device *rpdev)
 311{
 312        struct wcnss_ctrl *wcnss;
 313
 314        wcnss = devm_kzalloc(&rpdev->dev, sizeof(*wcnss), GFP_KERNEL);
 315        if (!wcnss)
 316                return -ENOMEM;
 317
 318        wcnss->dev = &rpdev->dev;
 319        wcnss->channel = rpdev->ept;
 320
 321        init_completion(&wcnss->ack);
 322        init_completion(&wcnss->cbc);
 323        INIT_WORK(&wcnss->probe_work, wcnss_async_probe);
 324
 325        dev_set_drvdata(&rpdev->dev, wcnss);
 326
 327        schedule_work(&wcnss->probe_work);
 328
 329        return 0;
 330}
 331
 332static void wcnss_ctrl_remove(struct rpmsg_device *rpdev)
 333{
 334        struct wcnss_ctrl *wcnss = dev_get_drvdata(&rpdev->dev);
 335
 336        cancel_work_sync(&wcnss->probe_work);
 337        of_platform_depopulate(&rpdev->dev);
 338}
 339
 340static const struct of_device_id wcnss_ctrl_of_match[] = {
 341        { .compatible = "qcom,wcnss", },
 342        {}
 343};
 344MODULE_DEVICE_TABLE(of, wcnss_ctrl_of_match);
 345
 346static struct rpmsg_driver wcnss_ctrl_driver = {
 347        .probe = wcnss_ctrl_probe,
 348        .remove = wcnss_ctrl_remove,
 349        .callback = wcnss_ctrl_smd_callback,
 350        .drv  = {
 351                .name  = "qcom_wcnss_ctrl",
 352                .owner = THIS_MODULE,
 353                .of_match_table = wcnss_ctrl_of_match,
 354        },
 355};
 356
 357module_rpmsg_driver(wcnss_ctrl_driver);
 358
 359MODULE_DESCRIPTION("Qualcomm WCNSS control driver");
 360MODULE_LICENSE("GPL v2");
 361