1 /*
2 * Copyright (c) 2020, 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 /**
18 * DOC: defines driver functions interfacing with linux kernel
19 */
20
21 #include <qdf_list.h>
22 #include <qdf_status.h>
23 #include <linux/wireless.h>
24 #include <linux/netdevice.h>
25 #include <wlan_cfg80211.h>
26 #include <wlan_osif_priv.h>
27 #include <wlan_gpio_ucfg_api.h>
28 #include <wlan_cfg80211_gpio.h>
29 #include "qdf_module.h"
30
31 const struct nla_policy
32 wlan_cfg80211_gpio_config_policy[QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_MAX + 1] = {
33 [QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND] = {
34 .type = NLA_U32,
35 .len = sizeof(uint32_t) },
36 [QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM] = {
37 .type = NLA_U32,
38 .len = sizeof(uint32_t) },
39 [QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE] = {
40 .type = NLA_U32,
41 .len = sizeof(uint32_t) },
42 [QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE] = {
43 .type = NLA_U32,
44 .len = sizeof(uint32_t) },
45 [QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE] = {
46 .type = NLA_U32,
47 .len = sizeof(uint32_t) },
48 [QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_DIR] = {
49 .type = NLA_U32,
50 .len = sizeof(uint32_t) },
51 [QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_MUX_CONFIG] = {
52 .type = NLA_U32,
53 .len = sizeof(uint32_t) },
54 [QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_DRIVE] = {
55 .type = NLA_U32,
56 .len = sizeof(uint32_t) },
57 [QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTERNAL_CONFIG] = {
58 .type = NLA_U32,
59 .len = sizeof(uint32_t) },
60 };
61
62 /**
63 * convert_vendor_gpio_direction() - Function to convert vendor gpio direction
64 * @dir: pointer to enum qca_gpio_direction
65 *
66 * Convert the vendor gpio direction to wmi unified gpio direction
67 *
68 * Return: wmi unified gpio direction
69 */
70 static enum gpio_direction
convert_vendor_gpio_direction(enum qca_gpio_direction dir)71 convert_vendor_gpio_direction(enum qca_gpio_direction dir)
72 {
73 switch (dir) {
74 case QCA_WLAN_GPIO_INPUT:
75 return WMI_HOST_GPIO_INPUT;
76 case QCA_WLAN_GPIO_OUTPUT:
77 return WMI_HOST_GPIO_OUTPUT;
78 default:
79 return WMI_HOST_GPIO_INPUT;
80 }
81 }
82
83 /**
84 * convert_vendor_gpio_pull_type() - Function to convert vendor pull type
85 * @pull_type: pointer to enum qca_gpio_pull_type
86 *
87 * Convert the vendor pull type to wmi unified pull type
88 *
89 * Return: wmi unified gpio pull type
90 */
91 static enum gpio_pull_type
convert_vendor_gpio_pull_type(enum qca_gpio_pull_type pull_type)92 convert_vendor_gpio_pull_type(enum qca_gpio_pull_type pull_type)
93 {
94 switch (pull_type) {
95 case QCA_WLAN_GPIO_PULL_NONE:
96 return WMI_HOST_GPIO_PULL_NONE;
97 case QCA_WLAN_GPIO_PULL_UP:
98 return WMI_HOST_GPIO_PULL_UP;
99 case QCA_WLAN_GPIO_PULL_DOWN:
100 return WMI_HOST_GPIO_PULL_DOWN;
101 default:
102 return WMI_HOST_GPIO_PULL_NONE;
103 }
104 }
105
106 /**
107 * convert_vendor_gpio_interrupt_mode() - Function to convert
108 * vendor interrupt mode
109 * @intr_mode: pointer to enum qca_gpio_interrupt_mode
110 *
111 * Convert the vendor interrupt mode to wmi unified interrupt mode
112 *
113 * Return: wmi unified gpio interrupt mode
114 */
115 static enum gpio_interrupt_mode
convert_vendor_gpio_interrupt_mode(enum qca_gpio_interrupt_mode intr_mode)116 convert_vendor_gpio_interrupt_mode(enum qca_gpio_interrupt_mode intr_mode)
117 {
118 switch (intr_mode) {
119 case QCA_WLAN_GPIO_INTMODE_DISABLE:
120 return WMI_HOST_GPIO_INTMODE_DISABLE;
121 case QCA_WLAN_GPIO_INTMODE_RISING_EDGE:
122 return WMI_HOST_GPIO_INTMODE_RISING_EDGE;
123 case QCA_WLAN_GPIO_INTMODE_FALLING_EDGE:
124 return WMI_HOST_GPIO_INTMODE_FALLING_EDGE;
125 case QCA_WLAN_GPIO_INTMODE_BOTH_EDGE:
126 return WMI_HOST_GPIO_INTMODE_BOTH_EDGE;
127 case QCA_WLAN_GPIO_INTMODE_LEVEL_LOW:
128 return WMI_HOST_GPIO_INTMODE_LEVEL_LOW;
129 case QCA_WLAN_GPIO_INTMODE_LEVEL_HIGH:
130 return WMI_HOST_GPIO_INTMODE_LEVEL_HIGH;
131 default:
132 return WMI_HOST_GPIO_INTMODE_DISABLE;
133 }
134 }
135
136 /**
137 * convert_vendor_gpio_output_value() - Function to convert vendor
138 * gpio output value
139 * @value: pointer to enum qca_gpio_value
140 *
141 * Convert the vendor gpio value to wmi unified gpio output value
142 *
143 * Return: wmi unified gpio output value
144 */
145 static enum gpio_value
convert_vendor_gpio_output_value(enum qca_gpio_value value)146 convert_vendor_gpio_output_value(enum qca_gpio_value value)
147 {
148 switch (value) {
149 case QCA_WLAN_GPIO_LEVEL_LOW:
150 return WMI_HOST_GPIO_LEVEL_LOW;
151 case QCA_WLAN_GPIO_LEVEL_HIGH:
152 return WMI_HOST_GPIO_LEVEL_HIGH;
153 default:
154 return WMI_HOST_GPIO_LEVEL_LOW;
155 }
156 }
157
158 /**
159 * convert_vendor_gpio_drive() - Function to convert vendor
160 * gpio drive
161 * @drive: value of enum gpio_drive
162 *
163 * Convert the vendor gpio drive to wmi unified gpio output drive
164 *
165 * Return: wmi unified gpio output drive config
166 */
167 static enum gpio_drive
convert_vendor_gpio_drive(enum qca_gpio_drive drive)168 convert_vendor_gpio_drive(enum qca_gpio_drive drive)
169 {
170 switch (drive) {
171 case QCA_WLAN_GPIO_DRIVE_2MA:
172 return WMI_HOST_GPIO_DRIVE_2MA;
173 case QCA_WLAN_GPIO_DRIVE_4MA:
174 return WMI_HOST_GPIO_DRIVE_4MA;
175 case QCA_WLAN_GPIO_DRIVE_6MA:
176 return WMI_HOST_GPIO_DRIVE_6MA;
177 case QCA_WLAN_GPIO_DRIVE_8MA:
178 return WMI_HOST_GPIO_DRIVE_8MA;
179 case QCA_WLAN_GPIO_DRIVE_10MA:
180 return WMI_HOST_GPIO_DRIVE_10MA;
181 case QCA_WLAN_GPIO_DRIVE_12MA:
182 return WMI_HOST_GPIO_DRIVE_12MA;
183 case QCA_WLAN_GPIO_DRIVE_14MA:
184 return WMI_HOST_GPIO_DRIVE_14MA;
185 case QCA_WLAN_GPIO_DRIVE_16MA:
186 return WMI_HOST_GPIO_DRIVE_16MA;
187 default:
188 return WMI_HOST_GPIO_DRIVE_2MA;
189 }
190 }
191
192 /**
193 * convert_vendor_gpio_init_enable() - Function to convert vendor
194 * gpio init_enable
195 * @internal_config: Param to decide whether to use internal config
196 *
197 * Convert the vendor internal_config to wmi unified gpio output init_enable
198 *
199 * Return: wmi unified gpio output init_enable config
200 */
201 static enum gpio_init_enable
convert_vendor_gpio_init_enable(uint32_t internal_config)202 convert_vendor_gpio_init_enable(uint32_t internal_config)
203 {
204 if(internal_config)
205 return WMI_HOST_GPIO_INIT_DISABLE;
206 else
207 return WMI_HOST_GPIO_INIT_ENABLE;
208 }
209
210 /**
211 * wlan_set_gpio_config() - set the gpio configuration info
212 * @psoc: the pointer of wlan_objmgr_psoc
213 * @attr: list of attributes
214 *
215 * Return: 0 on success; errno on failure
216 */
217 static int
wlan_set_gpio_config(struct wlan_objmgr_psoc * psoc,struct nlattr ** attr)218 wlan_set_gpio_config(struct wlan_objmgr_psoc *psoc,
219 struct nlattr **attr)
220 {
221 struct gpio_config_params cfg_param;
222 struct nlattr *gpio_attr;
223 enum qca_gpio_direction pin_dir;
224 enum qca_gpio_pull_type pull_type;
225 enum qca_gpio_interrupt_mode intr_mode;
226 enum qca_gpio_drive drive;
227 uint32_t internal_config;
228 QDF_STATUS status;
229
230 gpio_attr = attr[QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM];
231 if (!gpio_attr) {
232 osif_err_rl("attr gpio number failed");
233 return -EINVAL;
234 }
235 cfg_param.pin_num = nla_get_u32(gpio_attr);
236
237 gpio_attr = attr[QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_DIR];
238 if (!gpio_attr) {
239 osif_err_rl("attr gpio dir failed");
240 return -EINVAL;
241 }
242 pin_dir = nla_get_u32(gpio_attr);
243 if (pin_dir >= QCA_WLAN_GPIO_DIR_MAX) {
244 osif_err_rl("attr gpio direction invalid");
245 return -EINVAL;
246 }
247 cfg_param.pin_dir = convert_vendor_gpio_direction(pin_dir);
248
249 gpio_attr = attr[QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE];
250 if (!gpio_attr) {
251 osif_err_rl("attr gpio pull failed");
252 return -EINVAL;
253 }
254 pull_type = nla_get_u32(gpio_attr);
255 if (pull_type >= QCA_WLAN_GPIO_PULL_MAX) {
256 osif_err_rl("attr gpio pull type invalid");
257 return -EINVAL;
258 }
259 cfg_param.pin_pull_type = convert_vendor_gpio_pull_type(pull_type);
260
261 gpio_attr = attr[QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE];
262 if (!gpio_attr) {
263 osif_err_rl("attr gpio interrupt mode failed");
264 return -EINVAL;
265 }
266 intr_mode = nla_get_u32(gpio_attr);
267 if (intr_mode >= QCA_WLAN_GPIO_INTMODE_MAX) {
268 osif_err_rl("attr gpio interrupt mode invalid");
269 return -EINVAL;
270 }
271 cfg_param.pin_intr_mode = convert_vendor_gpio_interrupt_mode(intr_mode);
272
273 /* Below are optional parameters. Initialize to zero */
274 cfg_param.mux_config_val = WMI_HOST_GPIO_MUX_DEFAULT;
275 cfg_param.drive = WMI_HOST_GPIO_DRIVE_2MA;
276 cfg_param.init_enable = WMI_HOST_GPIO_INIT_DISABLE;
277
278 gpio_attr = attr[QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_MUX_CONFIG];
279 if (gpio_attr) {
280 cfg_param.mux_config_val = nla_get_u32(gpio_attr);
281 }
282
283 gpio_attr = attr[QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_DRIVE];
284 if (gpio_attr) {
285 drive = nla_get_u32(gpio_attr);
286 if (drive >= QCA_WLAN_GPIO_DRIVE_MAX) {
287 osif_err_rl("attr gpio drive invalid");
288 return -EINVAL;
289 }
290 cfg_param.drive = convert_vendor_gpio_drive(drive);
291 }
292
293 gpio_attr = attr[QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTERNAL_CONFIG];
294 if (gpio_attr) {
295 internal_config = nla_get_u32(gpio_attr);
296 cfg_param.init_enable =
297 convert_vendor_gpio_init_enable(internal_config);
298 }
299
300 status = ucfg_set_gpio_config(psoc, &cfg_param);
301 return status;
302 }
303
304 /**
305 * wlan_set_gpio_output() - set the gpio output info
306 * @psoc: the pointer of wlan_objmgr_psoc
307 * @attr: list of attributes
308 *
309 * Return: 0 on success; errno on failure
310 */
311 static int
wlan_set_gpio_output(struct wlan_objmgr_psoc * psoc,struct nlattr ** attr)312 wlan_set_gpio_output(struct wlan_objmgr_psoc *psoc,
313 struct nlattr **attr)
314 {
315 struct gpio_output_params out_param;
316 struct nlattr *gpio_attr;
317 enum qca_gpio_value pin_set;
318 QDF_STATUS status;
319
320 gpio_attr = attr[QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM];
321 if (!gpio_attr) {
322 osif_err_rl("attr gpio number failed");
323 return -EINVAL;
324 }
325 out_param.pin_num = nla_get_u32(gpio_attr);
326
327 gpio_attr = attr[QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE];
328 if (!gpio_attr) {
329 osif_err_rl("attr gpio value failed");
330 return -EINVAL;
331 }
332 pin_set = nla_get_u32(gpio_attr);
333 if (pin_set >= QCA_WLAN_GPIO_LEVEL_MAX) {
334 osif_err_rl("attr gpio level invalid");
335 return -EINVAL;
336 }
337 out_param.pin_set = convert_vendor_gpio_output_value(pin_set);
338
339 status = ucfg_set_gpio_output(psoc, &out_param);
340 return status;
341 }
342
343 /**
344 * wlan_cfg80211_start_gpio_config - Set the gpio configuration
345 * @wiphy: pointer to wiphy
346 * @psoc: the pointer of wlan_objmgr_psoc
347 * @data: pointer to data
348 * @data_len: data length
349 *
350 * __wlan_cfg80211_set_gpio_config will forward the GPIO setting to FW by
351 * WMI_GPIO_CONFIG/OUTPUT_CMDID
352 *
353 * Return: 0 on success; errno on failure
354 */
355 int
wlan_cfg80211_start_gpio_config(struct wiphy * wiphy,struct wlan_objmgr_psoc * psoc,const void * data,int data_len)356 wlan_cfg80211_start_gpio_config(struct wiphy *wiphy,
357 struct wlan_objmgr_psoc *psoc,
358 const void *data,
359 int data_len)
360 {
361 uint32_t command;
362 struct nlattr *attr[QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_MAX + 1];
363 int ret;
364
365 if (wlan_cfg80211_nla_parse(attr, QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_MAX,
366 data, data_len,
367 wlan_cfg80211_gpio_config_policy)) {
368 return -EINVAL;
369 }
370
371 if (attr[QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND]) {
372 command = nla_get_u32(
373 attr[QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND]);
374
375 if (command == QCA_WLAN_VENDOR_GPIO_CONFIG) {
376 ret = wlan_set_gpio_config(psoc, attr);
377 } else if (command == QCA_WLAN_VENDOR_GPIO_OUTPUT) {
378 ret = wlan_set_gpio_output(psoc, attr);
379 } else {
380 osif_err_rl("Invalid command");
381 return -EINVAL;
382 }
383 } else {
384 osif_err_rl("Invalid command");
385 return -EINVAL;
386 }
387
388 return ret;
389 }
390 qdf_export_symbol(wlan_cfg80211_start_gpio_config);
391
392