cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

wcnss_ctrl.c (8695B)


      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	struct device *dev = wcnss->dev;
    203	const char *nvbin = NVBIN_FILE;
    204	const void *data;
    205	ssize_t left;
    206	int ret;
    207
    208	req = kzalloc(sizeof(*req) + NV_FRAGMENT_SIZE, GFP_KERNEL);
    209	if (!req)
    210		return -ENOMEM;
    211
    212	ret = of_property_read_string(dev->of_node, "firmware-name", &nvbin);
    213	if (ret < 0 && ret != -EINVAL)
    214		goto free_req;
    215
    216	ret = request_firmware(&fw, nvbin, dev);
    217	if (ret < 0) {
    218		dev_err(dev, "Failed to load nv file %s: %d\n", nvbin, ret);
    219		goto free_req;
    220	}
    221
    222	data = fw->data;
    223	left = fw->size;
    224
    225	req->hdr.type = WCNSS_DOWNLOAD_NV_REQ;
    226	req->hdr.len = sizeof(*req) + NV_FRAGMENT_SIZE;
    227
    228	req->last = 0;
    229	req->frag_size = NV_FRAGMENT_SIZE;
    230
    231	req->seq = 0;
    232	do {
    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
    241		ret = rpmsg_send(wcnss->channel, req, req->hdr.len);
    242		if (ret < 0) {
    243			dev_err(dev, "failed to send smd packet\n");
    244			goto release_fw;
    245		}
    246
    247		/* Increment for next fragment */
    248		req->seq++;
    249
    250		data += NV_FRAGMENT_SIZE;
    251		left -= NV_FRAGMENT_SIZE;
    252	} while (left > 0);
    253
    254	ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_REQUEST_TIMEOUT);
    255	if (!ret) {
    256		dev_err(dev, "timeout waiting for nv upload ack\n");
    257		ret = -ETIMEDOUT;
    258	} else {
    259		*expect_cbc = wcnss->ack_status == WCNSS_ACK_COLD_BOOTING;
    260		ret = 0;
    261	}
    262
    263release_fw:
    264	release_firmware(fw);
    265free_req:
    266	kfree(req);
    267
    268	return ret;
    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 * @priv:	private data for use in the call-back
    277 */
    278struct rpmsg_endpoint *qcom_wcnss_open_channel(void *wcnss, const char *name, rpmsg_rx_cb_t cb, void *priv)
    279{
    280	struct rpmsg_channel_info chinfo;
    281	struct wcnss_ctrl *_wcnss = wcnss;
    282
    283	strscpy(chinfo.name, name, sizeof(chinfo.name));
    284	chinfo.src = RPMSG_ADDR_ANY;
    285	chinfo.dst = RPMSG_ADDR_ANY;
    286
    287	return rpmsg_create_ept(_wcnss->channel->rpdev, cb, priv, chinfo);
    288}
    289EXPORT_SYMBOL(qcom_wcnss_open_channel);
    290
    291static void wcnss_async_probe(struct work_struct *work)
    292{
    293	struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, probe_work);
    294	bool expect_cbc;
    295	int ret;
    296
    297	ret = wcnss_request_version(wcnss);
    298	if (ret < 0)
    299		return;
    300
    301	ret = wcnss_download_nv(wcnss, &expect_cbc);
    302	if (ret < 0)
    303		return;
    304
    305	/* Wait for pending cold boot completion if indicated by the nv downloader */
    306	if (expect_cbc) {
    307		ret = wait_for_completion_timeout(&wcnss->cbc, WCNSS_REQUEST_TIMEOUT);
    308		if (!ret)
    309			dev_err(wcnss->dev, "expected cold boot completion\n");
    310	}
    311
    312	of_platform_populate(wcnss->dev->of_node, NULL, NULL, wcnss->dev);
    313}
    314
    315static int wcnss_ctrl_probe(struct rpmsg_device *rpdev)
    316{
    317	struct wcnss_ctrl *wcnss;
    318
    319	wcnss = devm_kzalloc(&rpdev->dev, sizeof(*wcnss), GFP_KERNEL);
    320	if (!wcnss)
    321		return -ENOMEM;
    322
    323	wcnss->dev = &rpdev->dev;
    324	wcnss->channel = rpdev->ept;
    325
    326	init_completion(&wcnss->ack);
    327	init_completion(&wcnss->cbc);
    328	INIT_WORK(&wcnss->probe_work, wcnss_async_probe);
    329
    330	dev_set_drvdata(&rpdev->dev, wcnss);
    331
    332	schedule_work(&wcnss->probe_work);
    333
    334	return 0;
    335}
    336
    337static void wcnss_ctrl_remove(struct rpmsg_device *rpdev)
    338{
    339	struct wcnss_ctrl *wcnss = dev_get_drvdata(&rpdev->dev);
    340
    341	cancel_work_sync(&wcnss->probe_work);
    342	of_platform_depopulate(&rpdev->dev);
    343}
    344
    345static const struct of_device_id wcnss_ctrl_of_match[] = {
    346	{ .compatible = "qcom,wcnss", },
    347	{}
    348};
    349MODULE_DEVICE_TABLE(of, wcnss_ctrl_of_match);
    350
    351static struct rpmsg_driver wcnss_ctrl_driver = {
    352	.probe = wcnss_ctrl_probe,
    353	.remove = wcnss_ctrl_remove,
    354	.callback = wcnss_ctrl_smd_callback,
    355	.drv  = {
    356		.name  = "qcom_wcnss_ctrl",
    357		.owner = THIS_MODULE,
    358		.of_match_table = wcnss_ctrl_of_match,
    359	},
    360};
    361
    362module_rpmsg_driver(wcnss_ctrl_driver);
    363
    364MODULE_DESCRIPTION("Qualcomm WCNSS control driver");
    365MODULE_LICENSE("GPL v2");