162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// pin-controller/pin-mux/pin-config/gpio-driver for Samsung's SoC's. 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (c) 2012 Samsung Electronics Co., Ltd. 662306a36Sopenharmony_ci// http://www.samsung.com 762306a36Sopenharmony_ci// Copyright (c) 2012 Linaro Ltd 862306a36Sopenharmony_ci// http://www.linaro.org 962306a36Sopenharmony_ci// 1062306a36Sopenharmony_ci// Author: Thomas Abraham <thomas.ab@samsung.com> 1162306a36Sopenharmony_ci// 1262306a36Sopenharmony_ci// This driver implements the Samsung pinctrl driver. It supports setting up of 1362306a36Sopenharmony_ci// pinmux and pinconf configurations. The gpiolib interface is also included. 1462306a36Sopenharmony_ci// External interrupt (gpio and wakeup) support are not included in this driver 1562306a36Sopenharmony_ci// but provides extensions to which platform specific implementation of the gpio 1662306a36Sopenharmony_ci// and wakeup interrupts can be hooked to. 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/err.h> 1962306a36Sopenharmony_ci#include <linux/gpio/driver.h> 2062306a36Sopenharmony_ci#include <linux/init.h> 2162306a36Sopenharmony_ci#include <linux/io.h> 2262306a36Sopenharmony_ci#include <linux/irqdomain.h> 2362306a36Sopenharmony_ci#include <linux/of.h> 2462306a36Sopenharmony_ci#include <linux/platform_device.h> 2562306a36Sopenharmony_ci#include <linux/property.h> 2662306a36Sopenharmony_ci#include <linux/seq_file.h> 2762306a36Sopenharmony_ci#include <linux/slab.h> 2862306a36Sopenharmony_ci#include <linux/spinlock.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "../core.h" 3162306a36Sopenharmony_ci#include "pinctrl-samsung.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* maximum number of the memory resources */ 3462306a36Sopenharmony_ci#define SAMSUNG_PINCTRL_NUM_RESOURCES 2 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* list of all possible config options supported */ 3762306a36Sopenharmony_cistatic struct pin_config { 3862306a36Sopenharmony_ci const char *property; 3962306a36Sopenharmony_ci enum pincfg_type param; 4062306a36Sopenharmony_ci} cfg_params[] = { 4162306a36Sopenharmony_ci { "samsung,pin-pud", PINCFG_TYPE_PUD }, 4262306a36Sopenharmony_ci { "samsung,pin-drv", PINCFG_TYPE_DRV }, 4362306a36Sopenharmony_ci { "samsung,pin-con-pdn", PINCFG_TYPE_CON_PDN }, 4462306a36Sopenharmony_ci { "samsung,pin-pud-pdn", PINCFG_TYPE_PUD_PDN }, 4562306a36Sopenharmony_ci { "samsung,pin-val", PINCFG_TYPE_DAT }, 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic unsigned int pin_base; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int samsung_get_group_count(struct pinctrl_dev *pctldev) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *pmx = pinctrl_dev_get_drvdata(pctldev); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return pmx->nr_groups; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic const char *samsung_get_group_name(struct pinctrl_dev *pctldev, 5862306a36Sopenharmony_ci unsigned group) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *pmx = pinctrl_dev_get_drvdata(pctldev); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return pmx->pin_groups[group].name; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic int samsung_get_group_pins(struct pinctrl_dev *pctldev, 6662306a36Sopenharmony_ci unsigned group, 6762306a36Sopenharmony_ci const unsigned **pins, 6862306a36Sopenharmony_ci unsigned *num_pins) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *pmx = pinctrl_dev_get_drvdata(pctldev); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci *pins = pmx->pin_groups[group].pins; 7362306a36Sopenharmony_ci *num_pins = pmx->pin_groups[group].num_pins; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return 0; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int reserve_map(struct device *dev, struct pinctrl_map **map, 7962306a36Sopenharmony_ci unsigned *reserved_maps, unsigned *num_maps, 8062306a36Sopenharmony_ci unsigned reserve) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci unsigned old_num = *reserved_maps; 8362306a36Sopenharmony_ci unsigned new_num = *num_maps + reserve; 8462306a36Sopenharmony_ci struct pinctrl_map *new_map; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (old_num >= new_num) 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL); 9062306a36Sopenharmony_ci if (!new_map) 9162306a36Sopenharmony_ci return -ENOMEM; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map)); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci *map = new_map; 9662306a36Sopenharmony_ci *reserved_maps = new_num; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int add_map_mux(struct pinctrl_map **map, unsigned *reserved_maps, 10262306a36Sopenharmony_ci unsigned *num_maps, const char *group, 10362306a36Sopenharmony_ci const char *function) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci if (WARN_ON(*num_maps == *reserved_maps)) 10662306a36Sopenharmony_ci return -ENOSPC; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP; 10962306a36Sopenharmony_ci (*map)[*num_maps].data.mux.group = group; 11062306a36Sopenharmony_ci (*map)[*num_maps].data.mux.function = function; 11162306a36Sopenharmony_ci (*num_maps)++; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic int add_map_configs(struct device *dev, struct pinctrl_map **map, 11762306a36Sopenharmony_ci unsigned *reserved_maps, unsigned *num_maps, 11862306a36Sopenharmony_ci const char *group, unsigned long *configs, 11962306a36Sopenharmony_ci unsigned num_configs) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci unsigned long *dup_configs; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (WARN_ON(*num_maps == *reserved_maps)) 12462306a36Sopenharmony_ci return -ENOSPC; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs), 12762306a36Sopenharmony_ci GFP_KERNEL); 12862306a36Sopenharmony_ci if (!dup_configs) 12962306a36Sopenharmony_ci return -ENOMEM; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci (*map)[*num_maps].type = PIN_MAP_TYPE_CONFIGS_GROUP; 13262306a36Sopenharmony_ci (*map)[*num_maps].data.configs.group_or_pin = group; 13362306a36Sopenharmony_ci (*map)[*num_maps].data.configs.configs = dup_configs; 13462306a36Sopenharmony_ci (*map)[*num_maps].data.configs.num_configs = num_configs; 13562306a36Sopenharmony_ci (*num_maps)++; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int add_config(struct device *dev, unsigned long **configs, 14162306a36Sopenharmony_ci unsigned *num_configs, unsigned long config) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci unsigned old_num = *num_configs; 14462306a36Sopenharmony_ci unsigned new_num = old_num + 1; 14562306a36Sopenharmony_ci unsigned long *new_configs; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci new_configs = krealloc(*configs, sizeof(*new_configs) * new_num, 14862306a36Sopenharmony_ci GFP_KERNEL); 14962306a36Sopenharmony_ci if (!new_configs) 15062306a36Sopenharmony_ci return -ENOMEM; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci new_configs[old_num] = config; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci *configs = new_configs; 15562306a36Sopenharmony_ci *num_configs = new_num; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic void samsung_dt_free_map(struct pinctrl_dev *pctldev, 16162306a36Sopenharmony_ci struct pinctrl_map *map, 16262306a36Sopenharmony_ci unsigned num_maps) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci int i; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci for (i = 0; i < num_maps; i++) 16762306a36Sopenharmony_ci if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP) 16862306a36Sopenharmony_ci kfree(map[i].data.configs.configs); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci kfree(map); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int samsung_dt_subnode_to_map(struct samsung_pinctrl_drv_data *drvdata, 17462306a36Sopenharmony_ci struct device *dev, 17562306a36Sopenharmony_ci struct device_node *np, 17662306a36Sopenharmony_ci struct pinctrl_map **map, 17762306a36Sopenharmony_ci unsigned *reserved_maps, 17862306a36Sopenharmony_ci unsigned *num_maps) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci int ret, i; 18162306a36Sopenharmony_ci u32 val; 18262306a36Sopenharmony_ci unsigned long config; 18362306a36Sopenharmony_ci unsigned long *configs = NULL; 18462306a36Sopenharmony_ci unsigned num_configs = 0; 18562306a36Sopenharmony_ci unsigned reserve; 18662306a36Sopenharmony_ci struct property *prop; 18762306a36Sopenharmony_ci const char *group; 18862306a36Sopenharmony_ci bool has_func = false; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci ret = of_property_read_u32(np, "samsung,pin-function", &val); 19162306a36Sopenharmony_ci if (!ret) 19262306a36Sopenharmony_ci has_func = true; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cfg_params); i++) { 19562306a36Sopenharmony_ci ret = of_property_read_u32(np, cfg_params[i].property, &val); 19662306a36Sopenharmony_ci if (!ret) { 19762306a36Sopenharmony_ci config = PINCFG_PACK(cfg_params[i].param, val); 19862306a36Sopenharmony_ci ret = add_config(dev, &configs, &num_configs, config); 19962306a36Sopenharmony_ci if (ret < 0) 20062306a36Sopenharmony_ci goto exit; 20162306a36Sopenharmony_ci /* EINVAL=missing, which is fine since it's optional */ 20262306a36Sopenharmony_ci } else if (ret != -EINVAL) { 20362306a36Sopenharmony_ci dev_err(dev, "could not parse property %s\n", 20462306a36Sopenharmony_ci cfg_params[i].property); 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci reserve = 0; 20962306a36Sopenharmony_ci if (has_func) 21062306a36Sopenharmony_ci reserve++; 21162306a36Sopenharmony_ci if (num_configs) 21262306a36Sopenharmony_ci reserve++; 21362306a36Sopenharmony_ci ret = of_property_count_strings(np, "samsung,pins"); 21462306a36Sopenharmony_ci if (ret < 0) { 21562306a36Sopenharmony_ci dev_err(dev, "could not parse property samsung,pins\n"); 21662306a36Sopenharmony_ci goto exit; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci reserve *= ret; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci ret = reserve_map(dev, map, reserved_maps, num_maps, reserve); 22162306a36Sopenharmony_ci if (ret < 0) 22262306a36Sopenharmony_ci goto exit; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci of_property_for_each_string(np, "samsung,pins", prop, group) { 22562306a36Sopenharmony_ci if (has_func) { 22662306a36Sopenharmony_ci ret = add_map_mux(map, reserved_maps, 22762306a36Sopenharmony_ci num_maps, group, np->full_name); 22862306a36Sopenharmony_ci if (ret < 0) 22962306a36Sopenharmony_ci goto exit; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (num_configs) { 23362306a36Sopenharmony_ci ret = add_map_configs(dev, map, reserved_maps, 23462306a36Sopenharmony_ci num_maps, group, configs, 23562306a36Sopenharmony_ci num_configs); 23662306a36Sopenharmony_ci if (ret < 0) 23762306a36Sopenharmony_ci goto exit; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci ret = 0; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ciexit: 24462306a36Sopenharmony_ci kfree(configs); 24562306a36Sopenharmony_ci return ret; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int samsung_dt_node_to_map(struct pinctrl_dev *pctldev, 24962306a36Sopenharmony_ci struct device_node *np_config, 25062306a36Sopenharmony_ci struct pinctrl_map **map, 25162306a36Sopenharmony_ci unsigned *num_maps) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *drvdata; 25462306a36Sopenharmony_ci unsigned reserved_maps; 25562306a36Sopenharmony_ci struct device_node *np; 25662306a36Sopenharmony_ci int ret; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci drvdata = pinctrl_dev_get_drvdata(pctldev); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci reserved_maps = 0; 26162306a36Sopenharmony_ci *map = NULL; 26262306a36Sopenharmony_ci *num_maps = 0; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (!of_get_child_count(np_config)) 26562306a36Sopenharmony_ci return samsung_dt_subnode_to_map(drvdata, pctldev->dev, 26662306a36Sopenharmony_ci np_config, map, 26762306a36Sopenharmony_ci &reserved_maps, 26862306a36Sopenharmony_ci num_maps); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci for_each_child_of_node(np_config, np) { 27162306a36Sopenharmony_ci ret = samsung_dt_subnode_to_map(drvdata, pctldev->dev, np, map, 27262306a36Sopenharmony_ci &reserved_maps, num_maps); 27362306a36Sopenharmony_ci if (ret < 0) { 27462306a36Sopenharmony_ci samsung_dt_free_map(pctldev, *map, *num_maps); 27562306a36Sopenharmony_ci of_node_put(np); 27662306a36Sopenharmony_ci return ret; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci return 0; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 28462306a36Sopenharmony_ci/* Forward declaration which can be used by samsung_pin_dbg_show */ 28562306a36Sopenharmony_cistatic int samsung_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, 28662306a36Sopenharmony_ci unsigned long *config); 28762306a36Sopenharmony_cistatic const char * const reg_names[] = {"CON", "DAT", "PUD", "DRV", "CON_PDN", 28862306a36Sopenharmony_ci "PUD_PDN"}; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic void samsung_pin_dbg_show(struct pinctrl_dev *pctldev, 29162306a36Sopenharmony_ci struct seq_file *s, unsigned int pin) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci enum pincfg_type cfg_type; 29462306a36Sopenharmony_ci unsigned long config; 29562306a36Sopenharmony_ci int ret; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci for (cfg_type = 0; cfg_type < PINCFG_TYPE_NUM; cfg_type++) { 29862306a36Sopenharmony_ci config = PINCFG_PACK(cfg_type, 0); 29962306a36Sopenharmony_ci ret = samsung_pinconf_get(pctldev, pin, &config); 30062306a36Sopenharmony_ci if (ret < 0) 30162306a36Sopenharmony_ci continue; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci seq_printf(s, " %s(0x%lx)", reg_names[cfg_type], 30462306a36Sopenharmony_ci PINCFG_UNPACK_VALUE(config)); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci#endif 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci/* list of pinctrl callbacks for the pinctrl core */ 31062306a36Sopenharmony_cistatic const struct pinctrl_ops samsung_pctrl_ops = { 31162306a36Sopenharmony_ci .get_groups_count = samsung_get_group_count, 31262306a36Sopenharmony_ci .get_group_name = samsung_get_group_name, 31362306a36Sopenharmony_ci .get_group_pins = samsung_get_group_pins, 31462306a36Sopenharmony_ci .dt_node_to_map = samsung_dt_node_to_map, 31562306a36Sopenharmony_ci .dt_free_map = samsung_dt_free_map, 31662306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 31762306a36Sopenharmony_ci .pin_dbg_show = samsung_pin_dbg_show, 31862306a36Sopenharmony_ci#endif 31962306a36Sopenharmony_ci}; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci/* check if the selector is a valid pin function selector */ 32262306a36Sopenharmony_cistatic int samsung_get_functions_count(struct pinctrl_dev *pctldev) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *drvdata; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci drvdata = pinctrl_dev_get_drvdata(pctldev); 32762306a36Sopenharmony_ci return drvdata->nr_functions; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci/* return the name of the pin function specified */ 33162306a36Sopenharmony_cistatic const char *samsung_pinmux_get_fname(struct pinctrl_dev *pctldev, 33262306a36Sopenharmony_ci unsigned selector) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *drvdata; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci drvdata = pinctrl_dev_get_drvdata(pctldev); 33762306a36Sopenharmony_ci return drvdata->pmx_functions[selector].name; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci/* return the groups associated for the specified function selector */ 34162306a36Sopenharmony_cistatic int samsung_pinmux_get_groups(struct pinctrl_dev *pctldev, 34262306a36Sopenharmony_ci unsigned selector, const char * const **groups, 34362306a36Sopenharmony_ci unsigned * const num_groups) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *drvdata; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci drvdata = pinctrl_dev_get_drvdata(pctldev); 34862306a36Sopenharmony_ci *groups = drvdata->pmx_functions[selector].groups; 34962306a36Sopenharmony_ci *num_groups = drvdata->pmx_functions[selector].num_groups; 35062306a36Sopenharmony_ci return 0; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci/* 35462306a36Sopenharmony_ci * given a pin number that is local to a pin controller, find out the pin bank 35562306a36Sopenharmony_ci * and the register base of the pin bank. 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_cistatic void pin_to_reg_bank(struct samsung_pinctrl_drv_data *drvdata, 35862306a36Sopenharmony_ci unsigned pin, void __iomem **reg, u32 *offset, 35962306a36Sopenharmony_ci struct samsung_pin_bank **bank) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct samsung_pin_bank *b; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci b = drvdata->pin_banks; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci while ((pin >= b->pin_base) && 36662306a36Sopenharmony_ci ((b->pin_base + b->nr_pins - 1) < pin)) 36762306a36Sopenharmony_ci b++; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci *reg = b->pctl_base + b->pctl_offset; 37062306a36Sopenharmony_ci *offset = pin - b->pin_base; 37162306a36Sopenharmony_ci if (bank) 37262306a36Sopenharmony_ci *bank = b; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci/* enable or disable a pinmux function */ 37662306a36Sopenharmony_cistatic void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector, 37762306a36Sopenharmony_ci unsigned group) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *drvdata; 38062306a36Sopenharmony_ci const struct samsung_pin_bank_type *type; 38162306a36Sopenharmony_ci struct samsung_pin_bank *bank; 38262306a36Sopenharmony_ci void __iomem *reg; 38362306a36Sopenharmony_ci u32 mask, shift, data, pin_offset; 38462306a36Sopenharmony_ci unsigned long flags; 38562306a36Sopenharmony_ci const struct samsung_pmx_func *func; 38662306a36Sopenharmony_ci const struct samsung_pin_group *grp; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci drvdata = pinctrl_dev_get_drvdata(pctldev); 38962306a36Sopenharmony_ci func = &drvdata->pmx_functions[selector]; 39062306a36Sopenharmony_ci grp = &drvdata->pin_groups[group]; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci pin_to_reg_bank(drvdata, grp->pins[0] - drvdata->pin_base, 39362306a36Sopenharmony_ci ®, &pin_offset, &bank); 39462306a36Sopenharmony_ci type = bank->type; 39562306a36Sopenharmony_ci mask = (1 << type->fld_width[PINCFG_TYPE_FUNC]) - 1; 39662306a36Sopenharmony_ci shift = pin_offset * type->fld_width[PINCFG_TYPE_FUNC]; 39762306a36Sopenharmony_ci if (shift >= 32) { 39862306a36Sopenharmony_ci /* Some banks have two config registers */ 39962306a36Sopenharmony_ci shift -= 32; 40062306a36Sopenharmony_ci reg += 4; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci raw_spin_lock_irqsave(&bank->slock, flags); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci data = readl(reg + type->reg_offset[PINCFG_TYPE_FUNC]); 40662306a36Sopenharmony_ci data &= ~(mask << shift); 40762306a36Sopenharmony_ci data |= func->val << shift; 40862306a36Sopenharmony_ci writel(data, reg + type->reg_offset[PINCFG_TYPE_FUNC]); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&bank->slock, flags); 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci/* enable a specified pinmux by writing to registers */ 41462306a36Sopenharmony_cistatic int samsung_pinmux_set_mux(struct pinctrl_dev *pctldev, 41562306a36Sopenharmony_ci unsigned selector, 41662306a36Sopenharmony_ci unsigned group) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci samsung_pinmux_setup(pctldev, selector, group); 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci/* list of pinmux callbacks for the pinmux vertical in pinctrl core */ 42362306a36Sopenharmony_cistatic const struct pinmux_ops samsung_pinmux_ops = { 42462306a36Sopenharmony_ci .get_functions_count = samsung_get_functions_count, 42562306a36Sopenharmony_ci .get_function_name = samsung_pinmux_get_fname, 42662306a36Sopenharmony_ci .get_function_groups = samsung_pinmux_get_groups, 42762306a36Sopenharmony_ci .set_mux = samsung_pinmux_set_mux, 42862306a36Sopenharmony_ci}; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci/* set or get the pin config settings for a specified pin */ 43162306a36Sopenharmony_cistatic int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin, 43262306a36Sopenharmony_ci unsigned long *config, bool set) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *drvdata; 43562306a36Sopenharmony_ci const struct samsung_pin_bank_type *type; 43662306a36Sopenharmony_ci struct samsung_pin_bank *bank; 43762306a36Sopenharmony_ci void __iomem *reg_base; 43862306a36Sopenharmony_ci enum pincfg_type cfg_type = PINCFG_UNPACK_TYPE(*config); 43962306a36Sopenharmony_ci u32 data, width, pin_offset, mask, shift; 44062306a36Sopenharmony_ci u32 cfg_value, cfg_reg; 44162306a36Sopenharmony_ci unsigned long flags; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci drvdata = pinctrl_dev_get_drvdata(pctldev); 44462306a36Sopenharmony_ci pin_to_reg_bank(drvdata, pin - drvdata->pin_base, ®_base, 44562306a36Sopenharmony_ci &pin_offset, &bank); 44662306a36Sopenharmony_ci type = bank->type; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (cfg_type >= PINCFG_TYPE_NUM || !type->fld_width[cfg_type]) 44962306a36Sopenharmony_ci return -EINVAL; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci width = type->fld_width[cfg_type]; 45262306a36Sopenharmony_ci cfg_reg = type->reg_offset[cfg_type]; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci raw_spin_lock_irqsave(&bank->slock, flags); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci mask = (1 << width) - 1; 45762306a36Sopenharmony_ci shift = pin_offset * width; 45862306a36Sopenharmony_ci data = readl(reg_base + cfg_reg); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (set) { 46162306a36Sopenharmony_ci cfg_value = PINCFG_UNPACK_VALUE(*config); 46262306a36Sopenharmony_ci data &= ~(mask << shift); 46362306a36Sopenharmony_ci data |= (cfg_value << shift); 46462306a36Sopenharmony_ci writel(data, reg_base + cfg_reg); 46562306a36Sopenharmony_ci } else { 46662306a36Sopenharmony_ci data >>= shift; 46762306a36Sopenharmony_ci data &= mask; 46862306a36Sopenharmony_ci *config = PINCFG_PACK(cfg_type, data); 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&bank->slock, flags); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci/* set the pin config settings for a specified pin */ 47762306a36Sopenharmony_cistatic int samsung_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, 47862306a36Sopenharmony_ci unsigned long *configs, unsigned num_configs) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci int i, ret; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci for (i = 0; i < num_configs; i++) { 48362306a36Sopenharmony_ci ret = samsung_pinconf_rw(pctldev, pin, &configs[i], true); 48462306a36Sopenharmony_ci if (ret < 0) 48562306a36Sopenharmony_ci return ret; 48662306a36Sopenharmony_ci } /* for each config */ 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return 0; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci/* get the pin config settings for a specified pin */ 49262306a36Sopenharmony_cistatic int samsung_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, 49362306a36Sopenharmony_ci unsigned long *config) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci return samsung_pinconf_rw(pctldev, pin, config, false); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/* set the pin config settings for a specified pin group */ 49962306a36Sopenharmony_cistatic int samsung_pinconf_group_set(struct pinctrl_dev *pctldev, 50062306a36Sopenharmony_ci unsigned group, unsigned long *configs, 50162306a36Sopenharmony_ci unsigned num_configs) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *drvdata; 50462306a36Sopenharmony_ci const unsigned int *pins; 50562306a36Sopenharmony_ci unsigned int cnt; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci drvdata = pinctrl_dev_get_drvdata(pctldev); 50862306a36Sopenharmony_ci pins = drvdata->pin_groups[group].pins; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++) 51162306a36Sopenharmony_ci samsung_pinconf_set(pctldev, pins[cnt], configs, num_configs); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return 0; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci/* get the pin config settings for a specified pin group */ 51762306a36Sopenharmony_cistatic int samsung_pinconf_group_get(struct pinctrl_dev *pctldev, 51862306a36Sopenharmony_ci unsigned int group, unsigned long *config) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *drvdata; 52162306a36Sopenharmony_ci const unsigned int *pins; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci drvdata = pinctrl_dev_get_drvdata(pctldev); 52462306a36Sopenharmony_ci pins = drvdata->pin_groups[group].pins; 52562306a36Sopenharmony_ci samsung_pinconf_get(pctldev, pins[0], config); 52662306a36Sopenharmony_ci return 0; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci/* list of pinconfig callbacks for pinconfig vertical in the pinctrl code */ 53062306a36Sopenharmony_cistatic const struct pinconf_ops samsung_pinconf_ops = { 53162306a36Sopenharmony_ci .pin_config_get = samsung_pinconf_get, 53262306a36Sopenharmony_ci .pin_config_set = samsung_pinconf_set, 53362306a36Sopenharmony_ci .pin_config_group_get = samsung_pinconf_group_get, 53462306a36Sopenharmony_ci .pin_config_group_set = samsung_pinconf_group_set, 53562306a36Sopenharmony_ci}; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci/* 53862306a36Sopenharmony_ci * The samsung_gpio_set_vlaue() should be called with "bank->slock" held 53962306a36Sopenharmony_ci * to avoid race condition. 54062306a36Sopenharmony_ci */ 54162306a36Sopenharmony_cistatic void samsung_gpio_set_value(struct gpio_chip *gc, 54262306a36Sopenharmony_ci unsigned offset, int value) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct samsung_pin_bank *bank = gpiochip_get_data(gc); 54562306a36Sopenharmony_ci const struct samsung_pin_bank_type *type = bank->type; 54662306a36Sopenharmony_ci void __iomem *reg; 54762306a36Sopenharmony_ci u32 data; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci reg = bank->pctl_base + bank->pctl_offset; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci data = readl(reg + type->reg_offset[PINCFG_TYPE_DAT]); 55262306a36Sopenharmony_ci data &= ~(1 << offset); 55362306a36Sopenharmony_ci if (value) 55462306a36Sopenharmony_ci data |= 1 << offset; 55562306a36Sopenharmony_ci writel(data, reg + type->reg_offset[PINCFG_TYPE_DAT]); 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci/* gpiolib gpio_set callback function */ 55962306a36Sopenharmony_cistatic void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci struct samsung_pin_bank *bank = gpiochip_get_data(gc); 56262306a36Sopenharmony_ci unsigned long flags; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci raw_spin_lock_irqsave(&bank->slock, flags); 56562306a36Sopenharmony_ci samsung_gpio_set_value(gc, offset, value); 56662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&bank->slock, flags); 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci/* gpiolib gpio_get callback function */ 57062306a36Sopenharmony_cistatic int samsung_gpio_get(struct gpio_chip *gc, unsigned offset) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci void __iomem *reg; 57362306a36Sopenharmony_ci u32 data; 57462306a36Sopenharmony_ci struct samsung_pin_bank *bank = gpiochip_get_data(gc); 57562306a36Sopenharmony_ci const struct samsung_pin_bank_type *type = bank->type; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci reg = bank->pctl_base + bank->pctl_offset; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci data = readl(reg + type->reg_offset[PINCFG_TYPE_DAT]); 58062306a36Sopenharmony_ci data >>= offset; 58162306a36Sopenharmony_ci data &= 1; 58262306a36Sopenharmony_ci return data; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci/* 58662306a36Sopenharmony_ci * The samsung_gpio_set_direction() should be called with "bank->slock" held 58762306a36Sopenharmony_ci * to avoid race condition. 58862306a36Sopenharmony_ci * The calls to gpio_direction_output() and gpio_direction_input() 58962306a36Sopenharmony_ci * leads to this function call. 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_cistatic int samsung_gpio_set_direction(struct gpio_chip *gc, 59262306a36Sopenharmony_ci unsigned offset, bool input) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci const struct samsung_pin_bank_type *type; 59562306a36Sopenharmony_ci struct samsung_pin_bank *bank; 59662306a36Sopenharmony_ci void __iomem *reg; 59762306a36Sopenharmony_ci u32 data, mask, shift; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci bank = gpiochip_get_data(gc); 60062306a36Sopenharmony_ci type = bank->type; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci reg = bank->pctl_base + bank->pctl_offset 60362306a36Sopenharmony_ci + type->reg_offset[PINCFG_TYPE_FUNC]; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci mask = (1 << type->fld_width[PINCFG_TYPE_FUNC]) - 1; 60662306a36Sopenharmony_ci shift = offset * type->fld_width[PINCFG_TYPE_FUNC]; 60762306a36Sopenharmony_ci if (shift >= 32) { 60862306a36Sopenharmony_ci /* Some banks have two config registers */ 60962306a36Sopenharmony_ci shift -= 32; 61062306a36Sopenharmony_ci reg += 4; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci data = readl(reg); 61462306a36Sopenharmony_ci data &= ~(mask << shift); 61562306a36Sopenharmony_ci if (!input) 61662306a36Sopenharmony_ci data |= PIN_CON_FUNC_OUTPUT << shift; 61762306a36Sopenharmony_ci writel(data, reg); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci return 0; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci/* gpiolib gpio_direction_input callback function. */ 62362306a36Sopenharmony_cistatic int samsung_gpio_direction_input(struct gpio_chip *gc, unsigned offset) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct samsung_pin_bank *bank = gpiochip_get_data(gc); 62662306a36Sopenharmony_ci unsigned long flags; 62762306a36Sopenharmony_ci int ret; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci raw_spin_lock_irqsave(&bank->slock, flags); 63062306a36Sopenharmony_ci ret = samsung_gpio_set_direction(gc, offset, true); 63162306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&bank->slock, flags); 63262306a36Sopenharmony_ci return ret; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci/* gpiolib gpio_direction_output callback function. */ 63662306a36Sopenharmony_cistatic int samsung_gpio_direction_output(struct gpio_chip *gc, unsigned offset, 63762306a36Sopenharmony_ci int value) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci struct samsung_pin_bank *bank = gpiochip_get_data(gc); 64062306a36Sopenharmony_ci unsigned long flags; 64162306a36Sopenharmony_ci int ret; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci raw_spin_lock_irqsave(&bank->slock, flags); 64462306a36Sopenharmony_ci samsung_gpio_set_value(gc, offset, value); 64562306a36Sopenharmony_ci ret = samsung_gpio_set_direction(gc, offset, false); 64662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&bank->slock, flags); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci return ret; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci/* 65262306a36Sopenharmony_ci * gpiod_to_irq() callback function. Creates a mapping between a GPIO pin 65362306a36Sopenharmony_ci * and a virtual IRQ, if not already present. 65462306a36Sopenharmony_ci */ 65562306a36Sopenharmony_cistatic int samsung_gpio_to_irq(struct gpio_chip *gc, unsigned offset) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci struct samsung_pin_bank *bank = gpiochip_get_data(gc); 65862306a36Sopenharmony_ci unsigned int virq; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (!bank->irq_domain) 66162306a36Sopenharmony_ci return -ENXIO; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci virq = irq_create_mapping(bank->irq_domain, offset); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci return (virq) ? : -ENXIO; 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic struct samsung_pin_group *samsung_pinctrl_create_groups( 66962306a36Sopenharmony_ci struct device *dev, 67062306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *drvdata, 67162306a36Sopenharmony_ci unsigned int *cnt) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci struct pinctrl_desc *ctrldesc = &drvdata->pctl; 67462306a36Sopenharmony_ci struct samsung_pin_group *groups, *grp; 67562306a36Sopenharmony_ci const struct pinctrl_pin_desc *pdesc; 67662306a36Sopenharmony_ci int i; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci groups = devm_kcalloc(dev, ctrldesc->npins, sizeof(*groups), 67962306a36Sopenharmony_ci GFP_KERNEL); 68062306a36Sopenharmony_ci if (!groups) 68162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 68262306a36Sopenharmony_ci grp = groups; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci pdesc = ctrldesc->pins; 68562306a36Sopenharmony_ci for (i = 0; i < ctrldesc->npins; ++i, ++pdesc, ++grp) { 68662306a36Sopenharmony_ci grp->name = pdesc->name; 68762306a36Sopenharmony_ci grp->pins = &pdesc->number; 68862306a36Sopenharmony_ci grp->num_pins = 1; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci *cnt = ctrldesc->npins; 69262306a36Sopenharmony_ci return groups; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic int samsung_pinctrl_create_function(struct device *dev, 69662306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *drvdata, 69762306a36Sopenharmony_ci struct device_node *func_np, 69862306a36Sopenharmony_ci struct samsung_pmx_func *func) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci int npins; 70162306a36Sopenharmony_ci int ret; 70262306a36Sopenharmony_ci int i; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (of_property_read_u32(func_np, "samsung,pin-function", &func->val)) 70562306a36Sopenharmony_ci return 0; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci npins = of_property_count_strings(func_np, "samsung,pins"); 70862306a36Sopenharmony_ci if (npins < 1) { 70962306a36Sopenharmony_ci dev_err(dev, "invalid pin list in %pOFn node", func_np); 71062306a36Sopenharmony_ci return -EINVAL; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci func->name = func_np->full_name; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci func->groups = devm_kcalloc(dev, npins, sizeof(char *), GFP_KERNEL); 71662306a36Sopenharmony_ci if (!func->groups) 71762306a36Sopenharmony_ci return -ENOMEM; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci for (i = 0; i < npins; ++i) { 72062306a36Sopenharmony_ci const char *gname; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci ret = of_property_read_string_index(func_np, "samsung,pins", 72362306a36Sopenharmony_ci i, &gname); 72462306a36Sopenharmony_ci if (ret) { 72562306a36Sopenharmony_ci dev_err(dev, 72662306a36Sopenharmony_ci "failed to read pin name %d from %pOFn node\n", 72762306a36Sopenharmony_ci i, func_np); 72862306a36Sopenharmony_ci return ret; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci func->groups[i] = gname; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci func->num_groups = npins; 73562306a36Sopenharmony_ci return 1; 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic struct samsung_pmx_func *samsung_pinctrl_create_functions( 73962306a36Sopenharmony_ci struct device *dev, 74062306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *drvdata, 74162306a36Sopenharmony_ci unsigned int *cnt) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci struct samsung_pmx_func *functions, *func; 74462306a36Sopenharmony_ci struct device_node *dev_np = dev->of_node; 74562306a36Sopenharmony_ci struct device_node *cfg_np; 74662306a36Sopenharmony_ci unsigned int func_cnt = 0; 74762306a36Sopenharmony_ci int ret; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci /* 75062306a36Sopenharmony_ci * Iterate over all the child nodes of the pin controller node 75162306a36Sopenharmony_ci * and create pin groups and pin function lists. 75262306a36Sopenharmony_ci */ 75362306a36Sopenharmony_ci for_each_child_of_node(dev_np, cfg_np) { 75462306a36Sopenharmony_ci struct device_node *func_np; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (!of_get_child_count(cfg_np)) { 75762306a36Sopenharmony_ci if (!of_find_property(cfg_np, 75862306a36Sopenharmony_ci "samsung,pin-function", NULL)) 75962306a36Sopenharmony_ci continue; 76062306a36Sopenharmony_ci ++func_cnt; 76162306a36Sopenharmony_ci continue; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci for_each_child_of_node(cfg_np, func_np) { 76562306a36Sopenharmony_ci if (!of_find_property(func_np, 76662306a36Sopenharmony_ci "samsung,pin-function", NULL)) 76762306a36Sopenharmony_ci continue; 76862306a36Sopenharmony_ci ++func_cnt; 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci functions = devm_kcalloc(dev, func_cnt, sizeof(*functions), 77362306a36Sopenharmony_ci GFP_KERNEL); 77462306a36Sopenharmony_ci if (!functions) 77562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 77662306a36Sopenharmony_ci func = functions; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci /* 77962306a36Sopenharmony_ci * Iterate over all the child nodes of the pin controller node 78062306a36Sopenharmony_ci * and create pin groups and pin function lists. 78162306a36Sopenharmony_ci */ 78262306a36Sopenharmony_ci func_cnt = 0; 78362306a36Sopenharmony_ci for_each_child_of_node(dev_np, cfg_np) { 78462306a36Sopenharmony_ci struct device_node *func_np; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci if (!of_get_child_count(cfg_np)) { 78762306a36Sopenharmony_ci ret = samsung_pinctrl_create_function(dev, drvdata, 78862306a36Sopenharmony_ci cfg_np, func); 78962306a36Sopenharmony_ci if (ret < 0) { 79062306a36Sopenharmony_ci of_node_put(cfg_np); 79162306a36Sopenharmony_ci return ERR_PTR(ret); 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci if (ret > 0) { 79462306a36Sopenharmony_ci ++func; 79562306a36Sopenharmony_ci ++func_cnt; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci continue; 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci for_each_child_of_node(cfg_np, func_np) { 80162306a36Sopenharmony_ci ret = samsung_pinctrl_create_function(dev, drvdata, 80262306a36Sopenharmony_ci func_np, func); 80362306a36Sopenharmony_ci if (ret < 0) { 80462306a36Sopenharmony_ci of_node_put(func_np); 80562306a36Sopenharmony_ci of_node_put(cfg_np); 80662306a36Sopenharmony_ci return ERR_PTR(ret); 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci if (ret > 0) { 80962306a36Sopenharmony_ci ++func; 81062306a36Sopenharmony_ci ++func_cnt; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci *cnt = func_cnt; 81662306a36Sopenharmony_ci return functions; 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci/* 82062306a36Sopenharmony_ci * Parse the information about all the available pin groups and pin functions 82162306a36Sopenharmony_ci * from device node of the pin-controller. A pin group is formed with all 82262306a36Sopenharmony_ci * the pins listed in the "samsung,pins" property. 82362306a36Sopenharmony_ci */ 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic int samsung_pinctrl_parse_dt(struct platform_device *pdev, 82662306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *drvdata) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 82962306a36Sopenharmony_ci struct samsung_pin_group *groups; 83062306a36Sopenharmony_ci struct samsung_pmx_func *functions; 83162306a36Sopenharmony_ci unsigned int grp_cnt = 0, func_cnt = 0; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci groups = samsung_pinctrl_create_groups(dev, drvdata, &grp_cnt); 83462306a36Sopenharmony_ci if (IS_ERR(groups)) { 83562306a36Sopenharmony_ci dev_err(dev, "failed to parse pin groups\n"); 83662306a36Sopenharmony_ci return PTR_ERR(groups); 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci functions = samsung_pinctrl_create_functions(dev, drvdata, &func_cnt); 84062306a36Sopenharmony_ci if (IS_ERR(functions)) { 84162306a36Sopenharmony_ci dev_err(dev, "failed to parse pin functions\n"); 84262306a36Sopenharmony_ci return PTR_ERR(functions); 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci drvdata->pin_groups = groups; 84662306a36Sopenharmony_ci drvdata->nr_groups = grp_cnt; 84762306a36Sopenharmony_ci drvdata->pmx_functions = functions; 84862306a36Sopenharmony_ci drvdata->nr_functions = func_cnt; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci return 0; 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci/* register the pinctrl interface with the pinctrl subsystem */ 85462306a36Sopenharmony_cistatic int samsung_pinctrl_register(struct platform_device *pdev, 85562306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *drvdata) 85662306a36Sopenharmony_ci{ 85762306a36Sopenharmony_ci struct pinctrl_desc *ctrldesc = &drvdata->pctl; 85862306a36Sopenharmony_ci struct pinctrl_pin_desc *pindesc, *pdesc; 85962306a36Sopenharmony_ci struct samsung_pin_bank *pin_bank; 86062306a36Sopenharmony_ci char *pin_names; 86162306a36Sopenharmony_ci int pin, bank, ret; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci ctrldesc->name = "samsung-pinctrl"; 86462306a36Sopenharmony_ci ctrldesc->owner = THIS_MODULE; 86562306a36Sopenharmony_ci ctrldesc->pctlops = &samsung_pctrl_ops; 86662306a36Sopenharmony_ci ctrldesc->pmxops = &samsung_pinmux_ops; 86762306a36Sopenharmony_ci ctrldesc->confops = &samsung_pinconf_ops; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci pindesc = devm_kcalloc(&pdev->dev, 87062306a36Sopenharmony_ci drvdata->nr_pins, sizeof(*pindesc), 87162306a36Sopenharmony_ci GFP_KERNEL); 87262306a36Sopenharmony_ci if (!pindesc) 87362306a36Sopenharmony_ci return -ENOMEM; 87462306a36Sopenharmony_ci ctrldesc->pins = pindesc; 87562306a36Sopenharmony_ci ctrldesc->npins = drvdata->nr_pins; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* dynamically populate the pin number and pin name for pindesc */ 87862306a36Sopenharmony_ci for (pin = 0, pdesc = pindesc; pin < ctrldesc->npins; pin++, pdesc++) 87962306a36Sopenharmony_ci pdesc->number = pin + drvdata->pin_base; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* 88262306a36Sopenharmony_ci * allocate space for storing the dynamically generated names for all 88362306a36Sopenharmony_ci * the pins which belong to this pin-controller. 88462306a36Sopenharmony_ci */ 88562306a36Sopenharmony_ci pin_names = devm_kzalloc(&pdev->dev, 88662306a36Sopenharmony_ci array3_size(sizeof(char), PIN_NAME_LENGTH, 88762306a36Sopenharmony_ci drvdata->nr_pins), 88862306a36Sopenharmony_ci GFP_KERNEL); 88962306a36Sopenharmony_ci if (!pin_names) 89062306a36Sopenharmony_ci return -ENOMEM; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci /* for each pin, the name of the pin is pin-bank name + pin number */ 89362306a36Sopenharmony_ci for (bank = 0; bank < drvdata->nr_banks; bank++) { 89462306a36Sopenharmony_ci pin_bank = &drvdata->pin_banks[bank]; 89562306a36Sopenharmony_ci for (pin = 0; pin < pin_bank->nr_pins; pin++) { 89662306a36Sopenharmony_ci sprintf(pin_names, "%s-%d", pin_bank->name, pin); 89762306a36Sopenharmony_ci pdesc = pindesc + pin_bank->pin_base + pin; 89862306a36Sopenharmony_ci pdesc->name = pin_names; 89962306a36Sopenharmony_ci pin_names += PIN_NAME_LENGTH; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci ret = samsung_pinctrl_parse_dt(pdev, drvdata); 90462306a36Sopenharmony_ci if (ret) 90562306a36Sopenharmony_ci return ret; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci drvdata->pctl_dev = devm_pinctrl_register(&pdev->dev, ctrldesc, 90862306a36Sopenharmony_ci drvdata); 90962306a36Sopenharmony_ci if (IS_ERR(drvdata->pctl_dev)) { 91062306a36Sopenharmony_ci dev_err(&pdev->dev, "could not register pinctrl driver\n"); 91162306a36Sopenharmony_ci return PTR_ERR(drvdata->pctl_dev); 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci for (bank = 0; bank < drvdata->nr_banks; ++bank) { 91562306a36Sopenharmony_ci pin_bank = &drvdata->pin_banks[bank]; 91662306a36Sopenharmony_ci pin_bank->grange.name = pin_bank->name; 91762306a36Sopenharmony_ci pin_bank->grange.id = bank; 91862306a36Sopenharmony_ci pin_bank->grange.pin_base = drvdata->pin_base 91962306a36Sopenharmony_ci + pin_bank->pin_base; 92062306a36Sopenharmony_ci pin_bank->grange.base = pin_bank->grange.pin_base; 92162306a36Sopenharmony_ci pin_bank->grange.npins = pin_bank->nr_pins; 92262306a36Sopenharmony_ci pin_bank->grange.gc = &pin_bank->gpio_chip; 92362306a36Sopenharmony_ci pinctrl_add_gpio_range(drvdata->pctl_dev, &pin_bank->grange); 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci return 0; 92762306a36Sopenharmony_ci} 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci/* unregister the pinctrl interface with the pinctrl subsystem */ 93062306a36Sopenharmony_cistatic int samsung_pinctrl_unregister(struct platform_device *pdev, 93162306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *drvdata) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci struct samsung_pin_bank *bank = drvdata->pin_banks; 93462306a36Sopenharmony_ci int i; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci for (i = 0; i < drvdata->nr_banks; ++i, ++bank) 93762306a36Sopenharmony_ci pinctrl_remove_gpio_range(drvdata->pctl_dev, &bank->grange); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci return 0; 94062306a36Sopenharmony_ci} 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_cistatic const struct gpio_chip samsung_gpiolib_chip = { 94362306a36Sopenharmony_ci .request = gpiochip_generic_request, 94462306a36Sopenharmony_ci .free = gpiochip_generic_free, 94562306a36Sopenharmony_ci .set = samsung_gpio_set, 94662306a36Sopenharmony_ci .get = samsung_gpio_get, 94762306a36Sopenharmony_ci .direction_input = samsung_gpio_direction_input, 94862306a36Sopenharmony_ci .direction_output = samsung_gpio_direction_output, 94962306a36Sopenharmony_ci .to_irq = samsung_gpio_to_irq, 95062306a36Sopenharmony_ci .owner = THIS_MODULE, 95162306a36Sopenharmony_ci}; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci/* register the gpiolib interface with the gpiolib subsystem */ 95462306a36Sopenharmony_cistatic int samsung_gpiolib_register(struct platform_device *pdev, 95562306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *drvdata) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci struct samsung_pin_bank *bank = drvdata->pin_banks; 95862306a36Sopenharmony_ci struct gpio_chip *gc; 95962306a36Sopenharmony_ci int ret; 96062306a36Sopenharmony_ci int i; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci for (i = 0; i < drvdata->nr_banks; ++i, ++bank) { 96362306a36Sopenharmony_ci bank->gpio_chip = samsung_gpiolib_chip; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci gc = &bank->gpio_chip; 96662306a36Sopenharmony_ci gc->base = bank->grange.base; 96762306a36Sopenharmony_ci gc->ngpio = bank->nr_pins; 96862306a36Sopenharmony_ci gc->parent = &pdev->dev; 96962306a36Sopenharmony_ci gc->fwnode = bank->fwnode; 97062306a36Sopenharmony_ci gc->label = bank->name; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci ret = devm_gpiochip_add_data(&pdev->dev, gc, bank); 97362306a36Sopenharmony_ci if (ret) { 97462306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register gpio_chip %s, error code: %d\n", 97562306a36Sopenharmony_ci gc->label, ret); 97662306a36Sopenharmony_ci return ret; 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci return 0; 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_cistatic const struct samsung_pin_ctrl * 98462306a36Sopenharmony_cisamsung_pinctrl_get_soc_data_for_of_alias(struct platform_device *pdev) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 98762306a36Sopenharmony_ci const struct samsung_pinctrl_of_match_data *of_data; 98862306a36Sopenharmony_ci int id; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci id = of_alias_get_id(node, "pinctrl"); 99162306a36Sopenharmony_ci if (id < 0) { 99262306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get alias id\n"); 99362306a36Sopenharmony_ci return NULL; 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci of_data = of_device_get_match_data(&pdev->dev); 99762306a36Sopenharmony_ci if (id >= of_data->num_ctrl) { 99862306a36Sopenharmony_ci dev_err(&pdev->dev, "invalid alias id %d\n", id); 99962306a36Sopenharmony_ci return NULL; 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci return &(of_data->ctrl[id]); 100362306a36Sopenharmony_ci} 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_cistatic void samsung_banks_node_put(struct samsung_pinctrl_drv_data *d) 100662306a36Sopenharmony_ci{ 100762306a36Sopenharmony_ci struct samsung_pin_bank *bank; 100862306a36Sopenharmony_ci unsigned int i; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci bank = d->pin_banks; 101162306a36Sopenharmony_ci for (i = 0; i < d->nr_banks; ++i, ++bank) 101262306a36Sopenharmony_ci fwnode_handle_put(bank->fwnode); 101362306a36Sopenharmony_ci} 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci/* 101662306a36Sopenharmony_ci * Iterate over all driver pin banks to find one matching the name of node, 101762306a36Sopenharmony_ci * skipping optional "-gpio" node suffix. When found, assign node to the bank. 101862306a36Sopenharmony_ci */ 101962306a36Sopenharmony_cistatic void samsung_banks_node_get(struct device *dev, struct samsung_pinctrl_drv_data *d) 102062306a36Sopenharmony_ci{ 102162306a36Sopenharmony_ci const char *suffix = "-gpio-bank"; 102262306a36Sopenharmony_ci struct samsung_pin_bank *bank; 102362306a36Sopenharmony_ci struct fwnode_handle *child; 102462306a36Sopenharmony_ci /* Pin bank names are up to 4 characters */ 102562306a36Sopenharmony_ci char node_name[20]; 102662306a36Sopenharmony_ci unsigned int i; 102762306a36Sopenharmony_ci size_t len; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci bank = d->pin_banks; 103062306a36Sopenharmony_ci for (i = 0; i < d->nr_banks; ++i, ++bank) { 103162306a36Sopenharmony_ci strscpy(node_name, bank->name, sizeof(node_name)); 103262306a36Sopenharmony_ci len = strlcat(node_name, suffix, sizeof(node_name)); 103362306a36Sopenharmony_ci if (len >= sizeof(node_name)) { 103462306a36Sopenharmony_ci dev_err(dev, "Too long pin bank name '%s', ignoring\n", 103562306a36Sopenharmony_ci bank->name); 103662306a36Sopenharmony_ci continue; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci for_each_gpiochip_node(dev, child) { 104062306a36Sopenharmony_ci struct device_node *np = to_of_node(child); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci if (of_node_name_eq(np, node_name)) 104362306a36Sopenharmony_ci break; 104462306a36Sopenharmony_ci if (of_node_name_eq(np, bank->name)) 104562306a36Sopenharmony_ci break; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci if (child) 104962306a36Sopenharmony_ci bank->fwnode = child; 105062306a36Sopenharmony_ci else 105162306a36Sopenharmony_ci dev_warn(dev, "Missing node for bank %s - invalid DTB\n", 105262306a36Sopenharmony_ci bank->name); 105362306a36Sopenharmony_ci /* child reference dropped in samsung_drop_banks_of_node() */ 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci} 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci/* retrieve the soc specific data */ 105862306a36Sopenharmony_cistatic const struct samsung_pin_ctrl * 105962306a36Sopenharmony_cisamsung_pinctrl_get_soc_data(struct samsung_pinctrl_drv_data *d, 106062306a36Sopenharmony_ci struct platform_device *pdev) 106162306a36Sopenharmony_ci{ 106262306a36Sopenharmony_ci const struct samsung_pin_bank_data *bdata; 106362306a36Sopenharmony_ci const struct samsung_pin_ctrl *ctrl; 106462306a36Sopenharmony_ci struct samsung_pin_bank *bank; 106562306a36Sopenharmony_ci struct resource *res; 106662306a36Sopenharmony_ci void __iomem *virt_base[SAMSUNG_PINCTRL_NUM_RESOURCES]; 106762306a36Sopenharmony_ci unsigned int i; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci ctrl = samsung_pinctrl_get_soc_data_for_of_alias(pdev); 107062306a36Sopenharmony_ci if (!ctrl) 107162306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci d->suspend = ctrl->suspend; 107462306a36Sopenharmony_ci d->resume = ctrl->resume; 107562306a36Sopenharmony_ci d->nr_banks = ctrl->nr_banks; 107662306a36Sopenharmony_ci d->pin_banks = devm_kcalloc(&pdev->dev, d->nr_banks, 107762306a36Sopenharmony_ci sizeof(*d->pin_banks), GFP_KERNEL); 107862306a36Sopenharmony_ci if (!d->pin_banks) 107962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci if (ctrl->nr_ext_resources + 1 > SAMSUNG_PINCTRL_NUM_RESOURCES) 108262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci for (i = 0; i < ctrl->nr_ext_resources + 1; i++) { 108562306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, i); 108662306a36Sopenharmony_ci if (!res) { 108762306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get mem%d resource\n", i); 108862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci virt_base[i] = devm_ioremap(&pdev->dev, res->start, 109162306a36Sopenharmony_ci resource_size(res)); 109262306a36Sopenharmony_ci if (!virt_base[i]) { 109362306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to ioremap %pR\n", res); 109462306a36Sopenharmony_ci return ERR_PTR(-EIO); 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci } 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci bank = d->pin_banks; 109962306a36Sopenharmony_ci bdata = ctrl->pin_banks; 110062306a36Sopenharmony_ci for (i = 0; i < ctrl->nr_banks; ++i, ++bdata, ++bank) { 110162306a36Sopenharmony_ci bank->type = bdata->type; 110262306a36Sopenharmony_ci bank->pctl_offset = bdata->pctl_offset; 110362306a36Sopenharmony_ci bank->nr_pins = bdata->nr_pins; 110462306a36Sopenharmony_ci bank->eint_func = bdata->eint_func; 110562306a36Sopenharmony_ci bank->eint_type = bdata->eint_type; 110662306a36Sopenharmony_ci bank->eint_mask = bdata->eint_mask; 110762306a36Sopenharmony_ci bank->eint_offset = bdata->eint_offset; 110862306a36Sopenharmony_ci bank->name = bdata->name; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci raw_spin_lock_init(&bank->slock); 111162306a36Sopenharmony_ci bank->drvdata = d; 111262306a36Sopenharmony_ci bank->pin_base = d->nr_pins; 111362306a36Sopenharmony_ci d->nr_pins += bank->nr_pins; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci bank->eint_base = virt_base[0]; 111662306a36Sopenharmony_ci bank->pctl_base = virt_base[bdata->pctl_res_idx]; 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci /* 111962306a36Sopenharmony_ci * Legacy platforms should provide only one resource with IO memory. 112062306a36Sopenharmony_ci * Store it as virt_base because legacy driver needs to access it 112162306a36Sopenharmony_ci * through samsung_pinctrl_drv_data. 112262306a36Sopenharmony_ci */ 112362306a36Sopenharmony_ci d->virt_base = virt_base[0]; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci samsung_banks_node_get(&pdev->dev, d); 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci d->pin_base = pin_base; 112862306a36Sopenharmony_ci pin_base += d->nr_pins; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci return ctrl; 113162306a36Sopenharmony_ci} 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_cistatic int samsung_pinctrl_probe(struct platform_device *pdev) 113462306a36Sopenharmony_ci{ 113562306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *drvdata; 113662306a36Sopenharmony_ci const struct samsung_pin_ctrl *ctrl; 113762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 113862306a36Sopenharmony_ci int ret; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); 114162306a36Sopenharmony_ci if (!drvdata) 114262306a36Sopenharmony_ci return -ENOMEM; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci ctrl = samsung_pinctrl_get_soc_data(drvdata, pdev); 114562306a36Sopenharmony_ci if (IS_ERR(ctrl)) { 114662306a36Sopenharmony_ci dev_err(&pdev->dev, "driver data not available\n"); 114762306a36Sopenharmony_ci return PTR_ERR(ctrl); 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci drvdata->dev = dev; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci ret = platform_get_irq_optional(pdev, 0); 115262306a36Sopenharmony_ci if (ret < 0 && ret != -ENXIO) 115362306a36Sopenharmony_ci return ret; 115462306a36Sopenharmony_ci if (ret > 0) 115562306a36Sopenharmony_ci drvdata->irq = ret; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci if (ctrl->retention_data) { 115862306a36Sopenharmony_ci drvdata->retention_ctrl = ctrl->retention_data->init(drvdata, 115962306a36Sopenharmony_ci ctrl->retention_data); 116062306a36Sopenharmony_ci if (IS_ERR(drvdata->retention_ctrl)) { 116162306a36Sopenharmony_ci ret = PTR_ERR(drvdata->retention_ctrl); 116262306a36Sopenharmony_ci goto err_put_banks; 116362306a36Sopenharmony_ci } 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci ret = samsung_pinctrl_register(pdev, drvdata); 116762306a36Sopenharmony_ci if (ret) 116862306a36Sopenharmony_ci goto err_put_banks; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci if (ctrl->eint_gpio_init) 117162306a36Sopenharmony_ci ctrl->eint_gpio_init(drvdata); 117262306a36Sopenharmony_ci if (ctrl->eint_wkup_init) 117362306a36Sopenharmony_ci ctrl->eint_wkup_init(drvdata); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci ret = samsung_gpiolib_register(pdev, drvdata); 117662306a36Sopenharmony_ci if (ret) 117762306a36Sopenharmony_ci goto err_unregister; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci platform_set_drvdata(pdev, drvdata); 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci return 0; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_cierr_unregister: 118462306a36Sopenharmony_ci samsung_pinctrl_unregister(pdev, drvdata); 118562306a36Sopenharmony_cierr_put_banks: 118662306a36Sopenharmony_ci samsung_banks_node_put(drvdata); 118762306a36Sopenharmony_ci return ret; 118862306a36Sopenharmony_ci} 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci/* 119162306a36Sopenharmony_ci * samsung_pinctrl_suspend - save pinctrl state for suspend 119262306a36Sopenharmony_ci * 119362306a36Sopenharmony_ci * Save data for all banks handled by this device. 119462306a36Sopenharmony_ci */ 119562306a36Sopenharmony_cistatic int __maybe_unused samsung_pinctrl_suspend(struct device *dev) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *drvdata = dev_get_drvdata(dev); 119862306a36Sopenharmony_ci int i; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci for (i = 0; i < drvdata->nr_banks; i++) { 120162306a36Sopenharmony_ci struct samsung_pin_bank *bank = &drvdata->pin_banks[i]; 120262306a36Sopenharmony_ci void __iomem *reg = bank->pctl_base + bank->pctl_offset; 120362306a36Sopenharmony_ci const u8 *offs = bank->type->reg_offset; 120462306a36Sopenharmony_ci const u8 *widths = bank->type->fld_width; 120562306a36Sopenharmony_ci enum pincfg_type type; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci /* Registers without a powerdown config aren't lost */ 120862306a36Sopenharmony_ci if (!widths[PINCFG_TYPE_CON_PDN]) 120962306a36Sopenharmony_ci continue; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci for (type = 0; type < PINCFG_TYPE_NUM; type++) 121262306a36Sopenharmony_ci if (widths[type]) 121362306a36Sopenharmony_ci bank->pm_save[type] = readl(reg + offs[type]); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci if (widths[PINCFG_TYPE_FUNC] * bank->nr_pins > 32) { 121662306a36Sopenharmony_ci /* Some banks have two config registers */ 121762306a36Sopenharmony_ci bank->pm_save[PINCFG_TYPE_NUM] = 121862306a36Sopenharmony_ci readl(reg + offs[PINCFG_TYPE_FUNC] + 4); 121962306a36Sopenharmony_ci pr_debug("Save %s @ %p (con %#010x %08x)\n", 122062306a36Sopenharmony_ci bank->name, reg, 122162306a36Sopenharmony_ci bank->pm_save[PINCFG_TYPE_FUNC], 122262306a36Sopenharmony_ci bank->pm_save[PINCFG_TYPE_NUM]); 122362306a36Sopenharmony_ci } else { 122462306a36Sopenharmony_ci pr_debug("Save %s @ %p (con %#010x)\n", bank->name, 122562306a36Sopenharmony_ci reg, bank->pm_save[PINCFG_TYPE_FUNC]); 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci if (drvdata->suspend) 123062306a36Sopenharmony_ci drvdata->suspend(drvdata); 123162306a36Sopenharmony_ci if (drvdata->retention_ctrl && drvdata->retention_ctrl->enable) 123262306a36Sopenharmony_ci drvdata->retention_ctrl->enable(drvdata); 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci return 0; 123562306a36Sopenharmony_ci} 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci/* 123862306a36Sopenharmony_ci * samsung_pinctrl_resume - restore pinctrl state from suspend 123962306a36Sopenharmony_ci * 124062306a36Sopenharmony_ci * Restore one of the banks that was saved during suspend. 124162306a36Sopenharmony_ci * 124262306a36Sopenharmony_ci * We don't bother doing anything complicated to avoid glitching lines since 124362306a36Sopenharmony_ci * we're called before pad retention is turned off. 124462306a36Sopenharmony_ci */ 124562306a36Sopenharmony_cistatic int __maybe_unused samsung_pinctrl_resume(struct device *dev) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci struct samsung_pinctrl_drv_data *drvdata = dev_get_drvdata(dev); 124862306a36Sopenharmony_ci int i; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci if (drvdata->resume) 125162306a36Sopenharmony_ci drvdata->resume(drvdata); 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci for (i = 0; i < drvdata->nr_banks; i++) { 125462306a36Sopenharmony_ci struct samsung_pin_bank *bank = &drvdata->pin_banks[i]; 125562306a36Sopenharmony_ci void __iomem *reg = bank->pctl_base + bank->pctl_offset; 125662306a36Sopenharmony_ci const u8 *offs = bank->type->reg_offset; 125762306a36Sopenharmony_ci const u8 *widths = bank->type->fld_width; 125862306a36Sopenharmony_ci enum pincfg_type type; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci /* Registers without a powerdown config aren't lost */ 126162306a36Sopenharmony_ci if (!widths[PINCFG_TYPE_CON_PDN]) 126262306a36Sopenharmony_ci continue; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci if (widths[PINCFG_TYPE_FUNC] * bank->nr_pins > 32) { 126562306a36Sopenharmony_ci /* Some banks have two config registers */ 126662306a36Sopenharmony_ci pr_debug("%s @ %p (con %#010x %08x => %#010x %08x)\n", 126762306a36Sopenharmony_ci bank->name, reg, 126862306a36Sopenharmony_ci readl(reg + offs[PINCFG_TYPE_FUNC]), 126962306a36Sopenharmony_ci readl(reg + offs[PINCFG_TYPE_FUNC] + 4), 127062306a36Sopenharmony_ci bank->pm_save[PINCFG_TYPE_FUNC], 127162306a36Sopenharmony_ci bank->pm_save[PINCFG_TYPE_NUM]); 127262306a36Sopenharmony_ci writel(bank->pm_save[PINCFG_TYPE_NUM], 127362306a36Sopenharmony_ci reg + offs[PINCFG_TYPE_FUNC] + 4); 127462306a36Sopenharmony_ci } else { 127562306a36Sopenharmony_ci pr_debug("%s @ %p (con %#010x => %#010x)\n", bank->name, 127662306a36Sopenharmony_ci reg, readl(reg + offs[PINCFG_TYPE_FUNC]), 127762306a36Sopenharmony_ci bank->pm_save[PINCFG_TYPE_FUNC]); 127862306a36Sopenharmony_ci } 127962306a36Sopenharmony_ci for (type = 0; type < PINCFG_TYPE_NUM; type++) 128062306a36Sopenharmony_ci if (widths[type]) 128162306a36Sopenharmony_ci writel(bank->pm_save[type], reg + offs[type]); 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci if (drvdata->retention_ctrl && drvdata->retention_ctrl->disable) 128562306a36Sopenharmony_ci drvdata->retention_ctrl->disable(drvdata); 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci return 0; 128862306a36Sopenharmony_ci} 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_cistatic const struct of_device_id samsung_pinctrl_dt_match[] = { 129162306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_EXYNOS_ARM 129262306a36Sopenharmony_ci { .compatible = "samsung,exynos3250-pinctrl", 129362306a36Sopenharmony_ci .data = &exynos3250_of_data }, 129462306a36Sopenharmony_ci { .compatible = "samsung,exynos4210-pinctrl", 129562306a36Sopenharmony_ci .data = &exynos4210_of_data }, 129662306a36Sopenharmony_ci { .compatible = "samsung,exynos4x12-pinctrl", 129762306a36Sopenharmony_ci .data = &exynos4x12_of_data }, 129862306a36Sopenharmony_ci { .compatible = "samsung,exynos5250-pinctrl", 129962306a36Sopenharmony_ci .data = &exynos5250_of_data }, 130062306a36Sopenharmony_ci { .compatible = "samsung,exynos5260-pinctrl", 130162306a36Sopenharmony_ci .data = &exynos5260_of_data }, 130262306a36Sopenharmony_ci { .compatible = "samsung,exynos5410-pinctrl", 130362306a36Sopenharmony_ci .data = &exynos5410_of_data }, 130462306a36Sopenharmony_ci { .compatible = "samsung,exynos5420-pinctrl", 130562306a36Sopenharmony_ci .data = &exynos5420_of_data }, 130662306a36Sopenharmony_ci { .compatible = "samsung,s5pv210-pinctrl", 130762306a36Sopenharmony_ci .data = &s5pv210_of_data }, 130862306a36Sopenharmony_ci#endif 130962306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_EXYNOS_ARM64 131062306a36Sopenharmony_ci { .compatible = "samsung,exynos5433-pinctrl", 131162306a36Sopenharmony_ci .data = &exynos5433_of_data }, 131262306a36Sopenharmony_ci { .compatible = "samsung,exynos7-pinctrl", 131362306a36Sopenharmony_ci .data = &exynos7_of_data }, 131462306a36Sopenharmony_ci { .compatible = "samsung,exynos7885-pinctrl", 131562306a36Sopenharmony_ci .data = &exynos7885_of_data }, 131662306a36Sopenharmony_ci { .compatible = "samsung,exynos850-pinctrl", 131762306a36Sopenharmony_ci .data = &exynos850_of_data }, 131862306a36Sopenharmony_ci { .compatible = "samsung,exynosautov9-pinctrl", 131962306a36Sopenharmony_ci .data = &exynosautov9_of_data }, 132062306a36Sopenharmony_ci { .compatible = "tesla,fsd-pinctrl", 132162306a36Sopenharmony_ci .data = &fsd_of_data }, 132262306a36Sopenharmony_ci#endif 132362306a36Sopenharmony_ci#ifdef CONFIG_PINCTRL_S3C64XX 132462306a36Sopenharmony_ci { .compatible = "samsung,s3c64xx-pinctrl", 132562306a36Sopenharmony_ci .data = &s3c64xx_of_data }, 132662306a36Sopenharmony_ci#endif 132762306a36Sopenharmony_ci {}, 132862306a36Sopenharmony_ci}; 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_cistatic const struct dev_pm_ops samsung_pinctrl_pm_ops = { 133162306a36Sopenharmony_ci SET_LATE_SYSTEM_SLEEP_PM_OPS(samsung_pinctrl_suspend, 133262306a36Sopenharmony_ci samsung_pinctrl_resume) 133362306a36Sopenharmony_ci}; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_cistatic struct platform_driver samsung_pinctrl_driver = { 133662306a36Sopenharmony_ci .probe = samsung_pinctrl_probe, 133762306a36Sopenharmony_ci .driver = { 133862306a36Sopenharmony_ci .name = "samsung-pinctrl", 133962306a36Sopenharmony_ci .of_match_table = samsung_pinctrl_dt_match, 134062306a36Sopenharmony_ci .suppress_bind_attrs = true, 134162306a36Sopenharmony_ci .pm = &samsung_pinctrl_pm_ops, 134262306a36Sopenharmony_ci }, 134362306a36Sopenharmony_ci}; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_cistatic int __init samsung_pinctrl_drv_register(void) 134662306a36Sopenharmony_ci{ 134762306a36Sopenharmony_ci return platform_driver_register(&samsung_pinctrl_driver); 134862306a36Sopenharmony_ci} 134962306a36Sopenharmony_cipostcore_initcall(samsung_pinctrl_drv_register); 1350