Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Peng Fan | 2490 | 100.00% | 1 | 100.00% |
Total | 2490 | 1 |
// SPDX-License-Identifier: GPL-2.0 /* * System Control and Power Interface (SCMI) Protocol based pinctrl driver * * Copyright (C) 2024 EPAM * Copyright 2024 NXP */ #include <linux/device.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/module.h> #include <linux/mod_devicetable.h> #include <linux/scmi_protocol.h> #include <linux/slab.h> #include <linux/types.h> #include <linux/pinctrl/machine.h> #include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinconf-generic.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> #include "pinctrl-utils.h" #include "core.h" #include "pinconf.h" #define DRV_NAME "scmi-pinctrl" /* Define num configs, if not large than 4 use stack, else use kcalloc() */ #define SCMI_NUM_CONFIGS 4 static const struct scmi_pinctrl_proto_ops *pinctrl_ops; struct scmi_pinctrl { struct device *dev; struct scmi_protocol_handle *ph; struct pinctrl_dev *pctldev; struct pinctrl_desc pctl_desc; struct pinfunction *functions; unsigned int nr_functions; struct pinctrl_pin_desc *pins; unsigned int nr_pins; }; static int pinctrl_scmi_get_groups_count(struct pinctrl_dev *pctldev) { struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); return pinctrl_ops->count_get(pmx->ph, GROUP_TYPE); } static const char *pinctrl_scmi_get_group_name(struct pinctrl_dev *pctldev, unsigned int selector) { int ret; const char *name; struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); ret = pinctrl_ops->name_get(pmx->ph, selector, GROUP_TYPE, &name); if (ret) { dev_err(pmx->dev, "get name failed with err %d", ret); return NULL; } return name; } static int pinctrl_scmi_get_group_pins(struct pinctrl_dev *pctldev, unsigned int selector, const unsigned int **pins, unsigned int *num_pins) { struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); return pinctrl_ops->group_pins_get(pmx->ph, selector, pins, num_pins); } static const struct pinctrl_ops pinctrl_scmi_pinctrl_ops = { .get_groups_count = pinctrl_scmi_get_groups_count, .get_group_name = pinctrl_scmi_get_group_name, .get_group_pins = pinctrl_scmi_get_group_pins, #ifdef CONFIG_OF .dt_node_to_map = pinconf_generic_dt_node_to_map_all, .dt_free_map = pinconf_generic_dt_free_map, #endif }; static int pinctrl_scmi_get_functions_count(struct pinctrl_dev *pctldev) { struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); return pinctrl_ops->count_get(pmx->ph, FUNCTION_TYPE); } static const char *pinctrl_scmi_get_function_name(struct pinctrl_dev *pctldev, unsigned int selector) { int ret; const char *name; struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); ret = pinctrl_ops->name_get(pmx->ph, selector, FUNCTION_TYPE, &name); if (ret) { dev_err(pmx->dev, "get name failed with err %d", ret); return NULL; } return name; } static int pinctrl_scmi_get_function_groups(struct pinctrl_dev *pctldev, unsigned int selector, const char * const **p_groups, unsigned int * const p_num_groups) { struct pinfunction *func; const unsigned int *group_ids; unsigned int num_groups; const char **groups; int ret, i; struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); if (!p_groups || !p_num_groups) return -EINVAL; if (selector >= pmx->nr_functions) return -EINVAL; func = &pmx->functions[selector]; if (func->ngroups) goto done; ret = pinctrl_ops->function_groups_get(pmx->ph, selector, &num_groups, &group_ids); if (ret) { dev_err(pmx->dev, "Unable to get function groups, err %d", ret); return ret; } if (!num_groups) return -EINVAL; groups = kcalloc(num_groups, sizeof(*groups), GFP_KERNEL); if (!groups) return -ENOMEM; for (i = 0; i < num_groups; i++) { groups[i] = pinctrl_scmi_get_group_name(pctldev, group_ids[i]); if (!groups[i]) { ret = -EINVAL; goto err_free; } } func->ngroups = num_groups; func->groups = groups; done: *p_groups = func->groups; *p_num_groups = func->ngroups; return 0; err_free: kfree(groups); return ret; } static int pinctrl_scmi_func_set_mux(struct pinctrl_dev *pctldev, unsigned int selector, unsigned int group) { struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); return pinctrl_ops->mux_set(pmx->ph, selector, group); } static int pinctrl_scmi_request(struct pinctrl_dev *pctldev, unsigned int offset) { struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); return pinctrl_ops->pin_request(pmx->ph, offset); } static int pinctrl_scmi_free(struct pinctrl_dev *pctldev, unsigned int offset) { struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); return pinctrl_ops->pin_free(pmx->ph, offset); } static const struct pinmux_ops pinctrl_scmi_pinmux_ops = { .request = pinctrl_scmi_request, .free = pinctrl_scmi_free, .get_functions_count = pinctrl_scmi_get_functions_count, .get_function_name = pinctrl_scmi_get_function_name, .get_function_groups = pinctrl_scmi_get_function_groups, .set_mux = pinctrl_scmi_func_set_mux, }; static int pinctrl_scmi_map_pinconf_type(enum pin_config_param param, enum scmi_pinctrl_conf_type *type) { u32 arg = param; switch (arg) { case PIN_CONFIG_BIAS_BUS_HOLD: *type = SCMI_PIN_BIAS_BUS_HOLD; break; case PIN_CONFIG_BIAS_DISABLE: *type = SCMI_PIN_BIAS_DISABLE; break; case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: *type = SCMI_PIN_BIAS_HIGH_IMPEDANCE; break; case PIN_CONFIG_BIAS_PULL_DOWN: *type = SCMI_PIN_BIAS_PULL_DOWN; break; case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: *type = SCMI_PIN_BIAS_PULL_DEFAULT; break; case PIN_CONFIG_BIAS_PULL_UP: *type = SCMI_PIN_BIAS_PULL_UP; break; case PIN_CONFIG_DRIVE_OPEN_DRAIN: *type = SCMI_PIN_DRIVE_OPEN_DRAIN; break; case PIN_CONFIG_DRIVE_OPEN_SOURCE: *type = SCMI_PIN_DRIVE_OPEN_SOURCE; break; case PIN_CONFIG_DRIVE_PUSH_PULL: *type = SCMI_PIN_DRIVE_PUSH_PULL; break; case PIN_CONFIG_DRIVE_STRENGTH: *type = SCMI_PIN_DRIVE_STRENGTH; break; case PIN_CONFIG_DRIVE_STRENGTH_UA: *type = SCMI_PIN_DRIVE_STRENGTH; break; case PIN_CONFIG_INPUT_DEBOUNCE: *type = SCMI_PIN_INPUT_DEBOUNCE; break; case PIN_CONFIG_INPUT_ENABLE: *type = SCMI_PIN_INPUT_MODE; break; case PIN_CONFIG_INPUT_SCHMITT: *type = SCMI_PIN_INPUT_SCHMITT; break; case PIN_CONFIG_INPUT_SCHMITT_ENABLE: *type = SCMI_PIN_INPUT_MODE; break; case PIN_CONFIG_MODE_LOW_POWER: *type = SCMI_PIN_LOW_POWER_MODE; break; case PIN_CONFIG_OUTPUT: *type = SCMI_PIN_OUTPUT_VALUE; break; case PIN_CONFIG_OUTPUT_ENABLE: *type = SCMI_PIN_OUTPUT_MODE; break; case PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS: *type = SCMI_PIN_OUTPUT_VALUE; break; case PIN_CONFIG_POWER_SOURCE: *type = SCMI_PIN_POWER_SOURCE; break; case PIN_CONFIG_SLEW_RATE: *type = SCMI_PIN_SLEW_RATE; break; case SCMI_PIN_OEM_START ... SCMI_PIN_OEM_END: *type = arg; break; default: return -EINVAL; } return 0; } static int pinctrl_scmi_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, unsigned long *config) { int ret; struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); enum pin_config_param config_type; enum scmi_pinctrl_conf_type type; u32 config_value; if (!config) return -EINVAL; config_type = pinconf_to_config_param(*config); ret = pinctrl_scmi_map_pinconf_type(config_type, &type); if (ret) return ret; ret = pinctrl_ops->settings_get_one(pmx->ph, pin, PIN_TYPE, type, &config_value); /* Convert SCMI error code to PINCTRL expected error code */ if (ret == -EOPNOTSUPP) return -ENOTSUPP; if (ret) return ret; *config = pinconf_to_config_packed(config_type, config_value); return 0; } static int pinctrl_scmi_alloc_configs(struct pinctrl_dev *pctldev, u32 num_configs, u32 **p_config_value, enum scmi_pinctrl_conf_type **p_config_type) { if (num_configs <= SCMI_NUM_CONFIGS) return 0; *p_config_value = kcalloc(num_configs, sizeof(**p_config_value), GFP_KERNEL); if (!*p_config_value) return -ENOMEM; *p_config_type = kcalloc(num_configs, sizeof(**p_config_type), GFP_KERNEL); if (!*p_config_type) { kfree(*p_config_value); return -ENOMEM; } return 0; } static void pinctrl_scmi_free_configs(struct pinctrl_dev *pctldev, u32 num_configs, u32 **p_config_value, enum scmi_pinctrl_conf_type **p_config_type) { if (num_configs <= SCMI_NUM_CONFIGS) return; kfree(*p_config_value); kfree(*p_config_type); } static int pinctrl_scmi_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, unsigned long *configs, unsigned int num_configs) { int i, ret; struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); enum scmi_pinctrl_conf_type config_type[SCMI_NUM_CONFIGS]; u32 config_value[SCMI_NUM_CONFIGS]; enum scmi_pinctrl_conf_type *p_config_type = config_type; u32 *p_config_value = config_value; enum pin_config_param param; if (!configs || !num_configs) return -EINVAL; ret = pinctrl_scmi_alloc_configs(pctldev, num_configs, &p_config_type, &p_config_value); if (ret) return ret; for (i = 0; i < num_configs; i++) { param = pinconf_to_config_param(configs[i]); ret = pinctrl_scmi_map_pinconf_type(param, &p_config_type[i]); if (ret) { dev_err(pmx->dev, "Error map pinconf_type %d\n", ret); goto free_config; } p_config_value[i] = pinconf_to_config_argument(configs[i]); } ret = pinctrl_ops->settings_conf(pmx->ph, pin, PIN_TYPE, num_configs, p_config_type, p_config_value); if (ret) dev_err(pmx->dev, "Error parsing config %d\n", ret); free_config: pinctrl_scmi_free_configs(pctldev, num_configs, &p_config_type, &p_config_value); return ret; } static int pinctrl_scmi_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned int group, unsigned long *configs, unsigned int num_configs) { int i, ret; struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); enum scmi_pinctrl_conf_type config_type[SCMI_NUM_CONFIGS]; u32 config_value[SCMI_NUM_CONFIGS]; enum scmi_pinctrl_conf_type *p_config_type = config_type; u32 *p_config_value = config_value; enum pin_config_param param; if (!configs || !num_configs) return -EINVAL; ret = pinctrl_scmi_alloc_configs(pctldev, num_configs, &p_config_type, &p_config_value); if (ret) return ret; for (i = 0; i < num_configs; i++) { param = pinconf_to_config_param(configs[i]); ret = pinctrl_scmi_map_pinconf_type(param, &p_config_type[i]); if (ret) { dev_err(pmx->dev, "Error map pinconf_type %d\n", ret); goto free_config; } p_config_value[i] = pinconf_to_config_argument(configs[i]); } ret = pinctrl_ops->settings_conf(pmx->ph, group, GROUP_TYPE, num_configs, p_config_type, p_config_value); if (ret) dev_err(pmx->dev, "Error parsing config %d", ret); free_config: pinctrl_scmi_free_configs(pctldev, num_configs, &p_config_type, &p_config_value); return ret; }; static int pinctrl_scmi_pinconf_group_get(struct pinctrl_dev *pctldev, unsigned int group, unsigned long *config) { int ret; struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); enum pin_config_param config_type; enum scmi_pinctrl_conf_type type; u32 config_value; if (!config) return -EINVAL; config_type = pinconf_to_config_param(*config); ret = pinctrl_scmi_map_pinconf_type(config_type, &type); if (ret) { dev_err(pmx->dev, "Error map pinconf_type %d\n", ret); return ret; } ret = pinctrl_ops->settings_get_one(pmx->ph, group, GROUP_TYPE, type, &config_value); /* Convert SCMI error code to PINCTRL expected error code */ if (ret == -EOPNOTSUPP) return -ENOTSUPP; if (ret) return ret; *config = pinconf_to_config_packed(config_type, config_value); return 0; } static const struct pinconf_ops pinctrl_scmi_pinconf_ops = { .is_generic = true, .pin_config_get = pinctrl_scmi_pinconf_get, .pin_config_set = pinctrl_scmi_pinconf_set, .pin_config_group_set = pinctrl_scmi_pinconf_group_set, .pin_config_group_get = pinctrl_scmi_pinconf_group_get, .pin_config_config_dbg_show = pinconf_generic_dump_config, }; static int pinctrl_scmi_get_pins(struct scmi_pinctrl *pmx, struct pinctrl_desc *desc) { struct pinctrl_pin_desc *pins; unsigned int npins; int ret, i; npins = pinctrl_ops->count_get(pmx->ph, PIN_TYPE); /* * npins will never be zero, the scmi pinctrl driver has bailed out * if npins is zero. */ pins = devm_kmalloc_array(pmx->dev, npins, sizeof(*pins), GFP_KERNEL); if (!pins) return -ENOMEM; for (i = 0; i < npins; i++) { pins[i].number = i; /* * The memory for name is handled by the scmi firmware driver, * no need free here */ ret = pinctrl_ops->name_get(pmx->ph, i, PIN_TYPE, &pins[i].name); if (ret) return dev_err_probe(pmx->dev, ret, "Can't get name for pin %d", i); } desc->npins = npins; desc->pins = pins; dev_dbg(pmx->dev, "got pins %u", npins); return 0; } static int scmi_pinctrl_probe(struct scmi_device *sdev) { int ret; struct device *dev = &sdev->dev; struct scmi_pinctrl *pmx; const struct scmi_handle *handle; struct scmi_protocol_handle *ph; if (!sdev->handle) return -EINVAL; handle = sdev->handle; pinctrl_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_PINCTRL, &ph); if (IS_ERR(pinctrl_ops)) return PTR_ERR(pinctrl_ops); pmx = devm_kzalloc(dev, sizeof(*pmx), GFP_KERNEL); if (!pmx) return -ENOMEM; pmx->ph = ph; pmx->dev = dev; pmx->pctl_desc.name = DRV_NAME; pmx->pctl_desc.owner = THIS_MODULE; pmx->pctl_desc.pctlops = &pinctrl_scmi_pinctrl_ops; pmx->pctl_desc.pmxops = &pinctrl_scmi_pinmux_ops; pmx->pctl_desc.confops = &pinctrl_scmi_pinconf_ops; ret = pinctrl_scmi_get_pins(pmx, &pmx->pctl_desc); if (ret) return ret; ret = devm_pinctrl_register_and_init(dev, &pmx->pctl_desc, pmx, &pmx->pctldev); if (ret) return dev_err_probe(dev, ret, "Failed to register pinctrl\n"); pmx->nr_functions = pinctrl_scmi_get_functions_count(pmx->pctldev); pmx->functions = devm_kcalloc(dev, pmx->nr_functions, sizeof(*pmx->functions), GFP_KERNEL); if (!pmx->functions) return -ENOMEM; return pinctrl_enable(pmx->pctldev); } static const struct scmi_device_id scmi_id_table[] = { { SCMI_PROTOCOL_PINCTRL, "pinctrl" }, { } }; MODULE_DEVICE_TABLE(scmi, scmi_id_table); static struct scmi_driver scmi_pinctrl_driver = { .name = DRV_NAME, .probe = scmi_pinctrl_probe, .id_table = scmi_id_table, }; module_scmi_driver(scmi_pinctrl_driver); MODULE_AUTHOR("Oleksii Moisieiev <oleksii_moisieiev@epam.com>"); MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>"); MODULE_DESCRIPTION("ARM SCMI pin controller driver"); MODULE_LICENSE("GPL");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1