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