1*34acc5a8SFrank Li // SPDX-License-Identifier: GPL-2.0-only 2*34acc5a8SFrank Li /* 3*34acc5a8SFrank Li * Generic Pin Control Driver for Board-Level Mux Chips 4*34acc5a8SFrank Li * Copyright 2026 NXP 5*34acc5a8SFrank Li */ 6*34acc5a8SFrank Li 7*34acc5a8SFrank Li #include <linux/cleanup.h> 8*34acc5a8SFrank Li #include <linux/module.h> 9*34acc5a8SFrank Li #include <linux/of.h> 10*34acc5a8SFrank Li #include <linux/mutex.h> 11*34acc5a8SFrank Li #include <linux/mux/consumer.h> 12*34acc5a8SFrank Li #include <linux/platform_device.h> 13*34acc5a8SFrank Li #include <linux/pinctrl/pinconf-generic.h> 14*34acc5a8SFrank Li #include <linux/pinctrl/pinctrl.h> 15*34acc5a8SFrank Li #include <linux/pinctrl/pinmux.h> 16*34acc5a8SFrank Li #include <linux/slab.h> 17*34acc5a8SFrank Li 18*34acc5a8SFrank Li #include "core.h" 19*34acc5a8SFrank Li #include "pinconf.h" 20*34acc5a8SFrank Li #include "pinmux.h" 21*34acc5a8SFrank Li #include "pinctrl-utils.h" 22*34acc5a8SFrank Li 23*34acc5a8SFrank Li struct mux_pin_function { 24*34acc5a8SFrank Li struct mux_state *mux_state; 25*34acc5a8SFrank Li }; 26*34acc5a8SFrank Li 27*34acc5a8SFrank Li struct mux_pinctrl { 28*34acc5a8SFrank Li struct device *dev; 29*34acc5a8SFrank Li struct pinctrl_dev *pctl; 30*34acc5a8SFrank Li 31*34acc5a8SFrank Li /* mutex protect [pinctrl|pinmux]_generic functions */ 32*34acc5a8SFrank Li struct mutex lock; 33*34acc5a8SFrank Li }; 34*34acc5a8SFrank Li 35*34acc5a8SFrank Li static int 36*34acc5a8SFrank Li mux_pinmux_dt_node_to_map(struct pinctrl_dev *pctldev, 37*34acc5a8SFrank Li struct device_node *np_config, 38*34acc5a8SFrank Li struct pinctrl_map **maps, unsigned int *num_maps) 39*34acc5a8SFrank Li { 40*34acc5a8SFrank Li unsigned int num_reserved_maps = 0; 41*34acc5a8SFrank Li struct mux_pin_function *function; 42*34acc5a8SFrank Li const char **group_names; 43*34acc5a8SFrank Li int ret; 44*34acc5a8SFrank Li 45*34acc5a8SFrank Li function = devm_kzalloc(pctldev->dev, sizeof(*function), GFP_KERNEL); 46*34acc5a8SFrank Li if (!function) 47*34acc5a8SFrank Li return -ENOMEM; 48*34acc5a8SFrank Li 49*34acc5a8SFrank Li group_names = devm_kcalloc(pctldev->dev, 1, sizeof(*group_names), GFP_KERNEL); 50*34acc5a8SFrank Li if (!group_names) 51*34acc5a8SFrank Li return -ENOMEM; 52*34acc5a8SFrank Li 53*34acc5a8SFrank Li function->mux_state = devm_mux_state_get_from_np(pctldev->dev, NULL, np_config); 54*34acc5a8SFrank Li if (IS_ERR(function->mux_state)) 55*34acc5a8SFrank Li return PTR_ERR(function->mux_state); 56*34acc5a8SFrank Li 57*34acc5a8SFrank Li ret = pinctrl_generic_to_map(pctldev, np_config, np_config, maps, 58*34acc5a8SFrank Li num_maps, &num_reserved_maps, group_names, 59*34acc5a8SFrank Li 0, &np_config->name, NULL, 0); 60*34acc5a8SFrank Li 61*34acc5a8SFrank Li if (ret) 62*34acc5a8SFrank Li return ret; 63*34acc5a8SFrank Li 64*34acc5a8SFrank Li ret = pinmux_generic_add_function(pctldev, np_config->name, group_names, 65*34acc5a8SFrank Li 1, function); 66*34acc5a8SFrank Li if (ret < 0) { 67*34acc5a8SFrank Li pinctrl_utils_free_map(pctldev, *maps, *num_maps); 68*34acc5a8SFrank Li return ret; 69*34acc5a8SFrank Li } 70*34acc5a8SFrank Li 71*34acc5a8SFrank Li return 0; 72*34acc5a8SFrank Li } 73*34acc5a8SFrank Li 74*34acc5a8SFrank Li static const struct pinctrl_ops mux_pinctrl_ops = { 75*34acc5a8SFrank Li .get_groups_count = pinctrl_generic_get_group_count, 76*34acc5a8SFrank Li .get_group_name = pinctrl_generic_get_group_name, 77*34acc5a8SFrank Li .get_group_pins = pinctrl_generic_get_group_pins, 78*34acc5a8SFrank Li .dt_node_to_map = mux_pinmux_dt_node_to_map, 79*34acc5a8SFrank Li .dt_free_map = pinctrl_utils_free_map, 80*34acc5a8SFrank Li }; 81*34acc5a8SFrank Li 82*34acc5a8SFrank Li static int mux_pinmux_set_mux(struct pinctrl_dev *pctldev, 83*34acc5a8SFrank Li unsigned int func_selector, 84*34acc5a8SFrank Li unsigned int group_selector) 85*34acc5a8SFrank Li { 86*34acc5a8SFrank Li struct mux_pinctrl *mpctl = pinctrl_dev_get_drvdata(pctldev); 87*34acc5a8SFrank Li const struct function_desc *function; 88*34acc5a8SFrank Li struct mux_pin_function *func; 89*34acc5a8SFrank Li int ret; 90*34acc5a8SFrank Li 91*34acc5a8SFrank Li guard(mutex)(&mpctl->lock); 92*34acc5a8SFrank Li 93*34acc5a8SFrank Li function = pinmux_generic_get_function(pctldev, func_selector); 94*34acc5a8SFrank Li func = function->data; 95*34acc5a8SFrank Li 96*34acc5a8SFrank Li ret = mux_state_select(func->mux_state); 97*34acc5a8SFrank Li if (ret) 98*34acc5a8SFrank Li return ret; 99*34acc5a8SFrank Li 100*34acc5a8SFrank Li return 0; 101*34acc5a8SFrank Li } 102*34acc5a8SFrank Li 103*34acc5a8SFrank Li static void mux_pinmux_release_mux(struct pinctrl_dev *pctldev, 104*34acc5a8SFrank Li unsigned int func_selector, 105*34acc5a8SFrank Li unsigned int group_selector) 106*34acc5a8SFrank Li { 107*34acc5a8SFrank Li struct mux_pinctrl *mpctl = pinctrl_dev_get_drvdata(pctldev); 108*34acc5a8SFrank Li const struct function_desc *function; 109*34acc5a8SFrank Li struct mux_pin_function *func; 110*34acc5a8SFrank Li 111*34acc5a8SFrank Li guard(mutex)(&mpctl->lock); 112*34acc5a8SFrank Li 113*34acc5a8SFrank Li function = pinmux_generic_get_function(pctldev, func_selector); 114*34acc5a8SFrank Li func = function->data; 115*34acc5a8SFrank Li 116*34acc5a8SFrank Li mux_state_deselect(func->mux_state); 117*34acc5a8SFrank Li } 118*34acc5a8SFrank Li 119*34acc5a8SFrank Li static const struct pinmux_ops mux_pinmux_ops = { 120*34acc5a8SFrank Li .get_functions_count = pinmux_generic_get_function_count, 121*34acc5a8SFrank Li .get_function_name = pinmux_generic_get_function_name, 122*34acc5a8SFrank Li .get_function_groups = pinmux_generic_get_function_groups, 123*34acc5a8SFrank Li .set_mux = mux_pinmux_set_mux, 124*34acc5a8SFrank Li .release_mux = mux_pinmux_release_mux, 125*34acc5a8SFrank Li }; 126*34acc5a8SFrank Li 127*34acc5a8SFrank Li static int mux_pinctrl_probe(struct platform_device *pdev) 128*34acc5a8SFrank Li { 129*34acc5a8SFrank Li struct device *dev = &pdev->dev; 130*34acc5a8SFrank Li struct mux_pinctrl *mpctl; 131*34acc5a8SFrank Li struct pinctrl_desc *pctl_desc; 132*34acc5a8SFrank Li int ret; 133*34acc5a8SFrank Li 134*34acc5a8SFrank Li mpctl = devm_kzalloc(dev, sizeof(*mpctl), GFP_KERNEL); 135*34acc5a8SFrank Li if (!mpctl) 136*34acc5a8SFrank Li return -ENOMEM; 137*34acc5a8SFrank Li 138*34acc5a8SFrank Li mpctl->dev = dev; 139*34acc5a8SFrank Li 140*34acc5a8SFrank Li platform_set_drvdata(pdev, mpctl); 141*34acc5a8SFrank Li 142*34acc5a8SFrank Li pctl_desc = devm_kzalloc(dev, sizeof(*pctl_desc), GFP_KERNEL); 143*34acc5a8SFrank Li if (!pctl_desc) 144*34acc5a8SFrank Li return -ENOMEM; 145*34acc5a8SFrank Li 146*34acc5a8SFrank Li ret = devm_mutex_init(dev, &mpctl->lock); 147*34acc5a8SFrank Li if (ret) 148*34acc5a8SFrank Li return ret; 149*34acc5a8SFrank Li 150*34acc5a8SFrank Li pctl_desc->name = dev_name(dev); 151*34acc5a8SFrank Li pctl_desc->owner = THIS_MODULE; 152*34acc5a8SFrank Li pctl_desc->pctlops = &mux_pinctrl_ops; 153*34acc5a8SFrank Li pctl_desc->pmxops = &mux_pinmux_ops; 154*34acc5a8SFrank Li 155*34acc5a8SFrank Li ret = devm_pinctrl_register_and_init(dev, pctl_desc, mpctl, 156*34acc5a8SFrank Li &mpctl->pctl); 157*34acc5a8SFrank Li if (ret) 158*34acc5a8SFrank Li return dev_err_probe(dev, ret, "Failed to register pinctrl.\n"); 159*34acc5a8SFrank Li 160*34acc5a8SFrank Li ret = pinctrl_enable(mpctl->pctl); 161*34acc5a8SFrank Li if (ret) 162*34acc5a8SFrank Li return dev_err_probe(dev, ret, "Failed to enable pinctrl.\n"); 163*34acc5a8SFrank Li 164*34acc5a8SFrank Li return 0; 165*34acc5a8SFrank Li } 166*34acc5a8SFrank Li 167*34acc5a8SFrank Li static const struct of_device_id mux_pinctrl_of_match[] = { 168*34acc5a8SFrank Li { .compatible = "pinctrl-multiplexer" }, 169*34acc5a8SFrank Li { } 170*34acc5a8SFrank Li }; 171*34acc5a8SFrank Li MODULE_DEVICE_TABLE(of, mux_pinctrl_of_match); 172*34acc5a8SFrank Li 173*34acc5a8SFrank Li static struct platform_driver mux_pinctrl_driver = { 174*34acc5a8SFrank Li .driver = { 175*34acc5a8SFrank Li .name = "generic-pinctrl-mux", 176*34acc5a8SFrank Li .of_match_table = mux_pinctrl_of_match, 177*34acc5a8SFrank Li }, 178*34acc5a8SFrank Li .probe = mux_pinctrl_probe, 179*34acc5a8SFrank Li }; 180*34acc5a8SFrank Li module_platform_driver(mux_pinctrl_driver); 181*34acc5a8SFrank Li 182*34acc5a8SFrank Li MODULE_AUTHOR("Frank Li <Frank.Li@nxp.com>"); 183*34acc5a8SFrank Li MODULE_DESCRIPTION("Generic Pin Control Driver for Board-Level Mux Chips"); 184*34acc5a8SFrank Li MODULE_LICENSE("GPL"); 185