1 /* 2 * Copyright (c) 2015, Sony Mobile Communications Inc. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 and 6 * only version 2 as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 */ 13 #include <linux/firmware.h> 14 #include <linux/module.h> 15 #include <linux/slab.h> 16 #include <linux/soc/qcom/smd.h> 17 18 #define WCNSS_REQUEST_TIMEOUT (5 * HZ) 19 20 #define NV_FRAGMENT_SIZE 3072 21 #define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin" 22 23 /** 24 * struct wcnss_ctrl - driver context 25 * @dev: device handle 26 * @channel: SMD channel handle 27 * @ack: completion for outstanding requests 28 * @ack_status: status of the outstanding request 29 * @download_nv_work: worker for uploading nv binary 30 */ 31 struct wcnss_ctrl { 32 struct device *dev; 33 struct qcom_smd_channel *channel; 34 35 struct completion ack; 36 int ack_status; 37 38 struct work_struct download_nv_work; 39 }; 40 41 /* message types */ 42 enum { 43 WCNSS_VERSION_REQ = 0x01000000, 44 WCNSS_VERSION_RESP, 45 WCNSS_DOWNLOAD_NV_REQ, 46 WCNSS_DOWNLOAD_NV_RESP, 47 WCNSS_UPLOAD_CAL_REQ, 48 WCNSS_UPLOAD_CAL_RESP, 49 WCNSS_DOWNLOAD_CAL_REQ, 50 WCNSS_DOWNLOAD_CAL_RESP, 51 }; 52 53 /** 54 * struct wcnss_msg_hdr - common packet header for requests and responses 55 * @type: packet message type 56 * @len: total length of the packet, including this header 57 */ 58 struct wcnss_msg_hdr { 59 u32 type; 60 u32 len; 61 } __packed; 62 63 /** 64 * struct wcnss_version_resp - version request response 65 * @hdr: common packet wcnss_msg_hdr header 66 */ 67 struct wcnss_version_resp { 68 struct wcnss_msg_hdr hdr; 69 u8 major; 70 u8 minor; 71 u8 version; 72 u8 revision; 73 } __packed; 74 75 /** 76 * struct wcnss_download_nv_req - firmware fragment request 77 * @hdr: common packet wcnss_msg_hdr header 78 * @seq: sequence number of this fragment 79 * @last: boolean indicator of this being the last fragment of the binary 80 * @frag_size: length of this fragment 81 * @fragment: fragment data 82 */ 83 struct wcnss_download_nv_req { 84 struct wcnss_msg_hdr hdr; 85 u16 seq; 86 u16 last; 87 u32 frag_size; 88 u8 fragment[]; 89 } __packed; 90 91 /** 92 * struct wcnss_download_nv_resp - firmware download response 93 * @hdr: common packet wcnss_msg_hdr header 94 * @status: boolean to indicate success of the download 95 */ 96 struct wcnss_download_nv_resp { 97 struct wcnss_msg_hdr hdr; 98 u8 status; 99 } __packed; 100 101 /** 102 * wcnss_ctrl_smd_callback() - handler from SMD responses 103 * @channel: smd channel handle 104 * @data: pointer to the incoming data packet 105 * @count: size of the incoming data packet 106 * 107 * Handles any incoming packets from the remote WCNSS_CTRL service. 108 */ 109 static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel, 110 const void *data, 111 size_t count) 112 { 113 struct wcnss_ctrl *wcnss = qcom_smd_get_drvdata(channel); 114 const struct wcnss_download_nv_resp *nvresp; 115 const struct wcnss_version_resp *version; 116 const struct wcnss_msg_hdr *hdr = data; 117 118 switch (hdr->type) { 119 case WCNSS_VERSION_RESP: 120 if (count != sizeof(*version)) { 121 dev_err(wcnss->dev, 122 "invalid size of version response\n"); 123 break; 124 } 125 126 version = data; 127 dev_info(wcnss->dev, "WCNSS Version %d.%d %d.%d\n", 128 version->major, version->minor, 129 version->version, version->revision); 130 131 schedule_work(&wcnss->download_nv_work); 132 break; 133 case WCNSS_DOWNLOAD_NV_RESP: 134 if (count != sizeof(*nvresp)) { 135 dev_err(wcnss->dev, 136 "invalid size of download response\n"); 137 break; 138 } 139 140 nvresp = data; 141 wcnss->ack_status = nvresp->status; 142 complete(&wcnss->ack); 143 break; 144 default: 145 dev_info(wcnss->dev, "unknown message type %d\n", hdr->type); 146 break; 147 } 148 149 return 0; 150 } 151 152 /** 153 * wcnss_request_version() - send a version request to WCNSS 154 * @wcnss: wcnss ctrl driver context 155 */ 156 static int wcnss_request_version(struct wcnss_ctrl *wcnss) 157 { 158 struct wcnss_msg_hdr msg; 159 160 msg.type = WCNSS_VERSION_REQ; 161 msg.len = sizeof(msg); 162 163 return qcom_smd_send(wcnss->channel, &msg, sizeof(msg)); 164 } 165 166 /** 167 * wcnss_download_nv() - send nv binary to WCNSS 168 * @work: work struct to acquire wcnss context 169 */ 170 static void wcnss_download_nv(struct work_struct *work) 171 { 172 struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, download_nv_work); 173 struct wcnss_download_nv_req *req; 174 const struct firmware *fw; 175 const void *data; 176 ssize_t left; 177 int ret; 178 179 req = kzalloc(sizeof(*req) + NV_FRAGMENT_SIZE, GFP_KERNEL); 180 if (!req) 181 return; 182 183 ret = request_firmware(&fw, NVBIN_FILE, wcnss->dev); 184 if (ret) { 185 dev_err(wcnss->dev, "Failed to load nv file %s: %d\n", 186 NVBIN_FILE, ret); 187 goto free_req; 188 } 189 190 data = fw->data; 191 left = fw->size; 192 193 req->hdr.type = WCNSS_DOWNLOAD_NV_REQ; 194 req->hdr.len = sizeof(*req) + NV_FRAGMENT_SIZE; 195 196 req->last = 0; 197 req->frag_size = NV_FRAGMENT_SIZE; 198 199 req->seq = 0; 200 do { 201 if (left <= NV_FRAGMENT_SIZE) { 202 req->last = 1; 203 req->frag_size = left; 204 req->hdr.len = sizeof(*req) + left; 205 } 206 207 memcpy(req->fragment, data, req->frag_size); 208 209 ret = qcom_smd_send(wcnss->channel, req, req->hdr.len); 210 if (ret) { 211 dev_err(wcnss->dev, "failed to send smd packet\n"); 212 goto release_fw; 213 } 214 215 /* Increment for next fragment */ 216 req->seq++; 217 218 data += req->hdr.len; 219 left -= NV_FRAGMENT_SIZE; 220 } while (left > 0); 221 222 ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_REQUEST_TIMEOUT); 223 if (!ret) 224 dev_err(wcnss->dev, "timeout waiting for nv upload ack\n"); 225 else if (wcnss->ack_status != 1) 226 dev_err(wcnss->dev, "nv upload response failed err: %d\n", 227 wcnss->ack_status); 228 229 release_fw: 230 release_firmware(fw); 231 free_req: 232 kfree(req); 233 } 234 235 static int wcnss_ctrl_probe(struct qcom_smd_device *sdev) 236 { 237 struct wcnss_ctrl *wcnss; 238 239 wcnss = devm_kzalloc(&sdev->dev, sizeof(*wcnss), GFP_KERNEL); 240 if (!wcnss) 241 return -ENOMEM; 242 243 wcnss->dev = &sdev->dev; 244 wcnss->channel = sdev->channel; 245 246 init_completion(&wcnss->ack); 247 INIT_WORK(&wcnss->download_nv_work, wcnss_download_nv); 248 249 qcom_smd_set_drvdata(sdev->channel, wcnss); 250 251 return wcnss_request_version(wcnss); 252 } 253 254 static const struct qcom_smd_id wcnss_ctrl_smd_match[] = { 255 { .name = "WCNSS_CTRL" }, 256 {} 257 }; 258 259 static struct qcom_smd_driver wcnss_ctrl_driver = { 260 .probe = wcnss_ctrl_probe, 261 .callback = wcnss_ctrl_smd_callback, 262 .smd_match_table = wcnss_ctrl_smd_match, 263 .driver = { 264 .name = "qcom_wcnss_ctrl", 265 .owner = THIS_MODULE, 266 }, 267 }; 268 269 module_qcom_smd_driver(wcnss_ctrl_driver); 270 271 MODULE_DESCRIPTION("Qualcomm WCNSS control driver"); 272 MODULE_LICENSE("GPL v2"); 273