wcnss_ctrl.c (f26e8817b235d8764363bffcc9cbfc61867371f2) wcnss_ctrl.c (5052de8deff5619a9b7071f00084fd0264b58e17)
1/*
2 * Copyright (c) 2016, Linaro Ltd.
3 * Copyright (c) 2015, Sony Mobile Communications Inc.
4 *
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.
8 *
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.
13 */
14#include <linux/firmware.h>
15#include <linux/module.h>
16#include <linux/slab.h>
1/*
2 * Copyright (c) 2016, Linaro Ltd.
3 * Copyright (c) 2015, Sony Mobile Communications Inc.
4 *
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.
8 *
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.
13 */
14#include <linux/firmware.h>
15#include <linux/module.h>
16#include <linux/slab.h>
17#include <linux/soc/qcom/smd.h>
18#include <linux/io.h>
19#include <linux/of_platform.h>
20#include <linux/platform_device.h>
17#include <linux/io.h>
18#include <linux/of_platform.h>
19#include <linux/platform_device.h>
20#include <linux/rpmsg.h>
21#include <linux/soc/qcom/wcnss_ctrl.h>
22
23#define WCNSS_REQUEST_TIMEOUT (5 * HZ)
24#define WCNSS_CBC_TIMEOUT (10 * HZ)
25
26#define WCNSS_ACK_DONE_BOOTING 1
27#define WCNSS_ACK_COLD_BOOTING 2
28

--- 6 unchanged lines hidden (view full) ---

35 * @channel: SMD channel handle
36 * @ack: completion for outstanding requests
37 * @cbc: completion for cbc complete indication
38 * @ack_status: status of the outstanding request
39 * @probe_work: worker for uploading nv binary
40 */
41struct wcnss_ctrl {
42 struct device *dev;
21#include <linux/soc/qcom/wcnss_ctrl.h>
22
23#define WCNSS_REQUEST_TIMEOUT (5 * HZ)
24#define WCNSS_CBC_TIMEOUT (10 * HZ)
25
26#define WCNSS_ACK_DONE_BOOTING 1
27#define WCNSS_ACK_COLD_BOOTING 2
28

--- 6 unchanged lines hidden (view full) ---

35 * @channel: SMD channel handle
36 * @ack: completion for outstanding requests
37 * @cbc: completion for cbc complete indication
38 * @ack_status: status of the outstanding request
39 * @probe_work: worker for uploading nv binary
40 */
41struct wcnss_ctrl {
42 struct device *dev;
43 struct qcom_smd_channel *channel;
43 struct rpmsg_endpoint *channel;
44
45 struct completion ack;
46 struct completion cbc;
47 int ack_status;
48
49 struct work_struct probe_work;
50};
51

--- 65 unchanged lines hidden (view full) ---

117/**
118 * wcnss_ctrl_smd_callback() - handler from SMD responses
119 * @channel: smd channel handle
120 * @data: pointer to the incoming data packet
121 * @count: size of the incoming data packet
122 *
123 * Handles any incoming packets from the remote WCNSS_CTRL service.
124 */
44
45 struct completion ack;
46 struct completion cbc;
47 int ack_status;
48
49 struct work_struct probe_work;
50};
51

--- 65 unchanged lines hidden (view full) ---

117/**
118 * wcnss_ctrl_smd_callback() - handler from SMD responses
119 * @channel: smd channel handle
120 * @data: pointer to the incoming data packet
121 * @count: size of the incoming data packet
122 *
123 * Handles any incoming packets from the remote WCNSS_CTRL service.
124 */
125static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel,
126 const void *data,
127 size_t count)
125static int wcnss_ctrl_smd_callback(struct rpmsg_device *rpdev,
126 void *data,
127 int count,
128 void *priv,
129 u32 addr)
128{
130{
129 struct wcnss_ctrl *wcnss = qcom_smd_get_drvdata(channel);
131 struct wcnss_ctrl *wcnss = dev_get_drvdata(&rpdev->dev);
130 const struct wcnss_download_nv_resp *nvresp;
131 const struct wcnss_version_resp *version;
132 const struct wcnss_msg_hdr *hdr = data;
133
134 switch (hdr->type) {
135 case WCNSS_VERSION_RESP:
136 if (count != sizeof(*version)) {
137 dev_err(wcnss->dev,

--- 37 unchanged lines hidden (view full) ---

175 */
176static int wcnss_request_version(struct wcnss_ctrl *wcnss)
177{
178 struct wcnss_msg_hdr msg;
179 int ret;
180
181 msg.type = WCNSS_VERSION_REQ;
182 msg.len = sizeof(msg);
132 const struct wcnss_download_nv_resp *nvresp;
133 const struct wcnss_version_resp *version;
134 const struct wcnss_msg_hdr *hdr = data;
135
136 switch (hdr->type) {
137 case WCNSS_VERSION_RESP:
138 if (count != sizeof(*version)) {
139 dev_err(wcnss->dev,

--- 37 unchanged lines hidden (view full) ---

177 */
178static int wcnss_request_version(struct wcnss_ctrl *wcnss)
179{
180 struct wcnss_msg_hdr msg;
181 int ret;
182
183 msg.type = WCNSS_VERSION_REQ;
184 msg.len = sizeof(msg);
183 ret = qcom_smd_send(wcnss->channel, &msg, sizeof(msg));
185 ret = rpmsg_send(wcnss->channel, &msg, sizeof(msg));
184 if (ret < 0)
185 return ret;
186
187 ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_CBC_TIMEOUT);
188 if (!ret) {
189 dev_err(wcnss->dev, "timeout waiting for version response\n");
190 return -ETIMEDOUT;
191 }

--- 41 unchanged lines hidden (view full) ---

233 if (left <= NV_FRAGMENT_SIZE) {
234 req->last = 1;
235 req->frag_size = left;
236 req->hdr.len = sizeof(*req) + left;
237 }
238
239 memcpy(req->fragment, data, req->frag_size);
240
186 if (ret < 0)
187 return ret;
188
189 ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_CBC_TIMEOUT);
190 if (!ret) {
191 dev_err(wcnss->dev, "timeout waiting for version response\n");
192 return -ETIMEDOUT;
193 }

--- 41 unchanged lines hidden (view full) ---

235 if (left <= NV_FRAGMENT_SIZE) {
236 req->last = 1;
237 req->frag_size = left;
238 req->hdr.len = sizeof(*req) + left;
239 }
240
241 memcpy(req->fragment, data, req->frag_size);
242
241 ret = qcom_smd_send(wcnss->channel, req, req->hdr.len);
243 ret = rpmsg_send(wcnss->channel, req, req->hdr.len);
242 if (ret < 0) {
243 dev_err(wcnss->dev, "failed to send smd packet\n");
244 goto release_fw;
245 }
246
247 /* Increment for next fragment */
248 req->seq++;
249

--- 19 unchanged lines hidden (view full) ---

269}
270
271/**
272 * qcom_wcnss_open_channel() - open additional SMD channel to WCNSS
273 * @wcnss: wcnss handle, retrieved from drvdata
274 * @name: SMD channel name
275 * @cb: callback to handle incoming data on the channel
276 */
244 if (ret < 0) {
245 dev_err(wcnss->dev, "failed to send smd packet\n");
246 goto release_fw;
247 }
248
249 /* Increment for next fragment */
250 req->seq++;
251

--- 19 unchanged lines hidden (view full) ---

271}
272
273/**
274 * qcom_wcnss_open_channel() - open additional SMD channel to WCNSS
275 * @wcnss: wcnss handle, retrieved from drvdata
276 * @name: SMD channel name
277 * @cb: callback to handle incoming data on the channel
278 */
277struct qcom_smd_channel *qcom_wcnss_open_channel(void *wcnss, const char *name, qcom_smd_cb_t cb)
279struct rpmsg_endpoint *qcom_wcnss_open_channel(void *wcnss, const char *name, rpmsg_rx_cb_t cb, void *priv)
278{
280{
281 struct rpmsg_channel_info chinfo;
279 struct wcnss_ctrl *_wcnss = wcnss;
280
282 struct wcnss_ctrl *_wcnss = wcnss;
283
281 return qcom_smd_open_channel(_wcnss->channel, name, cb);
284 strncpy(chinfo.name, name, sizeof(chinfo.name));
285 chinfo.src = RPMSG_ADDR_ANY;
286 chinfo.dst = RPMSG_ADDR_ANY;
287
288 return rpmsg_create_ept(_wcnss->channel->rpdev, cb, priv, chinfo);
282}
283EXPORT_SYMBOL(qcom_wcnss_open_channel);
284
285static void wcnss_async_probe(struct work_struct *work)
286{
287 struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, probe_work);
288 bool expect_cbc;
289 int ret;

--- 11 unchanged lines hidden (view full) ---

301 ret = wait_for_completion_timeout(&wcnss->cbc, WCNSS_REQUEST_TIMEOUT);
302 if (!ret)
303 dev_err(wcnss->dev, "expected cold boot completion\n");
304 }
305
306 of_platform_populate(wcnss->dev->of_node, NULL, NULL, wcnss->dev);
307}
308
289}
290EXPORT_SYMBOL(qcom_wcnss_open_channel);
291
292static void wcnss_async_probe(struct work_struct *work)
293{
294 struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, probe_work);
295 bool expect_cbc;
296 int ret;

--- 11 unchanged lines hidden (view full) ---

308 ret = wait_for_completion_timeout(&wcnss->cbc, WCNSS_REQUEST_TIMEOUT);
309 if (!ret)
310 dev_err(wcnss->dev, "expected cold boot completion\n");
311 }
312
313 of_platform_populate(wcnss->dev->of_node, NULL, NULL, wcnss->dev);
314}
315
309static int wcnss_ctrl_probe(struct qcom_smd_device *sdev)
316static int wcnss_ctrl_probe(struct rpmsg_device *rpdev)
310{
311 struct wcnss_ctrl *wcnss;
312
317{
318 struct wcnss_ctrl *wcnss;
319
313 wcnss = devm_kzalloc(&sdev->dev, sizeof(*wcnss), GFP_KERNEL);
320 wcnss = devm_kzalloc(&rpdev->dev, sizeof(*wcnss), GFP_KERNEL);
314 if (!wcnss)
315 return -ENOMEM;
316
321 if (!wcnss)
322 return -ENOMEM;
323
317 wcnss->dev = &sdev->dev;
318 wcnss->channel = sdev->channel;
324 wcnss->dev = &rpdev->dev;
325 wcnss->channel = rpdev->ept;
319
320 init_completion(&wcnss->ack);
321 init_completion(&wcnss->cbc);
322 INIT_WORK(&wcnss->probe_work, wcnss_async_probe);
323
326
327 init_completion(&wcnss->ack);
328 init_completion(&wcnss->cbc);
329 INIT_WORK(&wcnss->probe_work, wcnss_async_probe);
330
324 qcom_smd_set_drvdata(sdev->channel, wcnss);
325 dev_set_drvdata(&sdev->dev, wcnss);
331 dev_set_drvdata(&rpdev->dev, wcnss);
326
327 schedule_work(&wcnss->probe_work);
328
329 return 0;
330}
331
332
333 schedule_work(&wcnss->probe_work);
334
335 return 0;
336}
337
332static void wcnss_ctrl_remove(struct qcom_smd_device *sdev)
338static void wcnss_ctrl_remove(struct rpmsg_device *rpdev)
333{
339{
334 struct wcnss_ctrl *wcnss = qcom_smd_get_drvdata(sdev->channel);
340 struct wcnss_ctrl *wcnss = dev_get_drvdata(&rpdev->dev);
335
336 cancel_work_sync(&wcnss->probe_work);
341
342 cancel_work_sync(&wcnss->probe_work);
337 of_platform_depopulate(&sdev->dev);
343 of_platform_depopulate(&rpdev->dev);
338}
339
340static const struct of_device_id wcnss_ctrl_of_match[] = {
341 { .compatible = "qcom,wcnss", },
342 {}
343};
344
344}
345
346static const struct of_device_id wcnss_ctrl_of_match[] = {
347 { .compatible = "qcom,wcnss", },
348 {}
349};
350
345static struct qcom_smd_driver wcnss_ctrl_driver = {
351static struct rpmsg_driver wcnss_ctrl_driver = {
346 .probe = wcnss_ctrl_probe,
347 .remove = wcnss_ctrl_remove,
348 .callback = wcnss_ctrl_smd_callback,
352 .probe = wcnss_ctrl_probe,
353 .remove = wcnss_ctrl_remove,
354 .callback = wcnss_ctrl_smd_callback,
349 .driver = {
355 .drv = {
350 .name = "qcom_wcnss_ctrl",
351 .owner = THIS_MODULE,
352 .of_match_table = wcnss_ctrl_of_match,
353 },
354};
355
356 .name = "qcom_wcnss_ctrl",
357 .owner = THIS_MODULE,
358 .of_match_table = wcnss_ctrl_of_match,
359 },
360};
361
356module_qcom_smd_driver(wcnss_ctrl_driver);
362module_rpmsg_driver(wcnss_ctrl_driver);
357
358MODULE_DESCRIPTION("Qualcomm WCNSS control driver");
359MODULE_LICENSE("GPL v2");
363
364MODULE_DESCRIPTION("Qualcomm WCNSS control driver");
365MODULE_LICENSE("GPL v2");