1 /*
2 * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for
6 * any purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include <linux/platform_device.h>
21 #include <linux/err.h>
22 #include <linux/list.h>
23 #include <linux/slab.h>
24
25 #include "pld_snoc_fw_sim.h"
26
27 #ifdef CONFIG_PLD_SNOC_FW_SIM
28 /**
29 * pld_snoc_fw_sim_probe() - Probe function for platform driver
30 * @dev: device
31 *
32 * The probe function will be called when platform device
33 * is detected.
34 *
35 * Return: int
36 */
pld_snoc_fw_sim_probe(struct device * dev)37 static int pld_snoc_fw_sim_probe(struct device *dev)
38 {
39 struct pld_context *pld_context;
40 int ret = 0;
41
42 pld_context = pld_get_global_context();
43 if (!pld_context) {
44 ret = -ENODEV;
45 goto out;
46 }
47
48 ret = pld_add_dev(pld_context, dev, NULL, PLD_BUS_TYPE_SNOC_FW_SIM);
49 if (ret)
50 goto out;
51
52 return pld_context->ops->probe(dev, PLD_BUS_TYPE_SNOC_FW_SIM,
53 NULL, NULL);
54
55 out:
56 return ret;
57 }
58
59 /**
60 * pld_snoc_fw_sim_remove() - Remove function for platform device
61 * @dev: device
62 *
63 * The remove function will be called when platform device
64 * is disconnected
65 *
66 * Return: void
67 */
pld_snoc_fw_sim_remove(struct device * dev)68 static void pld_snoc_fw_sim_remove(struct device *dev)
69 {
70 struct pld_context *pld_context;
71
72 pld_context = pld_get_global_context();
73
74 if (!pld_context)
75 return;
76
77 pld_context->ops->remove(dev, PLD_BUS_TYPE_SNOC_FW_SIM);
78
79 pld_del_dev(pld_context, dev);
80 }
81
82 /**
83 * pld_snoc_fw_sim_reinit() - SSR re-initialize function for platform device
84 * @dev: device
85 *
86 * During subsystem restart(SSR), this function will be called to
87 * re-initialize platform device.
88 *
89 * Return: int
90 */
pld_snoc_fw_sim_reinit(struct device * dev)91 static int pld_snoc_fw_sim_reinit(struct device *dev)
92 {
93 struct pld_context *pld_context;
94
95 pld_context = pld_get_global_context();
96 if (pld_context->ops->reinit)
97 return pld_context->ops->reinit(dev, PLD_BUS_TYPE_SNOC_FW_SIM,
98 NULL, NULL);
99
100 return -ENODEV;
101 }
102
103 /**
104 * pld_snoc_fw_sim_shutdown() - SSR shutdown function for platform device
105 * @dev: device
106 *
107 * During SSR, this function will be called to shutdown platform device.
108 *
109 * Return: void
110 */
pld_snoc_fw_sim_shutdown(struct device * dev)111 static void pld_snoc_fw_sim_shutdown(struct device *dev)
112 {
113 struct pld_context *pld_context;
114
115 pld_context = pld_get_global_context();
116 if (pld_context->ops->shutdown)
117 pld_context->ops->shutdown(dev, PLD_BUS_TYPE_SNOC_FW_SIM);
118 }
119
120 /**
121 * pld_snoc_fw_sim_crash_shutdown() -Crash shutdown function for platform device
122 * @dev: device
123 *
124 * This function will be called when a crash is detected, it will shutdown
125 * platform device.
126 *
127 * Return: void
128 */
pld_snoc_fw_sim_crash_shutdown(void * dev)129 static void pld_snoc_fw_sim_crash_shutdown(void *dev)
130 {
131 struct pld_context *pld_context;
132
133 pld_context = pld_get_global_context();
134 if (pld_context->ops->crash_shutdown)
135 pld_context->ops->crash_shutdown(dev, PLD_BUS_TYPE_SNOC_FW_SIM);
136 }
137
138 /**
139 * pld_snoc_fw_sim_pm_suspend() - PM suspend callback function for power
140 * management
141 * @dev: device
142 *
143 * This function is to suspend the platform device when power management
144 * is enabled.
145 *
146 * Return: void
147 */
pld_snoc_fw_sim_pm_suspend(struct device * dev)148 static int pld_snoc_fw_sim_pm_suspend(struct device *dev)
149 {
150 struct pld_context *pld_context;
151 pm_message_t state;
152
153 state.event = PM_EVENT_SUSPEND;
154 pld_context = pld_get_global_context();
155 return pld_context->ops->suspend(dev, PLD_BUS_TYPE_SNOC_FW_SIM, state);
156 }
157
158 /**
159 * pld_snoc_fw_sim_pm_resume() -PM resume callback function for power management
160 * @dev: device
161 *
162 * This function is to resume the platform device when power management
163 * is enabled.
164 *
165 * Return: void
166 */
pld_snoc_fw_sim_pm_resume(struct device * dev)167 static int pld_snoc_fw_sim_pm_resume(struct device *dev)
168 {
169 struct pld_context *pld_context;
170
171 pld_context = pld_get_global_context();
172 return pld_context->ops->resume(dev, PLD_BUS_TYPE_SNOC_FW_SIM);
173 }
174
175 /**
176 * pld_snoc_fw_sim_suspend_noirq() - Complete the actions started by suspend()
177 * @dev: device
178 *
179 * Complete the actions started by suspend(). Carry out any
180 * additional operations required for suspending the device that might be
181 * racing with its driver's interrupt handler, which is guaranteed not to
182 * run while suspend_noirq() is being executed.
183 *
184 * Return: 0 for success
185 * Non zero failure code for errors
186 */
pld_snoc_fw_sim_suspend_noirq(struct device * dev)187 static int pld_snoc_fw_sim_suspend_noirq(struct device *dev)
188 {
189 struct pld_context *pld_context;
190
191 pld_context = pld_get_global_context();
192 if (!pld_context)
193 return -EINVAL;
194
195 if (pld_context->ops->suspend_noirq)
196 return pld_context->ops->suspend_noirq(dev,
197 PLD_BUS_TYPE_SNOC_FW_SIM);
198 return 0;
199 }
200
201 /**
202 * pld_snoc_fw_sim_resume_noirq() - Prepare for the execution of resume()
203 * @dev: device
204 *
205 * Prepare for the execution of resume() by carrying out any
206 * operations required for resuming the device that might be racing with
207 * its driver's interrupt handler, which is guaranteed not to run while
208 * resume_noirq() is being executed.
209 *
210 * Return: 0 for success
211 * Non zero failure code for errors
212 */
pld_snoc_fw_sim_resume_noirq(struct device * dev)213 static int pld_snoc_fw_sim_resume_noirq(struct device *dev)
214 {
215 struct pld_context *pld_context;
216
217 pld_context = pld_get_global_context();
218 if (!pld_context)
219 return -EINVAL;
220
221 if (pld_context->ops->resume_noirq)
222 return pld_context->ops->resume_noirq(dev,
223 PLD_BUS_TYPE_SNOC_FW_SIM);
224
225 return 0;
226 }
227
pld_snoc_fw_sim_uevent(struct device * dev,struct icnss_uevent_data * uevent)228 static int pld_snoc_fw_sim_uevent(struct device *dev,
229 struct icnss_uevent_data *uevent)
230 {
231 struct pld_context *pld_context;
232 struct icnss_uevent_fw_down_data *uevent_data = NULL;
233 struct pld_uevent_data data = {0};
234
235 pld_context = pld_get_global_context();
236 if (!pld_context)
237 return -EINVAL;
238
239 if (!pld_context->ops->uevent)
240 return 0;
241
242 if (!uevent)
243 return -EINVAL;
244
245 switch (uevent->uevent) {
246 case ICNSS_UEVENT_FW_CRASHED:
247 data.uevent = PLD_FW_CRASHED;
248 break;
249 case ICNSS_UEVENT_FW_DOWN:
250 if (!uevent->data)
251 return -EINVAL;
252 uevent_data = (struct icnss_uevent_fw_down_data *)uevent->data;
253 data.uevent = PLD_FW_DOWN;
254 data.fw_down.crashed = uevent_data->crashed;
255 break;
256 default:
257 return 0;
258 }
259
260 pld_context->ops->uevent(dev, &data);
261 return 0;
262 }
263
264 #ifdef MULTI_IF_NAME
265 #define PLD_SNOC_FW_SIM_OPS_NAME "pld_snoc_fw_sim_" MULTI_IF_NAME
266 #else
267 #define PLD_SNOC_FW_SIM_OPS_NAME "pld_snoc_fw_sim"
268 #endif
269
270 struct icnss_driver_ops pld_snoc_fw_sim_ops = {
271 .name = PLD_SNOC_FW_SIM_OPS_NAME,
272 .probe = pld_snoc_fw_sim_probe,
273 .remove = pld_snoc_fw_sim_remove,
274 .shutdown = pld_snoc_fw_sim_shutdown,
275 .reinit = pld_snoc_fw_sim_reinit,
276 .crash_shutdown = pld_snoc_fw_sim_crash_shutdown,
277 .pm_suspend = pld_snoc_fw_sim_pm_suspend,
278 .pm_resume = pld_snoc_fw_sim_pm_resume,
279 .suspend_noirq = pld_snoc_fw_sim_suspend_noirq,
280 .resume_noirq = pld_snoc_fw_sim_resume_noirq,
281 .uevent = pld_snoc_fw_sim_uevent,
282 };
283
284 /**
285 * pld_snoc_fw_sim_register_driver() - Register platform device callback
286 * functions
287 *
288 * Return: int
289 */
pld_snoc_fw_sim_register_driver(void)290 int pld_snoc_fw_sim_register_driver(void)
291 {
292 return icnss_register_driver(&pld_snoc_fw_sim_ops);
293 }
294
295 /**
296 * pld_snoc_fw_sim_unregister_driver() - Unregister platform device callback
297 * functions
298 *
299 * Return: void
300 */
pld_snoc_fw_sim_unregister_driver(void)301 void pld_snoc_fw_sim_unregister_driver(void)
302 {
303 icnss_unregister_driver(&pld_snoc_fw_sim_ops);
304 }
305
306 /**
307 * pld_snoc_fw_sim_wlan_enable() - Enable WLAN
308 * @dev: device
309 * @config: WLAN configuration data
310 * @mode: WLAN mode
311 * @host_version: host software version
312 *
313 * This function enables WLAN FW. It passed WLAN configuration data,
314 * WLAN mode and host software version to FW.
315 *
316 * Return: 0 for success
317 * Non zero failure code for errors
318 */
pld_snoc_fw_sim_wlan_enable(struct device * dev,struct pld_wlan_enable_cfg * config,enum pld_driver_mode mode,const char * host_version)319 int pld_snoc_fw_sim_wlan_enable(struct device *dev,
320 struct pld_wlan_enable_cfg *config,
321 enum pld_driver_mode mode,
322 const char *host_version)
323 {
324 struct icnss_wlan_enable_cfg cfg;
325 enum icnss_driver_mode icnss_mode;
326
327 if (!dev)
328 return -ENODEV;
329
330 cfg.num_ce_tgt_cfg = config->num_ce_tgt_cfg;
331 cfg.ce_tgt_cfg = (struct ce_tgt_pipe_cfg *)
332 config->ce_tgt_cfg;
333 cfg.num_ce_svc_pipe_cfg = config->num_ce_svc_pipe_cfg;
334 cfg.ce_svc_cfg = (struct ce_svc_pipe_cfg *)
335 config->ce_svc_cfg;
336 cfg.num_shadow_reg_cfg = config->num_shadow_reg_cfg;
337 cfg.shadow_reg_cfg = (struct icnss_shadow_reg_cfg *)
338 config->shadow_reg_cfg;
339
340 switch (mode) {
341 case PLD_FTM:
342 icnss_mode = ICNSS_FTM;
343 break;
344 case PLD_EPPING:
345 icnss_mode = ICNSS_EPPING;
346 break;
347 default:
348 icnss_mode = ICNSS_MISSION;
349 break;
350 }
351
352 return icnss_wlan_enable(dev, &cfg, icnss_mode, host_version);
353 }
354
355 /**
356 * pld_snoc_fw_sim_wlan_disable() - Disable WLAN
357 * @dev: device
358 * @mode: WLAN mode
359 *
360 * This function disables WLAN FW. It passes WLAN mode to FW.
361 *
362 * Return: 0 for success
363 * Non zero failure code for errors
364 */
pld_snoc_fw_sim_wlan_disable(struct device * dev,enum pld_driver_mode mode)365 int pld_snoc_fw_sim_wlan_disable(struct device *dev, enum pld_driver_mode mode)
366 {
367 if (!dev)
368 return -ENODEV;
369
370 return icnss_wlan_disable(dev, ICNSS_OFF);
371 }
372
373 /**
374 * pld_snoc_fw_sim_get_soc_info() - Get SOC information
375 * @dev: device
376 * @info: buffer to SOC information
377 *
378 * Return SOC info to the buffer.
379 *
380 * Return: 0 for success
381 * Non zero failure code for errors
382 */
pld_snoc_fw_sim_get_soc_info(struct device * dev,struct pld_soc_info * info)383 int pld_snoc_fw_sim_get_soc_info(struct device *dev, struct pld_soc_info *info)
384 {
385 int ret = 0;
386 struct icnss_soc_info icnss_info;
387
388 if (!info || !dev)
389 return -ENODEV;
390
391 ret = icnss_get_soc_info(dev, &icnss_info);
392 if (0 != ret)
393 return ret;
394
395 memcpy(info, &icnss_info, sizeof(*info));
396 return 0;
397 }
398 #endif
399