18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SuperH Pin Function Controller pinmux support. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Paul Mundt 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define DRV_NAME "sh-pfc" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 168c2ecf20Sopenharmony_ci#include <linux/pinctrl/machine.h> 178c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 188c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h> 198c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 208c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinmux.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "core.h" 258c2ecf20Sopenharmony_ci#include "../core.h" 268c2ecf20Sopenharmony_ci#include "../pinconf.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct sh_pfc_pin_config { 298c2ecf20Sopenharmony_ci unsigned int mux_mark; 308c2ecf20Sopenharmony_ci bool mux_set; 318c2ecf20Sopenharmony_ci bool gpio_enabled; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct sh_pfc_pinctrl { 358c2ecf20Sopenharmony_ci struct pinctrl_dev *pctl; 368c2ecf20Sopenharmony_ci struct pinctrl_desc pctl_desc; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci struct sh_pfc *pfc; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci struct pinctrl_pin_desc *pins; 418c2ecf20Sopenharmony_ci struct sh_pfc_pin_config *configs; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci const char *func_prop_name; 448c2ecf20Sopenharmony_ci const char *groups_prop_name; 458c2ecf20Sopenharmony_ci const char *pins_prop_name; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int sh_pfc_get_groups_count(struct pinctrl_dev *pctldev) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return pmx->pfc->info->nr_groups; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic const char *sh_pfc_get_group_name(struct pinctrl_dev *pctldev, 568c2ecf20Sopenharmony_ci unsigned selector) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return pmx->pfc->info->groups[selector].name; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int sh_pfc_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector, 648c2ecf20Sopenharmony_ci const unsigned **pins, unsigned *num_pins) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci *pins = pmx->pfc->info->groups[selector].pins; 698c2ecf20Sopenharmony_ci *num_pins = pmx->pfc->info->groups[selector].nr_pins; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return 0; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void sh_pfc_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, 758c2ecf20Sopenharmony_ci unsigned offset) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci seq_puts(s, DRV_NAME); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 818c2ecf20Sopenharmony_cistatic int sh_pfc_map_add_config(struct pinctrl_map *map, 828c2ecf20Sopenharmony_ci const char *group_or_pin, 838c2ecf20Sopenharmony_ci enum pinctrl_map_type type, 848c2ecf20Sopenharmony_ci unsigned long *configs, 858c2ecf20Sopenharmony_ci unsigned int num_configs) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci unsigned long *cfgs; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci cfgs = kmemdup(configs, num_configs * sizeof(*cfgs), 908c2ecf20Sopenharmony_ci GFP_KERNEL); 918c2ecf20Sopenharmony_ci if (cfgs == NULL) 928c2ecf20Sopenharmony_ci return -ENOMEM; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci map->type = type; 958c2ecf20Sopenharmony_ci map->data.configs.group_or_pin = group_or_pin; 968c2ecf20Sopenharmony_ci map->data.configs.configs = cfgs; 978c2ecf20Sopenharmony_ci map->data.configs.num_configs = num_configs; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int sh_pfc_dt_subnode_to_map(struct pinctrl_dev *pctldev, 1038c2ecf20Sopenharmony_ci struct device_node *np, 1048c2ecf20Sopenharmony_ci struct pinctrl_map **map, 1058c2ecf20Sopenharmony_ci unsigned int *num_maps, unsigned int *index) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 1088c2ecf20Sopenharmony_ci struct device *dev = pmx->pfc->dev; 1098c2ecf20Sopenharmony_ci struct pinctrl_map *maps = *map; 1108c2ecf20Sopenharmony_ci unsigned int nmaps = *num_maps; 1118c2ecf20Sopenharmony_ci unsigned int idx = *index; 1128c2ecf20Sopenharmony_ci unsigned int num_configs; 1138c2ecf20Sopenharmony_ci const char *function = NULL; 1148c2ecf20Sopenharmony_ci unsigned long *configs; 1158c2ecf20Sopenharmony_ci struct property *prop; 1168c2ecf20Sopenharmony_ci unsigned int num_groups; 1178c2ecf20Sopenharmony_ci unsigned int num_pins; 1188c2ecf20Sopenharmony_ci const char *group; 1198c2ecf20Sopenharmony_ci const char *pin; 1208c2ecf20Sopenharmony_ci int ret; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* Support both the old Renesas-specific properties and the new standard 1238c2ecf20Sopenharmony_ci * properties. Mixing old and new properties isn't allowed, neither 1248c2ecf20Sopenharmony_ci * inside a subnode nor across subnodes. 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_ci if (!pmx->func_prop_name) { 1278c2ecf20Sopenharmony_ci if (of_find_property(np, "groups", NULL) || 1288c2ecf20Sopenharmony_ci of_find_property(np, "pins", NULL)) { 1298c2ecf20Sopenharmony_ci pmx->func_prop_name = "function"; 1308c2ecf20Sopenharmony_ci pmx->groups_prop_name = "groups"; 1318c2ecf20Sopenharmony_ci pmx->pins_prop_name = "pins"; 1328c2ecf20Sopenharmony_ci } else { 1338c2ecf20Sopenharmony_ci pmx->func_prop_name = "renesas,function"; 1348c2ecf20Sopenharmony_ci pmx->groups_prop_name = "renesas,groups"; 1358c2ecf20Sopenharmony_ci pmx->pins_prop_name = "renesas,pins"; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* Parse the function and configuration properties. At least a function 1408c2ecf20Sopenharmony_ci * or one configuration must be specified. 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_ci ret = of_property_read_string(np, pmx->func_prop_name, &function); 1438c2ecf20Sopenharmony_ci if (ret < 0 && ret != -EINVAL) { 1448c2ecf20Sopenharmony_ci dev_err(dev, "Invalid function in DT\n"); 1458c2ecf20Sopenharmony_ci return ret; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci ret = pinconf_generic_parse_dt_config(np, NULL, &configs, &num_configs); 1498c2ecf20Sopenharmony_ci if (ret < 0) 1508c2ecf20Sopenharmony_ci return ret; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (!function && num_configs == 0) { 1538c2ecf20Sopenharmony_ci dev_err(dev, 1548c2ecf20Sopenharmony_ci "DT node must contain at least a function or config\n"); 1558c2ecf20Sopenharmony_ci ret = -ENODEV; 1568c2ecf20Sopenharmony_ci goto done; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* Count the number of pins and groups and reallocate mappings. */ 1608c2ecf20Sopenharmony_ci ret = of_property_count_strings(np, pmx->pins_prop_name); 1618c2ecf20Sopenharmony_ci if (ret == -EINVAL) { 1628c2ecf20Sopenharmony_ci num_pins = 0; 1638c2ecf20Sopenharmony_ci } else if (ret < 0) { 1648c2ecf20Sopenharmony_ci dev_err(dev, "Invalid pins list in DT\n"); 1658c2ecf20Sopenharmony_ci goto done; 1668c2ecf20Sopenharmony_ci } else { 1678c2ecf20Sopenharmony_ci num_pins = ret; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci ret = of_property_count_strings(np, pmx->groups_prop_name); 1718c2ecf20Sopenharmony_ci if (ret == -EINVAL) { 1728c2ecf20Sopenharmony_ci num_groups = 0; 1738c2ecf20Sopenharmony_ci } else if (ret < 0) { 1748c2ecf20Sopenharmony_ci dev_err(dev, "Invalid pin groups list in DT\n"); 1758c2ecf20Sopenharmony_ci goto done; 1768c2ecf20Sopenharmony_ci } else { 1778c2ecf20Sopenharmony_ci num_groups = ret; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (!num_pins && !num_groups) { 1818c2ecf20Sopenharmony_ci dev_err(dev, "No pin or group provided in DT node\n"); 1828c2ecf20Sopenharmony_ci ret = -ENODEV; 1838c2ecf20Sopenharmony_ci goto done; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (function) 1878c2ecf20Sopenharmony_ci nmaps += num_groups; 1888c2ecf20Sopenharmony_ci if (configs) 1898c2ecf20Sopenharmony_ci nmaps += num_pins + num_groups; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci maps = krealloc(maps, sizeof(*maps) * nmaps, GFP_KERNEL); 1928c2ecf20Sopenharmony_ci if (maps == NULL) { 1938c2ecf20Sopenharmony_ci ret = -ENOMEM; 1948c2ecf20Sopenharmony_ci goto done; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci *map = maps; 1988c2ecf20Sopenharmony_ci *num_maps = nmaps; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* Iterate over pins and groups and create the mappings. */ 2018c2ecf20Sopenharmony_ci of_property_for_each_string(np, pmx->groups_prop_name, prop, group) { 2028c2ecf20Sopenharmony_ci if (function) { 2038c2ecf20Sopenharmony_ci maps[idx].type = PIN_MAP_TYPE_MUX_GROUP; 2048c2ecf20Sopenharmony_ci maps[idx].data.mux.group = group; 2058c2ecf20Sopenharmony_ci maps[idx].data.mux.function = function; 2068c2ecf20Sopenharmony_ci idx++; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (configs) { 2108c2ecf20Sopenharmony_ci ret = sh_pfc_map_add_config(&maps[idx], group, 2118c2ecf20Sopenharmony_ci PIN_MAP_TYPE_CONFIGS_GROUP, 2128c2ecf20Sopenharmony_ci configs, num_configs); 2138c2ecf20Sopenharmony_ci if (ret < 0) 2148c2ecf20Sopenharmony_ci goto done; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci idx++; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (!configs) { 2218c2ecf20Sopenharmony_ci ret = 0; 2228c2ecf20Sopenharmony_ci goto done; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci of_property_for_each_string(np, pmx->pins_prop_name, prop, pin) { 2268c2ecf20Sopenharmony_ci ret = sh_pfc_map_add_config(&maps[idx], pin, 2278c2ecf20Sopenharmony_ci PIN_MAP_TYPE_CONFIGS_PIN, 2288c2ecf20Sopenharmony_ci configs, num_configs); 2298c2ecf20Sopenharmony_ci if (ret < 0) 2308c2ecf20Sopenharmony_ci goto done; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci idx++; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cidone: 2368c2ecf20Sopenharmony_ci *index = idx; 2378c2ecf20Sopenharmony_ci kfree(configs); 2388c2ecf20Sopenharmony_ci return ret; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic void sh_pfc_dt_free_map(struct pinctrl_dev *pctldev, 2428c2ecf20Sopenharmony_ci struct pinctrl_map *map, unsigned num_maps) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci unsigned int i; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (map == NULL) 2478c2ecf20Sopenharmony_ci return; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci for (i = 0; i < num_maps; ++i) { 2508c2ecf20Sopenharmony_ci if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP || 2518c2ecf20Sopenharmony_ci map[i].type == PIN_MAP_TYPE_CONFIGS_PIN) 2528c2ecf20Sopenharmony_ci kfree(map[i].data.configs.configs); 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci kfree(map); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int sh_pfc_dt_node_to_map(struct pinctrl_dev *pctldev, 2598c2ecf20Sopenharmony_ci struct device_node *np, 2608c2ecf20Sopenharmony_ci struct pinctrl_map **map, unsigned *num_maps) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 2638c2ecf20Sopenharmony_ci struct device *dev = pmx->pfc->dev; 2648c2ecf20Sopenharmony_ci struct device_node *child; 2658c2ecf20Sopenharmony_ci unsigned int index; 2668c2ecf20Sopenharmony_ci int ret; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci *map = NULL; 2698c2ecf20Sopenharmony_ci *num_maps = 0; 2708c2ecf20Sopenharmony_ci index = 0; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci for_each_child_of_node(np, child) { 2738c2ecf20Sopenharmony_ci ret = sh_pfc_dt_subnode_to_map(pctldev, child, map, num_maps, 2748c2ecf20Sopenharmony_ci &index); 2758c2ecf20Sopenharmony_ci if (ret < 0) { 2768c2ecf20Sopenharmony_ci of_node_put(child); 2778c2ecf20Sopenharmony_ci goto done; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* If no mapping has been found in child nodes try the config node. */ 2828c2ecf20Sopenharmony_ci if (*num_maps == 0) { 2838c2ecf20Sopenharmony_ci ret = sh_pfc_dt_subnode_to_map(pctldev, np, map, num_maps, 2848c2ecf20Sopenharmony_ci &index); 2858c2ecf20Sopenharmony_ci if (ret < 0) 2868c2ecf20Sopenharmony_ci goto done; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (*num_maps) 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci dev_err(dev, "no mapping found in node %pOF\n", np); 2938c2ecf20Sopenharmony_ci ret = -EINVAL; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cidone: 2968c2ecf20Sopenharmony_ci if (ret < 0) 2978c2ecf20Sopenharmony_ci sh_pfc_dt_free_map(pctldev, *map, *num_maps); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci return ret; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci#endif /* CONFIG_OF */ 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic const struct pinctrl_ops sh_pfc_pinctrl_ops = { 3048c2ecf20Sopenharmony_ci .get_groups_count = sh_pfc_get_groups_count, 3058c2ecf20Sopenharmony_ci .get_group_name = sh_pfc_get_group_name, 3068c2ecf20Sopenharmony_ci .get_group_pins = sh_pfc_get_group_pins, 3078c2ecf20Sopenharmony_ci .pin_dbg_show = sh_pfc_pin_dbg_show, 3088c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 3098c2ecf20Sopenharmony_ci .dt_node_to_map = sh_pfc_dt_node_to_map, 3108c2ecf20Sopenharmony_ci .dt_free_map = sh_pfc_dt_free_map, 3118c2ecf20Sopenharmony_ci#endif 3128c2ecf20Sopenharmony_ci}; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int sh_pfc_get_functions_count(struct pinctrl_dev *pctldev) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return pmx->pfc->info->nr_functions; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic const char *sh_pfc_get_function_name(struct pinctrl_dev *pctldev, 3228c2ecf20Sopenharmony_ci unsigned selector) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return pmx->pfc->info->functions[selector].name; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic int sh_pfc_get_function_groups(struct pinctrl_dev *pctldev, 3308c2ecf20Sopenharmony_ci unsigned selector, 3318c2ecf20Sopenharmony_ci const char * const **groups, 3328c2ecf20Sopenharmony_ci unsigned * const num_groups) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci *groups = pmx->pfc->info->functions[selector].groups; 3378c2ecf20Sopenharmony_ci *num_groups = pmx->pfc->info->functions[selector].nr_groups; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic int sh_pfc_func_set_mux(struct pinctrl_dev *pctldev, unsigned selector, 3438c2ecf20Sopenharmony_ci unsigned group) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 3468c2ecf20Sopenharmony_ci struct sh_pfc *pfc = pmx->pfc; 3478c2ecf20Sopenharmony_ci const struct sh_pfc_pin_group *grp = &pfc->info->groups[group]; 3488c2ecf20Sopenharmony_ci unsigned long flags; 3498c2ecf20Sopenharmony_ci unsigned int i; 3508c2ecf20Sopenharmony_ci int ret = 0; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci dev_dbg(pctldev->dev, "Configuring pin group %s\n", grp->name); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci spin_lock_irqsave(&pfc->lock, flags); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci for (i = 0; i < grp->nr_pins; ++i) { 3578c2ecf20Sopenharmony_ci int idx = sh_pfc_get_pin_index(pfc, grp->pins[i]); 3588c2ecf20Sopenharmony_ci struct sh_pfc_pin_config *cfg = &pmx->configs[idx]; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* 3618c2ecf20Sopenharmony_ci * This driver cannot manage both gpio and mux when the gpio 3628c2ecf20Sopenharmony_ci * pin is already enabled. So, this function fails. 3638c2ecf20Sopenharmony_ci */ 3648c2ecf20Sopenharmony_ci if (cfg->gpio_enabled) { 3658c2ecf20Sopenharmony_ci ret = -EBUSY; 3668c2ecf20Sopenharmony_ci goto done; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci ret = sh_pfc_config_mux(pfc, grp->mux[i], PINMUX_TYPE_FUNCTION); 3708c2ecf20Sopenharmony_ci if (ret < 0) 3718c2ecf20Sopenharmony_ci goto done; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* All group pins are configured, mark the pins as mux_set */ 3758c2ecf20Sopenharmony_ci for (i = 0; i < grp->nr_pins; ++i) { 3768c2ecf20Sopenharmony_ci int idx = sh_pfc_get_pin_index(pfc, grp->pins[i]); 3778c2ecf20Sopenharmony_ci struct sh_pfc_pin_config *cfg = &pmx->configs[idx]; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci cfg->mux_set = true; 3808c2ecf20Sopenharmony_ci cfg->mux_mark = grp->mux[i]; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cidone: 3848c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pfc->lock, flags); 3858c2ecf20Sopenharmony_ci return ret; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic int sh_pfc_gpio_request_enable(struct pinctrl_dev *pctldev, 3898c2ecf20Sopenharmony_ci struct pinctrl_gpio_range *range, 3908c2ecf20Sopenharmony_ci unsigned offset) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 3938c2ecf20Sopenharmony_ci struct sh_pfc *pfc = pmx->pfc; 3948c2ecf20Sopenharmony_ci int idx = sh_pfc_get_pin_index(pfc, offset); 3958c2ecf20Sopenharmony_ci struct sh_pfc_pin_config *cfg = &pmx->configs[idx]; 3968c2ecf20Sopenharmony_ci unsigned long flags; 3978c2ecf20Sopenharmony_ci int ret; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci spin_lock_irqsave(&pfc->lock, flags); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (!pfc->gpio) { 4028c2ecf20Sopenharmony_ci /* If GPIOs are handled externally the pin mux type need to be 4038c2ecf20Sopenharmony_ci * set to GPIO here. 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_ci const struct sh_pfc_pin *pin = &pfc->info->pins[idx]; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci ret = sh_pfc_config_mux(pfc, pin->enum_id, PINMUX_TYPE_GPIO); 4088c2ecf20Sopenharmony_ci if (ret < 0) 4098c2ecf20Sopenharmony_ci goto done; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci cfg->gpio_enabled = true; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci ret = 0; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cidone: 4178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pfc->lock, flags); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci return ret; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic void sh_pfc_gpio_disable_free(struct pinctrl_dev *pctldev, 4238c2ecf20Sopenharmony_ci struct pinctrl_gpio_range *range, 4248c2ecf20Sopenharmony_ci unsigned offset) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 4278c2ecf20Sopenharmony_ci struct sh_pfc *pfc = pmx->pfc; 4288c2ecf20Sopenharmony_ci int idx = sh_pfc_get_pin_index(pfc, offset); 4298c2ecf20Sopenharmony_ci struct sh_pfc_pin_config *cfg = &pmx->configs[idx]; 4308c2ecf20Sopenharmony_ci unsigned long flags; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci spin_lock_irqsave(&pfc->lock, flags); 4338c2ecf20Sopenharmony_ci cfg->gpio_enabled = false; 4348c2ecf20Sopenharmony_ci /* If mux is already set, this configures it here */ 4358c2ecf20Sopenharmony_ci if (cfg->mux_set) 4368c2ecf20Sopenharmony_ci sh_pfc_config_mux(pfc, cfg->mux_mark, PINMUX_TYPE_FUNCTION); 4378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pfc->lock, flags); 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic int sh_pfc_gpio_set_direction(struct pinctrl_dev *pctldev, 4418c2ecf20Sopenharmony_ci struct pinctrl_gpio_range *range, 4428c2ecf20Sopenharmony_ci unsigned offset, bool input) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 4458c2ecf20Sopenharmony_ci struct sh_pfc *pfc = pmx->pfc; 4468c2ecf20Sopenharmony_ci int new_type = input ? PINMUX_TYPE_INPUT : PINMUX_TYPE_OUTPUT; 4478c2ecf20Sopenharmony_ci int idx = sh_pfc_get_pin_index(pfc, offset); 4488c2ecf20Sopenharmony_ci const struct sh_pfc_pin *pin = &pfc->info->pins[idx]; 4498c2ecf20Sopenharmony_ci unsigned long flags; 4508c2ecf20Sopenharmony_ci unsigned int dir; 4518c2ecf20Sopenharmony_ci int ret; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* Check if the requested direction is supported by the pin. Not all SoC 4548c2ecf20Sopenharmony_ci * provide pin config data, so perform the check conditionally. 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_ci if (pin->configs) { 4578c2ecf20Sopenharmony_ci dir = input ? SH_PFC_PIN_CFG_INPUT : SH_PFC_PIN_CFG_OUTPUT; 4588c2ecf20Sopenharmony_ci if (!(pin->configs & dir)) 4598c2ecf20Sopenharmony_ci return -EINVAL; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci spin_lock_irqsave(&pfc->lock, flags); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci ret = sh_pfc_config_mux(pfc, pin->enum_id, new_type); 4658c2ecf20Sopenharmony_ci if (ret < 0) 4668c2ecf20Sopenharmony_ci goto done; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cidone: 4698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pfc->lock, flags); 4708c2ecf20Sopenharmony_ci return ret; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic const struct pinmux_ops sh_pfc_pinmux_ops = { 4748c2ecf20Sopenharmony_ci .get_functions_count = sh_pfc_get_functions_count, 4758c2ecf20Sopenharmony_ci .get_function_name = sh_pfc_get_function_name, 4768c2ecf20Sopenharmony_ci .get_function_groups = sh_pfc_get_function_groups, 4778c2ecf20Sopenharmony_ci .set_mux = sh_pfc_func_set_mux, 4788c2ecf20Sopenharmony_ci .gpio_request_enable = sh_pfc_gpio_request_enable, 4798c2ecf20Sopenharmony_ci .gpio_disable_free = sh_pfc_gpio_disable_free, 4808c2ecf20Sopenharmony_ci .gpio_set_direction = sh_pfc_gpio_set_direction, 4818c2ecf20Sopenharmony_ci}; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic u32 sh_pfc_pinconf_find_drive_strength_reg(struct sh_pfc *pfc, 4848c2ecf20Sopenharmony_ci unsigned int pin, unsigned int *offset, unsigned int *size) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci const struct pinmux_drive_reg_field *field; 4878c2ecf20Sopenharmony_ci const struct pinmux_drive_reg *reg; 4888c2ecf20Sopenharmony_ci unsigned int i; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci for (reg = pfc->info->drive_regs; reg->reg; ++reg) { 4918c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(reg->fields); ++i) { 4928c2ecf20Sopenharmony_ci field = ®->fields[i]; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (field->size && field->pin == pin) { 4958c2ecf20Sopenharmony_ci *offset = field->offset; 4968c2ecf20Sopenharmony_ci *size = field->size; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci return reg->reg; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci return 0; 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic int sh_pfc_pinconf_get_drive_strength(struct sh_pfc *pfc, 5078c2ecf20Sopenharmony_ci unsigned int pin) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci unsigned long flags; 5108c2ecf20Sopenharmony_ci unsigned int offset; 5118c2ecf20Sopenharmony_ci unsigned int size; 5128c2ecf20Sopenharmony_ci u32 reg; 5138c2ecf20Sopenharmony_ci u32 val; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci reg = sh_pfc_pinconf_find_drive_strength_reg(pfc, pin, &offset, &size); 5168c2ecf20Sopenharmony_ci if (!reg) 5178c2ecf20Sopenharmony_ci return -EINVAL; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci spin_lock_irqsave(&pfc->lock, flags); 5208c2ecf20Sopenharmony_ci val = sh_pfc_read(pfc, reg); 5218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pfc->lock, flags); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci val = (val >> offset) & GENMASK(size - 1, 0); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* Convert the value to mA based on a full drive strength value of 24mA. 5268c2ecf20Sopenharmony_ci * We can make the full value configurable later if needed. 5278c2ecf20Sopenharmony_ci */ 5288c2ecf20Sopenharmony_ci return (val + 1) * (size == 2 ? 6 : 3); 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic int sh_pfc_pinconf_set_drive_strength(struct sh_pfc *pfc, 5328c2ecf20Sopenharmony_ci unsigned int pin, u16 strength) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci unsigned long flags; 5358c2ecf20Sopenharmony_ci unsigned int offset; 5368c2ecf20Sopenharmony_ci unsigned int size; 5378c2ecf20Sopenharmony_ci unsigned int step; 5388c2ecf20Sopenharmony_ci u32 reg; 5398c2ecf20Sopenharmony_ci u32 val; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci reg = sh_pfc_pinconf_find_drive_strength_reg(pfc, pin, &offset, &size); 5428c2ecf20Sopenharmony_ci if (!reg) 5438c2ecf20Sopenharmony_ci return -EINVAL; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci step = size == 2 ? 6 : 3; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci if (strength < step || strength > 24) 5488c2ecf20Sopenharmony_ci return -EINVAL; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* Convert the value from mA based on a full drive strength value of 5518c2ecf20Sopenharmony_ci * 24mA. We can make the full value configurable later if needed. 5528c2ecf20Sopenharmony_ci */ 5538c2ecf20Sopenharmony_ci strength = strength / step - 1; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci spin_lock_irqsave(&pfc->lock, flags); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci val = sh_pfc_read(pfc, reg); 5588c2ecf20Sopenharmony_ci val &= ~GENMASK(offset + size - 1, offset); 5598c2ecf20Sopenharmony_ci val |= strength << offset; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci sh_pfc_write(pfc, reg, val); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pfc->lock, flags); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci return 0; 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci/* Check whether the requested parameter is supported for a pin. */ 5698c2ecf20Sopenharmony_cistatic bool sh_pfc_pinconf_validate(struct sh_pfc *pfc, unsigned int _pin, 5708c2ecf20Sopenharmony_ci enum pin_config_param param) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci int idx = sh_pfc_get_pin_index(pfc, _pin); 5738c2ecf20Sopenharmony_ci const struct sh_pfc_pin *pin = &pfc->info->pins[idx]; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci switch (param) { 5768c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 5778c2ecf20Sopenharmony_ci return pin->configs & SH_PFC_PIN_CFG_PULL_UP_DOWN; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 5808c2ecf20Sopenharmony_ci return pin->configs & SH_PFC_PIN_CFG_PULL_UP; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 5838c2ecf20Sopenharmony_ci return pin->configs & SH_PFC_PIN_CFG_PULL_DOWN; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: 5868c2ecf20Sopenharmony_ci return pin->configs & SH_PFC_PIN_CFG_DRIVE_STRENGTH; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci case PIN_CONFIG_POWER_SOURCE: 5898c2ecf20Sopenharmony_ci return pin->configs & SH_PFC_PIN_CFG_IO_VOLTAGE; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci default: 5928c2ecf20Sopenharmony_ci return false; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic int sh_pfc_pinconf_get(struct pinctrl_dev *pctldev, unsigned _pin, 5978c2ecf20Sopenharmony_ci unsigned long *config) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 6008c2ecf20Sopenharmony_ci struct sh_pfc *pfc = pmx->pfc; 6018c2ecf20Sopenharmony_ci enum pin_config_param param = pinconf_to_config_param(*config); 6028c2ecf20Sopenharmony_ci unsigned long flags; 6038c2ecf20Sopenharmony_ci unsigned int arg; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (!sh_pfc_pinconf_validate(pfc, _pin, param)) 6068c2ecf20Sopenharmony_ci return -ENOTSUPP; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci switch (param) { 6098c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 6108c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 6118c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: { 6128c2ecf20Sopenharmony_ci unsigned int bias; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (!pfc->info->ops || !pfc->info->ops->get_bias) 6158c2ecf20Sopenharmony_ci return -ENOTSUPP; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci spin_lock_irqsave(&pfc->lock, flags); 6188c2ecf20Sopenharmony_ci bias = pfc->info->ops->get_bias(pfc, _pin); 6198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pfc->lock, flags); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (bias != param) 6228c2ecf20Sopenharmony_ci return -EINVAL; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci arg = 0; 6258c2ecf20Sopenharmony_ci break; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: { 6298c2ecf20Sopenharmony_ci int ret; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci ret = sh_pfc_pinconf_get_drive_strength(pfc, _pin); 6328c2ecf20Sopenharmony_ci if (ret < 0) 6338c2ecf20Sopenharmony_ci return ret; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci arg = ret; 6368c2ecf20Sopenharmony_ci break; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci case PIN_CONFIG_POWER_SOURCE: { 6408c2ecf20Sopenharmony_ci u32 pocctrl, val; 6418c2ecf20Sopenharmony_ci int bit; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (!pfc->info->ops || !pfc->info->ops->pin_to_pocctrl) 6448c2ecf20Sopenharmony_ci return -ENOTSUPP; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci bit = pfc->info->ops->pin_to_pocctrl(pfc, _pin, &pocctrl); 6478c2ecf20Sopenharmony_ci if (WARN(bit < 0, "invalid pin %#x", _pin)) 6488c2ecf20Sopenharmony_ci return bit; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci spin_lock_irqsave(&pfc->lock, flags); 6518c2ecf20Sopenharmony_ci val = sh_pfc_read(pfc, pocctrl); 6528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pfc->lock, flags); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci arg = (val & BIT(bit)) ? 3300 : 1800; 6558c2ecf20Sopenharmony_ci break; 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci default: 6598c2ecf20Sopenharmony_ci return -ENOTSUPP; 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci *config = pinconf_to_config_packed(param, arg); 6638c2ecf20Sopenharmony_ci return 0; 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cistatic int sh_pfc_pinconf_set(struct pinctrl_dev *pctldev, unsigned _pin, 6678c2ecf20Sopenharmony_ci unsigned long *configs, unsigned num_configs) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 6708c2ecf20Sopenharmony_ci struct sh_pfc *pfc = pmx->pfc; 6718c2ecf20Sopenharmony_ci enum pin_config_param param; 6728c2ecf20Sopenharmony_ci unsigned long flags; 6738c2ecf20Sopenharmony_ci unsigned int i; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci for (i = 0; i < num_configs; i++) { 6768c2ecf20Sopenharmony_ci param = pinconf_to_config_param(configs[i]); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (!sh_pfc_pinconf_validate(pfc, _pin, param)) 6798c2ecf20Sopenharmony_ci return -ENOTSUPP; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci switch (param) { 6828c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 6838c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 6848c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 6858c2ecf20Sopenharmony_ci if (!pfc->info->ops || !pfc->info->ops->set_bias) 6868c2ecf20Sopenharmony_ci return -ENOTSUPP; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci spin_lock_irqsave(&pfc->lock, flags); 6898c2ecf20Sopenharmony_ci pfc->info->ops->set_bias(pfc, _pin, param); 6908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pfc->lock, flags); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: { 6958c2ecf20Sopenharmony_ci unsigned int arg = 6968c2ecf20Sopenharmony_ci pinconf_to_config_argument(configs[i]); 6978c2ecf20Sopenharmony_ci int ret; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci ret = sh_pfc_pinconf_set_drive_strength(pfc, _pin, arg); 7008c2ecf20Sopenharmony_ci if (ret < 0) 7018c2ecf20Sopenharmony_ci return ret; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci break; 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci case PIN_CONFIG_POWER_SOURCE: { 7078c2ecf20Sopenharmony_ci unsigned int mV = pinconf_to_config_argument(configs[i]); 7088c2ecf20Sopenharmony_ci u32 pocctrl, val; 7098c2ecf20Sopenharmony_ci int bit; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci if (!pfc->info->ops || !pfc->info->ops->pin_to_pocctrl) 7128c2ecf20Sopenharmony_ci return -ENOTSUPP; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci bit = pfc->info->ops->pin_to_pocctrl(pfc, _pin, &pocctrl); 7158c2ecf20Sopenharmony_ci if (WARN(bit < 0, "invalid pin %#x", _pin)) 7168c2ecf20Sopenharmony_ci return bit; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci if (mV != 1800 && mV != 3300) 7198c2ecf20Sopenharmony_ci return -EINVAL; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci spin_lock_irqsave(&pfc->lock, flags); 7228c2ecf20Sopenharmony_ci val = sh_pfc_read(pfc, pocctrl); 7238c2ecf20Sopenharmony_ci if (mV == 3300) 7248c2ecf20Sopenharmony_ci val |= BIT(bit); 7258c2ecf20Sopenharmony_ci else 7268c2ecf20Sopenharmony_ci val &= ~BIT(bit); 7278c2ecf20Sopenharmony_ci sh_pfc_write(pfc, pocctrl, val); 7288c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pfc->lock, flags); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci break; 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci default: 7348c2ecf20Sopenharmony_ci return -ENOTSUPP; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci } /* for each config */ 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci return 0; 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic int sh_pfc_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned group, 7428c2ecf20Sopenharmony_ci unsigned long *configs, 7438c2ecf20Sopenharmony_ci unsigned num_configs) 7448c2ecf20Sopenharmony_ci{ 7458c2ecf20Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 7468c2ecf20Sopenharmony_ci const unsigned int *pins; 7478c2ecf20Sopenharmony_ci unsigned int num_pins; 7488c2ecf20Sopenharmony_ci unsigned int i, ret; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci pins = pmx->pfc->info->groups[group].pins; 7518c2ecf20Sopenharmony_ci num_pins = pmx->pfc->info->groups[group].nr_pins; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci for (i = 0; i < num_pins; ++i) { 7548c2ecf20Sopenharmony_ci ret = sh_pfc_pinconf_set(pctldev, pins[i], configs, num_configs); 7558c2ecf20Sopenharmony_ci if (ret) 7568c2ecf20Sopenharmony_ci return ret; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci return 0; 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic const struct pinconf_ops sh_pfc_pinconf_ops = { 7638c2ecf20Sopenharmony_ci .is_generic = true, 7648c2ecf20Sopenharmony_ci .pin_config_get = sh_pfc_pinconf_get, 7658c2ecf20Sopenharmony_ci .pin_config_set = sh_pfc_pinconf_set, 7668c2ecf20Sopenharmony_ci .pin_config_group_set = sh_pfc_pinconf_group_set, 7678c2ecf20Sopenharmony_ci .pin_config_config_dbg_show = pinconf_generic_dump_config, 7688c2ecf20Sopenharmony_ci}; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci/* PFC ranges -> pinctrl pin descs */ 7718c2ecf20Sopenharmony_cistatic int sh_pfc_map_pins(struct sh_pfc *pfc, struct sh_pfc_pinctrl *pmx) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci unsigned int i; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci /* Allocate and initialize the pins and configs arrays. */ 7768c2ecf20Sopenharmony_ci pmx->pins = devm_kcalloc(pfc->dev, 7778c2ecf20Sopenharmony_ci pfc->info->nr_pins, sizeof(*pmx->pins), 7788c2ecf20Sopenharmony_ci GFP_KERNEL); 7798c2ecf20Sopenharmony_ci if (unlikely(!pmx->pins)) 7808c2ecf20Sopenharmony_ci return -ENOMEM; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci pmx->configs = devm_kcalloc(pfc->dev, 7838c2ecf20Sopenharmony_ci pfc->info->nr_pins, sizeof(*pmx->configs), 7848c2ecf20Sopenharmony_ci GFP_KERNEL); 7858c2ecf20Sopenharmony_ci if (unlikely(!pmx->configs)) 7868c2ecf20Sopenharmony_ci return -ENOMEM; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci for (i = 0; i < pfc->info->nr_pins; ++i) { 7898c2ecf20Sopenharmony_ci const struct sh_pfc_pin *info = &pfc->info->pins[i]; 7908c2ecf20Sopenharmony_ci struct pinctrl_pin_desc *pin = &pmx->pins[i]; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci /* If the pin number is equal to -1 all pins are considered */ 7938c2ecf20Sopenharmony_ci pin->number = info->pin != (u16)-1 ? info->pin : i; 7948c2ecf20Sopenharmony_ci pin->name = info->name; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci return 0; 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ciint sh_pfc_register_pinctrl(struct sh_pfc *pfc) 8018c2ecf20Sopenharmony_ci{ 8028c2ecf20Sopenharmony_ci struct sh_pfc_pinctrl *pmx; 8038c2ecf20Sopenharmony_ci int ret; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci pmx = devm_kzalloc(pfc->dev, sizeof(*pmx), GFP_KERNEL); 8068c2ecf20Sopenharmony_ci if (unlikely(!pmx)) 8078c2ecf20Sopenharmony_ci return -ENOMEM; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci pmx->pfc = pfc; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci ret = sh_pfc_map_pins(pfc, pmx); 8128c2ecf20Sopenharmony_ci if (ret < 0) 8138c2ecf20Sopenharmony_ci return ret; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci pmx->pctl_desc.name = DRV_NAME; 8168c2ecf20Sopenharmony_ci pmx->pctl_desc.owner = THIS_MODULE; 8178c2ecf20Sopenharmony_ci pmx->pctl_desc.pctlops = &sh_pfc_pinctrl_ops; 8188c2ecf20Sopenharmony_ci pmx->pctl_desc.pmxops = &sh_pfc_pinmux_ops; 8198c2ecf20Sopenharmony_ci pmx->pctl_desc.confops = &sh_pfc_pinconf_ops; 8208c2ecf20Sopenharmony_ci pmx->pctl_desc.pins = pmx->pins; 8218c2ecf20Sopenharmony_ci pmx->pctl_desc.npins = pfc->info->nr_pins; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci ret = devm_pinctrl_register_and_init(pfc->dev, &pmx->pctl_desc, pmx, 8248c2ecf20Sopenharmony_ci &pmx->pctl); 8258c2ecf20Sopenharmony_ci if (ret) { 8268c2ecf20Sopenharmony_ci dev_err(pfc->dev, "could not register: %i\n", ret); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci return ret; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci return pinctrl_enable(pmx->pctl); 8328c2ecf20Sopenharmony_ci} 833