Commit | Line | Data |
---|---|---|
eb524cb6 PF |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * System Control and Power Interface (SCMI) Protocol based pinctrl driver | |
4 | * | |
5 | * Copyright (C) 2024 EPAM | |
6 | * Copyright 2024 NXP | |
7 | */ | |
8 | ||
9 | #include <linux/device.h> | |
10 | #include <linux/err.h> | |
11 | #include <linux/errno.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/mod_devicetable.h> | |
14 | #include <linux/scmi_protocol.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/types.h> | |
17 | ||
18 | #include <linux/pinctrl/machine.h> | |
19 | #include <linux/pinctrl/pinconf.h> | |
20 | #include <linux/pinctrl/pinconf-generic.h> | |
21 | #include <linux/pinctrl/pinctrl.h> | |
22 | #include <linux/pinctrl/pinmux.h> | |
23 | ||
24 | #include "pinctrl-utils.h" | |
25 | #include "core.h" | |
26 | #include "pinconf.h" | |
27 | ||
28 | #define DRV_NAME "scmi-pinctrl" | |
29 | ||
30 | /* Define num configs, if not large than 4 use stack, else use kcalloc() */ | |
31 | #define SCMI_NUM_CONFIGS 4 | |
32 | ||
33 | static const struct scmi_pinctrl_proto_ops *pinctrl_ops; | |
34 | ||
35 | struct scmi_pinctrl { | |
36 | struct device *dev; | |
37 | struct scmi_protocol_handle *ph; | |
38 | struct pinctrl_dev *pctldev; | |
39 | struct pinctrl_desc pctl_desc; | |
40 | struct pinfunction *functions; | |
41 | unsigned int nr_functions; | |
42 | struct pinctrl_pin_desc *pins; | |
43 | unsigned int nr_pins; | |
44 | }; | |
45 | ||
46 | static int pinctrl_scmi_get_groups_count(struct pinctrl_dev *pctldev) | |
47 | { | |
48 | struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | |
49 | ||
50 | return pinctrl_ops->count_get(pmx->ph, GROUP_TYPE); | |
51 | } | |
52 | ||
53 | static const char *pinctrl_scmi_get_group_name(struct pinctrl_dev *pctldev, | |
54 | unsigned int selector) | |
55 | { | |
56 | int ret; | |
57 | const char *name; | |
58 | struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | |
59 | ||
60 | ret = pinctrl_ops->name_get(pmx->ph, selector, GROUP_TYPE, &name); | |
61 | if (ret) { | |
62 | dev_err(pmx->dev, "get name failed with err %d", ret); | |
63 | return NULL; | |
64 | } | |
65 | ||
66 | return name; | |
67 | } | |
68 | ||
69 | static int pinctrl_scmi_get_group_pins(struct pinctrl_dev *pctldev, | |
70 | unsigned int selector, | |
71 | const unsigned int **pins, | |
72 | unsigned int *num_pins) | |
73 | { | |
74 | struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | |
75 | ||
76 | return pinctrl_ops->group_pins_get(pmx->ph, selector, pins, num_pins); | |
77 | } | |
78 | ||
79 | static const struct pinctrl_ops pinctrl_scmi_pinctrl_ops = { | |
80 | .get_groups_count = pinctrl_scmi_get_groups_count, | |
81 | .get_group_name = pinctrl_scmi_get_group_name, | |
82 | .get_group_pins = pinctrl_scmi_get_group_pins, | |
83 | #ifdef CONFIG_OF | |
84 | .dt_node_to_map = pinconf_generic_dt_node_to_map_all, | |
85 | .dt_free_map = pinconf_generic_dt_free_map, | |
86 | #endif | |
87 | }; | |
88 | ||
89 | static int pinctrl_scmi_get_functions_count(struct pinctrl_dev *pctldev) | |
90 | { | |
91 | struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | |
92 | ||
93 | return pinctrl_ops->count_get(pmx->ph, FUNCTION_TYPE); | |
94 | } | |
95 | ||
96 | static const char *pinctrl_scmi_get_function_name(struct pinctrl_dev *pctldev, | |
97 | unsigned int selector) | |
98 | { | |
99 | int ret; | |
100 | const char *name; | |
101 | struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | |
102 | ||
103 | ret = pinctrl_ops->name_get(pmx->ph, selector, FUNCTION_TYPE, &name); | |
104 | if (ret) { | |
105 | dev_err(pmx->dev, "get name failed with err %d", ret); | |
106 | return NULL; | |
107 | } | |
108 | ||
109 | return name; | |
110 | } | |
111 | ||
112 | static int pinctrl_scmi_get_function_groups(struct pinctrl_dev *pctldev, | |
113 | unsigned int selector, | |
114 | const char * const **p_groups, | |
115 | unsigned int * const p_num_groups) | |
116 | { | |
117 | struct pinfunction *func; | |
118 | const unsigned int *group_ids; | |
119 | unsigned int num_groups; | |
120 | const char **groups; | |
121 | int ret, i; | |
122 | struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | |
123 | ||
124 | if (!p_groups || !p_num_groups) | |
125 | return -EINVAL; | |
126 | ||
127 | if (selector >= pmx->nr_functions) | |
128 | return -EINVAL; | |
129 | ||
130 | func = &pmx->functions[selector]; | |
131 | if (func->ngroups) | |
132 | goto done; | |
133 | ||
134 | ret = pinctrl_ops->function_groups_get(pmx->ph, selector, &num_groups, | |
135 | &group_ids); | |
136 | if (ret) { | |
137 | dev_err(pmx->dev, "Unable to get function groups, err %d", ret); | |
138 | return ret; | |
139 | } | |
140 | if (!num_groups) | |
141 | return -EINVAL; | |
142 | ||
143 | groups = kcalloc(num_groups, sizeof(*groups), GFP_KERNEL); | |
144 | if (!groups) | |
145 | return -ENOMEM; | |
146 | ||
147 | for (i = 0; i < num_groups; i++) { | |
148 | groups[i] = pinctrl_scmi_get_group_name(pctldev, group_ids[i]); | |
149 | if (!groups[i]) { | |
150 | ret = -EINVAL; | |
151 | goto err_free; | |
152 | } | |
153 | } | |
154 | ||
155 | func->ngroups = num_groups; | |
156 | func->groups = groups; | |
157 | done: | |
158 | *p_groups = func->groups; | |
159 | *p_num_groups = func->ngroups; | |
160 | ||
161 | return 0; | |
162 | ||
163 | err_free: | |
164 | kfree(groups); | |
165 | ||
166 | return ret; | |
167 | } | |
168 | ||
169 | static int pinctrl_scmi_func_set_mux(struct pinctrl_dev *pctldev, | |
170 | unsigned int selector, unsigned int group) | |
171 | { | |
172 | struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | |
173 | ||
174 | return pinctrl_ops->mux_set(pmx->ph, selector, group); | |
175 | } | |
176 | ||
177 | static int pinctrl_scmi_request(struct pinctrl_dev *pctldev, | |
178 | unsigned int offset) | |
179 | { | |
180 | struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | |
181 | ||
182 | return pinctrl_ops->pin_request(pmx->ph, offset); | |
183 | } | |
184 | ||
185 | static int pinctrl_scmi_free(struct pinctrl_dev *pctldev, unsigned int offset) | |
186 | { | |
187 | struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | |
188 | ||
189 | return pinctrl_ops->pin_free(pmx->ph, offset); | |
190 | } | |
191 | ||
192 | static const struct pinmux_ops pinctrl_scmi_pinmux_ops = { | |
193 | .request = pinctrl_scmi_request, | |
194 | .free = pinctrl_scmi_free, | |
195 | .get_functions_count = pinctrl_scmi_get_functions_count, | |
196 | .get_function_name = pinctrl_scmi_get_function_name, | |
197 | .get_function_groups = pinctrl_scmi_get_function_groups, | |
198 | .set_mux = pinctrl_scmi_func_set_mux, | |
199 | }; | |
200 | ||
201 | static int pinctrl_scmi_map_pinconf_type(enum pin_config_param param, | |
202 | enum scmi_pinctrl_conf_type *type) | |
203 | { | |
204 | u32 arg = param; | |
205 | ||
206 | switch (arg) { | |
207 | case PIN_CONFIG_BIAS_BUS_HOLD: | |
208 | *type = SCMI_PIN_BIAS_BUS_HOLD; | |
209 | break; | |
210 | case PIN_CONFIG_BIAS_DISABLE: | |
211 | *type = SCMI_PIN_BIAS_DISABLE; | |
212 | break; | |
213 | case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: | |
214 | *type = SCMI_PIN_BIAS_HIGH_IMPEDANCE; | |
215 | break; | |
216 | case PIN_CONFIG_BIAS_PULL_DOWN: | |
217 | *type = SCMI_PIN_BIAS_PULL_DOWN; | |
218 | break; | |
219 | case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: | |
220 | *type = SCMI_PIN_BIAS_PULL_DEFAULT; | |
221 | break; | |
222 | case PIN_CONFIG_BIAS_PULL_UP: | |
223 | *type = SCMI_PIN_BIAS_PULL_UP; | |
224 | break; | |
225 | case PIN_CONFIG_DRIVE_OPEN_DRAIN: | |
226 | *type = SCMI_PIN_DRIVE_OPEN_DRAIN; | |
227 | break; | |
228 | case PIN_CONFIG_DRIVE_OPEN_SOURCE: | |
229 | *type = SCMI_PIN_DRIVE_OPEN_SOURCE; | |
230 | break; | |
231 | case PIN_CONFIG_DRIVE_PUSH_PULL: | |
232 | *type = SCMI_PIN_DRIVE_PUSH_PULL; | |
233 | break; | |
234 | case PIN_CONFIG_DRIVE_STRENGTH: | |
235 | *type = SCMI_PIN_DRIVE_STRENGTH; | |
236 | break; | |
237 | case PIN_CONFIG_DRIVE_STRENGTH_UA: | |
238 | *type = SCMI_PIN_DRIVE_STRENGTH; | |
239 | break; | |
240 | case PIN_CONFIG_INPUT_DEBOUNCE: | |
241 | *type = SCMI_PIN_INPUT_DEBOUNCE; | |
242 | break; | |
243 | case PIN_CONFIG_INPUT_ENABLE: | |
244 | *type = SCMI_PIN_INPUT_MODE; | |
245 | break; | |
246 | case PIN_CONFIG_INPUT_SCHMITT: | |
247 | *type = SCMI_PIN_INPUT_SCHMITT; | |
248 | break; | |
249 | case PIN_CONFIG_INPUT_SCHMITT_ENABLE: | |
250 | *type = SCMI_PIN_INPUT_MODE; | |
251 | break; | |
252 | case PIN_CONFIG_MODE_LOW_POWER: | |
253 | *type = SCMI_PIN_LOW_POWER_MODE; | |
254 | break; | |
255 | case PIN_CONFIG_OUTPUT: | |
256 | *type = SCMI_PIN_OUTPUT_VALUE; | |
257 | break; | |
258 | case PIN_CONFIG_OUTPUT_ENABLE: | |
259 | *type = SCMI_PIN_OUTPUT_MODE; | |
260 | break; | |
261 | case PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS: | |
262 | *type = SCMI_PIN_OUTPUT_VALUE; | |
263 | break; | |
264 | case PIN_CONFIG_POWER_SOURCE: | |
265 | *type = SCMI_PIN_POWER_SOURCE; | |
266 | break; | |
267 | case PIN_CONFIG_SLEW_RATE: | |
268 | *type = SCMI_PIN_SLEW_RATE; | |
269 | break; | |
270 | case SCMI_PIN_OEM_START ... SCMI_PIN_OEM_END: | |
271 | *type = arg; | |
272 | break; | |
273 | default: | |
274 | return -EINVAL; | |
275 | } | |
276 | ||
277 | return 0; | |
278 | } | |
279 | ||
280 | static int pinctrl_scmi_pinconf_get(struct pinctrl_dev *pctldev, | |
281 | unsigned int pin, unsigned long *config) | |
282 | { | |
283 | int ret; | |
284 | struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | |
285 | enum pin_config_param config_type; | |
286 | enum scmi_pinctrl_conf_type type; | |
287 | u32 config_value; | |
288 | ||
289 | if (!config) | |
290 | return -EINVAL; | |
291 | ||
292 | config_type = pinconf_to_config_param(*config); | |
293 | ||
294 | ret = pinctrl_scmi_map_pinconf_type(config_type, &type); | |
295 | if (ret) | |
296 | return ret; | |
297 | ||
298 | ret = pinctrl_ops->settings_get_one(pmx->ph, pin, PIN_TYPE, type, | |
299 | &config_value); | |
300 | /* Convert SCMI error code to PINCTRL expected error code */ | |
301 | if (ret == -EOPNOTSUPP) | |
302 | return -ENOTSUPP; | |
303 | if (ret) | |
304 | return ret; | |
305 | ||
306 | *config = pinconf_to_config_packed(config_type, config_value); | |
307 | ||
308 | return 0; | |
309 | } | |
310 | ||
311 | static int | |
312 | pinctrl_scmi_alloc_configs(struct pinctrl_dev *pctldev, u32 num_configs, | |
313 | u32 **p_config_value, | |
314 | enum scmi_pinctrl_conf_type **p_config_type) | |
315 | { | |
316 | if (num_configs <= SCMI_NUM_CONFIGS) | |
317 | return 0; | |
318 | ||
319 | *p_config_value = kcalloc(num_configs, sizeof(**p_config_value), GFP_KERNEL); | |
320 | if (!*p_config_value) | |
321 | return -ENOMEM; | |
322 | ||
323 | *p_config_type = kcalloc(num_configs, sizeof(**p_config_type), GFP_KERNEL); | |
324 | if (!*p_config_type) { | |
325 | kfree(*p_config_value); | |
326 | return -ENOMEM; | |
327 | } | |
328 | ||
329 | return 0; | |
330 | } | |
331 | ||
332 | static void | |
333 | pinctrl_scmi_free_configs(struct pinctrl_dev *pctldev, u32 num_configs, | |
334 | u32 **p_config_value, | |
335 | enum scmi_pinctrl_conf_type **p_config_type) | |
336 | { | |
337 | if (num_configs <= SCMI_NUM_CONFIGS) | |
338 | return; | |
339 | ||
340 | kfree(*p_config_value); | |
341 | kfree(*p_config_type); | |
342 | } | |
343 | ||
344 | static int pinctrl_scmi_pinconf_set(struct pinctrl_dev *pctldev, | |
345 | unsigned int pin, | |
346 | unsigned long *configs, | |
347 | unsigned int num_configs) | |
348 | { | |
349 | int i, ret; | |
350 | struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | |
351 | enum scmi_pinctrl_conf_type config_type[SCMI_NUM_CONFIGS]; | |
352 | u32 config_value[SCMI_NUM_CONFIGS]; | |
353 | enum scmi_pinctrl_conf_type *p_config_type = config_type; | |
354 | u32 *p_config_value = config_value; | |
355 | enum pin_config_param param; | |
356 | ||
357 | if (!configs || !num_configs) | |
358 | return -EINVAL; | |
359 | ||
360 | ret = pinctrl_scmi_alloc_configs(pctldev, num_configs, &p_config_type, | |
361 | &p_config_value); | |
362 | if (ret) | |
363 | return ret; | |
364 | ||
365 | for (i = 0; i < num_configs; i++) { | |
366 | param = pinconf_to_config_param(configs[i]); | |
367 | ret = pinctrl_scmi_map_pinconf_type(param, &p_config_type[i]); | |
368 | if (ret) { | |
369 | dev_err(pmx->dev, "Error map pinconf_type %d\n", ret); | |
370 | goto free_config; | |
371 | } | |
372 | p_config_value[i] = pinconf_to_config_argument(configs[i]); | |
373 | } | |
374 | ||
375 | ret = pinctrl_ops->settings_conf(pmx->ph, pin, PIN_TYPE, num_configs, | |
376 | p_config_type, p_config_value); | |
377 | if (ret) | |
378 | dev_err(pmx->dev, "Error parsing config %d\n", ret); | |
379 | ||
380 | free_config: | |
381 | pinctrl_scmi_free_configs(pctldev, num_configs, &p_config_type, | |
382 | &p_config_value); | |
383 | return ret; | |
384 | } | |
385 | ||
386 | static int pinctrl_scmi_pinconf_group_set(struct pinctrl_dev *pctldev, | |
387 | unsigned int group, | |
388 | unsigned long *configs, | |
389 | unsigned int num_configs) | |
390 | { | |
391 | int i, ret; | |
392 | struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | |
393 | enum scmi_pinctrl_conf_type config_type[SCMI_NUM_CONFIGS]; | |
394 | u32 config_value[SCMI_NUM_CONFIGS]; | |
395 | enum scmi_pinctrl_conf_type *p_config_type = config_type; | |
396 | u32 *p_config_value = config_value; | |
397 | enum pin_config_param param; | |
398 | ||
399 | if (!configs || !num_configs) | |
400 | return -EINVAL; | |
401 | ||
402 | ret = pinctrl_scmi_alloc_configs(pctldev, num_configs, &p_config_type, | |
403 | &p_config_value); | |
404 | if (ret) | |
405 | return ret; | |
406 | ||
407 | for (i = 0; i < num_configs; i++) { | |
408 | param = pinconf_to_config_param(configs[i]); | |
409 | ret = pinctrl_scmi_map_pinconf_type(param, &p_config_type[i]); | |
410 | if (ret) { | |
411 | dev_err(pmx->dev, "Error map pinconf_type %d\n", ret); | |
412 | goto free_config; | |
413 | } | |
414 | ||
415 | p_config_value[i] = pinconf_to_config_argument(configs[i]); | |
416 | } | |
417 | ||
418 | ret = pinctrl_ops->settings_conf(pmx->ph, group, GROUP_TYPE, | |
419 | num_configs, p_config_type, | |
420 | p_config_value); | |
421 | if (ret) | |
422 | dev_err(pmx->dev, "Error parsing config %d", ret); | |
423 | ||
424 | free_config: | |
425 | pinctrl_scmi_free_configs(pctldev, num_configs, &p_config_type, | |
426 | &p_config_value); | |
427 | return ret; | |
428 | }; | |
429 | ||
430 | static int pinctrl_scmi_pinconf_group_get(struct pinctrl_dev *pctldev, | |
431 | unsigned int group, | |
432 | unsigned long *config) | |
433 | { | |
434 | int ret; | |
435 | struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); | |
436 | enum pin_config_param config_type; | |
437 | enum scmi_pinctrl_conf_type type; | |
438 | u32 config_value; | |
439 | ||
440 | if (!config) | |
441 | return -EINVAL; | |
442 | ||
443 | config_type = pinconf_to_config_param(*config); | |
444 | ret = pinctrl_scmi_map_pinconf_type(config_type, &type); | |
445 | if (ret) { | |
446 | dev_err(pmx->dev, "Error map pinconf_type %d\n", ret); | |
447 | return ret; | |
448 | } | |
449 | ||
450 | ret = pinctrl_ops->settings_get_one(pmx->ph, group, GROUP_TYPE, type, | |
451 | &config_value); | |
452 | /* Convert SCMI error code to PINCTRL expected error code */ | |
453 | if (ret == -EOPNOTSUPP) | |
454 | return -ENOTSUPP; | |
455 | if (ret) | |
456 | return ret; | |
457 | ||
458 | *config = pinconf_to_config_packed(config_type, config_value); | |
459 | ||
460 | return 0; | |
461 | } | |
462 | ||
463 | static const struct pinconf_ops pinctrl_scmi_pinconf_ops = { | |
464 | .is_generic = true, | |
465 | .pin_config_get = pinctrl_scmi_pinconf_get, | |
466 | .pin_config_set = pinctrl_scmi_pinconf_set, | |
467 | .pin_config_group_set = pinctrl_scmi_pinconf_group_set, | |
468 | .pin_config_group_get = pinctrl_scmi_pinconf_group_get, | |
469 | .pin_config_config_dbg_show = pinconf_generic_dump_config, | |
470 | }; | |
471 | ||
472 | static int pinctrl_scmi_get_pins(struct scmi_pinctrl *pmx, | |
473 | struct pinctrl_desc *desc) | |
474 | { | |
475 | struct pinctrl_pin_desc *pins; | |
476 | unsigned int npins; | |
477 | int ret, i; | |
478 | ||
479 | npins = pinctrl_ops->count_get(pmx->ph, PIN_TYPE); | |
480 | /* | |
481 | * npins will never be zero, the scmi pinctrl driver has bailed out | |
482 | * if npins is zero. | |
483 | */ | |
484 | pins = devm_kmalloc_array(pmx->dev, npins, sizeof(*pins), GFP_KERNEL); | |
485 | if (!pins) | |
486 | return -ENOMEM; | |
487 | ||
488 | for (i = 0; i < npins; i++) { | |
489 | pins[i].number = i; | |
490 | /* | |
491 | * The memory for name is handled by the scmi firmware driver, | |
492 | * no need free here | |
493 | */ | |
494 | ret = pinctrl_ops->name_get(pmx->ph, i, PIN_TYPE, &pins[i].name); | |
495 | if (ret) | |
496 | return dev_err_probe(pmx->dev, ret, | |
497 | "Can't get name for pin %d", i); | |
498 | } | |
499 | ||
500 | desc->npins = npins; | |
501 | desc->pins = pins; | |
502 | dev_dbg(pmx->dev, "got pins %u", npins); | |
503 | ||
504 | return 0; | |
505 | } | |
506 | ||
507 | static int scmi_pinctrl_probe(struct scmi_device *sdev) | |
508 | { | |
509 | int ret; | |
510 | struct device *dev = &sdev->dev; | |
511 | struct scmi_pinctrl *pmx; | |
512 | const struct scmi_handle *handle; | |
513 | struct scmi_protocol_handle *ph; | |
514 | ||
515 | if (!sdev->handle) | |
516 | return -EINVAL; | |
517 | ||
518 | handle = sdev->handle; | |
519 | ||
520 | pinctrl_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_PINCTRL, &ph); | |
521 | if (IS_ERR(pinctrl_ops)) | |
522 | return PTR_ERR(pinctrl_ops); | |
523 | ||
524 | pmx = devm_kzalloc(dev, sizeof(*pmx), GFP_KERNEL); | |
525 | if (!pmx) | |
526 | return -ENOMEM; | |
527 | ||
528 | pmx->ph = ph; | |
529 | ||
530 | pmx->dev = dev; | |
531 | pmx->pctl_desc.name = DRV_NAME; | |
532 | pmx->pctl_desc.owner = THIS_MODULE; | |
533 | pmx->pctl_desc.pctlops = &pinctrl_scmi_pinctrl_ops; | |
534 | pmx->pctl_desc.pmxops = &pinctrl_scmi_pinmux_ops; | |
535 | pmx->pctl_desc.confops = &pinctrl_scmi_pinconf_ops; | |
536 | ||
537 | ret = pinctrl_scmi_get_pins(pmx, &pmx->pctl_desc); | |
538 | if (ret) | |
539 | return ret; | |
540 | ||
541 | ret = devm_pinctrl_register_and_init(dev, &pmx->pctl_desc, pmx, | |
542 | &pmx->pctldev); | |
543 | if (ret) | |
544 | return dev_err_probe(dev, ret, "Failed to register pinctrl\n"); | |
545 | ||
546 | pmx->nr_functions = pinctrl_scmi_get_functions_count(pmx->pctldev); | |
547 | pmx->functions = devm_kcalloc(dev, pmx->nr_functions, | |
548 | sizeof(*pmx->functions), GFP_KERNEL); | |
549 | if (!pmx->functions) | |
550 | return -ENOMEM; | |
551 | ||
552 | return pinctrl_enable(pmx->pctldev); | |
553 | } | |
554 | ||
555 | static const struct scmi_device_id scmi_id_table[] = { | |
556 | { SCMI_PROTOCOL_PINCTRL, "pinctrl" }, | |
557 | { } | |
558 | }; | |
559 | MODULE_DEVICE_TABLE(scmi, scmi_id_table); | |
560 | ||
561 | static struct scmi_driver scmi_pinctrl_driver = { | |
562 | .name = DRV_NAME, | |
563 | .probe = scmi_pinctrl_probe, | |
564 | .id_table = scmi_id_table, | |
565 | }; | |
566 | module_scmi_driver(scmi_pinctrl_driver); | |
567 | ||
568 | MODULE_AUTHOR("Oleksii Moisieiev <oleksii_moisieiev@epam.com>"); | |
569 | MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>"); | |
570 | MODULE_DESCRIPTION("ARM SCMI pin controller driver"); | |
571 | MODULE_LICENSE("GPL"); |