2 * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
3 * Copyright (c) 2013 Qualcomm Atheros, Inc.
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
12 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
14 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
15 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <linux/completion.h>
19 #include <linux/firmware.h>
20 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/regulator/consumer.h>
23 #include <linux/workqueue.h>
25 #include <linux/of_platform.h>
26 #include <linux/clk.h>
27 #include <linux/remoteproc.h>
28 #include <linux/soc/qcom/smd.h>
31 #define MAC_ADDR_0 "wlan/macaddr0"
33 struct smd_packet_item {
34 struct list_head list;
39 static int wcn36xx_msm_smsm_change_state(u32 clear_mask, u32 set_mask)
44 static int wcn36xx_msm_get_hw_mac(struct wcn36xx *wcn, u8 *addr)
46 const struct firmware *addr_file = NULL;
49 static const u8 qcom_oui[3] = {0x00, 0x0A, 0xF5};
50 static const char *files = {MAC_ADDR_0};
51 struct wcn36xx_platform_data *pdata = wcn->wcn36xx_data;
53 status = request_firmware(&addr_file, files, &pdata->core->dev);
56 /* Assign a random mac with Qualcomm oui */
57 dev_err(&pdata->core->dev, "Failed (%d) to read macaddress"
58 "file %s, using a random address instead", status, files);
59 memcpy(addr, qcom_oui, 3);
60 get_random_bytes(addr + 3, 3);
62 memset(tmp, 0, sizeof(tmp));
63 memcpy(tmp, addr_file->data, sizeof(tmp) - 1);
64 sscanf(tmp, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
72 release_firmware(addr_file);
78 static int wcn36xx_msm_smd_send_and_wait(struct wcn36xx *wcn, char *buf, size_t len)
81 struct wcn36xx_platform_data *pdata = wcn->wcn36xx_data;
83 mutex_lock(&pdata->wlan_ctrl_lock);
84 ret = qcom_smd_send(pdata->wlan_ctrl_channel, buf, len);
86 dev_err(wcn->dev, "wlan ctrl channel tx failed\n");
88 mutex_unlock(&pdata->wlan_ctrl_lock);
93 static int wcn36xx_msm_smd_open(struct wcn36xx *wcn, void *rsp_cb)
95 struct wcn36xx_platform_data *pdata = wcn->wcn36xx_data;
101 static void wcn36xx_msm_smd_close(struct wcn36xx *wcn)
106 static int wcn36xx_msm_get_chip_type(struct wcn36xx *wcn)
108 struct wcn36xx_platform_data *pdata = wcn->wcn36xx_data;
109 return pdata->chip_type;
112 static struct wcn36xx_platform_data wcn36xx_data = {
114 .open = wcn36xx_msm_smd_open,
115 .close = wcn36xx_msm_smd_close,
116 .tx = wcn36xx_msm_smd_send_and_wait,
117 .get_hw_mac = wcn36xx_msm_get_hw_mac,
118 .smsm_change_state = wcn36xx_msm_smsm_change_state,
119 .get_chip_type = wcn36xx_msm_get_chip_type,
123 static void wlan_ctrl_smd_process(struct work_struct *worker)
126 struct wcn36xx_platform_data *pdata =
128 struct wcn36xx_platform_data, packet_process_work);
130 spin_lock_irqsave(&pdata->packet_lock, flags);
131 while (!list_empty(&pdata->packet_list)) {
132 struct smd_packet_item *packet;
134 packet = list_first_entry(&pdata->packet_list,
135 struct smd_packet_item, list);
136 list_del(&packet->list);
137 spin_unlock_irqrestore(&pdata->packet_lock, flags);
138 pdata->cb(pdata->wcn, packet->buf, packet->count);
140 spin_lock_irqsave(&pdata->packet_lock, flags);
142 spin_unlock_irqrestore(&pdata->packet_lock, flags);
145 static int qcom_smd_wlan_ctrl_probe(struct qcom_smd_device *sdev)
147 pr_info("%s: enter\n", __func__);
148 mutex_init(&wcn36xx_data.wlan_ctrl_lock);
149 init_completion(&wcn36xx_data.wlan_ctrl_ack);
151 wcn36xx_data.sdev = sdev;
152 spin_lock_init(&wcn36xx_data.packet_lock);
153 INIT_LIST_HEAD(&wcn36xx_data.packet_list);
154 INIT_WORK(&wcn36xx_data.packet_process_work, wlan_ctrl_smd_process);
156 dev_set_drvdata(&sdev->dev, &wcn36xx_data);
157 wcn36xx_data.wlan_ctrl_channel = sdev->channel;
159 wcn36xx_data.chip_type = (enum wcn36xx_chip_type)of_device_get_match_data(&sdev->dev);
160 wcn36xx_data.core = platform_device_alloc("wcn36xx", -1);
161 wcn36xx_data.core->dev.parent = &sdev->dev;
162 wcn36xx_data.core->dev.platform_data = &wcn36xx_data;
164 platform_device_add(wcn36xx_data.core);
166 of_platform_populate(sdev->dev.of_node, NULL, NULL, &sdev->dev);
171 static void qcom_smd_wlan_ctrl_remove(struct qcom_smd_device *sdev)
173 of_platform_depopulate(&sdev->dev);
174 platform_device_del(wcn36xx_data.core);
175 platform_device_put(wcn36xx_data.core);
178 static int qcom_smd_wlan_ctrl_callback(struct qcom_smd_device *qsdev,
183 struct smd_packet_item *packet = NULL;
184 struct wcn36xx_platform_data *pdata = dev_get_drvdata(&qsdev->dev);
185 void *buf = kzalloc(count + sizeof(struct smd_packet_item),
188 dev_err(&pdata->core->dev, "can't allocate buffer\n");
192 memcpy_fromio(buf, data, count);
193 packet = buf + count;
195 packet->count = count;
197 spin_lock_irqsave(&pdata->packet_lock, flags);
198 list_add_tail(&packet->list, &pdata->packet_list);
199 spin_unlock_irqrestore(&pdata->packet_lock, flags);
200 schedule_work(&pdata->packet_process_work);
202 /* buf will be freed in workqueue */
207 static const struct of_device_id wcn36xx_msm_match_table[] = {
208 { .compatible = "qcom,wcn3660-wlan", .data = (void *)WCN36XX_CHIP_3660 },
209 { .compatible = "qcom,wcn3680-wlan", .data = (void *)WCN36XX_CHIP_3680 },
210 { .compatible = "qcom,wcn3620-wlan", .data = (void *)WCN36XX_CHIP_3620 },
213 MODULE_DEVICE_TABLE(of, wcn36xx_msm_match_table);
215 static struct qcom_smd_driver qcom_smd_wlan_ctrl_driver = {
216 .probe = qcom_smd_wlan_ctrl_probe,
217 .remove = qcom_smd_wlan_ctrl_remove,
218 .callback = qcom_smd_wlan_ctrl_callback,
220 .name = "qcom_smd_wlan_ctrl",
221 .owner = THIS_MODULE,
222 .of_match_table = wcn36xx_msm_match_table,
226 static int __init wcn36xx_msm_init(void)
228 return qcom_smd_driver_register(&qcom_smd_wlan_ctrl_driver);
230 module_init(wcn36xx_msm_init);
232 static void __exit wcn36xx_msm_exit(void)
235 module_exit(wcn36xx_msm_exit);
237 MODULE_LICENSE("GPL");
238 MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com");
239 MODULE_FIRMWARE(MAC_ADDR_0);