root/drivers/soc/qcom/wcnss_ctrl.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. wcnss_ctrl_smd_callback
  2. wcnss_request_version
  3. wcnss_download_nv
  4. qcom_wcnss_open_channel
  5. wcnss_async_probe
  6. wcnss_ctrl_probe
  7. wcnss_ctrl_remove

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

/* [<][>][^][v][top][bottom][index][help] */