2 * Copyright (c) 2016, Linaro Ltd.
3 * Copyright (c) 2015, Sony Mobile Communications Inc.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 and
7 * only version 2 as published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 #include <linux/firmware.h>
15 #include <linux/module.h>
16 #include <linux/slab.h>
17 #include <linux/soc/qcom/smd.h>
19 #include <linux/of_platform.h>
20 #include <linux/platform_device.h>
21 #include <linux/soc/qcom/wcnss_ctrl.h>
23 #define WCNSS_REQUEST_TIMEOUT (5 * HZ)
24 #define WCNSS_CBC_TIMEOUT (10 * HZ)
26 #define NV_FRAGMENT_SIZE 3072
27 #define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin"
30 * struct wcnss_ctrl - driver context
32 * @channel: SMD channel handle
33 * @ack: completion for outstanding requests
34 * @cbc: completion for cbc complete indication
35 * @ack_status: status of the outstanding request
36 * @probe_work: worker for uploading nv binary
40 struct qcom_smd_channel *channel;
42 struct completion ack;
43 struct completion cbc;
46 struct work_struct probe_work;
51 WCNSS_VERSION_REQ = 0x01000000,
53 WCNSS_DOWNLOAD_NV_REQ,
54 WCNSS_DOWNLOAD_NV_RESP,
56 WCNSS_UPLOAD_CAL_RESP,
57 WCNSS_DOWNLOAD_CAL_REQ,
58 WCNSS_DOWNLOAD_CAL_RESP,
60 WCNSS_BUILD_VERSION_REQ,
61 WCNSS_BUILD_VERSION_RESP,
63 WCNSS_CBC_COMPLETE_IND,
67 * struct wcnss_msg_hdr - common packet header for requests and responses
68 * @type: packet message type
69 * @len: total length of the packet, including this header
71 struct wcnss_msg_hdr {
77 * struct wcnss_version_resp - version request response
78 * @hdr: common packet wcnss_msg_hdr header
80 struct wcnss_version_resp {
81 struct wcnss_msg_hdr hdr;
89 * struct wcnss_download_nv_req - firmware fragment request
90 * @hdr: common packet wcnss_msg_hdr header
91 * @seq: sequence number of this fragment
92 * @last: boolean indicator of this being the last fragment of the binary
93 * @frag_size: length of this fragment
94 * @fragment: fragment data
96 struct wcnss_download_nv_req {
97 struct wcnss_msg_hdr hdr;
105 * struct wcnss_download_nv_resp - firmware download response
106 * @hdr: common packet wcnss_msg_hdr header
107 * @status: boolean to indicate success of the download
109 struct wcnss_download_nv_resp {
110 struct wcnss_msg_hdr hdr;
115 * wcnss_ctrl_smd_callback() - handler from SMD responses
116 * @qsdev: smd device handle
117 * @data: pointer to the incoming data packet
118 * @count: size of the incoming data packet
120 * Handles any incoming packets from the remote WCNSS_CTRL service.
122 static int wcnss_ctrl_smd_callback(struct qcom_smd_device *qsdev,
126 struct wcnss_ctrl *wcnss = dev_get_drvdata(&qsdev->dev);
127 const struct wcnss_download_nv_resp *nvresp;
128 const struct wcnss_version_resp *version;
129 const struct wcnss_msg_hdr *hdr = data;
132 case WCNSS_VERSION_RESP:
133 if (count != sizeof(*version)) {
135 "invalid size of version response\n");
140 dev_info(wcnss->dev, "WCNSS Version %d.%d %d.%d\n",
141 version->major, version->minor,
142 version->version, version->revision);
144 complete(&wcnss->ack);
146 case WCNSS_DOWNLOAD_NV_RESP:
147 if (count != sizeof(*nvresp)) {
149 "invalid size of download response\n");
154 wcnss->ack_status = nvresp->status;
155 complete(&wcnss->ack);
157 case WCNSS_CBC_COMPLETE_IND:
158 dev_dbg(wcnss->dev, "WCNSS booted\n");
159 complete(&wcnss->cbc);
162 dev_info(wcnss->dev, "unknown message type %d\n", hdr->type);
170 * wcnss_request_version() - send a version request to WCNSS
171 * @wcnss: wcnss ctrl driver context
173 static int wcnss_request_version(struct wcnss_ctrl *wcnss)
175 struct wcnss_msg_hdr msg;
178 msg.type = WCNSS_VERSION_REQ;
179 msg.len = sizeof(msg);
180 ret = qcom_smd_send(wcnss->channel, &msg, sizeof(msg));
184 ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_CBC_TIMEOUT);
186 dev_err(wcnss->dev, "timeout waiting for version response\n");
194 * wcnss_download_nv() - send nv binary to WCNSS
195 * @work: work struct to acquire wcnss context
197 static int wcnss_download_nv(struct wcnss_ctrl *wcnss)
199 struct wcnss_download_nv_req *req;
200 const struct firmware *fw;
205 req = kzalloc(sizeof(*req) + NV_FRAGMENT_SIZE, GFP_KERNEL);
209 ret = request_firmware(&fw, NVBIN_FILE, wcnss->dev);
211 dev_err(wcnss->dev, "Failed to load nv file %s: %d\n",
219 req->hdr.type = WCNSS_DOWNLOAD_NV_REQ;
220 req->hdr.len = sizeof(*req) + NV_FRAGMENT_SIZE;
223 req->frag_size = NV_FRAGMENT_SIZE;
227 if (left <= NV_FRAGMENT_SIZE) {
229 req->frag_size = left;
230 req->hdr.len = sizeof(*req) + left;
233 memcpy(req->fragment, data, req->frag_size);
235 ret = qcom_smd_send(wcnss->channel, req, req->hdr.len);
237 dev_err(wcnss->dev, "failed to send smd packet\n");
241 /* Increment for next fragment */
244 data += req->hdr.len;
245 left -= NV_FRAGMENT_SIZE;
248 ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_REQUEST_TIMEOUT);
250 dev_err(wcnss->dev, "timeout waiting for nv upload ack\n");
253 ret = wcnss->ack_status;
257 release_firmware(fw);
264 static void wcnss_async_probe(struct work_struct *work)
266 struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, probe_work);
269 ret = wcnss_request_version(wcnss);
273 ret = wcnss_download_nv(wcnss);
278 ret = wait_for_completion_timeout(&wcnss->cbc, WCNSS_REQUEST_TIMEOUT);
280 dev_err(wcnss->dev, "expected cbc completion\n");
283 of_platform_populate(wcnss->dev->of_node, NULL, NULL, wcnss->dev);
286 static int wcnss_ctrl_probe(struct qcom_smd_device *sdev)
288 struct wcnss_ctrl *wcnss;
290 wcnss = devm_kzalloc(&sdev->dev, sizeof(*wcnss), GFP_KERNEL);
294 wcnss->dev = &sdev->dev;
295 wcnss->channel = sdev->channel;
297 init_completion(&wcnss->ack);
298 init_completion(&wcnss->cbc);
299 INIT_WORK(&wcnss->probe_work, wcnss_async_probe);
301 dev_set_drvdata(&sdev->dev, wcnss);
303 schedule_work(&wcnss->probe_work);
308 static void wcnss_ctrl_remove(struct qcom_smd_device *sdev)
310 struct wcnss_ctrl *wcnss = dev_get_drvdata(&sdev->dev);
312 cancel_work_sync(&wcnss->probe_work);
313 of_platform_depopulate(&sdev->dev);
316 static const struct of_device_id wcnss_ctrl_of_match[] = {
317 { .compatible = "qcom,wcnss", },
321 static struct qcom_smd_driver wcnss_ctrl_driver = {
322 .probe = wcnss_ctrl_probe,
323 .remove = wcnss_ctrl_remove,
324 .callback = wcnss_ctrl_smd_callback,
326 .name = "qcom_wcnss_ctrl",
327 .owner = THIS_MODULE,
328 .of_match_table = wcnss_ctrl_of_match,
332 module_qcom_smd_driver(wcnss_ctrl_driver);
334 MODULE_DESCRIPTION("Qualcomm WCNSS control driver");
335 MODULE_LICENSE("GPL v2");