blob: 87df90a4d737149ef882c617802041f4df8017a3 [file] [log] [blame]
Govind Singh6a2fe032016-05-13 14:09:37 +05301/*
Yu Wang66e4ff22017-02-15 19:09:24 +08002 * Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
Govind Singh6a2fe032016-05-13 14:09:37 +05303 *
4 * Permission to use, copy, modify, and/or distribute this software for
5 * any purpose with or without fee is hereby granted, provided that the
6 * above copyright notice and this permission notice appear in all
7 * copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <linux/platform_device.h>
20#include <linux/err.h>
21#include <linux/mmc/sdio_func.h>
22#include <linux/list.h>
23#include <linux/slab.h>
24
25#ifdef CONFIG_PLD_SDIO_CNSS
26#include <net/cnss.h>
27#endif
28
29#include "pld_common.h"
30#include "pld_internal.h"
Yu Wang66e4ff22017-02-15 19:09:24 +080031#include "pld_sdio.h"
32
Govind Singh6a2fe032016-05-13 14:09:37 +053033
34#ifdef CONFIG_SDIO
35/* SDIO manufacturer ID and Codes */
36#define MANUFACTURER_ID_AR6320_BASE 0x500
37#define MANUFACTURER_ID_QCA9377_BASE 0x700
38#define MANUFACTURER_CODE 0x271
39
40/**
41 * pld_sdio_probe() - Probe function for SDIO platform driver
42 * sdio_func: pointer to sdio device function
43 * @id: SDIO device ID table
44 *
45 * The probe function will be called when SDIO device provided
46 * in the ID table is detected.
47 *
48 * Return: int
49 */
50static int pld_sdio_probe(struct sdio_func *sdio_func,
51 const struct sdio_device_id *id)
52{
53 struct pld_context *pld_context;
54 struct device *dev = &sdio_func->dev;
55 int ret = 0;
56
57 pld_context = pld_get_global_context();
58 if (!pld_context) {
59 ret = -ENODEV;
60 goto out;
61 }
62
63 ret = pld_add_dev(pld_context, dev, PLD_BUS_TYPE_SDIO);
64 if (ret)
65 goto out;
66
67 return pld_context->ops->probe(dev, PLD_BUS_TYPE_SDIO,
68 sdio_func, (void *)id);
69
70out:
71 return ret;
72}
73
74
75/**
76 * pld_sdio_remove() - Remove function for SDIO device
77 * @sdio_func: pointer to sdio device function
78 *
79 * The remove function will be called when SDIO device is disconnected
80 *
81 * Return: void
82 */
83static void pld_sdio_remove(struct sdio_func *sdio_func)
84{
85 struct pld_context *pld_context;
86 struct device *dev = &sdio_func->dev;
87
88 pld_context = pld_get_global_context();
89
90 if (!pld_context)
91 return;
92
93 pld_context->ops->remove(dev, PLD_BUS_TYPE_SDIO);
94 pld_del_dev(pld_context, dev);
95}
96
97#ifdef CONFIG_PLD_SDIO_CNSS
98/**
99 * pld_sdio_reinit() - SSR re-initialize function for SDIO device
100 * @sdio_func: pointer to sdio device function
101 * @id: SDIO device ID
102 *
103 * During subsystem restart(SSR), this function will be called to
104 * re-initialize SDIO device.
105 *
106 * Return: int
107 */
108static int pld_sdio_reinit(struct sdio_func *sdio_func,
109 const struct sdio_device_id *id)
110{
111 struct pld_context *pld_context;
112 struct device *dev = &sdio_func->dev;
113
114 pld_context = pld_get_global_context();
115 if (pld_context->ops->reinit)
116 return pld_context->ops->reinit(dev, PLD_BUS_TYPE_SDIO,
117 sdio_func, (void *)id);
118
119 return -ENODEV;
120}
121
122/**
123 * pld_sdio_shutdown() - SSR shutdown function for SDIO device
124 * @sdio_func: pointer to sdio device function
125 *
126 * During SSR, this function will be called to shutdown SDIO device.
127 *
128 * Return: void
129 */
130static void pld_sdio_shutdown(struct sdio_func *sdio_func)
131{
132 struct pld_context *pld_context;
133 struct device *dev = &sdio_func->dev;
134
135 pld_context = pld_get_global_context();
136 if (pld_context->ops->shutdown)
137 pld_context->ops->shutdown(dev, PLD_BUS_TYPE_SDIO);
138}
139
140/**
141 * pld_sdio_crash_shutdown() - Crash shutdown function for SDIO device
142 * @sdio_func: pointer to sdio device function
143 *
144 * This function will be called when a crash is detected, it will shutdown
145 * the SDIO device.
146 *
147 * Return: void
148 */
149static void pld_sdio_crash_shutdown(struct sdio_func *sdio_func)
150{
151 struct pld_context *pld_context;
152 struct device *dev = &sdio_func->dev;
153
154 pld_context = pld_get_global_context();
155 if (pld_context->ops->crash_shutdown)
156 pld_context->ops->crash_shutdown(dev, PLD_BUS_TYPE_SDIO);
157}
158
159#endif
160
161#ifdef CONFIG_PM
162/**
163 * pld_sdio_suspend() - Suspend callback function for power management
164 * @dev: SDIO device
165 *
166 * This function is to suspend the SDIO device when power management is
167 * enabled.
168 *
169 * Return: void
170 */
171static int pld_sdio_suspend(struct device *dev)
172{
173 struct pld_context *pld_context;
174 pm_message_t state = { .event = PM_EVENT_SUSPEND };
175
176 pld_context = pld_get_global_context();
177 return pld_context->ops->suspend(dev,
178 PLD_BUS_TYPE_SDIO, state);
179}
180
181/**
182 * pld_sdio_resume() - Resume callback function for power management
183 * @dev: SDIO device
184 *
185 * This function is to resume the SDIO device when power management is
186 * enabled.
187 *
188 * Return: void
189 */
190static int pld_sdio_resume(struct device *dev)
191{
192 struct pld_context *pld_context;
193
194 pld_context = pld_get_global_context();
195 return pld_context->ops->resume(dev, PLD_BUS_TYPE_SDIO);
196}
197#endif
198
199static struct sdio_device_id pld_sdio_id_table[] = {
200 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x0))},
201 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x1))},
202 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x2))},
203 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x3))},
204 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x4))},
205 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x5))},
206 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x6))},
207 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x7))},
208 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x8))},
209 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0x9))},
210 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0xA))},
211 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0xB))},
212 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0xC))},
213 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0xD))},
214 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0xE))},
215 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6320_BASE | 0xF))},
216 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x0))},
217 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x1))},
218 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x2))},
219 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x3))},
220 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x4))},
221 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x5))},
222 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x6))},
223 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x7))},
224 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x8))},
225 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0x9))},
226 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0xA))},
227 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0xB))},
228 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0xC))},
229 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0xD))},
230 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0xE))},
231 {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCA9377_BASE | 0xF))},
232 {},
233};
234
235#ifdef CONFIG_PLD_SDIO_CNSS
236struct cnss_sdio_wlan_driver pld_sdio_ops = {
237 .name = "pld_sdio",
238 .id_table = pld_sdio_id_table,
239 .probe = pld_sdio_probe,
240 .remove = pld_sdio_remove,
241 .reinit = pld_sdio_reinit,
242 .shutdown = pld_sdio_shutdown,
243 .crash_shutdown = pld_sdio_crash_shutdown,
244#ifdef CONFIG_PM
245 .suspend = pld_sdio_suspend,
246 .resume = pld_sdio_resume,
247#endif
248};
249
250/**
251 * pld_sdio_register_driver() - Register SDIO device callback functions
252 *
253 * Return: int
254 */
255int pld_sdio_register_driver(void)
256{
257 return cnss_sdio_wlan_register_driver(&pld_sdio_ops);
258}
259
260/**
261 * pld_sdio_unregister_driver() - Unregister SDIO device callback functions
262 *
263 * Return: void
264 */
265void pld_sdio_unregister_driver(void)
266{
267 cnss_sdio_wlan_unregister_driver(&pld_sdio_ops);
268}
269#else
270struct sdio_driver pld_sdio_ops = {
271 .name = "pld_sdio",
272 .id_table = pld_sdio_id_table,
273 .probe = pld_sdio_probe,
274 .remove = pld_sdio_remove,
275#ifdef CONFIG_PM
276 .suspend = pld_sdio_suspend,
277 .resume = pld_sdio_resume,
278#endif
279};
280
281int pld_sdio_register_driver(void)
282{
283 return sdio_register_driver(&pld_sdio_ops);
284}
285
286void pld_sdio_unregister_driver(void)
287{
288 sdio_unregister_driver(&pld_sdio_ops);
289}
290#endif
291
292#ifdef CONFIG_PLD_SDIO_CNSS
Yu Wang66e4ff22017-02-15 19:09:24 +0800293#ifdef CONFIG_TUFELLO_DUAL_FW_SUPPORT
294static inline int pld_sdio_is_tufello_dual_fw_supported(void)
295{
296 return 1;
297}
298#else
299static inline int pld_sdio_is_tufello_dual_fw_supported(void)
300{
301 return 0;
302#endif
303}
304
Govind Singh6a2fe032016-05-13 14:09:37 +0530305/**
306 * pld_sdio_get_fw_files_for_target() - Get FW file names
307 * @pfw_files: buffer for FW file names
308 * @target_type: target type
309 * @target_version: target version
310 *
311 * Return target specific FW file names to the buffer.
312 *
313 * Return: 0 for success
314 * Non zero failure code for errors
315 */
316int pld_sdio_get_fw_files_for_target(struct pld_fw_files *pfw_files,
317 u32 target_type, u32 target_version)
318{
319 int ret = 0;
320 struct cnss_fw_files cnss_fw_files;
321
322 if (pfw_files == NULL)
323 return -ENODEV;
324
325 memset(pfw_files, 0, sizeof(*pfw_files));
326
Yu Wang66e4ff22017-02-15 19:09:24 +0800327 if (target_version == PLD_QCA9377_REV1_1_VERSION) {
328 cnss_get_qca9377_fw_files(&cnss_fw_files, PLD_MAX_FILE_NAME,
329 pld_sdio_is_tufello_dual_fw_supported());
330 } else {
331 ret = cnss_get_fw_files_for_target(&cnss_fw_files,
Govind Singh6a2fe032016-05-13 14:09:37 +0530332 target_type, target_version);
Yu Wang66e4ff22017-02-15 19:09:24 +0800333 }
Govind Singh6a2fe032016-05-13 14:09:37 +0530334 if (0 != ret)
335 return ret;
336
Yu Wang66e4ff22017-02-15 19:09:24 +0800337 snprintf(pfw_files->image_file, PLD_MAX_FILE_NAME, PREFIX "%s",
338 cnss_fw_files.image_file);
339 snprintf(pfw_files->board_data, PLD_MAX_FILE_NAME, PREFIX "%s",
340 cnss_fw_files.board_data);
341 snprintf(pfw_files->otp_data, PLD_MAX_FILE_NAME, PREFIX "%s",
342 cnss_fw_files.otp_data);
343 snprintf(pfw_files->utf_file, PLD_MAX_FILE_NAME, PREFIX "%s",
344 cnss_fw_files.utf_file);
345 snprintf(pfw_files->utf_board_data, PLD_MAX_FILE_NAME, PREFIX "%s",
346 cnss_fw_files.utf_board_data);
347 snprintf(pfw_files->epping_file, PLD_MAX_FILE_NAME, PREFIX "%s",
348 cnss_fw_files.epping_file);
349 snprintf(pfw_files->evicted_data, PLD_MAX_FILE_NAME, PREFIX "%s",
350 cnss_fw_files.evicted_data);
Govind Singh6a2fe032016-05-13 14:09:37 +0530351
352 return ret;
353}
354#else
355#ifdef CONFIG_TUFELLO_DUAL_FW_SUPPORT
356static inline void get_qca9377_fw_files(struct pld_fw_files *pfw_files,
357 u32 size)
358{
359 memcpy(pfw_files, &fw_files_default, sizeof(*pfw_files));
360}
361#else
362static inline void get_qca9377_fw_files(struct pld_fw_files *pfw_files,
363 u32 size)
364{
365 memcpy(pfw_files, &fw_files_qca6174_fw_3_0, sizeof(*pfw_files));
366}
367#endif
368
369int pld_sdio_get_fw_files_for_target(struct pld_fw_files *pfw_files,
370 u32 target_type, u32 target_version)
371{
372 if (!pfw_files)
373 return -ENODEV;
374
375 switch (target_version) {
376 case PLD_AR6320_REV1_VERSION:
377 case PLD_AR6320_REV1_1_VERSION:
378 memcpy(pfw_files, &fw_files_qca6174_fw_1_1, sizeof(*pfw_files));
379 break;
380 case PLD_AR6320_REV1_3_VERSION:
381 memcpy(pfw_files, &fw_files_qca6174_fw_1_3, sizeof(*pfw_files));
382 break;
383 case PLD_AR6320_REV2_1_VERSION:
384 memcpy(pfw_files, &fw_files_qca6174_fw_2_0, sizeof(*pfw_files));
385 break;
386 case PLD_AR6320_REV3_VERSION:
387 case PLD_AR6320_REV3_2_VERSION:
388 memcpy(pfw_files, &fw_files_qca6174_fw_3_0, sizeof(*pfw_files));
389 break;
390 case PLD_QCA9377_REV1_1_VERSION:
391 get_qca9377_fw_files(pfw_files, sizeof(*pfw_files));
392 break;
393 default:
394 memcpy(pfw_files, &fw_files_default, sizeof(*pfw_files));
395 pr_err("%s version mismatch 0x%X ",
396 __func__, target_version);
397 break;
398 }
399
400 return 0;
401}
Govind Singh6a2fe032016-05-13 14:09:37 +0530402#endif
403#endif