]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/net/wireless/ath/wcn36xx/wcn36xx-msm.c
soc: qcom: wcnss_ctrl: Make wcnss_ctrl parent the other components
[karo-tx-linux.git] / drivers / net / wireless / ath / wcn36xx / wcn36xx-msm.c
1 /*
2  * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
3  * Copyright (c) 2013 Qualcomm Atheros, Inc.
4  *
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.
8  *
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.
16  */
17
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>
24 #include <linux/of.h>
25 #include <linux/of_platform.h>
26 #include <linux/clk.h>
27 #include <linux/remoteproc.h>
28 #include <linux/soc/qcom/smd.h>
29 #include "wcn36xx.h"
30
31 #define MAC_ADDR_0 "wlan/macaddr0"
32
33 struct smd_packet_item {
34         struct list_head list;
35         void *buf;
36         size_t count;
37 };
38
39 static int wcn36xx_msm_smsm_change_state(u32 clear_mask, u32 set_mask)
40 {
41         return 0;
42 }
43
44 static int wcn36xx_msm_get_hw_mac(struct wcn36xx *wcn, u8 *addr)
45 {
46         const struct firmware *addr_file = NULL;
47         int status;
48         u8 tmp[18];
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;
52
53         status = request_firmware(&addr_file, files, &pdata->core->dev);
54
55         if (status < 0) {
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);
61         } else {
62                 memset(tmp, 0, sizeof(tmp));
63                 memcpy(tmp, addr_file->data, sizeof(tmp) - 1);
64                 sscanf(tmp, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
65                        &addr[0],
66                        &addr[1],
67                        &addr[2],
68                        &addr[3],
69                        &addr[4],
70                        &addr[5]);
71
72                 release_firmware(addr_file);
73         }
74
75         return 0;
76 }
77
78 static int wcn36xx_msm_smd_send_and_wait(struct wcn36xx *wcn, char *buf, size_t len)
79 {
80         int ret = 0;
81         struct wcn36xx_platform_data *pdata = wcn->wcn36xx_data;
82
83         mutex_lock(&pdata->wlan_ctrl_lock);
84         ret = qcom_smd_send(pdata->wlan_ctrl_channel, buf, len);
85         if (ret) {
86                 dev_err(wcn->dev, "wlan ctrl channel tx failed\n");
87         }
88         mutex_unlock(&pdata->wlan_ctrl_lock);
89
90         return ret;
91 }
92
93 static int wcn36xx_msm_smd_open(struct wcn36xx *wcn, void *rsp_cb)
94 {
95         struct wcn36xx_platform_data *pdata = wcn->wcn36xx_data;
96
97         pdata->cb = rsp_cb;
98         return 0;
99 }
100
101 static void wcn36xx_msm_smd_close(struct wcn36xx *wcn)
102 {
103         return;
104 }
105
106 static int wcn36xx_msm_get_chip_type(struct wcn36xx *wcn)
107 {
108         struct wcn36xx_platform_data *pdata = wcn->wcn36xx_data;
109         return pdata->chip_type;
110 }
111
112 static struct wcn36xx_platform_data wcn36xx_data = {
113         .ctrl_ops = {
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,
120         },
121 };
122
123 static void wlan_ctrl_smd_process(struct work_struct *worker)
124 {
125         unsigned long flags;
126         struct wcn36xx_platform_data *pdata =
127                 container_of(worker,
128                         struct wcn36xx_platform_data, packet_process_work);
129
130         spin_lock_irqsave(&pdata->packet_lock, flags);
131         while (!list_empty(&pdata->packet_list)) {
132                 struct smd_packet_item *packet;
133
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);
139                 kfree(packet->buf);
140                 spin_lock_irqsave(&pdata->packet_lock, flags);
141         }
142         spin_unlock_irqrestore(&pdata->packet_lock, flags);
143 }
144
145 static int qcom_smd_wlan_ctrl_probe(struct qcom_smd_device *sdev)
146 {
147         pr_info("%s: enter\n", __func__);
148         mutex_init(&wcn36xx_data.wlan_ctrl_lock);
149         init_completion(&wcn36xx_data.wlan_ctrl_ack);
150
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);
155
156         dev_set_drvdata(&sdev->dev, &wcn36xx_data);
157         wcn36xx_data.wlan_ctrl_channel = sdev->channel;
158
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;
163
164         platform_device_add(wcn36xx_data.core);
165
166         of_platform_populate(sdev->dev.of_node, NULL, NULL, &sdev->dev);
167
168         return 0;
169 }
170
171 static void qcom_smd_wlan_ctrl_remove(struct qcom_smd_device *sdev)
172 {
173         of_platform_depopulate(&sdev->dev);
174         platform_device_del(wcn36xx_data.core);
175         platform_device_put(wcn36xx_data.core);
176 }
177
178 static int qcom_smd_wlan_ctrl_callback(struct qcom_smd_device *qsdev,
179                                  const void *data,
180                                  size_t count)
181 {
182         unsigned long flags;
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),
186                                 GFP_ATOMIC);
187         if (!buf) {
188                 dev_err(&pdata->core->dev, "can't allocate buffer\n");
189                 return -ENOMEM;
190         }
191
192         memcpy_fromio(buf, data, count);
193         packet = buf + count;
194         packet->buf = buf;
195         packet->count = count;
196
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);
201
202         /* buf will be freed in workqueue */
203
204         return 0;
205 }
206
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 },
211         { }
212 };
213 MODULE_DEVICE_TABLE(of, wcn36xx_msm_match_table);
214
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,
219         .driver  = {
220                 .name  = "qcom_smd_wlan_ctrl",
221                 .owner = THIS_MODULE,
222                 .of_match_table = wcn36xx_msm_match_table,
223         },
224 };
225
226 static int __init wcn36xx_msm_init(void)
227 {
228         return qcom_smd_driver_register(&qcom_smd_wlan_ctrl_driver);
229 }
230 module_init(wcn36xx_msm_init);
231
232 static void __exit wcn36xx_msm_exit(void)
233 {
234 }
235 module_exit(wcn36xx_msm_exit);
236
237 MODULE_LICENSE("GPL");
238 MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com");
239 MODULE_FIRMWARE(MAC_ADDR_0);
240