162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SuperH Pin Function Controller pinmux support. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Paul Mundt 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define DRV_NAME "sh-pfc" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/seq_file.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/spinlock.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 2062306a36Sopenharmony_ci#include <linux/pinctrl/machine.h> 2162306a36Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h> 2262306a36Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 2362306a36Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 2462306a36Sopenharmony_ci#include <linux/pinctrl/pinmux.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "core.h" 2762306a36Sopenharmony_ci#include "../core.h" 2862306a36Sopenharmony_ci#include "../pinconf.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct sh_pfc_pin_config { 3162306a36Sopenharmony_ci u16 gpio_enabled:1; 3262306a36Sopenharmony_ci u16 mux_mark:15; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct sh_pfc_pinctrl { 3662306a36Sopenharmony_ci struct pinctrl_dev *pctl; 3762306a36Sopenharmony_ci struct pinctrl_desc pctl_desc; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci struct sh_pfc *pfc; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci struct pinctrl_pin_desc *pins; 4262306a36Sopenharmony_ci struct sh_pfc_pin_config *configs; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int sh_pfc_get_groups_count(struct pinctrl_dev *pctldev) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return pmx->pfc->info->nr_groups; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic const char *sh_pfc_get_group_name(struct pinctrl_dev *pctldev, 5362306a36Sopenharmony_ci unsigned selector) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return pmx->pfc->info->groups[selector].name; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic int sh_pfc_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector, 6162306a36Sopenharmony_ci const unsigned **pins, unsigned *num_pins) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci *pins = pmx->pfc->info->groups[selector].pins; 6662306a36Sopenharmony_ci *num_pins = pmx->pfc->info->groups[selector].nr_pins; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic void sh_pfc_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, 7262306a36Sopenharmony_ci unsigned offset) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci seq_puts(s, DRV_NAME); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#ifdef CONFIG_OF 7862306a36Sopenharmony_cistatic int sh_pfc_map_add_config(struct pinctrl_map *map, 7962306a36Sopenharmony_ci const char *group_or_pin, 8062306a36Sopenharmony_ci enum pinctrl_map_type type, 8162306a36Sopenharmony_ci unsigned long *configs, 8262306a36Sopenharmony_ci unsigned int num_configs) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci unsigned long *cfgs; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci cfgs = kmemdup(configs, num_configs * sizeof(*cfgs), 8762306a36Sopenharmony_ci GFP_KERNEL); 8862306a36Sopenharmony_ci if (cfgs == NULL) 8962306a36Sopenharmony_ci return -ENOMEM; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci map->type = type; 9262306a36Sopenharmony_ci map->data.configs.group_or_pin = group_or_pin; 9362306a36Sopenharmony_ci map->data.configs.configs = cfgs; 9462306a36Sopenharmony_ci map->data.configs.num_configs = num_configs; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return 0; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int sh_pfc_dt_subnode_to_map(struct pinctrl_dev *pctldev, 10062306a36Sopenharmony_ci struct device_node *np, 10162306a36Sopenharmony_ci struct pinctrl_map **map, 10262306a36Sopenharmony_ci unsigned int *num_maps, unsigned int *index) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 10562306a36Sopenharmony_ci struct device *dev = pmx->pfc->dev; 10662306a36Sopenharmony_ci struct pinctrl_map *maps = *map; 10762306a36Sopenharmony_ci unsigned int nmaps = *num_maps; 10862306a36Sopenharmony_ci unsigned int idx = *index; 10962306a36Sopenharmony_ci unsigned int num_configs; 11062306a36Sopenharmony_ci const char *function = NULL; 11162306a36Sopenharmony_ci unsigned long *configs; 11262306a36Sopenharmony_ci struct property *prop; 11362306a36Sopenharmony_ci unsigned int num_groups; 11462306a36Sopenharmony_ci unsigned int num_pins; 11562306a36Sopenharmony_ci const char *group; 11662306a36Sopenharmony_ci const char *pin; 11762306a36Sopenharmony_ci int ret; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* Parse the function and configuration properties. At least a function 12062306a36Sopenharmony_ci * or one configuration must be specified. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_ci ret = of_property_read_string(np, "function", &function); 12362306a36Sopenharmony_ci if (ret < 0 && ret != -EINVAL) { 12462306a36Sopenharmony_ci dev_err(dev, "Invalid function in DT\n"); 12562306a36Sopenharmony_ci return ret; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci ret = pinconf_generic_parse_dt_config(np, NULL, &configs, &num_configs); 12962306a36Sopenharmony_ci if (ret < 0) 13062306a36Sopenharmony_ci return ret; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (!function && num_configs == 0) { 13362306a36Sopenharmony_ci dev_err(dev, 13462306a36Sopenharmony_ci "DT node must contain at least a function or config\n"); 13562306a36Sopenharmony_ci ret = -ENODEV; 13662306a36Sopenharmony_ci goto done; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Count the number of pins and groups and reallocate mappings. */ 14062306a36Sopenharmony_ci ret = of_property_count_strings(np, "pins"); 14162306a36Sopenharmony_ci if (ret == -EINVAL) { 14262306a36Sopenharmony_ci num_pins = 0; 14362306a36Sopenharmony_ci } else if (ret < 0) { 14462306a36Sopenharmony_ci dev_err(dev, "Invalid pins list in DT\n"); 14562306a36Sopenharmony_ci goto done; 14662306a36Sopenharmony_ci } else { 14762306a36Sopenharmony_ci num_pins = ret; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci ret = of_property_count_strings(np, "groups"); 15162306a36Sopenharmony_ci if (ret == -EINVAL) { 15262306a36Sopenharmony_ci num_groups = 0; 15362306a36Sopenharmony_ci } else if (ret < 0) { 15462306a36Sopenharmony_ci dev_err(dev, "Invalid pin groups list in DT\n"); 15562306a36Sopenharmony_ci goto done; 15662306a36Sopenharmony_ci } else { 15762306a36Sopenharmony_ci num_groups = ret; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (!num_pins && !num_groups) { 16162306a36Sopenharmony_ci dev_err(dev, "No pin or group provided in DT node\n"); 16262306a36Sopenharmony_ci ret = -ENODEV; 16362306a36Sopenharmony_ci goto done; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (function) 16762306a36Sopenharmony_ci nmaps += num_groups; 16862306a36Sopenharmony_ci if (configs) 16962306a36Sopenharmony_ci nmaps += num_pins + num_groups; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci maps = krealloc(maps, sizeof(*maps) * nmaps, GFP_KERNEL); 17262306a36Sopenharmony_ci if (maps == NULL) { 17362306a36Sopenharmony_ci ret = -ENOMEM; 17462306a36Sopenharmony_ci goto done; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci *map = maps; 17862306a36Sopenharmony_ci *num_maps = nmaps; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* Iterate over pins and groups and create the mappings. */ 18162306a36Sopenharmony_ci of_property_for_each_string(np, "groups", prop, group) { 18262306a36Sopenharmony_ci if (function) { 18362306a36Sopenharmony_ci maps[idx].type = PIN_MAP_TYPE_MUX_GROUP; 18462306a36Sopenharmony_ci maps[idx].data.mux.group = group; 18562306a36Sopenharmony_ci maps[idx].data.mux.function = function; 18662306a36Sopenharmony_ci idx++; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (configs) { 19062306a36Sopenharmony_ci ret = sh_pfc_map_add_config(&maps[idx], group, 19162306a36Sopenharmony_ci PIN_MAP_TYPE_CONFIGS_GROUP, 19262306a36Sopenharmony_ci configs, num_configs); 19362306a36Sopenharmony_ci if (ret < 0) 19462306a36Sopenharmony_ci goto done; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci idx++; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (!configs) { 20162306a36Sopenharmony_ci ret = 0; 20262306a36Sopenharmony_ci goto done; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci of_property_for_each_string(np, "pins", prop, pin) { 20662306a36Sopenharmony_ci ret = sh_pfc_map_add_config(&maps[idx], pin, 20762306a36Sopenharmony_ci PIN_MAP_TYPE_CONFIGS_PIN, 20862306a36Sopenharmony_ci configs, num_configs); 20962306a36Sopenharmony_ci if (ret < 0) 21062306a36Sopenharmony_ci goto done; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci idx++; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cidone: 21662306a36Sopenharmony_ci *index = idx; 21762306a36Sopenharmony_ci kfree(configs); 21862306a36Sopenharmony_ci return ret; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic void sh_pfc_dt_free_map(struct pinctrl_dev *pctldev, 22262306a36Sopenharmony_ci struct pinctrl_map *map, unsigned num_maps) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci unsigned int i; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (map == NULL) 22762306a36Sopenharmony_ci return; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci for (i = 0; i < num_maps; ++i) { 23062306a36Sopenharmony_ci if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP || 23162306a36Sopenharmony_ci map[i].type == PIN_MAP_TYPE_CONFIGS_PIN) 23262306a36Sopenharmony_ci kfree(map[i].data.configs.configs); 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci kfree(map); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic int sh_pfc_dt_node_to_map(struct pinctrl_dev *pctldev, 23962306a36Sopenharmony_ci struct device_node *np, 24062306a36Sopenharmony_ci struct pinctrl_map **map, unsigned *num_maps) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 24362306a36Sopenharmony_ci struct device *dev = pmx->pfc->dev; 24462306a36Sopenharmony_ci struct device_node *child; 24562306a36Sopenharmony_ci unsigned int index; 24662306a36Sopenharmony_ci int ret; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci *map = NULL; 24962306a36Sopenharmony_ci *num_maps = 0; 25062306a36Sopenharmony_ci index = 0; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci for_each_child_of_node(np, child) { 25362306a36Sopenharmony_ci ret = sh_pfc_dt_subnode_to_map(pctldev, child, map, num_maps, 25462306a36Sopenharmony_ci &index); 25562306a36Sopenharmony_ci if (ret < 0) { 25662306a36Sopenharmony_ci of_node_put(child); 25762306a36Sopenharmony_ci goto done; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* If no mapping has been found in child nodes try the config node. */ 26262306a36Sopenharmony_ci if (*num_maps == 0) { 26362306a36Sopenharmony_ci ret = sh_pfc_dt_subnode_to_map(pctldev, np, map, num_maps, 26462306a36Sopenharmony_ci &index); 26562306a36Sopenharmony_ci if (ret < 0) 26662306a36Sopenharmony_ci goto done; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (*num_maps) 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci dev_err(dev, "no mapping found in node %pOF\n", np); 27362306a36Sopenharmony_ci ret = -EINVAL; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cidone: 27662306a36Sopenharmony_ci if (ret < 0) 27762306a36Sopenharmony_ci sh_pfc_dt_free_map(pctldev, *map, *num_maps); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return ret; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci#endif /* CONFIG_OF */ 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic const struct pinctrl_ops sh_pfc_pinctrl_ops = { 28462306a36Sopenharmony_ci .get_groups_count = sh_pfc_get_groups_count, 28562306a36Sopenharmony_ci .get_group_name = sh_pfc_get_group_name, 28662306a36Sopenharmony_ci .get_group_pins = sh_pfc_get_group_pins, 28762306a36Sopenharmony_ci .pin_dbg_show = sh_pfc_pin_dbg_show, 28862306a36Sopenharmony_ci#ifdef CONFIG_OF 28962306a36Sopenharmony_ci .dt_node_to_map = sh_pfc_dt_node_to_map, 29062306a36Sopenharmony_ci .dt_free_map = sh_pfc_dt_free_map, 29162306a36Sopenharmony_ci#endif 29262306a36Sopenharmony_ci}; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic int sh_pfc_get_functions_count(struct pinctrl_dev *pctldev) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return pmx->pfc->info->nr_functions; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic const char *sh_pfc_get_function_name(struct pinctrl_dev *pctldev, 30262306a36Sopenharmony_ci unsigned selector) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return pmx->pfc->info->functions[selector].name; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int sh_pfc_get_function_groups(struct pinctrl_dev *pctldev, 31062306a36Sopenharmony_ci unsigned selector, 31162306a36Sopenharmony_ci const char * const **groups, 31262306a36Sopenharmony_ci unsigned * const num_groups) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci *groups = pmx->pfc->info->functions[selector].groups; 31762306a36Sopenharmony_ci *num_groups = pmx->pfc->info->functions[selector].nr_groups; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return 0; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic int sh_pfc_func_set_mux(struct pinctrl_dev *pctldev, unsigned selector, 32362306a36Sopenharmony_ci unsigned group) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 32662306a36Sopenharmony_ci struct sh_pfc *pfc = pmx->pfc; 32762306a36Sopenharmony_ci const struct sh_pfc_pin_group *grp = &pfc->info->groups[group]; 32862306a36Sopenharmony_ci unsigned long flags; 32962306a36Sopenharmony_ci unsigned int i; 33062306a36Sopenharmony_ci int ret = 0; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci dev_dbg(pctldev->dev, "Configuring pin group %s\n", grp->name); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci spin_lock_irqsave(&pfc->lock, flags); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci for (i = 0; i < grp->nr_pins; ++i) { 33762306a36Sopenharmony_ci int idx = sh_pfc_get_pin_index(pfc, grp->pins[i]); 33862306a36Sopenharmony_ci struct sh_pfc_pin_config *cfg = &pmx->configs[idx]; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* 34162306a36Sopenharmony_ci * This driver cannot manage both gpio and mux when the gpio 34262306a36Sopenharmony_ci * pin is already enabled. So, this function fails. 34362306a36Sopenharmony_ci */ 34462306a36Sopenharmony_ci if (cfg->gpio_enabled) { 34562306a36Sopenharmony_ci ret = -EBUSY; 34662306a36Sopenharmony_ci goto done; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci ret = sh_pfc_config_mux(pfc, grp->mux[i], PINMUX_TYPE_FUNCTION); 35062306a36Sopenharmony_ci if (ret < 0) 35162306a36Sopenharmony_ci goto done; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* All group pins are configured, mark the pins as muxed */ 35562306a36Sopenharmony_ci for (i = 0; i < grp->nr_pins; ++i) { 35662306a36Sopenharmony_ci int idx = sh_pfc_get_pin_index(pfc, grp->pins[i]); 35762306a36Sopenharmony_ci struct sh_pfc_pin_config *cfg = &pmx->configs[idx]; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci cfg->mux_mark = grp->mux[i]; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cidone: 36362306a36Sopenharmony_ci spin_unlock_irqrestore(&pfc->lock, flags); 36462306a36Sopenharmony_ci return ret; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic int sh_pfc_gpio_request_enable(struct pinctrl_dev *pctldev, 36862306a36Sopenharmony_ci struct pinctrl_gpio_range *range, 36962306a36Sopenharmony_ci unsigned offset) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 37262306a36Sopenharmony_ci struct sh_pfc *pfc = pmx->pfc; 37362306a36Sopenharmony_ci int idx = sh_pfc_get_pin_index(pfc, offset); 37462306a36Sopenharmony_ci struct sh_pfc_pin_config *cfg = &pmx->configs[idx]; 37562306a36Sopenharmony_ci unsigned long flags; 37662306a36Sopenharmony_ci int ret; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci spin_lock_irqsave(&pfc->lock, flags); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (!pfc->gpio && !cfg->mux_mark) { 38162306a36Sopenharmony_ci /* If GPIOs are handled externally the pin mux type needs to be 38262306a36Sopenharmony_ci * set to GPIO here. 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ci const struct sh_pfc_pin *pin = &pfc->info->pins[idx]; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci ret = sh_pfc_config_mux(pfc, pin->enum_id, PINMUX_TYPE_GPIO); 38762306a36Sopenharmony_ci if (ret < 0) 38862306a36Sopenharmony_ci goto done; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci cfg->gpio_enabled = true; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci ret = 0; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cidone: 39662306a36Sopenharmony_ci spin_unlock_irqrestore(&pfc->lock, flags); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return ret; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic void sh_pfc_gpio_disable_free(struct pinctrl_dev *pctldev, 40262306a36Sopenharmony_ci struct pinctrl_gpio_range *range, 40362306a36Sopenharmony_ci unsigned offset) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 40662306a36Sopenharmony_ci struct sh_pfc *pfc = pmx->pfc; 40762306a36Sopenharmony_ci int idx = sh_pfc_get_pin_index(pfc, offset); 40862306a36Sopenharmony_ci struct sh_pfc_pin_config *cfg = &pmx->configs[idx]; 40962306a36Sopenharmony_ci unsigned long flags; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci spin_lock_irqsave(&pfc->lock, flags); 41262306a36Sopenharmony_ci cfg->gpio_enabled = false; 41362306a36Sopenharmony_ci /* If mux is already set, this configures it here */ 41462306a36Sopenharmony_ci if (cfg->mux_mark) 41562306a36Sopenharmony_ci sh_pfc_config_mux(pfc, cfg->mux_mark, PINMUX_TYPE_FUNCTION); 41662306a36Sopenharmony_ci spin_unlock_irqrestore(&pfc->lock, flags); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_SH_PFC_GPIO 42062306a36Sopenharmony_cistatic int sh_pfc_gpio_set_direction(struct pinctrl_dev *pctldev, 42162306a36Sopenharmony_ci struct pinctrl_gpio_range *range, 42262306a36Sopenharmony_ci unsigned offset, bool input) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 42562306a36Sopenharmony_ci struct sh_pfc *pfc = pmx->pfc; 42662306a36Sopenharmony_ci int new_type = input ? PINMUX_TYPE_INPUT : PINMUX_TYPE_OUTPUT; 42762306a36Sopenharmony_ci int idx = sh_pfc_get_pin_index(pfc, offset); 42862306a36Sopenharmony_ci const struct sh_pfc_pin *pin = &pfc->info->pins[idx]; 42962306a36Sopenharmony_ci unsigned long flags; 43062306a36Sopenharmony_ci unsigned int dir; 43162306a36Sopenharmony_ci int ret; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* Check if the requested direction is supported by the pin. Not all 43462306a36Sopenharmony_ci * SoCs provide pin config data, so perform the check conditionally. 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_ci if (pin->configs) { 43762306a36Sopenharmony_ci dir = input ? SH_PFC_PIN_CFG_INPUT : SH_PFC_PIN_CFG_OUTPUT; 43862306a36Sopenharmony_ci if (!(pin->configs & dir)) 43962306a36Sopenharmony_ci return -EINVAL; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci spin_lock_irqsave(&pfc->lock, flags); 44362306a36Sopenharmony_ci ret = sh_pfc_config_mux(pfc, pin->enum_id, new_type); 44462306a36Sopenharmony_ci spin_unlock_irqrestore(&pfc->lock, flags); 44562306a36Sopenharmony_ci return ret; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci#else 44862306a36Sopenharmony_ci#define sh_pfc_gpio_set_direction NULL 44962306a36Sopenharmony_ci#endif 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic const struct pinmux_ops sh_pfc_pinmux_ops = { 45262306a36Sopenharmony_ci .get_functions_count = sh_pfc_get_functions_count, 45362306a36Sopenharmony_ci .get_function_name = sh_pfc_get_function_name, 45462306a36Sopenharmony_ci .get_function_groups = sh_pfc_get_function_groups, 45562306a36Sopenharmony_ci .set_mux = sh_pfc_func_set_mux, 45662306a36Sopenharmony_ci .gpio_request_enable = sh_pfc_gpio_request_enable, 45762306a36Sopenharmony_ci .gpio_disable_free = sh_pfc_gpio_disable_free, 45862306a36Sopenharmony_ci .gpio_set_direction = sh_pfc_gpio_set_direction, 45962306a36Sopenharmony_ci}; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic u32 sh_pfc_pinconf_find_drive_strength_reg(struct sh_pfc *pfc, 46262306a36Sopenharmony_ci unsigned int pin, unsigned int *offset, unsigned int *size) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci const struct pinmux_drive_reg_field *field; 46562306a36Sopenharmony_ci const struct pinmux_drive_reg *reg; 46662306a36Sopenharmony_ci unsigned int i; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci for (reg = pfc->info->drive_regs; reg->reg; ++reg) { 46962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(reg->fields); ++i) { 47062306a36Sopenharmony_ci field = ®->fields[i]; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (field->size && field->pin == pin) { 47362306a36Sopenharmony_ci *offset = field->offset; 47462306a36Sopenharmony_ci *size = field->size; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return reg->reg; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci return 0; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic int sh_pfc_pinconf_get_drive_strength(struct sh_pfc *pfc, 48562306a36Sopenharmony_ci unsigned int pin) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci unsigned int offset; 48862306a36Sopenharmony_ci unsigned int size; 48962306a36Sopenharmony_ci u32 reg; 49062306a36Sopenharmony_ci u32 val; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci reg = sh_pfc_pinconf_find_drive_strength_reg(pfc, pin, &offset, &size); 49362306a36Sopenharmony_ci if (!reg) 49462306a36Sopenharmony_ci return -EINVAL; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci val = (sh_pfc_read(pfc, reg) >> offset) & GENMASK(size - 1, 0); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* Convert the value to mA based on a full drive strength value of 24mA. 49962306a36Sopenharmony_ci * We can make the full value configurable later if needed. 50062306a36Sopenharmony_ci */ 50162306a36Sopenharmony_ci return (val + 1) * (size == 2 ? 6 : 3); 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic int sh_pfc_pinconf_set_drive_strength(struct sh_pfc *pfc, 50562306a36Sopenharmony_ci unsigned int pin, u16 strength) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci unsigned long flags; 50862306a36Sopenharmony_ci unsigned int offset; 50962306a36Sopenharmony_ci unsigned int size; 51062306a36Sopenharmony_ci unsigned int step; 51162306a36Sopenharmony_ci u32 reg; 51262306a36Sopenharmony_ci u32 val; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci reg = sh_pfc_pinconf_find_drive_strength_reg(pfc, pin, &offset, &size); 51562306a36Sopenharmony_ci if (!reg) 51662306a36Sopenharmony_ci return -EINVAL; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci step = size == 2 ? 6 : 3; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (strength < step || strength > 24) 52162306a36Sopenharmony_ci return -EINVAL; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Convert the value from mA based on a full drive strength value of 52462306a36Sopenharmony_ci * 24mA. We can make the full value configurable later if needed. 52562306a36Sopenharmony_ci */ 52662306a36Sopenharmony_ci strength = strength / step - 1; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci spin_lock_irqsave(&pfc->lock, flags); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci val = sh_pfc_read(pfc, reg); 53162306a36Sopenharmony_ci val &= ~GENMASK(offset + size - 1, offset); 53262306a36Sopenharmony_ci val |= strength << offset; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci sh_pfc_write(pfc, reg, val); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci spin_unlock_irqrestore(&pfc->lock, flags); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci return 0; 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci/* Check whether the requested parameter is supported for a pin. */ 54262306a36Sopenharmony_cistatic bool sh_pfc_pinconf_validate(struct sh_pfc *pfc, unsigned int _pin, 54362306a36Sopenharmony_ci enum pin_config_param param) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci int idx = sh_pfc_get_pin_index(pfc, _pin); 54662306a36Sopenharmony_ci const struct sh_pfc_pin *pin = &pfc->info->pins[idx]; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci switch (param) { 54962306a36Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 55062306a36Sopenharmony_ci return pin->configs & SH_PFC_PIN_CFG_PULL_UP_DOWN; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 55362306a36Sopenharmony_ci return pin->configs & SH_PFC_PIN_CFG_PULL_UP; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 55662306a36Sopenharmony_ci return pin->configs & SH_PFC_PIN_CFG_PULL_DOWN; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: 55962306a36Sopenharmony_ci return pin->configs & SH_PFC_PIN_CFG_DRIVE_STRENGTH; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci case PIN_CONFIG_POWER_SOURCE: 56262306a36Sopenharmony_ci return pin->configs & SH_PFC_PIN_CFG_IO_VOLTAGE_MASK; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci default: 56562306a36Sopenharmony_ci return false; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic int sh_pfc_pinconf_get(struct pinctrl_dev *pctldev, unsigned _pin, 57062306a36Sopenharmony_ci unsigned long *config) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 57362306a36Sopenharmony_ci struct sh_pfc *pfc = pmx->pfc; 57462306a36Sopenharmony_ci enum pin_config_param param = pinconf_to_config_param(*config); 57562306a36Sopenharmony_ci unsigned long flags; 57662306a36Sopenharmony_ci unsigned int arg; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (!sh_pfc_pinconf_validate(pfc, _pin, param)) 57962306a36Sopenharmony_ci return -ENOTSUPP; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci switch (param) { 58262306a36Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 58362306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 58462306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: { 58562306a36Sopenharmony_ci unsigned int bias; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (!pfc->info->ops || !pfc->info->ops->get_bias) 58862306a36Sopenharmony_ci return -ENOTSUPP; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci spin_lock_irqsave(&pfc->lock, flags); 59162306a36Sopenharmony_ci bias = pfc->info->ops->get_bias(pfc, _pin); 59262306a36Sopenharmony_ci spin_unlock_irqrestore(&pfc->lock, flags); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (bias != param) 59562306a36Sopenharmony_ci return -EINVAL; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci arg = 0; 59862306a36Sopenharmony_ci break; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: { 60262306a36Sopenharmony_ci int ret; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci ret = sh_pfc_pinconf_get_drive_strength(pfc, _pin); 60562306a36Sopenharmony_ci if (ret < 0) 60662306a36Sopenharmony_ci return ret; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci arg = ret; 60962306a36Sopenharmony_ci break; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci case PIN_CONFIG_POWER_SOURCE: { 61362306a36Sopenharmony_ci int idx = sh_pfc_get_pin_index(pfc, _pin); 61462306a36Sopenharmony_ci const struct sh_pfc_pin *pin = &pfc->info->pins[idx]; 61562306a36Sopenharmony_ci unsigned int mode, lo, hi; 61662306a36Sopenharmony_ci u32 pocctrl, val; 61762306a36Sopenharmony_ci int bit; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (!pfc->info->ops || !pfc->info->ops->pin_to_pocctrl) 62062306a36Sopenharmony_ci return -ENOTSUPP; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci bit = pfc->info->ops->pin_to_pocctrl(_pin, &pocctrl); 62362306a36Sopenharmony_ci if (WARN(bit < 0, "invalid pin %#x", _pin)) 62462306a36Sopenharmony_ci return bit; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci val = sh_pfc_read(pfc, pocctrl); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci mode = pin->configs & SH_PFC_PIN_CFG_IO_VOLTAGE_MASK; 62962306a36Sopenharmony_ci lo = mode <= SH_PFC_PIN_CFG_IO_VOLTAGE_18_33 ? 1800 : 2500; 63062306a36Sopenharmony_ci hi = mode >= SH_PFC_PIN_CFG_IO_VOLTAGE_18_33 ? 3300 : 2500; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci arg = (val & BIT(bit)) ? hi : lo; 63362306a36Sopenharmony_ci break; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci default: 63762306a36Sopenharmony_ci return -ENOTSUPP; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci *config = pinconf_to_config_packed(param, arg); 64162306a36Sopenharmony_ci return 0; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistatic int sh_pfc_pinconf_set(struct pinctrl_dev *pctldev, unsigned _pin, 64562306a36Sopenharmony_ci unsigned long *configs, unsigned num_configs) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 64862306a36Sopenharmony_ci struct sh_pfc *pfc = pmx->pfc; 64962306a36Sopenharmony_ci enum pin_config_param param; 65062306a36Sopenharmony_ci unsigned long flags; 65162306a36Sopenharmony_ci unsigned int i; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci for (i = 0; i < num_configs; i++) { 65462306a36Sopenharmony_ci param = pinconf_to_config_param(configs[i]); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (!sh_pfc_pinconf_validate(pfc, _pin, param)) 65762306a36Sopenharmony_ci return -ENOTSUPP; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci switch (param) { 66062306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 66162306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 66262306a36Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 66362306a36Sopenharmony_ci if (!pfc->info->ops || !pfc->info->ops->set_bias) 66462306a36Sopenharmony_ci return -ENOTSUPP; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci spin_lock_irqsave(&pfc->lock, flags); 66762306a36Sopenharmony_ci pfc->info->ops->set_bias(pfc, _pin, param); 66862306a36Sopenharmony_ci spin_unlock_irqrestore(&pfc->lock, flags); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci break; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: { 67362306a36Sopenharmony_ci unsigned int arg = 67462306a36Sopenharmony_ci pinconf_to_config_argument(configs[i]); 67562306a36Sopenharmony_ci int ret; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci ret = sh_pfc_pinconf_set_drive_strength(pfc, _pin, arg); 67862306a36Sopenharmony_ci if (ret < 0) 67962306a36Sopenharmony_ci return ret; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci break; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci case PIN_CONFIG_POWER_SOURCE: { 68562306a36Sopenharmony_ci unsigned int mV = pinconf_to_config_argument(configs[i]); 68662306a36Sopenharmony_ci int idx = sh_pfc_get_pin_index(pfc, _pin); 68762306a36Sopenharmony_ci const struct sh_pfc_pin *pin = &pfc->info->pins[idx]; 68862306a36Sopenharmony_ci unsigned int mode, lo, hi; 68962306a36Sopenharmony_ci u32 pocctrl, val; 69062306a36Sopenharmony_ci int bit; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (!pfc->info->ops || !pfc->info->ops->pin_to_pocctrl) 69362306a36Sopenharmony_ci return -ENOTSUPP; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci bit = pfc->info->ops->pin_to_pocctrl(_pin, &pocctrl); 69662306a36Sopenharmony_ci if (WARN(bit < 0, "invalid pin %#x", _pin)) 69762306a36Sopenharmony_ci return bit; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci mode = pin->configs & SH_PFC_PIN_CFG_IO_VOLTAGE_MASK; 70062306a36Sopenharmony_ci lo = mode <= SH_PFC_PIN_CFG_IO_VOLTAGE_18_33 ? 1800 : 2500; 70162306a36Sopenharmony_ci hi = mode >= SH_PFC_PIN_CFG_IO_VOLTAGE_18_33 ? 3300 : 2500; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (mV != lo && mV != hi) 70462306a36Sopenharmony_ci return -EINVAL; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci spin_lock_irqsave(&pfc->lock, flags); 70762306a36Sopenharmony_ci val = sh_pfc_read(pfc, pocctrl); 70862306a36Sopenharmony_ci if (mV == hi) 70962306a36Sopenharmony_ci val |= BIT(bit); 71062306a36Sopenharmony_ci else 71162306a36Sopenharmony_ci val &= ~BIT(bit); 71262306a36Sopenharmony_ci sh_pfc_write(pfc, pocctrl, val); 71362306a36Sopenharmony_ci spin_unlock_irqrestore(&pfc->lock, flags); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci break; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci default: 71962306a36Sopenharmony_ci return -ENOTSUPP; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci } /* for each config */ 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci return 0; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic int sh_pfc_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned group, 72762306a36Sopenharmony_ci unsigned long *configs, 72862306a36Sopenharmony_ci unsigned num_configs) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); 73162306a36Sopenharmony_ci const unsigned int *pins; 73262306a36Sopenharmony_ci unsigned int num_pins; 73362306a36Sopenharmony_ci unsigned int i, ret; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci pins = pmx->pfc->info->groups[group].pins; 73662306a36Sopenharmony_ci num_pins = pmx->pfc->info->groups[group].nr_pins; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci for (i = 0; i < num_pins; ++i) { 73962306a36Sopenharmony_ci ret = sh_pfc_pinconf_set(pctldev, pins[i], configs, num_configs); 74062306a36Sopenharmony_ci if (ret) 74162306a36Sopenharmony_ci return ret; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci return 0; 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_cistatic const struct pinconf_ops sh_pfc_pinconf_ops = { 74862306a36Sopenharmony_ci .is_generic = true, 74962306a36Sopenharmony_ci .pin_config_get = sh_pfc_pinconf_get, 75062306a36Sopenharmony_ci .pin_config_set = sh_pfc_pinconf_set, 75162306a36Sopenharmony_ci .pin_config_group_set = sh_pfc_pinconf_group_set, 75262306a36Sopenharmony_ci .pin_config_config_dbg_show = pinconf_generic_dump_config, 75362306a36Sopenharmony_ci}; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci/* PFC ranges -> pinctrl pin descs */ 75662306a36Sopenharmony_cistatic int sh_pfc_map_pins(struct sh_pfc *pfc, struct sh_pfc_pinctrl *pmx) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci unsigned int i; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci /* Allocate and initialize the pins and configs arrays. */ 76162306a36Sopenharmony_ci pmx->pins = devm_kcalloc(pfc->dev, 76262306a36Sopenharmony_ci pfc->info->nr_pins, sizeof(*pmx->pins), 76362306a36Sopenharmony_ci GFP_KERNEL); 76462306a36Sopenharmony_ci if (unlikely(!pmx->pins)) 76562306a36Sopenharmony_ci return -ENOMEM; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci pmx->configs = devm_kcalloc(pfc->dev, 76862306a36Sopenharmony_ci pfc->info->nr_pins, sizeof(*pmx->configs), 76962306a36Sopenharmony_ci GFP_KERNEL); 77062306a36Sopenharmony_ci if (unlikely(!pmx->configs)) 77162306a36Sopenharmony_ci return -ENOMEM; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci for (i = 0; i < pfc->info->nr_pins; ++i) { 77462306a36Sopenharmony_ci const struct sh_pfc_pin *info = &pfc->info->pins[i]; 77562306a36Sopenharmony_ci struct pinctrl_pin_desc *pin = &pmx->pins[i]; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* If the pin number is equal to -1 all pins are considered */ 77862306a36Sopenharmony_ci pin->number = info->pin != (u16)-1 ? info->pin : i; 77962306a36Sopenharmony_ci pin->name = info->name; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci return 0; 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ciint sh_pfc_register_pinctrl(struct sh_pfc *pfc) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci struct sh_pfc_pinctrl *pmx; 78862306a36Sopenharmony_ci int ret; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci pmx = devm_kzalloc(pfc->dev, sizeof(*pmx), GFP_KERNEL); 79162306a36Sopenharmony_ci if (unlikely(!pmx)) 79262306a36Sopenharmony_ci return -ENOMEM; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci pmx->pfc = pfc; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci ret = sh_pfc_map_pins(pfc, pmx); 79762306a36Sopenharmony_ci if (ret < 0) 79862306a36Sopenharmony_ci return ret; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci pmx->pctl_desc.name = DRV_NAME; 80162306a36Sopenharmony_ci pmx->pctl_desc.owner = THIS_MODULE; 80262306a36Sopenharmony_ci pmx->pctl_desc.pctlops = &sh_pfc_pinctrl_ops; 80362306a36Sopenharmony_ci pmx->pctl_desc.pmxops = &sh_pfc_pinmux_ops; 80462306a36Sopenharmony_ci pmx->pctl_desc.confops = &sh_pfc_pinconf_ops; 80562306a36Sopenharmony_ci pmx->pctl_desc.pins = pmx->pins; 80662306a36Sopenharmony_ci pmx->pctl_desc.npins = pfc->info->nr_pins; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci ret = devm_pinctrl_register_and_init(pfc->dev, &pmx->pctl_desc, pmx, 80962306a36Sopenharmony_ci &pmx->pctl); 81062306a36Sopenharmony_ci if (ret) { 81162306a36Sopenharmony_ci dev_err(pfc->dev, "could not register: %i\n", ret); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci return ret; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci return pinctrl_enable(pmx->pctl); 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ciconst struct pinmux_bias_reg * 82062306a36Sopenharmony_circar_pin_to_bias_reg(const struct sh_pfc_soc_info *info, unsigned int pin, 82162306a36Sopenharmony_ci unsigned int *bit) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci unsigned int i, j; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci for (i = 0; info->bias_regs[i].puen || info->bias_regs[i].pud; i++) { 82662306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(info->bias_regs[i].pins); j++) { 82762306a36Sopenharmony_ci if (info->bias_regs[i].pins[j] == pin) { 82862306a36Sopenharmony_ci *bit = j; 82962306a36Sopenharmony_ci return &info->bias_regs[i]; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci WARN_ONCE(1, "Pin %u is not in bias info list\n", pin); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci return NULL; 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ciunsigned int rcar_pinmux_get_bias(struct sh_pfc *pfc, unsigned int pin) 84062306a36Sopenharmony_ci{ 84162306a36Sopenharmony_ci const struct pinmux_bias_reg *reg; 84262306a36Sopenharmony_ci unsigned int bit; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci reg = rcar_pin_to_bias_reg(pfc->info, pin, &bit); 84562306a36Sopenharmony_ci if (!reg) 84662306a36Sopenharmony_ci return PIN_CONFIG_BIAS_DISABLE; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (reg->puen) { 84962306a36Sopenharmony_ci if (!(sh_pfc_read(pfc, reg->puen) & BIT(bit))) 85062306a36Sopenharmony_ci return PIN_CONFIG_BIAS_DISABLE; 85162306a36Sopenharmony_ci else if (!reg->pud || (sh_pfc_read(pfc, reg->pud) & BIT(bit))) 85262306a36Sopenharmony_ci return PIN_CONFIG_BIAS_PULL_UP; 85362306a36Sopenharmony_ci else 85462306a36Sopenharmony_ci return PIN_CONFIG_BIAS_PULL_DOWN; 85562306a36Sopenharmony_ci } else { 85662306a36Sopenharmony_ci if (sh_pfc_read(pfc, reg->pud) & BIT(bit)) 85762306a36Sopenharmony_ci return PIN_CONFIG_BIAS_PULL_DOWN; 85862306a36Sopenharmony_ci else 85962306a36Sopenharmony_ci return PIN_CONFIG_BIAS_DISABLE; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_civoid rcar_pinmux_set_bias(struct sh_pfc *pfc, unsigned int pin, 86462306a36Sopenharmony_ci unsigned int bias) 86562306a36Sopenharmony_ci{ 86662306a36Sopenharmony_ci const struct pinmux_bias_reg *reg; 86762306a36Sopenharmony_ci u32 enable, updown; 86862306a36Sopenharmony_ci unsigned int bit; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci reg = rcar_pin_to_bias_reg(pfc->info, pin, &bit); 87162306a36Sopenharmony_ci if (!reg) 87262306a36Sopenharmony_ci return; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (reg->puen) { 87562306a36Sopenharmony_ci enable = sh_pfc_read(pfc, reg->puen) & ~BIT(bit); 87662306a36Sopenharmony_ci if (bias != PIN_CONFIG_BIAS_DISABLE) { 87762306a36Sopenharmony_ci enable |= BIT(bit); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (reg->pud) { 88062306a36Sopenharmony_ci updown = sh_pfc_read(pfc, reg->pud) & ~BIT(bit); 88162306a36Sopenharmony_ci if (bias == PIN_CONFIG_BIAS_PULL_UP) 88262306a36Sopenharmony_ci updown |= BIT(bit); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci sh_pfc_write(pfc, reg->pud, updown); 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci sh_pfc_write(pfc, reg->puen, enable); 88862306a36Sopenharmony_ci } else { 88962306a36Sopenharmony_ci enable = sh_pfc_read(pfc, reg->pud) & ~BIT(bit); 89062306a36Sopenharmony_ci if (bias == PIN_CONFIG_BIAS_PULL_DOWN) 89162306a36Sopenharmony_ci enable |= BIT(bit); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci sh_pfc_write(pfc, reg->pud, enable); 89462306a36Sopenharmony_ci } 89562306a36Sopenharmony_ci} 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci#define PORTnCR_PULMD_OFF (0 << 6) 89862306a36Sopenharmony_ci#define PORTnCR_PULMD_DOWN (2 << 6) 89962306a36Sopenharmony_ci#define PORTnCR_PULMD_UP (3 << 6) 90062306a36Sopenharmony_ci#define PORTnCR_PULMD_MASK (3 << 6) 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ciunsigned int rmobile_pinmux_get_bias(struct sh_pfc *pfc, unsigned int pin) 90362306a36Sopenharmony_ci{ 90462306a36Sopenharmony_ci void __iomem *reg = pfc->windows->virt + 90562306a36Sopenharmony_ci pfc->info->ops->pin_to_portcr(pin); 90662306a36Sopenharmony_ci u32 value = ioread8(reg) & PORTnCR_PULMD_MASK; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci switch (value) { 90962306a36Sopenharmony_ci case PORTnCR_PULMD_UP: 91062306a36Sopenharmony_ci return PIN_CONFIG_BIAS_PULL_UP; 91162306a36Sopenharmony_ci case PORTnCR_PULMD_DOWN: 91262306a36Sopenharmony_ci return PIN_CONFIG_BIAS_PULL_DOWN; 91362306a36Sopenharmony_ci case PORTnCR_PULMD_OFF: 91462306a36Sopenharmony_ci default: 91562306a36Sopenharmony_ci return PIN_CONFIG_BIAS_DISABLE; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci} 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_civoid rmobile_pinmux_set_bias(struct sh_pfc *pfc, unsigned int pin, 92062306a36Sopenharmony_ci unsigned int bias) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci void __iomem *reg = pfc->windows->virt + 92362306a36Sopenharmony_ci pfc->info->ops->pin_to_portcr(pin); 92462306a36Sopenharmony_ci u32 value = ioread8(reg) & ~PORTnCR_PULMD_MASK; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci switch (bias) { 92762306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 92862306a36Sopenharmony_ci value |= PORTnCR_PULMD_UP; 92962306a36Sopenharmony_ci break; 93062306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 93162306a36Sopenharmony_ci value |= PORTnCR_PULMD_DOWN; 93262306a36Sopenharmony_ci break; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci iowrite8(value, reg); 93662306a36Sopenharmony_ci} 937