blob: aa958ec335d67ad3597e5008d205e447bb295589 [file] [log] [blame]
Prakash Dhavalid5c9f1c2015-11-08 19:04:44 -08001/*
2 * Copyright (c) 2015 The Linux Foundation. All rights reserved.
3 *
4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
5 *
6 *
7 * Permission to use, copy, modify, and/or distribute this software for
8 * any purpose with or without fee is hereby granted, provided that the
9 * above copyright notice and this permission notice appear in all
10 * copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
13 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
15 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
18 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19 * PERFORMANCE OF THIS SOFTWARE.
20 */
21
22/*
23 * This file was originally distributed by Qualcomm Atheros, Inc.
24 * under proprietary terms before Copyright ownership was assigned
25 * to the Linux Foundation.
26 */
27
28#ifdef HIF_PCI
29
30#include "icnss_stub.h"
31#include "hif_io32.h"
32#include <hif.h>
33#include "regtable.h"
34#include "hif_debug.h"
35#include "cds_api.h"
36#include "cdf_status.h"
37#include "qwlan_version.h"
38#include <net/cnss.h>
39
40static int icnss_get_irq_num(int ce_id);
41
42/**
43 * struct icnss_stub_entry
44 *
45 * @irq_handler: irq_handler
46 * @data: data
47 * @name: name
48 * @ce_id: ce_id
49 */
50struct icnss_stub_entry {
51 irqreturn_t (*irq_handler)(int, void *);
52 void *data;
53 const char *name;
54 int ce_id;
55};
56
57/**
58 * struct icnss_stub_context
59 *
60 * @stub: icnss_stub_entry
61 * @regged_irq: regged_irq
62 */
63struct icnss_stub_context {
64 struct icnss_stub_entry stub[ICNSS_MAX_IRQ_REGISTRATIONS];
65 uint32_t regged_irq;
66};
67
68static struct icnss_stub_context cnss_stub;
69
70#ifndef QCA_WIFI_3_0_ADRASTEA
71/**
72 * icnss_wlan_enable() - icnss_wlan_enable
73 * @config: ce configuration information
74 * @mode: driver_mode
75 * @host_version: version string to send to the fw
76 *
77 * Return: int
78 */
79int icnss_wlan_enable(struct icnss_wlan_enable_cfg *config,
80 enum icnss_driver_mode mode, const char *host_version)
81{
82 return 0;
83}
84
85/**
86 * icnss_wlan_disable() - icnss_wlan_disable
87 * @mode: driver_mode
88 *
89 * Return: int
90 */
91int icnss_wlan_disable(enum icnss_driver_mode mode)
92{
93 return 0;
94}
95
Yuanyuan Liu44748d42015-12-02 10:39:51 -080096/**
97 * icnss_set_fw_debug_mode() - icnss_set_fw_debug_mode
98 * @mode: fw debug mode, 0 for QXDM, 1 for WMI
99 *
100 * Return: int
101 */
102int icnss_set_fw_debug_mode(bool mode)
103{
104 return 0;
105}
106
Prakash Dhavalid5c9f1c2015-11-08 19:04:44 -0800107#else
108
109/**
110 * icnss_wlan_enable(): call the platform driver to enable wlan
111 * @config: ce configuration information
112 * @mode: driver_mode
113 * @host_version: version string to send to the fw
114 *
115 * This function passes the con_mode and CE configuration to
116 * platform driver to enable wlan.
117 * cnss_wlan_enable has been hacked to do a qmi handshake with fw.
118 * this is not needed for rome.
119 *
120 * Return: 0 on success, error number otherwise.
121 */
122int icnss_wlan_enable(struct icnss_wlan_enable_cfg *config,
123 enum icnss_driver_mode mode, const char *host_version)
124{
125 struct cnss_wlan_enable_cfg cfg;
126 enum cnss_driver_mode cnss_mode;
127
128 cfg.num_ce_tgt_cfg = config->num_ce_tgt_cfg;
129 cfg.ce_tgt_cfg = (struct cnss_ce_tgt_pipe_cfg *)
130 config->ce_tgt_cfg;
131 cfg.num_ce_svc_pipe_cfg = config->num_ce_svc_pipe_cfg;
132 cfg.ce_svc_cfg = (struct cnss_ce_svc_pipe_cfg *)
133 config->ce_svc_cfg;
134
135 cfg.num_shadow_reg_cfg = config->num_shadow_reg_cfg;
136 cfg.shadow_reg_cfg = (struct cnss_shadow_reg_cfg *)
137 config->shadow_reg_cfg;
138
139 switch (mode) {
140 case ICNSS_FTM:
141 cnss_mode = CNSS_FTM;
142 break;
143 case ICNSS_EPPING:
144 cnss_mode = CNSS_EPPING;
145 break;
146 default:
147 cnss_mode = CNSS_MISSION;
148 break;
149 }
150 return cnss_wlan_enable(&cfg, cnss_mode, host_version);
151}
152
153/**
154 * icnss_wlan_disable(): call the platform driver to disable wlan
155 *
156 * This function passes the con_mode to platform driver to disable wlan.
157 * cnss_wlan_disable has been hacked to do a qmi handshake with fw.
158 * this is not needed for rome.
159 *
160 * Return: void
161 */
162int icnss_wlan_disable(enum icnss_driver_mode con_mode)
163{
164 enum cnss_driver_mode mode;
165
166 switch (con_mode) {
167 case ICNSS_FTM:
168 mode = CNSS_FTM;
169 break;
170 case ICNSS_EPPING:
171 mode = CNSS_EPPING;
172 break;
173 default:
174 mode = CNSS_MISSION;
175 break;
176 }
177
178 cnss_wlan_disable(mode);
179 return 0;
180}
Yuanyuan Liu44748d42015-12-02 10:39:51 -0800181
182/**
183 * icnss_set_fw_debug_mode() - call the platform driver to set fw
184 * debug mode
185 * @mode: fw debug mode, 0 for QXDM, 1 for WMI
186 *
187 * This function passes the fw debug mode to platform driver.
188 * cnss_set_fw_debug_mode has been hacked to do a qmi handshake with fw.
189 * This is not needed for rome.
190 *
191 * Return: int
192 */
193int icnss_set_fw_debug_mode(bool mode)
194{
195 return cnss_set_fw_debug_mode(mode);
196}
Prakash Dhavalid5c9f1c2015-11-08 19:04:44 -0800197#endif
198
199/**
200 * icnss_ce_request_irq() - register an irq handler
201 * @ce_id: ce_id
202 * @handler: handler
203 * @flags: flags to pass to the kernel api
204 * @name: name
205 * @context: context to pass to the irq handler
206 *
207 * Return: integer status
208 */
209int icnss_ce_request_irq(int ce_id,
210 irqreturn_t (*handler)(int, void *),
211 unsigned long flags, const char *name,
212 void *context)
213{
214 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
215 HIF_ERROR("%s: invalid ce_id = %d", __func__, ce_id);
216 return -EINVAL;
217 }
218
219 cnss_stub.stub[ce_id].irq_handler = handler;
220 cnss_stub.stub[ce_id].ce_id = ce_id;
221 cnss_stub.stub[ce_id].data = context;
222 cnss_stub.stub[ce_id].name = name;
223 cnss_stub.regged_irq |= (1 << ce_id);
224 return 0;
225}
226
227/**
228 * icnss_ce_free_irq() - icnss_unregister_irq
229 * @ce_id: the ce_id that the irq belongs to
230 * @context: context with witch the irq was requested.
231 * Return: integer status
232 */
233int icnss_ce_free_irq(int ce_id, void *context)
234{
235 if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
236 HIF_ERROR("%s: invalid ce_id = %d", __func__, ce_id);
237 return -EINVAL;
238 }
239
240 if (cnss_stub.stub[ce_id].data != context) {
241 HIF_ERROR("%s: context match failure for ce_id %d",
242 __func__, ce_id);
243 return -EINVAL;
244 }
245
246 if (cnss_stub.regged_irq & (1 << ce_id)) {
247 cnss_stub.stub[ce_id].irq_handler = NULL;
248 cnss_stub.stub[ce_id].ce_id = 0;
249 cnss_stub.stub[ce_id].data = 0;
250 cnss_stub.stub[ce_id].name = NULL;
251 cnss_stub.regged_irq &= ~(1 << ce_id);
252 }
253 return 0;
254}
255
256/**
257 * icnss_dispatch_one_ce_irq() - icnss_dispatch_one_ce_irq
258 * @ce_id: ce_id
259 *
260 * Return: irqreturn_t
261 */
262static irqreturn_t icnss_dispatch_one_ce_irq(int ce_id)
263{
264 irqreturn_t ret = IRQ_NONE;
265
266 if (cnss_stub.stub[ce_id].irq_handler)
267 ret = cnss_stub.stub[ce_id].irq_handler(
268 icnss_get_irq_num(ce_id),
269 (void *)cnss_stub.stub[ce_id].data);
270 else
271 HIF_ERROR(
272 "%sd: error - ce_id = %d, no IRQ handler",
273 __func__, ce_id);
274
275 return ret;
276}
277
278/**
279 * icnss_dispatch_ce_irq() - icnss_dispatch_ce_irq
280 * @scn: scn
281 *
282 * Return: N/A
283 */
284void icnss_dispatch_ce_irq(struct ol_softc *scn)
285{
286 uint32_t intr_summary;
287 int id;
288 irqreturn_t ret;
289
290 if (scn->hif_init_done != true)
291 return;
292
293 A_TARGET_ACCESS_BEGIN(scn);
294 intr_summary = CE_INTERRUPT_SUMMARY(scn);
295
296 if (intr_summary == 0) {
297 if ((scn->target_status != OL_TRGET_STATUS_RESET) &&
298 (!cdf_atomic_read(&scn->link_suspended))) {
299
300 hif_write32_mb(scn->mem +
301 (SOC_CORE_BASE_ADDRESS |
302 PCIE_INTR_ENABLE_ADDRESS),
303 HOST_GROUP0_MASK);
304
305 hif_read32_mb(scn->mem +
306 (SOC_CORE_BASE_ADDRESS |
307 PCIE_INTR_ENABLE_ADDRESS));
308 }
309 A_TARGET_ACCESS_END(scn);
310 return;
311 } else {
312 A_TARGET_ACCESS_END(scn);
313 }
314
315 scn->ce_irq_summary = intr_summary;
316 for (id = 0; intr_summary && (id < scn->ce_count); id++) {
317 if (intr_summary & (1 << id)) {
318 intr_summary &= ~(1 << id);
319 ret = icnss_dispatch_one_ce_irq(id);
320 }
321 }
322}
323
324/**
325 * icnss_get_soc_info() - get soc info
326 *
327 * This function query the soc information from the platform
328 * driver
329 *
330 * @info: struct icnss_soc_info
331 *
332 * Return: 0 for success
333 */
334int icnss_get_soc_info(struct icnss_soc_info *info)
335{
336 struct ol_softc *scn = cds_get_context(CDF_MODULE_ID_HIF);
337
338 if (!scn) {
339 HIF_ERROR("%s: SCN = NULL", __func__);
340 return -EINVAL;
341 }
342 info->v_addr = scn->mem;
343 info->p_addr = scn->mem_pa;
344 info->version = 0;
345 return 0;
346}
347
348
349/* icnss_get_irq_num() - generate a number to represent an irq number
350*/
351static int icnss_get_irq_num(int ce_id)
352{
Orhan K AKYILDIZ06158912015-11-11 18:01:15 -0800353 if (ce_id < CE_COUNT_MAX && ce_id >= 0)
Prakash Dhavalid5c9f1c2015-11-08 19:04:44 -0800354 return ce_id + 100;
355
356 pr_err("icnss: No irq registered for CE id %d\n", ce_id);
357 return -EINVAL;
358}
359
360int icnss_get_ce_id(int irq)
361{
362 int ce_id = irq - 100;
Orhan K AKYILDIZ06158912015-11-11 18:01:15 -0800363 if (ce_id < CE_COUNT_MAX && ce_id >= 0)
Prakash Dhavalid5c9f1c2015-11-08 19:04:44 -0800364 return ce_id;
365
366 pr_err("icnss: No matching CE id for irq %d\n", irq);
367 return -EINVAL;
368}
369#endif /* HIF_PCI */