blob: 575355ce675f325b71aafeb9ed2ff3979ed34e82 [file] [log] [blame]
Govind Singh17f55592018-04-10 18:01:14 +03001/*
2 * Copyright (c) 2018 The Linux Foundation. All rights reserved.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <linux/module.h>
18#include <linux/kernel.h>
19#include "debug.h"
20#include "hif.h"
21#include "htc.h"
22#include "ce.h"
23#include "snoc.h"
24#include <linux/of.h>
25#include <linux/of_device.h>
26#include <linux/platform_device.h>
Govind Singhc963a682018-04-10 18:01:15 +030027#define WCN3990_CE_ATTR_FLAGS 0
28
29static char *const ce_name[] = {
30 "WLAN_CE_0",
31 "WLAN_CE_1",
32 "WLAN_CE_2",
33 "WLAN_CE_3",
34 "WLAN_CE_4",
35 "WLAN_CE_5",
36 "WLAN_CE_6",
37 "WLAN_CE_7",
38 "WLAN_CE_8",
39 "WLAN_CE_9",
40 "WLAN_CE_10",
41 "WLAN_CE_11",
42};
Govind Singh17f55592018-04-10 18:01:14 +030043
44static const struct ath10k_snoc_drv_priv drv_priv = {
45 .hw_rev = ATH10K_HW_WCN3990,
46 .dma_mask = DMA_BIT_MASK(37),
47};
48
Govind Singhc963a682018-04-10 18:01:15 +030049static struct ce_attr host_ce_config_wlan[] = {
50 /* CE0: host->target HTC control streams */
51 {
52 .flags = WCN3990_CE_ATTR_FLAGS,
53 .src_nentries = 16,
54 .src_sz_max = 2048,
55 .dest_nentries = 0,
56 .send_cb = NULL,
57 },
58
59 /* CE1: target->host HTT + HTC control */
60 {
61 .flags = WCN3990_CE_ATTR_FLAGS,
62 .src_nentries = 0,
63 .src_sz_max = 2048,
64 .dest_nentries = 512,
65 .recv_cb = NULL,
66 },
67
68 /* CE2: target->host WMI */
69 {
70 .flags = WCN3990_CE_ATTR_FLAGS,
71 .src_nentries = 0,
72 .src_sz_max = 2048,
73 .dest_nentries = 64,
74 .recv_cb = NULL,
75 },
76
77 /* CE3: host->target WMI */
78 {
79 .flags = WCN3990_CE_ATTR_FLAGS,
80 .src_nentries = 32,
81 .src_sz_max = 2048,
82 .dest_nentries = 0,
83 .send_cb = NULL,
84 },
85
86 /* CE4: host->target HTT */
87 {
88 .flags = WCN3990_CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
89 .src_nentries = 256,
90 .src_sz_max = 256,
91 .dest_nentries = 0,
92 .send_cb = NULL,
93 },
94
95 /* CE5: target->host HTT (ipa_uc->target ) */
96 {
97 .flags = WCN3990_CE_ATTR_FLAGS,
98 .src_nentries = 0,
99 .src_sz_max = 512,
100 .dest_nentries = 512,
101 .recv_cb = NULL,
102 },
103
104 /* CE6: target autonomous hif_memcpy */
105 {
106 .flags = WCN3990_CE_ATTR_FLAGS,
107 .src_nentries = 0,
108 .src_sz_max = 0,
109 .dest_nentries = 0,
110 },
111
112 /* CE7: ce_diag, the Diagnostic Window */
113 {
114 .flags = WCN3990_CE_ATTR_FLAGS,
115 .src_nentries = 2,
116 .src_sz_max = 2048,
117 .dest_nentries = 2,
118 },
119
120 /* CE8: Target to uMC */
121 {
122 .flags = WCN3990_CE_ATTR_FLAGS,
123 .src_nentries = 0,
124 .src_sz_max = 2048,
125 .dest_nentries = 128,
126 },
127
128 /* CE9 target->host HTT */
129 {
130 .flags = WCN3990_CE_ATTR_FLAGS,
131 .src_nentries = 0,
132 .src_sz_max = 2048,
133 .dest_nentries = 512,
134 .recv_cb = NULL,
135 },
136
137 /* CE10: target->host HTT */
138 {
139 .flags = WCN3990_CE_ATTR_FLAGS,
140 .src_nentries = 0,
141 .src_sz_max = 2048,
142 .dest_nentries = 512,
143 .recv_cb = NULL,
144 },
145
146 /* CE11: target -> host PKTLOG */
147 {
148 .flags = WCN3990_CE_ATTR_FLAGS,
149 .src_nentries = 0,
150 .src_sz_max = 2048,
151 .dest_nentries = 512,
152 .recv_cb = NULL,
153 },
154};
155
Govind Singh17f55592018-04-10 18:01:14 +0300156void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value)
157{
158 struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
159
160 iowrite32(value, ar_snoc->mem + offset);
161}
162
163u32 ath10k_snoc_read32(struct ath10k *ar, u32 offset)
164{
165 struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
166 u32 val;
167
168 val = ioread32(ar_snoc->mem + offset);
169
170 return val;
171}
172
173static const struct ath10k_hif_ops ath10k_snoc_hif_ops = {
174 .read32 = ath10k_snoc_read32,
175 .write32 = ath10k_snoc_write32,
176};
177
178static const struct ath10k_bus_ops ath10k_snoc_bus_ops = {
179 .read32 = ath10k_snoc_read32,
180 .write32 = ath10k_snoc_write32,
181};
182
Govind Singhc963a682018-04-10 18:01:15 +0300183static irqreturn_t ath10k_snoc_per_engine_handler(int irq, void *arg)
184{
185 return IRQ_HANDLED;
186}
187
188static int ath10k_snoc_request_irq(struct ath10k *ar)
189{
190 struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
191 int irqflags = IRQF_TRIGGER_RISING;
192 int ret, id;
193
194 for (id = 0; id < CE_COUNT_MAX; id++) {
195 ret = request_irq(ar_snoc->ce_irqs[id].irq_line,
196 ath10k_snoc_per_engine_handler,
197 irqflags, ce_name[id], ar);
198 if (ret) {
199 ath10k_err(ar,
200 "failed to register IRQ handler for CE %d: %d",
201 id, ret);
202 goto err_irq;
203 }
204 }
205
206 return 0;
207
208err_irq:
209 for (id -= 1; id >= 0; id--)
210 free_irq(ar_snoc->ce_irqs[id].irq_line, ar);
211
212 return ret;
213}
214
215static void ath10k_snoc_free_irq(struct ath10k *ar)
216{
217 struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
218 int id;
219
220 for (id = 0; id < CE_COUNT_MAX; id++)
221 free_irq(ar_snoc->ce_irqs[id].irq_line, ar);
222}
223
224static int ath10k_snoc_resource_init(struct ath10k *ar)
225{
226 struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
227 struct platform_device *pdev;
228 struct resource *res;
229 int i, ret = 0;
230
231 pdev = ar_snoc->dev;
232 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase");
233 if (!res) {
234 ath10k_err(ar, "Memory base not found in DT\n");
235 return -EINVAL;
236 }
237
238 ar_snoc->mem_pa = res->start;
239 ar_snoc->mem = devm_ioremap(&pdev->dev, ar_snoc->mem_pa,
240 resource_size(res));
241 if (!ar_snoc->mem) {
242 ath10k_err(ar, "Memory base ioremap failed with physical address %pa\n",
243 &ar_snoc->mem_pa);
244 return -EINVAL;
245 }
246
247 for (i = 0; i < CE_COUNT; i++) {
248 res = platform_get_resource(ar_snoc->dev, IORESOURCE_IRQ, i);
249 if (!res) {
250 ath10k_err(ar, "failed to get IRQ%d\n", i);
251 ret = -ENODEV;
252 goto out;
253 }
254 ar_snoc->ce_irqs[i].irq_line = res->start;
255 }
256
257out:
258 return ret;
259}
260
261static int ath10k_snoc_setup_resource(struct ath10k *ar)
262{
263 struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
264 struct ath10k_ce *ce = ath10k_ce_priv(ar);
265 struct ath10k_snoc_pipe *pipe;
266 int i, ret;
267
268 spin_lock_init(&ce->ce_lock);
269 for (i = 0; i < CE_COUNT; i++) {
270 pipe = &ar_snoc->pipe_info[i];
271 pipe->ce_hdl = &ce->ce_states[i];
272 pipe->pipe_num = i;
273 pipe->hif_ce_state = ar;
274
275 ret = ath10k_ce_alloc_pipe(ar, i, &host_ce_config_wlan[i]);
276 if (ret) {
277 ath10k_err(ar, "failed to allocate copy engine pipe %d: %d\n",
278 i, ret);
279 return ret;
280 }
281
282 pipe->buf_sz = host_ce_config_wlan[i].src_sz_max;
283 }
284
285 return 0;
286}
287
288static void ath10k_snoc_release_resource(struct ath10k *ar)
289{
290 int i;
291
292 for (i = 0; i < CE_COUNT; i++)
293 ath10k_ce_free_pipe(ar, i);
294}
295
Govind Singh17f55592018-04-10 18:01:14 +0300296static const struct of_device_id ath10k_snoc_dt_match[] = {
297 { .compatible = "qcom,wcn3990-wifi",
298 .data = &drv_priv,
299 },
300 { }
301};
302MODULE_DEVICE_TABLE(of, ath10k_snoc_dt_match);
303
304static int ath10k_snoc_probe(struct platform_device *pdev)
305{
306 const struct ath10k_snoc_drv_priv *drv_data;
307 const struct of_device_id *of_id;
308 struct ath10k_snoc *ar_snoc;
309 struct device *dev;
310 struct ath10k *ar;
311 int ret;
312
313 of_id = of_match_device(ath10k_snoc_dt_match, &pdev->dev);
314 if (!of_id) {
315 dev_err(&pdev->dev, "failed to find matching device tree id\n");
316 return -EINVAL;
317 }
318
319 drv_data = of_id->data;
320 dev = &pdev->dev;
321
322 ret = dma_set_mask_and_coherent(dev, drv_data->dma_mask);
323 if (ret) {
324 dev_err(dev, "failed to set dma mask: %d", ret);
325 return ret;
326 }
327
328 ar = ath10k_core_create(sizeof(*ar_snoc), dev, ATH10K_BUS_SNOC,
329 drv_data->hw_rev, &ath10k_snoc_hif_ops);
330 if (!ar) {
331 dev_err(dev, "failed to allocate core\n");
332 return -ENOMEM;
333 }
334
335 ar_snoc = ath10k_snoc_priv(ar);
336 ar_snoc->dev = pdev;
337 platform_set_drvdata(pdev, ar);
338 ar_snoc->ar = ar;
339 ar_snoc->ce.bus_ops = &ath10k_snoc_bus_ops;
340 ar->ce_priv = &ar_snoc->ce;
341
Govind Singhc963a682018-04-10 18:01:15 +0300342 ath10k_snoc_resource_init(ar);
343 if (ret) {
344 ath10k_warn(ar, "failed to initialize resource: %d\n", ret);
345 goto err_core_destroy;
346 }
347
348 ath10k_snoc_setup_resource(ar);
349 if (ret) {
350 ath10k_warn(ar, "failed to setup resource: %d\n", ret);
351 goto err_core_destroy;
352 }
353 ret = ath10k_snoc_request_irq(ar);
354 if (ret) {
355 ath10k_warn(ar, "failed to request irqs: %d\n", ret);
356 goto err_release_resource;
357 }
358 ret = ath10k_core_register(ar, drv_data->hw_rev);
359 if (ret) {
360 ath10k_err(ar, "failed to register driver core: %d\n", ret);
361 goto err_free_irq;
362 }
Govind Singh17f55592018-04-10 18:01:14 +0300363 ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc probe\n");
364 ath10k_warn(ar, "Warning: SNOC support is still work-in-progress, it will not work properly!");
365
Govind Singhc963a682018-04-10 18:01:15 +0300366 return 0;
367
368err_free_irq:
369 ath10k_snoc_free_irq(ar);
370
371err_release_resource:
372 ath10k_snoc_release_resource(ar);
373
374err_core_destroy:
375 ath10k_core_destroy(ar);
376
Govind Singh17f55592018-04-10 18:01:14 +0300377 return ret;
378}
379
380static int ath10k_snoc_remove(struct platform_device *pdev)
381{
382 struct ath10k *ar = platform_get_drvdata(pdev);
383
384 ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc remove\n");
Govind Singhc963a682018-04-10 18:01:15 +0300385 ath10k_core_unregister(ar);
386 ath10k_snoc_free_irq(ar);
387 ath10k_snoc_release_resource(ar);
Govind Singh17f55592018-04-10 18:01:14 +0300388 ath10k_core_destroy(ar);
389
390 return 0;
391}
392
393static struct platform_driver ath10k_snoc_driver = {
394 .probe = ath10k_snoc_probe,
395 .remove = ath10k_snoc_remove,
396 .driver = {
397 .name = "ath10k_snoc",
398 .owner = THIS_MODULE,
399 .of_match_table = ath10k_snoc_dt_match,
400 },
401};
402
403static int __init ath10k_snoc_init(void)
404{
405 int ret;
406
407 ret = platform_driver_register(&ath10k_snoc_driver);
408 if (ret)
409 pr_err("failed to register ath10k snoc driver: %d\n",
410 ret);
411
412 return ret;
413}
414module_init(ath10k_snoc_init);
415
416static void __exit ath10k_snoc_exit(void)
417{
418 platform_driver_unregister(&ath10k_snoc_driver);
419}
420module_exit(ath10k_snoc_exit);
421
422MODULE_AUTHOR("Qualcomm");
423MODULE_LICENSE("Dual BSD/GPL");
424MODULE_DESCRIPTION("Driver support for Atheros WCN3990 SNOC devices");