162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (C) 2015-2017 Socionext Inc. 462306a36Sopenharmony_ci// Author: Masahiro Yamada <yamada.masahiro@socionext.com> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/list.h> 762306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 862306a36Sopenharmony_ci#include <linux/of.h> 962306a36Sopenharmony_ci#include <linux/platform_device.h> 1062306a36Sopenharmony_ci#include <linux/regmap.h> 1162306a36Sopenharmony_ci#include <linux/seq_file.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h> 1462306a36Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 1562306a36Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 1662306a36Sopenharmony_ci#include <linux/pinctrl/pinmux.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "../core.h" 1962306a36Sopenharmony_ci#include "../pinctrl-utils.h" 2062306a36Sopenharmony_ci#include "pinctrl-uniphier.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define UNIPHIER_PINCTRL_PINMUX_BASE 0x1000 2362306a36Sopenharmony_ci#define UNIPHIER_PINCTRL_LOAD_PINMUX 0x1700 2462306a36Sopenharmony_ci#define UNIPHIER_PINCTRL_DRVCTRL_BASE 0x1800 2562306a36Sopenharmony_ci#define UNIPHIER_PINCTRL_DRV2CTRL_BASE 0x1900 2662306a36Sopenharmony_ci#define UNIPHIER_PINCTRL_DRV3CTRL_BASE 0x1980 2762306a36Sopenharmony_ci#define UNIPHIER_PINCTRL_PUPDCTRL_BASE 0x1a00 2862306a36Sopenharmony_ci#define UNIPHIER_PINCTRL_IECTRL_BASE 0x1d00 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct uniphier_pinctrl_reg_region { 3162306a36Sopenharmony_ci struct list_head node; 3262306a36Sopenharmony_ci unsigned int base; 3362306a36Sopenharmony_ci unsigned int nregs; 3462306a36Sopenharmony_ci u32 vals[]; 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct uniphier_pinctrl_priv { 3862306a36Sopenharmony_ci struct pinctrl_desc pctldesc; 3962306a36Sopenharmony_ci struct pinctrl_dev *pctldev; 4062306a36Sopenharmony_ci struct regmap *regmap; 4162306a36Sopenharmony_ci const struct uniphier_pinctrl_socdata *socdata; 4262306a36Sopenharmony_ci struct list_head reg_regions; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int uniphier_pctl_get_groups_count(struct pinctrl_dev *pctldev) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return priv->socdata->groups_count; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic const char *uniphier_pctl_get_group_name(struct pinctrl_dev *pctldev, 5362306a36Sopenharmony_ci unsigned selector) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return priv->socdata->groups[selector].name; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic int uniphier_pctl_get_group_pins(struct pinctrl_dev *pctldev, 6162306a36Sopenharmony_ci unsigned selector, 6262306a36Sopenharmony_ci const unsigned **pins, 6362306a36Sopenharmony_ci unsigned *num_pins) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci *pins = priv->socdata->groups[selector].pins; 6862306a36Sopenharmony_ci *num_pins = priv->socdata->groups[selector].num_pins; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return 0; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 7462306a36Sopenharmony_cistatic void uniphier_pctl_pin_dbg_show(struct pinctrl_dev *pctldev, 7562306a36Sopenharmony_ci struct seq_file *s, unsigned offset) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci const struct pin_desc *desc = pin_desc_get(pctldev, offset); 7862306a36Sopenharmony_ci const char *pull_dir, *drv_type; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci switch (uniphier_pin_get_pull_dir(desc->drv_data)) { 8162306a36Sopenharmony_ci case UNIPHIER_PIN_PULL_UP: 8262306a36Sopenharmony_ci pull_dir = "UP"; 8362306a36Sopenharmony_ci break; 8462306a36Sopenharmony_ci case UNIPHIER_PIN_PULL_DOWN: 8562306a36Sopenharmony_ci pull_dir = "DOWN"; 8662306a36Sopenharmony_ci break; 8762306a36Sopenharmony_ci case UNIPHIER_PIN_PULL_UP_FIXED: 8862306a36Sopenharmony_ci pull_dir = "UP(FIXED)"; 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci case UNIPHIER_PIN_PULL_DOWN_FIXED: 9162306a36Sopenharmony_ci pull_dir = "DOWN(FIXED)"; 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci case UNIPHIER_PIN_PULL_NONE: 9462306a36Sopenharmony_ci pull_dir = "NONE"; 9562306a36Sopenharmony_ci break; 9662306a36Sopenharmony_ci default: 9762306a36Sopenharmony_ci BUG(); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci switch (uniphier_pin_get_drv_type(desc->drv_data)) { 10162306a36Sopenharmony_ci case UNIPHIER_PIN_DRV_1BIT: 10262306a36Sopenharmony_ci drv_type = "4/8(mA)"; 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci case UNIPHIER_PIN_DRV_2BIT: 10562306a36Sopenharmony_ci drv_type = "8/12/16/20(mA)"; 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci case UNIPHIER_PIN_DRV_3BIT: 10862306a36Sopenharmony_ci drv_type = "4/5/7/9/11/12/14/16(mA)"; 10962306a36Sopenharmony_ci break; 11062306a36Sopenharmony_ci case UNIPHIER_PIN_DRV_FIXED4: 11162306a36Sopenharmony_ci drv_type = "4(mA)"; 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci case UNIPHIER_PIN_DRV_FIXED5: 11462306a36Sopenharmony_ci drv_type = "5(mA)"; 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci case UNIPHIER_PIN_DRV_FIXED8: 11762306a36Sopenharmony_ci drv_type = "8(mA)"; 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci case UNIPHIER_PIN_DRV_NONE: 12062306a36Sopenharmony_ci drv_type = "NONE"; 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci default: 12362306a36Sopenharmony_ci BUG(); 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci seq_printf(s, " PULL_DIR=%s DRV_TYPE=%s", pull_dir, drv_type); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci#endif 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic const struct pinctrl_ops uniphier_pctlops = { 13162306a36Sopenharmony_ci .get_groups_count = uniphier_pctl_get_groups_count, 13262306a36Sopenharmony_ci .get_group_name = uniphier_pctl_get_group_name, 13362306a36Sopenharmony_ci .get_group_pins = uniphier_pctl_get_group_pins, 13462306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 13562306a36Sopenharmony_ci .pin_dbg_show = uniphier_pctl_pin_dbg_show, 13662306a36Sopenharmony_ci#endif 13762306a36Sopenharmony_ci .dt_node_to_map = pinconf_generic_dt_node_to_map_all, 13862306a36Sopenharmony_ci .dt_free_map = pinctrl_utils_free_map, 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic const unsigned int uniphier_conf_drv_strengths_1bit[] = {4, 8}; 14262306a36Sopenharmony_cistatic const unsigned int uniphier_conf_drv_strengths_2bit[] = {8, 12, 16, 20}; 14362306a36Sopenharmony_cistatic const unsigned int uniphier_conf_drv_strengths_3bit[] = {4, 5, 7, 9, 11, 14462306a36Sopenharmony_ci 12, 14, 16}; 14562306a36Sopenharmony_cistatic const unsigned int uniphier_conf_drv_strengths_fixed4[] = {4}; 14662306a36Sopenharmony_cistatic const unsigned int uniphier_conf_drv_strengths_fixed5[] = {5}; 14762306a36Sopenharmony_cistatic const unsigned int uniphier_conf_drv_strengths_fixed8[] = {8}; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int uniphier_conf_get_drvctrl_data(struct pinctrl_dev *pctldev, 15062306a36Sopenharmony_ci unsigned int pin, unsigned int *reg, 15162306a36Sopenharmony_ci unsigned int *shift, 15262306a36Sopenharmony_ci unsigned int *mask, 15362306a36Sopenharmony_ci const unsigned int **strengths) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci const struct pin_desc *desc = pin_desc_get(pctldev, pin); 15662306a36Sopenharmony_ci enum uniphier_pin_drv_type type = 15762306a36Sopenharmony_ci uniphier_pin_get_drv_type(desc->drv_data); 15862306a36Sopenharmony_ci unsigned int base = 0; 15962306a36Sopenharmony_ci unsigned int stride = 0; 16062306a36Sopenharmony_ci unsigned int width = 0; 16162306a36Sopenharmony_ci unsigned int drvctrl; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci switch (type) { 16462306a36Sopenharmony_ci case UNIPHIER_PIN_DRV_1BIT: 16562306a36Sopenharmony_ci *strengths = uniphier_conf_drv_strengths_1bit; 16662306a36Sopenharmony_ci base = UNIPHIER_PINCTRL_DRVCTRL_BASE; 16762306a36Sopenharmony_ci stride = 1; 16862306a36Sopenharmony_ci width = 1; 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci case UNIPHIER_PIN_DRV_2BIT: 17162306a36Sopenharmony_ci *strengths = uniphier_conf_drv_strengths_2bit; 17262306a36Sopenharmony_ci base = UNIPHIER_PINCTRL_DRV2CTRL_BASE; 17362306a36Sopenharmony_ci stride = 2; 17462306a36Sopenharmony_ci width = 2; 17562306a36Sopenharmony_ci break; 17662306a36Sopenharmony_ci case UNIPHIER_PIN_DRV_3BIT: 17762306a36Sopenharmony_ci *strengths = uniphier_conf_drv_strengths_3bit; 17862306a36Sopenharmony_ci base = UNIPHIER_PINCTRL_DRV3CTRL_BASE; 17962306a36Sopenharmony_ci stride = 4; 18062306a36Sopenharmony_ci width = 3; 18162306a36Sopenharmony_ci break; 18262306a36Sopenharmony_ci case UNIPHIER_PIN_DRV_FIXED4: 18362306a36Sopenharmony_ci *strengths = uniphier_conf_drv_strengths_fixed4; 18462306a36Sopenharmony_ci break; 18562306a36Sopenharmony_ci case UNIPHIER_PIN_DRV_FIXED5: 18662306a36Sopenharmony_ci *strengths = uniphier_conf_drv_strengths_fixed5; 18762306a36Sopenharmony_ci break; 18862306a36Sopenharmony_ci case UNIPHIER_PIN_DRV_FIXED8: 18962306a36Sopenharmony_ci *strengths = uniphier_conf_drv_strengths_fixed8; 19062306a36Sopenharmony_ci break; 19162306a36Sopenharmony_ci default: 19262306a36Sopenharmony_ci /* drive strength control is not supported for this pin */ 19362306a36Sopenharmony_ci return -EINVAL; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci drvctrl = uniphier_pin_get_drvctrl(desc->drv_data); 19762306a36Sopenharmony_ci drvctrl *= stride; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci *reg = base + drvctrl / 32 * 4; 20062306a36Sopenharmony_ci *shift = drvctrl % 32; 20162306a36Sopenharmony_ci *mask = (1U << width) - 1; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int uniphier_conf_pin_bias_get(struct pinctrl_dev *pctldev, 20762306a36Sopenharmony_ci unsigned int pin, 20862306a36Sopenharmony_ci enum pin_config_param param) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); 21162306a36Sopenharmony_ci const struct pin_desc *desc = pin_desc_get(pctldev, pin); 21262306a36Sopenharmony_ci enum uniphier_pin_pull_dir pull_dir = 21362306a36Sopenharmony_ci uniphier_pin_get_pull_dir(desc->drv_data); 21462306a36Sopenharmony_ci unsigned int pupdctrl, reg, shift, val; 21562306a36Sopenharmony_ci unsigned int expected = 1; 21662306a36Sopenharmony_ci int ret; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci switch (param) { 21962306a36Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 22062306a36Sopenharmony_ci if (pull_dir == UNIPHIER_PIN_PULL_NONE) 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci if (pull_dir == UNIPHIER_PIN_PULL_UP_FIXED || 22362306a36Sopenharmony_ci pull_dir == UNIPHIER_PIN_PULL_DOWN_FIXED) 22462306a36Sopenharmony_ci return -EINVAL; 22562306a36Sopenharmony_ci expected = 0; 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 22862306a36Sopenharmony_ci if (pull_dir == UNIPHIER_PIN_PULL_UP_FIXED) 22962306a36Sopenharmony_ci return 0; 23062306a36Sopenharmony_ci if (pull_dir != UNIPHIER_PIN_PULL_UP) 23162306a36Sopenharmony_ci return -EINVAL; 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 23462306a36Sopenharmony_ci if (pull_dir == UNIPHIER_PIN_PULL_DOWN_FIXED) 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci if (pull_dir != UNIPHIER_PIN_PULL_DOWN) 23762306a36Sopenharmony_ci return -EINVAL; 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci default: 24062306a36Sopenharmony_ci BUG(); 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci pupdctrl = uniphier_pin_get_pupdctrl(desc->drv_data); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci reg = UNIPHIER_PINCTRL_PUPDCTRL_BASE + pupdctrl / 32 * 4; 24662306a36Sopenharmony_ci shift = pupdctrl % 32; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci ret = regmap_read(priv->regmap, reg, &val); 24962306a36Sopenharmony_ci if (ret) 25062306a36Sopenharmony_ci return ret; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci val = (val >> shift) & 1; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return (val == expected) ? 0 : -EINVAL; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic int uniphier_conf_pin_drive_get(struct pinctrl_dev *pctldev, 25862306a36Sopenharmony_ci unsigned int pin, u32 *strength) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); 26162306a36Sopenharmony_ci unsigned int reg, shift, mask, val; 26262306a36Sopenharmony_ci const unsigned int *strengths; 26362306a36Sopenharmony_ci int ret; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci ret = uniphier_conf_get_drvctrl_data(pctldev, pin, ®, &shift, 26662306a36Sopenharmony_ci &mask, &strengths); 26762306a36Sopenharmony_ci if (ret) 26862306a36Sopenharmony_ci return ret; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (mask) { 27162306a36Sopenharmony_ci ret = regmap_read(priv->regmap, reg, &val); 27262306a36Sopenharmony_ci if (ret) 27362306a36Sopenharmony_ci return ret; 27462306a36Sopenharmony_ci } else { 27562306a36Sopenharmony_ci val = 0; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci *strength = strengths[(val >> shift) & mask]; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci return 0; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic int uniphier_conf_pin_input_enable_get(struct pinctrl_dev *pctldev, 28462306a36Sopenharmony_ci unsigned int pin) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); 28762306a36Sopenharmony_ci const struct pin_desc *desc = pin_desc_get(pctldev, pin); 28862306a36Sopenharmony_ci unsigned int iectrl = uniphier_pin_get_iectrl(desc->drv_data); 28962306a36Sopenharmony_ci unsigned int reg, mask, val; 29062306a36Sopenharmony_ci int ret; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (iectrl == UNIPHIER_PIN_IECTRL_NONE) 29362306a36Sopenharmony_ci /* This pin is always input-enabled. */ 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL) 29762306a36Sopenharmony_ci iectrl = pin; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci reg = UNIPHIER_PINCTRL_IECTRL_BASE + iectrl / 32 * 4; 30062306a36Sopenharmony_ci mask = BIT(iectrl % 32); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci ret = regmap_read(priv->regmap, reg, &val); 30362306a36Sopenharmony_ci if (ret) 30462306a36Sopenharmony_ci return ret; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return val & mask ? 0 : -EINVAL; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int uniphier_conf_pin_config_get(struct pinctrl_dev *pctldev, 31062306a36Sopenharmony_ci unsigned pin, 31162306a36Sopenharmony_ci unsigned long *configs) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci enum pin_config_param param = pinconf_to_config_param(*configs); 31462306a36Sopenharmony_ci bool has_arg = false; 31562306a36Sopenharmony_ci u32 arg; 31662306a36Sopenharmony_ci int ret; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci switch (param) { 31962306a36Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 32062306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 32162306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 32262306a36Sopenharmony_ci ret = uniphier_conf_pin_bias_get(pctldev, pin, param); 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: 32562306a36Sopenharmony_ci ret = uniphier_conf_pin_drive_get(pctldev, pin, &arg); 32662306a36Sopenharmony_ci has_arg = true; 32762306a36Sopenharmony_ci break; 32862306a36Sopenharmony_ci case PIN_CONFIG_INPUT_ENABLE: 32962306a36Sopenharmony_ci ret = uniphier_conf_pin_input_enable_get(pctldev, pin); 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci default: 33262306a36Sopenharmony_ci /* unsupported parameter */ 33362306a36Sopenharmony_ci ret = -EINVAL; 33462306a36Sopenharmony_ci break; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (ret == 0 && has_arg) 33862306a36Sopenharmony_ci *configs = pinconf_to_config_packed(param, arg); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci return ret; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic int uniphier_conf_pin_bias_set(struct pinctrl_dev *pctldev, 34462306a36Sopenharmony_ci unsigned int pin, 34562306a36Sopenharmony_ci enum pin_config_param param, u32 arg) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); 34862306a36Sopenharmony_ci const struct pin_desc *desc = pin_desc_get(pctldev, pin); 34962306a36Sopenharmony_ci enum uniphier_pin_pull_dir pull_dir = 35062306a36Sopenharmony_ci uniphier_pin_get_pull_dir(desc->drv_data); 35162306a36Sopenharmony_ci unsigned int pupdctrl, reg, shift; 35262306a36Sopenharmony_ci unsigned int val = 1; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci switch (param) { 35562306a36Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 35662306a36Sopenharmony_ci if (pull_dir == UNIPHIER_PIN_PULL_NONE) 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci if (pull_dir == UNIPHIER_PIN_PULL_UP_FIXED || 35962306a36Sopenharmony_ci pull_dir == UNIPHIER_PIN_PULL_DOWN_FIXED) { 36062306a36Sopenharmony_ci dev_err(pctldev->dev, 36162306a36Sopenharmony_ci "can not disable pull register for pin %s\n", 36262306a36Sopenharmony_ci desc->name); 36362306a36Sopenharmony_ci return -EINVAL; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci val = 0; 36662306a36Sopenharmony_ci break; 36762306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 36862306a36Sopenharmony_ci if (pull_dir == UNIPHIER_PIN_PULL_UP_FIXED && arg != 0) 36962306a36Sopenharmony_ci return 0; 37062306a36Sopenharmony_ci if (pull_dir != UNIPHIER_PIN_PULL_UP) { 37162306a36Sopenharmony_ci dev_err(pctldev->dev, 37262306a36Sopenharmony_ci "pull-up is unsupported for pin %s\n", 37362306a36Sopenharmony_ci desc->name); 37462306a36Sopenharmony_ci return -EINVAL; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci if (arg == 0) { 37762306a36Sopenharmony_ci dev_err(pctldev->dev, "pull-up can not be total\n"); 37862306a36Sopenharmony_ci return -EINVAL; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 38262306a36Sopenharmony_ci if (pull_dir == UNIPHIER_PIN_PULL_DOWN_FIXED && arg != 0) 38362306a36Sopenharmony_ci return 0; 38462306a36Sopenharmony_ci if (pull_dir != UNIPHIER_PIN_PULL_DOWN) { 38562306a36Sopenharmony_ci dev_err(pctldev->dev, 38662306a36Sopenharmony_ci "pull-down is unsupported for pin %s\n", 38762306a36Sopenharmony_ci desc->name); 38862306a36Sopenharmony_ci return -EINVAL; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci if (arg == 0) { 39162306a36Sopenharmony_ci dev_err(pctldev->dev, "pull-down can not be total\n"); 39262306a36Sopenharmony_ci return -EINVAL; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: 39662306a36Sopenharmony_ci if (pull_dir == UNIPHIER_PIN_PULL_NONE) { 39762306a36Sopenharmony_ci dev_err(pctldev->dev, 39862306a36Sopenharmony_ci "pull-up/down is unsupported for pin %s\n", 39962306a36Sopenharmony_ci desc->name); 40062306a36Sopenharmony_ci return -EINVAL; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (arg == 0) 40462306a36Sopenharmony_ci return 0; /* configuration ingored */ 40562306a36Sopenharmony_ci break; 40662306a36Sopenharmony_ci default: 40762306a36Sopenharmony_ci BUG(); 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci pupdctrl = uniphier_pin_get_pupdctrl(desc->drv_data); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci reg = UNIPHIER_PINCTRL_PUPDCTRL_BASE + pupdctrl / 32 * 4; 41362306a36Sopenharmony_ci shift = pupdctrl % 32; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return regmap_update_bits(priv->regmap, reg, 1 << shift, val << shift); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic int uniphier_conf_pin_drive_set(struct pinctrl_dev *pctldev, 41962306a36Sopenharmony_ci unsigned int pin, u32 strength) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); 42262306a36Sopenharmony_ci const struct pin_desc *desc = pin_desc_get(pctldev, pin); 42362306a36Sopenharmony_ci unsigned int reg, shift, mask, val; 42462306a36Sopenharmony_ci const unsigned int *strengths; 42562306a36Sopenharmony_ci int ret; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci ret = uniphier_conf_get_drvctrl_data(pctldev, pin, ®, &shift, 42862306a36Sopenharmony_ci &mask, &strengths); 42962306a36Sopenharmony_ci if (ret) { 43062306a36Sopenharmony_ci dev_err(pctldev->dev, "cannot set drive strength for pin %s\n", 43162306a36Sopenharmony_ci desc->name); 43262306a36Sopenharmony_ci return ret; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci for (val = 0; val <= mask; val++) { 43662306a36Sopenharmony_ci if (strengths[val] > strength) 43762306a36Sopenharmony_ci break; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (val == 0) { 44162306a36Sopenharmony_ci dev_err(pctldev->dev, 44262306a36Sopenharmony_ci "unsupported drive strength %u mA for pin %s\n", 44362306a36Sopenharmony_ci strength, desc->name); 44462306a36Sopenharmony_ci return -EINVAL; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (!mask) 44862306a36Sopenharmony_ci return 0; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci val--; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return regmap_update_bits(priv->regmap, reg, 45362306a36Sopenharmony_ci mask << shift, val << shift); 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic int uniphier_conf_pin_input_enable(struct pinctrl_dev *pctldev, 45762306a36Sopenharmony_ci unsigned int pin, u32 enable) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); 46062306a36Sopenharmony_ci const struct pin_desc *desc = pin_desc_get(pctldev, pin); 46162306a36Sopenharmony_ci unsigned int iectrl = uniphier_pin_get_iectrl(desc->drv_data); 46262306a36Sopenharmony_ci unsigned int reg, mask; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* 46562306a36Sopenharmony_ci * Multiple pins share one input enable, per-pin disabling is 46662306a36Sopenharmony_ci * impossible. 46762306a36Sopenharmony_ci */ 46862306a36Sopenharmony_ci if (!(priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL) && 46962306a36Sopenharmony_ci !enable) 47062306a36Sopenharmony_ci return -EINVAL; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* UNIPHIER_PIN_IECTRL_NONE means the pin is always input-enabled */ 47362306a36Sopenharmony_ci if (iectrl == UNIPHIER_PIN_IECTRL_NONE) 47462306a36Sopenharmony_ci return enable ? 0 : -EINVAL; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci if (priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL) 47762306a36Sopenharmony_ci iectrl = pin; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci reg = UNIPHIER_PINCTRL_IECTRL_BASE + iectrl / 32 * 4; 48062306a36Sopenharmony_ci mask = BIT(iectrl % 32); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci return regmap_update_bits(priv->regmap, reg, mask, enable ? mask : 0); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int uniphier_conf_pin_config_set(struct pinctrl_dev *pctldev, 48662306a36Sopenharmony_ci unsigned pin, 48762306a36Sopenharmony_ci unsigned long *configs, 48862306a36Sopenharmony_ci unsigned num_configs) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci int i, ret; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci for (i = 0; i < num_configs; i++) { 49362306a36Sopenharmony_ci enum pin_config_param param = 49462306a36Sopenharmony_ci pinconf_to_config_param(configs[i]); 49562306a36Sopenharmony_ci u32 arg = pinconf_to_config_argument(configs[i]); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci switch (param) { 49862306a36Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 49962306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 50062306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 50162306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: 50262306a36Sopenharmony_ci ret = uniphier_conf_pin_bias_set(pctldev, pin, 50362306a36Sopenharmony_ci param, arg); 50462306a36Sopenharmony_ci break; 50562306a36Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: 50662306a36Sopenharmony_ci ret = uniphier_conf_pin_drive_set(pctldev, pin, arg); 50762306a36Sopenharmony_ci break; 50862306a36Sopenharmony_ci case PIN_CONFIG_INPUT_ENABLE: 50962306a36Sopenharmony_ci ret = uniphier_conf_pin_input_enable(pctldev, pin, arg); 51062306a36Sopenharmony_ci break; 51162306a36Sopenharmony_ci default: 51262306a36Sopenharmony_ci dev_err(pctldev->dev, 51362306a36Sopenharmony_ci "unsupported configuration parameter %u\n", 51462306a36Sopenharmony_ci param); 51562306a36Sopenharmony_ci return -EINVAL; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (ret) 51962306a36Sopenharmony_ci return ret; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci return 0; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic int uniphier_conf_pin_config_group_set(struct pinctrl_dev *pctldev, 52662306a36Sopenharmony_ci unsigned selector, 52762306a36Sopenharmony_ci unsigned long *configs, 52862306a36Sopenharmony_ci unsigned num_configs) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); 53162306a36Sopenharmony_ci const unsigned *pins = priv->socdata->groups[selector].pins; 53262306a36Sopenharmony_ci unsigned num_pins = priv->socdata->groups[selector].num_pins; 53362306a36Sopenharmony_ci int i, ret; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci for (i = 0; i < num_pins; i++) { 53662306a36Sopenharmony_ci ret = uniphier_conf_pin_config_set(pctldev, pins[i], 53762306a36Sopenharmony_ci configs, num_configs); 53862306a36Sopenharmony_ci if (ret) 53962306a36Sopenharmony_ci return ret; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci return 0; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic const struct pinconf_ops uniphier_confops = { 54662306a36Sopenharmony_ci .is_generic = true, 54762306a36Sopenharmony_ci .pin_config_get = uniphier_conf_pin_config_get, 54862306a36Sopenharmony_ci .pin_config_set = uniphier_conf_pin_config_set, 54962306a36Sopenharmony_ci .pin_config_group_set = uniphier_conf_pin_config_group_set, 55062306a36Sopenharmony_ci}; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic int uniphier_pmx_get_functions_count(struct pinctrl_dev *pctldev) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci return priv->socdata->functions_count; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic const char *uniphier_pmx_get_function_name(struct pinctrl_dev *pctldev, 56062306a36Sopenharmony_ci unsigned selector) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci return priv->socdata->functions[selector].name; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int uniphier_pmx_get_function_groups(struct pinctrl_dev *pctldev, 56862306a36Sopenharmony_ci unsigned selector, 56962306a36Sopenharmony_ci const char * const **groups, 57062306a36Sopenharmony_ci unsigned *num_groups) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci *groups = priv->socdata->functions[selector].groups; 57562306a36Sopenharmony_ci *num_groups = priv->socdata->functions[selector].num_groups; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci return 0; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic int uniphier_pmx_set_one_mux(struct pinctrl_dev *pctldev, unsigned pin, 58162306a36Sopenharmony_ci int muxval) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); 58462306a36Sopenharmony_ci unsigned int mux_bits, reg_stride, reg, reg_end, shift, mask; 58562306a36Sopenharmony_ci bool load_pinctrl; 58662306a36Sopenharmony_ci int ret; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* some pins need input-enabling */ 58962306a36Sopenharmony_ci ret = uniphier_conf_pin_input_enable(pctldev, pin, 1); 59062306a36Sopenharmony_ci if (ret) 59162306a36Sopenharmony_ci return ret; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (muxval < 0) 59462306a36Sopenharmony_ci return 0; /* dedicated pin; nothing to do for pin-mux */ 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_DBGMUX_SEPARATE) { 59762306a36Sopenharmony_ci /* 59862306a36Sopenharmony_ci * Mode reg_offset bit_position 59962306a36Sopenharmony_ci * Normal 4 * n shift+3:shift 60062306a36Sopenharmony_ci * Debug 4 * n shift+7:shift+4 60162306a36Sopenharmony_ci */ 60262306a36Sopenharmony_ci mux_bits = 4; 60362306a36Sopenharmony_ci reg_stride = 8; 60462306a36Sopenharmony_ci load_pinctrl = true; 60562306a36Sopenharmony_ci } else { 60662306a36Sopenharmony_ci /* 60762306a36Sopenharmony_ci * Mode reg_offset bit_position 60862306a36Sopenharmony_ci * Normal 8 * n shift+3:shift 60962306a36Sopenharmony_ci * Debug 8 * n + 4 shift+3:shift 61062306a36Sopenharmony_ci */ 61162306a36Sopenharmony_ci mux_bits = 8; 61262306a36Sopenharmony_ci reg_stride = 4; 61362306a36Sopenharmony_ci load_pinctrl = false; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci reg = UNIPHIER_PINCTRL_PINMUX_BASE + pin * mux_bits / 32 * reg_stride; 61762306a36Sopenharmony_ci reg_end = reg + reg_stride; 61862306a36Sopenharmony_ci shift = pin * mux_bits % 32; 61962306a36Sopenharmony_ci mask = (1U << mux_bits) - 1; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* 62262306a36Sopenharmony_ci * If reg_stride is greater than 4, the MSB of each pinsel shall be 62362306a36Sopenharmony_ci * stored in the offset+4. 62462306a36Sopenharmony_ci */ 62562306a36Sopenharmony_ci for (; reg < reg_end; reg += 4) { 62662306a36Sopenharmony_ci ret = regmap_update_bits(priv->regmap, reg, 62762306a36Sopenharmony_ci mask << shift, muxval << shift); 62862306a36Sopenharmony_ci if (ret) 62962306a36Sopenharmony_ci return ret; 63062306a36Sopenharmony_ci muxval >>= mux_bits; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (load_pinctrl) { 63462306a36Sopenharmony_ci ret = regmap_write(priv->regmap, 63562306a36Sopenharmony_ci UNIPHIER_PINCTRL_LOAD_PINMUX, 1); 63662306a36Sopenharmony_ci if (ret) 63762306a36Sopenharmony_ci return ret; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci return 0; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic int uniphier_pmx_set_mux(struct pinctrl_dev *pctldev, 64462306a36Sopenharmony_ci unsigned func_selector, 64562306a36Sopenharmony_ci unsigned group_selector) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); 64862306a36Sopenharmony_ci const struct uniphier_pinctrl_group *grp = 64962306a36Sopenharmony_ci &priv->socdata->groups[group_selector]; 65062306a36Sopenharmony_ci int i; 65162306a36Sopenharmony_ci int ret; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci for (i = 0; i < grp->num_pins; i++) { 65462306a36Sopenharmony_ci ret = uniphier_pmx_set_one_mux(pctldev, grp->pins[i], 65562306a36Sopenharmony_ci grp->muxvals[i]); 65662306a36Sopenharmony_ci if (ret) 65762306a36Sopenharmony_ci return ret; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci return 0; 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic int uniphier_pmx_gpio_request_enable(struct pinctrl_dev *pctldev, 66462306a36Sopenharmony_ci struct pinctrl_gpio_range *range, 66562306a36Sopenharmony_ci unsigned offset) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev); 66862306a36Sopenharmony_ci unsigned int gpio_offset; 66962306a36Sopenharmony_ci int muxval, i; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (range->pins) { 67262306a36Sopenharmony_ci for (i = 0; i < range->npins; i++) 67362306a36Sopenharmony_ci if (range->pins[i] == offset) 67462306a36Sopenharmony_ci break; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (WARN_ON(i == range->npins)) 67762306a36Sopenharmony_ci return -EINVAL; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci gpio_offset = i; 68062306a36Sopenharmony_ci } else { 68162306a36Sopenharmony_ci gpio_offset = offset - range->pin_base; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci gpio_offset += range->id; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci muxval = priv->socdata->get_gpio_muxval(offset, gpio_offset); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci return uniphier_pmx_set_one_mux(pctldev, offset, muxval); 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic const struct pinmux_ops uniphier_pmxops = { 69262306a36Sopenharmony_ci .get_functions_count = uniphier_pmx_get_functions_count, 69362306a36Sopenharmony_ci .get_function_name = uniphier_pmx_get_function_name, 69462306a36Sopenharmony_ci .get_function_groups = uniphier_pmx_get_function_groups, 69562306a36Sopenharmony_ci .set_mux = uniphier_pmx_set_mux, 69662306a36Sopenharmony_ci .gpio_request_enable = uniphier_pmx_gpio_request_enable, 69762306a36Sopenharmony_ci .strict = true, 69862306a36Sopenharmony_ci}; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 70162306a36Sopenharmony_cistatic int uniphier_pinctrl_suspend(struct device *dev) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv = dev_get_drvdata(dev); 70462306a36Sopenharmony_ci struct uniphier_pinctrl_reg_region *r; 70562306a36Sopenharmony_ci int ret; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci list_for_each_entry(r, &priv->reg_regions, node) { 70862306a36Sopenharmony_ci ret = regmap_bulk_read(priv->regmap, r->base, r->vals, 70962306a36Sopenharmony_ci r->nregs); 71062306a36Sopenharmony_ci if (ret) 71162306a36Sopenharmony_ci return ret; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci return 0; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic int uniphier_pinctrl_resume(struct device *dev) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv = dev_get_drvdata(dev); 72062306a36Sopenharmony_ci struct uniphier_pinctrl_reg_region *r; 72162306a36Sopenharmony_ci int ret; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci list_for_each_entry(r, &priv->reg_regions, node) { 72462306a36Sopenharmony_ci ret = regmap_bulk_write(priv->regmap, r->base, r->vals, 72562306a36Sopenharmony_ci r->nregs); 72662306a36Sopenharmony_ci if (ret) 72762306a36Sopenharmony_ci return ret; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_DBGMUX_SEPARATE) { 73162306a36Sopenharmony_ci ret = regmap_write(priv->regmap, 73262306a36Sopenharmony_ci UNIPHIER_PINCTRL_LOAD_PINMUX, 1); 73362306a36Sopenharmony_ci if (ret) 73462306a36Sopenharmony_ci return ret; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci return 0; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic int uniphier_pinctrl_add_reg_region(struct device *dev, 74162306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv, 74262306a36Sopenharmony_ci unsigned int base, 74362306a36Sopenharmony_ci unsigned int count, 74462306a36Sopenharmony_ci unsigned int width) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci struct uniphier_pinctrl_reg_region *region; 74762306a36Sopenharmony_ci unsigned int nregs; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (!count) 75062306a36Sopenharmony_ci return 0; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci nregs = DIV_ROUND_UP(count * width, 32); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci region = devm_kzalloc(dev, struct_size(region, vals, nregs), 75562306a36Sopenharmony_ci GFP_KERNEL); 75662306a36Sopenharmony_ci if (!region) 75762306a36Sopenharmony_ci return -ENOMEM; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci region->base = base; 76062306a36Sopenharmony_ci region->nregs = nregs; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci list_add_tail(®ion->node, &priv->reg_regions); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci return 0; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci#endif 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic int uniphier_pinctrl_pm_init(struct device *dev, 76962306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 77262306a36Sopenharmony_ci const struct uniphier_pinctrl_socdata *socdata = priv->socdata; 77362306a36Sopenharmony_ci unsigned int num_drvctrl = 0; 77462306a36Sopenharmony_ci unsigned int num_drv2ctrl = 0; 77562306a36Sopenharmony_ci unsigned int num_drv3ctrl = 0; 77662306a36Sopenharmony_ci unsigned int num_pupdctrl = 0; 77762306a36Sopenharmony_ci unsigned int num_iectrl = 0; 77862306a36Sopenharmony_ci unsigned int iectrl, drvctrl, pupdctrl; 77962306a36Sopenharmony_ci enum uniphier_pin_drv_type drv_type; 78062306a36Sopenharmony_ci enum uniphier_pin_pull_dir pull_dir; 78162306a36Sopenharmony_ci int i, ret; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci for (i = 0; i < socdata->npins; i++) { 78462306a36Sopenharmony_ci void *drv_data = socdata->pins[i].drv_data; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci drvctrl = uniphier_pin_get_drvctrl(drv_data); 78762306a36Sopenharmony_ci drv_type = uniphier_pin_get_drv_type(drv_data); 78862306a36Sopenharmony_ci pupdctrl = uniphier_pin_get_pupdctrl(drv_data); 78962306a36Sopenharmony_ci pull_dir = uniphier_pin_get_pull_dir(drv_data); 79062306a36Sopenharmony_ci iectrl = uniphier_pin_get_iectrl(drv_data); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci switch (drv_type) { 79362306a36Sopenharmony_ci case UNIPHIER_PIN_DRV_1BIT: 79462306a36Sopenharmony_ci num_drvctrl = max(num_drvctrl, drvctrl + 1); 79562306a36Sopenharmony_ci break; 79662306a36Sopenharmony_ci case UNIPHIER_PIN_DRV_2BIT: 79762306a36Sopenharmony_ci num_drv2ctrl = max(num_drv2ctrl, drvctrl + 1); 79862306a36Sopenharmony_ci break; 79962306a36Sopenharmony_ci case UNIPHIER_PIN_DRV_3BIT: 80062306a36Sopenharmony_ci num_drv3ctrl = max(num_drv3ctrl, drvctrl + 1); 80162306a36Sopenharmony_ci break; 80262306a36Sopenharmony_ci default: 80362306a36Sopenharmony_ci break; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (pull_dir == UNIPHIER_PIN_PULL_UP || 80762306a36Sopenharmony_ci pull_dir == UNIPHIER_PIN_PULL_DOWN) 80862306a36Sopenharmony_ci num_pupdctrl = max(num_pupdctrl, pupdctrl + 1); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (iectrl != UNIPHIER_PIN_IECTRL_NONE) { 81162306a36Sopenharmony_ci if (socdata->caps & UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL) 81262306a36Sopenharmony_ci iectrl = i; 81362306a36Sopenharmony_ci num_iectrl = max(num_iectrl, iectrl + 1); 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci INIT_LIST_HEAD(&priv->reg_regions); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci ret = uniphier_pinctrl_add_reg_region(dev, priv, 82062306a36Sopenharmony_ci UNIPHIER_PINCTRL_PINMUX_BASE, 82162306a36Sopenharmony_ci socdata->npins, 8); 82262306a36Sopenharmony_ci if (ret) 82362306a36Sopenharmony_ci return ret; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci ret = uniphier_pinctrl_add_reg_region(dev, priv, 82662306a36Sopenharmony_ci UNIPHIER_PINCTRL_DRVCTRL_BASE, 82762306a36Sopenharmony_ci num_drvctrl, 1); 82862306a36Sopenharmony_ci if (ret) 82962306a36Sopenharmony_ci return ret; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci ret = uniphier_pinctrl_add_reg_region(dev, priv, 83262306a36Sopenharmony_ci UNIPHIER_PINCTRL_DRV2CTRL_BASE, 83362306a36Sopenharmony_ci num_drv2ctrl, 2); 83462306a36Sopenharmony_ci if (ret) 83562306a36Sopenharmony_ci return ret; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci ret = uniphier_pinctrl_add_reg_region(dev, priv, 83862306a36Sopenharmony_ci UNIPHIER_PINCTRL_DRV3CTRL_BASE, 83962306a36Sopenharmony_ci num_drv3ctrl, 3); 84062306a36Sopenharmony_ci if (ret) 84162306a36Sopenharmony_ci return ret; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci ret = uniphier_pinctrl_add_reg_region(dev, priv, 84462306a36Sopenharmony_ci UNIPHIER_PINCTRL_PUPDCTRL_BASE, 84562306a36Sopenharmony_ci num_pupdctrl, 1); 84662306a36Sopenharmony_ci if (ret) 84762306a36Sopenharmony_ci return ret; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci ret = uniphier_pinctrl_add_reg_region(dev, priv, 85062306a36Sopenharmony_ci UNIPHIER_PINCTRL_IECTRL_BASE, 85162306a36Sopenharmony_ci num_iectrl, 1); 85262306a36Sopenharmony_ci if (ret) 85362306a36Sopenharmony_ci return ret; 85462306a36Sopenharmony_ci#endif 85562306a36Sopenharmony_ci return 0; 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ciconst struct dev_pm_ops uniphier_pinctrl_pm_ops = { 85962306a36Sopenharmony_ci SET_LATE_SYSTEM_SLEEP_PM_OPS(uniphier_pinctrl_suspend, 86062306a36Sopenharmony_ci uniphier_pinctrl_resume) 86162306a36Sopenharmony_ci}; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ciint uniphier_pinctrl_probe(struct platform_device *pdev, 86462306a36Sopenharmony_ci const struct uniphier_pinctrl_socdata *socdata) 86562306a36Sopenharmony_ci{ 86662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 86762306a36Sopenharmony_ci struct uniphier_pinctrl_priv *priv; 86862306a36Sopenharmony_ci struct device_node *parent; 86962306a36Sopenharmony_ci int ret; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (!socdata || 87262306a36Sopenharmony_ci !socdata->pins || !socdata->npins || 87362306a36Sopenharmony_ci !socdata->groups || !socdata->groups_count || 87462306a36Sopenharmony_ci !socdata->functions || !socdata->functions_count) { 87562306a36Sopenharmony_ci dev_err(dev, "pinctrl socdata lacks necessary members\n"); 87662306a36Sopenharmony_ci return -EINVAL; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 88062306a36Sopenharmony_ci if (!priv) 88162306a36Sopenharmony_ci return -ENOMEM; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci parent = of_get_parent(dev->of_node); 88462306a36Sopenharmony_ci priv->regmap = syscon_node_to_regmap(parent); 88562306a36Sopenharmony_ci of_node_put(parent); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci if (IS_ERR(priv->regmap)) { 88862306a36Sopenharmony_ci dev_err(dev, "failed to get regmap\n"); 88962306a36Sopenharmony_ci return PTR_ERR(priv->regmap); 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci priv->socdata = socdata; 89362306a36Sopenharmony_ci priv->pctldesc.name = dev->driver->name; 89462306a36Sopenharmony_ci priv->pctldesc.pins = socdata->pins; 89562306a36Sopenharmony_ci priv->pctldesc.npins = socdata->npins; 89662306a36Sopenharmony_ci priv->pctldesc.pctlops = &uniphier_pctlops; 89762306a36Sopenharmony_ci priv->pctldesc.pmxops = &uniphier_pmxops; 89862306a36Sopenharmony_ci priv->pctldesc.confops = &uniphier_confops; 89962306a36Sopenharmony_ci priv->pctldesc.owner = dev->driver->owner; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci ret = uniphier_pinctrl_pm_init(dev, priv); 90262306a36Sopenharmony_ci if (ret) 90362306a36Sopenharmony_ci return ret; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci priv->pctldev = devm_pinctrl_register(dev, &priv->pctldesc, priv); 90662306a36Sopenharmony_ci if (IS_ERR(priv->pctldev)) { 90762306a36Sopenharmony_ci dev_err(dev, "failed to register UniPhier pinctrl driver\n"); 90862306a36Sopenharmony_ci return PTR_ERR(priv->pctldev); 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci platform_set_drvdata(pdev, priv); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci return 0; 91462306a36Sopenharmony_ci} 915