18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// Copyright (C) 2015-2017 Socionext Inc.
48c2ecf20Sopenharmony_ci//   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/list.h>
78c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
88c2ecf20Sopenharmony_ci#include <linux/of.h>
98c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf.h>
108c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h>
118c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinctrl.h>
128c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinmux.h>
138c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
148c2ecf20Sopenharmony_ci#include <linux/regmap.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "../core.h"
178c2ecf20Sopenharmony_ci#include "../pinctrl-utils.h"
188c2ecf20Sopenharmony_ci#include "pinctrl-uniphier.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define UNIPHIER_PINCTRL_PINMUX_BASE	0x1000
218c2ecf20Sopenharmony_ci#define UNIPHIER_PINCTRL_LOAD_PINMUX	0x1700
228c2ecf20Sopenharmony_ci#define UNIPHIER_PINCTRL_DRVCTRL_BASE	0x1800
238c2ecf20Sopenharmony_ci#define UNIPHIER_PINCTRL_DRV2CTRL_BASE	0x1900
248c2ecf20Sopenharmony_ci#define UNIPHIER_PINCTRL_DRV3CTRL_BASE	0x1980
258c2ecf20Sopenharmony_ci#define UNIPHIER_PINCTRL_PUPDCTRL_BASE	0x1a00
268c2ecf20Sopenharmony_ci#define UNIPHIER_PINCTRL_IECTRL_BASE	0x1d00
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistruct uniphier_pinctrl_reg_region {
298c2ecf20Sopenharmony_ci	struct list_head node;
308c2ecf20Sopenharmony_ci	unsigned int base;
318c2ecf20Sopenharmony_ci	unsigned int nregs;
328c2ecf20Sopenharmony_ci	u32 vals[];
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct uniphier_pinctrl_priv {
368c2ecf20Sopenharmony_ci	struct pinctrl_desc pctldesc;
378c2ecf20Sopenharmony_ci	struct pinctrl_dev *pctldev;
388c2ecf20Sopenharmony_ci	struct regmap *regmap;
398c2ecf20Sopenharmony_ci	const struct uniphier_pinctrl_socdata *socdata;
408c2ecf20Sopenharmony_ci	struct list_head reg_regions;
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int uniphier_pctl_get_groups_count(struct pinctrl_dev *pctldev)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	return priv->socdata->groups_count;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic const char *uniphier_pctl_get_group_name(struct pinctrl_dev *pctldev,
518c2ecf20Sopenharmony_ci						unsigned selector)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	return priv->socdata->groups[selector].name;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic int uniphier_pctl_get_group_pins(struct pinctrl_dev *pctldev,
598c2ecf20Sopenharmony_ci					unsigned selector,
608c2ecf20Sopenharmony_ci					const unsigned **pins,
618c2ecf20Sopenharmony_ci					unsigned *num_pins)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	*pins = priv->socdata->groups[selector].pins;
668c2ecf20Sopenharmony_ci	*num_pins = priv->socdata->groups[selector].num_pins;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	return 0;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
728c2ecf20Sopenharmony_cistatic void uniphier_pctl_pin_dbg_show(struct pinctrl_dev *pctldev,
738c2ecf20Sopenharmony_ci				       struct seq_file *s, unsigned offset)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	const struct pin_desc *desc = pin_desc_get(pctldev, offset);
768c2ecf20Sopenharmony_ci	const char *pull_dir, *drv_type;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	switch (uniphier_pin_get_pull_dir(desc->drv_data)) {
798c2ecf20Sopenharmony_ci	case UNIPHIER_PIN_PULL_UP:
808c2ecf20Sopenharmony_ci		pull_dir = "UP";
818c2ecf20Sopenharmony_ci		break;
828c2ecf20Sopenharmony_ci	case UNIPHIER_PIN_PULL_DOWN:
838c2ecf20Sopenharmony_ci		pull_dir = "DOWN";
848c2ecf20Sopenharmony_ci		break;
858c2ecf20Sopenharmony_ci	case UNIPHIER_PIN_PULL_UP_FIXED:
868c2ecf20Sopenharmony_ci		pull_dir = "UP(FIXED)";
878c2ecf20Sopenharmony_ci		break;
888c2ecf20Sopenharmony_ci	case UNIPHIER_PIN_PULL_DOWN_FIXED:
898c2ecf20Sopenharmony_ci		pull_dir = "DOWN(FIXED)";
908c2ecf20Sopenharmony_ci		break;
918c2ecf20Sopenharmony_ci	case UNIPHIER_PIN_PULL_NONE:
928c2ecf20Sopenharmony_ci		pull_dir = "NONE";
938c2ecf20Sopenharmony_ci		break;
948c2ecf20Sopenharmony_ci	default:
958c2ecf20Sopenharmony_ci		BUG();
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	switch (uniphier_pin_get_drv_type(desc->drv_data)) {
998c2ecf20Sopenharmony_ci	case UNIPHIER_PIN_DRV_1BIT:
1008c2ecf20Sopenharmony_ci		drv_type = "4/8(mA)";
1018c2ecf20Sopenharmony_ci		break;
1028c2ecf20Sopenharmony_ci	case UNIPHIER_PIN_DRV_2BIT:
1038c2ecf20Sopenharmony_ci		drv_type = "8/12/16/20(mA)";
1048c2ecf20Sopenharmony_ci		break;
1058c2ecf20Sopenharmony_ci	case UNIPHIER_PIN_DRV_3BIT:
1068c2ecf20Sopenharmony_ci		drv_type = "4/5/7/9/11/12/14/16(mA)";
1078c2ecf20Sopenharmony_ci		break;
1088c2ecf20Sopenharmony_ci	case UNIPHIER_PIN_DRV_FIXED4:
1098c2ecf20Sopenharmony_ci		drv_type = "4(mA)";
1108c2ecf20Sopenharmony_ci		break;
1118c2ecf20Sopenharmony_ci	case UNIPHIER_PIN_DRV_FIXED5:
1128c2ecf20Sopenharmony_ci		drv_type = "5(mA)";
1138c2ecf20Sopenharmony_ci		break;
1148c2ecf20Sopenharmony_ci	case UNIPHIER_PIN_DRV_FIXED8:
1158c2ecf20Sopenharmony_ci		drv_type = "8(mA)";
1168c2ecf20Sopenharmony_ci		break;
1178c2ecf20Sopenharmony_ci	case UNIPHIER_PIN_DRV_NONE:
1188c2ecf20Sopenharmony_ci		drv_type = "NONE";
1198c2ecf20Sopenharmony_ci		break;
1208c2ecf20Sopenharmony_ci	default:
1218c2ecf20Sopenharmony_ci		BUG();
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	seq_printf(s, " PULL_DIR=%s  DRV_TYPE=%s", pull_dir, drv_type);
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci#endif
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic const struct pinctrl_ops uniphier_pctlops = {
1298c2ecf20Sopenharmony_ci	.get_groups_count = uniphier_pctl_get_groups_count,
1308c2ecf20Sopenharmony_ci	.get_group_name = uniphier_pctl_get_group_name,
1318c2ecf20Sopenharmony_ci	.get_group_pins = uniphier_pctl_get_group_pins,
1328c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
1338c2ecf20Sopenharmony_ci	.pin_dbg_show = uniphier_pctl_pin_dbg_show,
1348c2ecf20Sopenharmony_ci#endif
1358c2ecf20Sopenharmony_ci	.dt_node_to_map = pinconf_generic_dt_node_to_map_all,
1368c2ecf20Sopenharmony_ci	.dt_free_map = pinctrl_utils_free_map,
1378c2ecf20Sopenharmony_ci};
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic const unsigned int uniphier_conf_drv_strengths_1bit[] = {4, 8};
1408c2ecf20Sopenharmony_cistatic const unsigned int uniphier_conf_drv_strengths_2bit[] = {8, 12, 16, 20};
1418c2ecf20Sopenharmony_cistatic const unsigned int uniphier_conf_drv_strengths_3bit[] = {4, 5, 7, 9, 11,
1428c2ecf20Sopenharmony_ci								12, 14, 16};
1438c2ecf20Sopenharmony_cistatic const unsigned int uniphier_conf_drv_strengths_fixed4[] = {4};
1448c2ecf20Sopenharmony_cistatic const unsigned int uniphier_conf_drv_strengths_fixed5[] = {5};
1458c2ecf20Sopenharmony_cistatic const unsigned int uniphier_conf_drv_strengths_fixed8[] = {8};
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic int uniphier_conf_get_drvctrl_data(struct pinctrl_dev *pctldev,
1488c2ecf20Sopenharmony_ci					  unsigned int pin, unsigned int *reg,
1498c2ecf20Sopenharmony_ci					  unsigned int *shift,
1508c2ecf20Sopenharmony_ci					  unsigned int *mask,
1518c2ecf20Sopenharmony_ci					  const unsigned int **strengths)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	const struct pin_desc *desc = pin_desc_get(pctldev, pin);
1548c2ecf20Sopenharmony_ci	enum uniphier_pin_drv_type type =
1558c2ecf20Sopenharmony_ci				uniphier_pin_get_drv_type(desc->drv_data);
1568c2ecf20Sopenharmony_ci	unsigned int base = 0;
1578c2ecf20Sopenharmony_ci	unsigned int stride = 0;
1588c2ecf20Sopenharmony_ci	unsigned int width = 0;
1598c2ecf20Sopenharmony_ci	unsigned int drvctrl;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	switch (type) {
1628c2ecf20Sopenharmony_ci	case UNIPHIER_PIN_DRV_1BIT:
1638c2ecf20Sopenharmony_ci		*strengths = uniphier_conf_drv_strengths_1bit;
1648c2ecf20Sopenharmony_ci		base = UNIPHIER_PINCTRL_DRVCTRL_BASE;
1658c2ecf20Sopenharmony_ci		stride = 1;
1668c2ecf20Sopenharmony_ci		width = 1;
1678c2ecf20Sopenharmony_ci		break;
1688c2ecf20Sopenharmony_ci	case UNIPHIER_PIN_DRV_2BIT:
1698c2ecf20Sopenharmony_ci		*strengths = uniphier_conf_drv_strengths_2bit;
1708c2ecf20Sopenharmony_ci		base = UNIPHIER_PINCTRL_DRV2CTRL_BASE;
1718c2ecf20Sopenharmony_ci		stride = 2;
1728c2ecf20Sopenharmony_ci		width = 2;
1738c2ecf20Sopenharmony_ci		break;
1748c2ecf20Sopenharmony_ci	case UNIPHIER_PIN_DRV_3BIT:
1758c2ecf20Sopenharmony_ci		*strengths = uniphier_conf_drv_strengths_3bit;
1768c2ecf20Sopenharmony_ci		base = UNIPHIER_PINCTRL_DRV3CTRL_BASE;
1778c2ecf20Sopenharmony_ci		stride = 4;
1788c2ecf20Sopenharmony_ci		width = 3;
1798c2ecf20Sopenharmony_ci		break;
1808c2ecf20Sopenharmony_ci	case UNIPHIER_PIN_DRV_FIXED4:
1818c2ecf20Sopenharmony_ci		*strengths = uniphier_conf_drv_strengths_fixed4;
1828c2ecf20Sopenharmony_ci		break;
1838c2ecf20Sopenharmony_ci	case UNIPHIER_PIN_DRV_FIXED5:
1848c2ecf20Sopenharmony_ci		*strengths = uniphier_conf_drv_strengths_fixed5;
1858c2ecf20Sopenharmony_ci		break;
1868c2ecf20Sopenharmony_ci	case UNIPHIER_PIN_DRV_FIXED8:
1878c2ecf20Sopenharmony_ci		*strengths = uniphier_conf_drv_strengths_fixed8;
1888c2ecf20Sopenharmony_ci		break;
1898c2ecf20Sopenharmony_ci	default:
1908c2ecf20Sopenharmony_ci		/* drive strength control is not supported for this pin */
1918c2ecf20Sopenharmony_ci		return -EINVAL;
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	drvctrl = uniphier_pin_get_drvctrl(desc->drv_data);
1958c2ecf20Sopenharmony_ci	drvctrl *= stride;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	*reg = base + drvctrl / 32 * 4;
1988c2ecf20Sopenharmony_ci	*shift = drvctrl % 32;
1998c2ecf20Sopenharmony_ci	*mask = (1U << width) - 1;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return 0;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic int uniphier_conf_pin_bias_get(struct pinctrl_dev *pctldev,
2058c2ecf20Sopenharmony_ci				      unsigned int pin,
2068c2ecf20Sopenharmony_ci				      enum pin_config_param param)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
2098c2ecf20Sopenharmony_ci	const struct pin_desc *desc = pin_desc_get(pctldev, pin);
2108c2ecf20Sopenharmony_ci	enum uniphier_pin_pull_dir pull_dir =
2118c2ecf20Sopenharmony_ci				uniphier_pin_get_pull_dir(desc->drv_data);
2128c2ecf20Sopenharmony_ci	unsigned int pupdctrl, reg, shift, val;
2138c2ecf20Sopenharmony_ci	unsigned int expected = 1;
2148c2ecf20Sopenharmony_ci	int ret;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	switch (param) {
2178c2ecf20Sopenharmony_ci	case PIN_CONFIG_BIAS_DISABLE:
2188c2ecf20Sopenharmony_ci		if (pull_dir == UNIPHIER_PIN_PULL_NONE)
2198c2ecf20Sopenharmony_ci			return 0;
2208c2ecf20Sopenharmony_ci		if (pull_dir == UNIPHIER_PIN_PULL_UP_FIXED ||
2218c2ecf20Sopenharmony_ci		    pull_dir == UNIPHIER_PIN_PULL_DOWN_FIXED)
2228c2ecf20Sopenharmony_ci			return -EINVAL;
2238c2ecf20Sopenharmony_ci		expected = 0;
2248c2ecf20Sopenharmony_ci		break;
2258c2ecf20Sopenharmony_ci	case PIN_CONFIG_BIAS_PULL_UP:
2268c2ecf20Sopenharmony_ci		if (pull_dir == UNIPHIER_PIN_PULL_UP_FIXED)
2278c2ecf20Sopenharmony_ci			return 0;
2288c2ecf20Sopenharmony_ci		if (pull_dir != UNIPHIER_PIN_PULL_UP)
2298c2ecf20Sopenharmony_ci			return -EINVAL;
2308c2ecf20Sopenharmony_ci		break;
2318c2ecf20Sopenharmony_ci	case PIN_CONFIG_BIAS_PULL_DOWN:
2328c2ecf20Sopenharmony_ci		if (pull_dir == UNIPHIER_PIN_PULL_DOWN_FIXED)
2338c2ecf20Sopenharmony_ci			return 0;
2348c2ecf20Sopenharmony_ci		if (pull_dir != UNIPHIER_PIN_PULL_DOWN)
2358c2ecf20Sopenharmony_ci			return -EINVAL;
2368c2ecf20Sopenharmony_ci		break;
2378c2ecf20Sopenharmony_ci	default:
2388c2ecf20Sopenharmony_ci		BUG();
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	pupdctrl = uniphier_pin_get_pupdctrl(desc->drv_data);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	reg = UNIPHIER_PINCTRL_PUPDCTRL_BASE + pupdctrl / 32 * 4;
2448c2ecf20Sopenharmony_ci	shift = pupdctrl % 32;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	ret = regmap_read(priv->regmap, reg, &val);
2478c2ecf20Sopenharmony_ci	if (ret)
2488c2ecf20Sopenharmony_ci		return ret;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	val = (val >> shift) & 1;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	return (val == expected) ? 0 : -EINVAL;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic int uniphier_conf_pin_drive_get(struct pinctrl_dev *pctldev,
2568c2ecf20Sopenharmony_ci				       unsigned int pin, u32 *strength)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
2598c2ecf20Sopenharmony_ci	unsigned int reg, shift, mask, val;
2608c2ecf20Sopenharmony_ci	const unsigned int *strengths;
2618c2ecf20Sopenharmony_ci	int ret;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	ret = uniphier_conf_get_drvctrl_data(pctldev, pin, &reg, &shift,
2648c2ecf20Sopenharmony_ci					     &mask, &strengths);
2658c2ecf20Sopenharmony_ci	if (ret)
2668c2ecf20Sopenharmony_ci		return ret;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (mask) {
2698c2ecf20Sopenharmony_ci		ret = regmap_read(priv->regmap, reg, &val);
2708c2ecf20Sopenharmony_ci		if (ret)
2718c2ecf20Sopenharmony_ci			return ret;
2728c2ecf20Sopenharmony_ci	} else {
2738c2ecf20Sopenharmony_ci		val = 0;
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	*strength = strengths[(val >> shift) & mask];
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	return 0;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic int uniphier_conf_pin_input_enable_get(struct pinctrl_dev *pctldev,
2828c2ecf20Sopenharmony_ci					      unsigned int pin)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
2858c2ecf20Sopenharmony_ci	const struct pin_desc *desc = pin_desc_get(pctldev, pin);
2868c2ecf20Sopenharmony_ci	unsigned int iectrl = uniphier_pin_get_iectrl(desc->drv_data);
2878c2ecf20Sopenharmony_ci	unsigned int reg, mask, val;
2888c2ecf20Sopenharmony_ci	int ret;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (iectrl == UNIPHIER_PIN_IECTRL_NONE)
2918c2ecf20Sopenharmony_ci		/* This pin is always input-enabled. */
2928c2ecf20Sopenharmony_ci		return 0;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	if (priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL)
2958c2ecf20Sopenharmony_ci		iectrl = pin;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	reg = UNIPHIER_PINCTRL_IECTRL_BASE + iectrl / 32 * 4;
2988c2ecf20Sopenharmony_ci	mask = BIT(iectrl % 32);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	ret = regmap_read(priv->regmap, reg, &val);
3018c2ecf20Sopenharmony_ci	if (ret)
3028c2ecf20Sopenharmony_ci		return ret;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	return val & mask ? 0 : -EINVAL;
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic int uniphier_conf_pin_config_get(struct pinctrl_dev *pctldev,
3088c2ecf20Sopenharmony_ci					unsigned pin,
3098c2ecf20Sopenharmony_ci					unsigned long *configs)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	enum pin_config_param param = pinconf_to_config_param(*configs);
3128c2ecf20Sopenharmony_ci	bool has_arg = false;
3138c2ecf20Sopenharmony_ci	u32 arg;
3148c2ecf20Sopenharmony_ci	int ret;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	switch (param) {
3178c2ecf20Sopenharmony_ci	case PIN_CONFIG_BIAS_DISABLE:
3188c2ecf20Sopenharmony_ci	case PIN_CONFIG_BIAS_PULL_UP:
3198c2ecf20Sopenharmony_ci	case PIN_CONFIG_BIAS_PULL_DOWN:
3208c2ecf20Sopenharmony_ci		ret = uniphier_conf_pin_bias_get(pctldev, pin, param);
3218c2ecf20Sopenharmony_ci		break;
3228c2ecf20Sopenharmony_ci	case PIN_CONFIG_DRIVE_STRENGTH:
3238c2ecf20Sopenharmony_ci		ret = uniphier_conf_pin_drive_get(pctldev, pin, &arg);
3248c2ecf20Sopenharmony_ci		has_arg = true;
3258c2ecf20Sopenharmony_ci		break;
3268c2ecf20Sopenharmony_ci	case PIN_CONFIG_INPUT_ENABLE:
3278c2ecf20Sopenharmony_ci		ret = uniphier_conf_pin_input_enable_get(pctldev, pin);
3288c2ecf20Sopenharmony_ci		break;
3298c2ecf20Sopenharmony_ci	default:
3308c2ecf20Sopenharmony_ci		/* unsupported parameter */
3318c2ecf20Sopenharmony_ci		ret = -EINVAL;
3328c2ecf20Sopenharmony_ci		break;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (ret == 0 && has_arg)
3368c2ecf20Sopenharmony_ci		*configs = pinconf_to_config_packed(param, arg);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	return ret;
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic int uniphier_conf_pin_bias_set(struct pinctrl_dev *pctldev,
3428c2ecf20Sopenharmony_ci				      unsigned int pin,
3438c2ecf20Sopenharmony_ci				      enum pin_config_param param, u32 arg)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
3468c2ecf20Sopenharmony_ci	const struct pin_desc *desc = pin_desc_get(pctldev, pin);
3478c2ecf20Sopenharmony_ci	enum uniphier_pin_pull_dir pull_dir =
3488c2ecf20Sopenharmony_ci				uniphier_pin_get_pull_dir(desc->drv_data);
3498c2ecf20Sopenharmony_ci	unsigned int pupdctrl, reg, shift;
3508c2ecf20Sopenharmony_ci	unsigned int val = 1;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	switch (param) {
3538c2ecf20Sopenharmony_ci	case PIN_CONFIG_BIAS_DISABLE:
3548c2ecf20Sopenharmony_ci		if (pull_dir == UNIPHIER_PIN_PULL_NONE)
3558c2ecf20Sopenharmony_ci			return 0;
3568c2ecf20Sopenharmony_ci		if (pull_dir == UNIPHIER_PIN_PULL_UP_FIXED ||
3578c2ecf20Sopenharmony_ci		    pull_dir == UNIPHIER_PIN_PULL_DOWN_FIXED) {
3588c2ecf20Sopenharmony_ci			dev_err(pctldev->dev,
3598c2ecf20Sopenharmony_ci				"can not disable pull register for pin %s\n",
3608c2ecf20Sopenharmony_ci				desc->name);
3618c2ecf20Sopenharmony_ci			return -EINVAL;
3628c2ecf20Sopenharmony_ci		}
3638c2ecf20Sopenharmony_ci		val = 0;
3648c2ecf20Sopenharmony_ci		break;
3658c2ecf20Sopenharmony_ci	case PIN_CONFIG_BIAS_PULL_UP:
3668c2ecf20Sopenharmony_ci		if (pull_dir == UNIPHIER_PIN_PULL_UP_FIXED && arg != 0)
3678c2ecf20Sopenharmony_ci			return 0;
3688c2ecf20Sopenharmony_ci		if (pull_dir != UNIPHIER_PIN_PULL_UP) {
3698c2ecf20Sopenharmony_ci			dev_err(pctldev->dev,
3708c2ecf20Sopenharmony_ci				"pull-up is unsupported for pin %s\n",
3718c2ecf20Sopenharmony_ci				desc->name);
3728c2ecf20Sopenharmony_ci			return -EINVAL;
3738c2ecf20Sopenharmony_ci		}
3748c2ecf20Sopenharmony_ci		if (arg == 0) {
3758c2ecf20Sopenharmony_ci			dev_err(pctldev->dev, "pull-up can not be total\n");
3768c2ecf20Sopenharmony_ci			return -EINVAL;
3778c2ecf20Sopenharmony_ci		}
3788c2ecf20Sopenharmony_ci		break;
3798c2ecf20Sopenharmony_ci	case PIN_CONFIG_BIAS_PULL_DOWN:
3808c2ecf20Sopenharmony_ci		if (pull_dir == UNIPHIER_PIN_PULL_DOWN_FIXED && arg != 0)
3818c2ecf20Sopenharmony_ci			return 0;
3828c2ecf20Sopenharmony_ci		if (pull_dir != UNIPHIER_PIN_PULL_DOWN) {
3838c2ecf20Sopenharmony_ci			dev_err(pctldev->dev,
3848c2ecf20Sopenharmony_ci				"pull-down is unsupported for pin %s\n",
3858c2ecf20Sopenharmony_ci				desc->name);
3868c2ecf20Sopenharmony_ci			return -EINVAL;
3878c2ecf20Sopenharmony_ci		}
3888c2ecf20Sopenharmony_ci		if (arg == 0) {
3898c2ecf20Sopenharmony_ci			dev_err(pctldev->dev, "pull-down can not be total\n");
3908c2ecf20Sopenharmony_ci			return -EINVAL;
3918c2ecf20Sopenharmony_ci		}
3928c2ecf20Sopenharmony_ci		break;
3938c2ecf20Sopenharmony_ci	case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
3948c2ecf20Sopenharmony_ci		if (pull_dir == UNIPHIER_PIN_PULL_NONE) {
3958c2ecf20Sopenharmony_ci			dev_err(pctldev->dev,
3968c2ecf20Sopenharmony_ci				"pull-up/down is unsupported for pin %s\n",
3978c2ecf20Sopenharmony_ci				desc->name);
3988c2ecf20Sopenharmony_ci			return -EINVAL;
3998c2ecf20Sopenharmony_ci		}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci		if (arg == 0)
4028c2ecf20Sopenharmony_ci			return 0; /* configuration ingored */
4038c2ecf20Sopenharmony_ci		break;
4048c2ecf20Sopenharmony_ci	default:
4058c2ecf20Sopenharmony_ci		BUG();
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	pupdctrl = uniphier_pin_get_pupdctrl(desc->drv_data);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	reg = UNIPHIER_PINCTRL_PUPDCTRL_BASE + pupdctrl / 32 * 4;
4118c2ecf20Sopenharmony_ci	shift = pupdctrl % 32;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	return regmap_update_bits(priv->regmap, reg, 1 << shift, val << shift);
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic int uniphier_conf_pin_drive_set(struct pinctrl_dev *pctldev,
4178c2ecf20Sopenharmony_ci				       unsigned int pin, u32 strength)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
4208c2ecf20Sopenharmony_ci	const struct pin_desc *desc = pin_desc_get(pctldev, pin);
4218c2ecf20Sopenharmony_ci	unsigned int reg, shift, mask, val;
4228c2ecf20Sopenharmony_ci	const unsigned int *strengths;
4238c2ecf20Sopenharmony_ci	int ret;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	ret = uniphier_conf_get_drvctrl_data(pctldev, pin, &reg, &shift,
4268c2ecf20Sopenharmony_ci					     &mask, &strengths);
4278c2ecf20Sopenharmony_ci	if (ret) {
4288c2ecf20Sopenharmony_ci		dev_err(pctldev->dev, "cannot set drive strength for pin %s\n",
4298c2ecf20Sopenharmony_ci			desc->name);
4308c2ecf20Sopenharmony_ci		return ret;
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	for (val = 0; val <= mask; val++) {
4348c2ecf20Sopenharmony_ci		if (strengths[val] > strength)
4358c2ecf20Sopenharmony_ci			break;
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	if (val == 0) {
4398c2ecf20Sopenharmony_ci		dev_err(pctldev->dev,
4408c2ecf20Sopenharmony_ci			"unsupported drive strength %u mA for pin %s\n",
4418c2ecf20Sopenharmony_ci			strength, desc->name);
4428c2ecf20Sopenharmony_ci		return -EINVAL;
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	if (!mask)
4468c2ecf20Sopenharmony_ci		return 0;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	val--;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	return regmap_update_bits(priv->regmap, reg,
4518c2ecf20Sopenharmony_ci				  mask << shift, val << shift);
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistatic int uniphier_conf_pin_input_enable(struct pinctrl_dev *pctldev,
4558c2ecf20Sopenharmony_ci					  unsigned int pin, u32 enable)
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
4588c2ecf20Sopenharmony_ci	const struct pin_desc *desc = pin_desc_get(pctldev, pin);
4598c2ecf20Sopenharmony_ci	unsigned int iectrl = uniphier_pin_get_iectrl(desc->drv_data);
4608c2ecf20Sopenharmony_ci	unsigned int reg, mask;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	/*
4638c2ecf20Sopenharmony_ci	 * Multiple pins share one input enable, per-pin disabling is
4648c2ecf20Sopenharmony_ci	 * impossible.
4658c2ecf20Sopenharmony_ci	 */
4668c2ecf20Sopenharmony_ci	if (!(priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL) &&
4678c2ecf20Sopenharmony_ci	    !enable)
4688c2ecf20Sopenharmony_ci		return -EINVAL;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	/* UNIPHIER_PIN_IECTRL_NONE means the pin is always input-enabled */
4718c2ecf20Sopenharmony_ci	if (iectrl == UNIPHIER_PIN_IECTRL_NONE)
4728c2ecf20Sopenharmony_ci		return enable ? 0 : -EINVAL;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	if (priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL)
4758c2ecf20Sopenharmony_ci		iectrl = pin;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	reg = UNIPHIER_PINCTRL_IECTRL_BASE + iectrl / 32 * 4;
4788c2ecf20Sopenharmony_ci	mask = BIT(iectrl % 32);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	return regmap_update_bits(priv->regmap, reg, mask, enable ? mask : 0);
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic int uniphier_conf_pin_config_set(struct pinctrl_dev *pctldev,
4848c2ecf20Sopenharmony_ci					unsigned pin,
4858c2ecf20Sopenharmony_ci					unsigned long *configs,
4868c2ecf20Sopenharmony_ci					unsigned num_configs)
4878c2ecf20Sopenharmony_ci{
4888c2ecf20Sopenharmony_ci	int i, ret;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	for (i = 0; i < num_configs; i++) {
4918c2ecf20Sopenharmony_ci		enum pin_config_param param =
4928c2ecf20Sopenharmony_ci					pinconf_to_config_param(configs[i]);
4938c2ecf20Sopenharmony_ci		u32 arg = pinconf_to_config_argument(configs[i]);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci		switch (param) {
4968c2ecf20Sopenharmony_ci		case PIN_CONFIG_BIAS_DISABLE:
4978c2ecf20Sopenharmony_ci		case PIN_CONFIG_BIAS_PULL_UP:
4988c2ecf20Sopenharmony_ci		case PIN_CONFIG_BIAS_PULL_DOWN:
4998c2ecf20Sopenharmony_ci		case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
5008c2ecf20Sopenharmony_ci			ret = uniphier_conf_pin_bias_set(pctldev, pin,
5018c2ecf20Sopenharmony_ci							 param, arg);
5028c2ecf20Sopenharmony_ci			break;
5038c2ecf20Sopenharmony_ci		case PIN_CONFIG_DRIVE_STRENGTH:
5048c2ecf20Sopenharmony_ci			ret = uniphier_conf_pin_drive_set(pctldev, pin, arg);
5058c2ecf20Sopenharmony_ci			break;
5068c2ecf20Sopenharmony_ci		case PIN_CONFIG_INPUT_ENABLE:
5078c2ecf20Sopenharmony_ci			ret = uniphier_conf_pin_input_enable(pctldev, pin, arg);
5088c2ecf20Sopenharmony_ci			break;
5098c2ecf20Sopenharmony_ci		default:
5108c2ecf20Sopenharmony_ci			dev_err(pctldev->dev,
5118c2ecf20Sopenharmony_ci				"unsupported configuration parameter %u\n",
5128c2ecf20Sopenharmony_ci				param);
5138c2ecf20Sopenharmony_ci			return -EINVAL;
5148c2ecf20Sopenharmony_ci		}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci		if (ret)
5178c2ecf20Sopenharmony_ci			return ret;
5188c2ecf20Sopenharmony_ci	}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	return 0;
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistatic int uniphier_conf_pin_config_group_set(struct pinctrl_dev *pctldev,
5248c2ecf20Sopenharmony_ci					      unsigned selector,
5258c2ecf20Sopenharmony_ci					      unsigned long *configs,
5268c2ecf20Sopenharmony_ci					      unsigned num_configs)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
5298c2ecf20Sopenharmony_ci	const unsigned *pins = priv->socdata->groups[selector].pins;
5308c2ecf20Sopenharmony_ci	unsigned num_pins = priv->socdata->groups[selector].num_pins;
5318c2ecf20Sopenharmony_ci	int i, ret;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	for (i = 0; i < num_pins; i++) {
5348c2ecf20Sopenharmony_ci		ret = uniphier_conf_pin_config_set(pctldev, pins[i],
5358c2ecf20Sopenharmony_ci						   configs, num_configs);
5368c2ecf20Sopenharmony_ci		if (ret)
5378c2ecf20Sopenharmony_ci			return ret;
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	return 0;
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_cistatic const struct pinconf_ops uniphier_confops = {
5448c2ecf20Sopenharmony_ci	.is_generic = true,
5458c2ecf20Sopenharmony_ci	.pin_config_get = uniphier_conf_pin_config_get,
5468c2ecf20Sopenharmony_ci	.pin_config_set = uniphier_conf_pin_config_set,
5478c2ecf20Sopenharmony_ci	.pin_config_group_set = uniphier_conf_pin_config_group_set,
5488c2ecf20Sopenharmony_ci};
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic int uniphier_pmx_get_functions_count(struct pinctrl_dev *pctldev)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	return priv->socdata->functions_count;
5558c2ecf20Sopenharmony_ci}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_cistatic const char *uniphier_pmx_get_function_name(struct pinctrl_dev *pctldev,
5588c2ecf20Sopenharmony_ci						  unsigned selector)
5598c2ecf20Sopenharmony_ci{
5608c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	return priv->socdata->functions[selector].name;
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_cistatic int uniphier_pmx_get_function_groups(struct pinctrl_dev *pctldev,
5668c2ecf20Sopenharmony_ci					    unsigned selector,
5678c2ecf20Sopenharmony_ci					    const char * const **groups,
5688c2ecf20Sopenharmony_ci					    unsigned *num_groups)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	*groups = priv->socdata->functions[selector].groups;
5738c2ecf20Sopenharmony_ci	*num_groups = priv->socdata->functions[selector].num_groups;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	return 0;
5768c2ecf20Sopenharmony_ci}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_cistatic int uniphier_pmx_set_one_mux(struct pinctrl_dev *pctldev, unsigned pin,
5798c2ecf20Sopenharmony_ci				    int muxval)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
5828c2ecf20Sopenharmony_ci	unsigned int mux_bits, reg_stride, reg, reg_end, shift, mask;
5838c2ecf20Sopenharmony_ci	bool load_pinctrl;
5848c2ecf20Sopenharmony_ci	int ret;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	/* some pins need input-enabling */
5878c2ecf20Sopenharmony_ci	ret = uniphier_conf_pin_input_enable(pctldev, pin, 1);
5888c2ecf20Sopenharmony_ci	if (ret)
5898c2ecf20Sopenharmony_ci		return ret;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	if (muxval < 0)
5928c2ecf20Sopenharmony_ci		return 0;	/* dedicated pin; nothing to do for pin-mux */
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if (priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_DBGMUX_SEPARATE) {
5958c2ecf20Sopenharmony_ci		/*
5968c2ecf20Sopenharmony_ci		 *  Mode     reg_offset     bit_position
5978c2ecf20Sopenharmony_ci		 *  Normal    4 * n        shift+3:shift
5988c2ecf20Sopenharmony_ci		 *  Debug     4 * n        shift+7:shift+4
5998c2ecf20Sopenharmony_ci		 */
6008c2ecf20Sopenharmony_ci		mux_bits = 4;
6018c2ecf20Sopenharmony_ci		reg_stride = 8;
6028c2ecf20Sopenharmony_ci		load_pinctrl = true;
6038c2ecf20Sopenharmony_ci	} else {
6048c2ecf20Sopenharmony_ci		/*
6058c2ecf20Sopenharmony_ci		 *  Mode     reg_offset     bit_position
6068c2ecf20Sopenharmony_ci		 *  Normal    8 * n        shift+3:shift
6078c2ecf20Sopenharmony_ci		 *  Debug     8 * n + 4    shift+3:shift
6088c2ecf20Sopenharmony_ci		 */
6098c2ecf20Sopenharmony_ci		mux_bits = 8;
6108c2ecf20Sopenharmony_ci		reg_stride = 4;
6118c2ecf20Sopenharmony_ci		load_pinctrl = false;
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	reg = UNIPHIER_PINCTRL_PINMUX_BASE + pin * mux_bits / 32 * reg_stride;
6158c2ecf20Sopenharmony_ci	reg_end = reg + reg_stride;
6168c2ecf20Sopenharmony_ci	shift = pin * mux_bits % 32;
6178c2ecf20Sopenharmony_ci	mask = (1U << mux_bits) - 1;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	/*
6208c2ecf20Sopenharmony_ci	 * If reg_stride is greater than 4, the MSB of each pinsel shall be
6218c2ecf20Sopenharmony_ci	 * stored in the offset+4.
6228c2ecf20Sopenharmony_ci	 */
6238c2ecf20Sopenharmony_ci	for (; reg < reg_end; reg += 4) {
6248c2ecf20Sopenharmony_ci		ret = regmap_update_bits(priv->regmap, reg,
6258c2ecf20Sopenharmony_ci					 mask << shift, muxval << shift);
6268c2ecf20Sopenharmony_ci		if (ret)
6278c2ecf20Sopenharmony_ci			return ret;
6288c2ecf20Sopenharmony_ci		muxval >>= mux_bits;
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	if (load_pinctrl) {
6328c2ecf20Sopenharmony_ci		ret = regmap_write(priv->regmap,
6338c2ecf20Sopenharmony_ci				   UNIPHIER_PINCTRL_LOAD_PINMUX, 1);
6348c2ecf20Sopenharmony_ci		if (ret)
6358c2ecf20Sopenharmony_ci			return ret;
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	return 0;
6398c2ecf20Sopenharmony_ci}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_cistatic int uniphier_pmx_set_mux(struct pinctrl_dev *pctldev,
6428c2ecf20Sopenharmony_ci				unsigned func_selector,
6438c2ecf20Sopenharmony_ci				unsigned group_selector)
6448c2ecf20Sopenharmony_ci{
6458c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
6468c2ecf20Sopenharmony_ci	const struct uniphier_pinctrl_group *grp =
6478c2ecf20Sopenharmony_ci					&priv->socdata->groups[group_selector];
6488c2ecf20Sopenharmony_ci	int i;
6498c2ecf20Sopenharmony_ci	int ret;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	for (i = 0; i < grp->num_pins; i++) {
6528c2ecf20Sopenharmony_ci		ret = uniphier_pmx_set_one_mux(pctldev, grp->pins[i],
6538c2ecf20Sopenharmony_ci					       grp->muxvals[i]);
6548c2ecf20Sopenharmony_ci		if (ret)
6558c2ecf20Sopenharmony_ci			return ret;
6568c2ecf20Sopenharmony_ci	}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	return 0;
6598c2ecf20Sopenharmony_ci}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_cistatic int uniphier_pmx_gpio_request_enable(struct pinctrl_dev *pctldev,
6628c2ecf20Sopenharmony_ci					    struct pinctrl_gpio_range *range,
6638c2ecf20Sopenharmony_ci					    unsigned offset)
6648c2ecf20Sopenharmony_ci{
6658c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
6668c2ecf20Sopenharmony_ci	unsigned int gpio_offset;
6678c2ecf20Sopenharmony_ci	int muxval, i;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	if (range->pins) {
6708c2ecf20Sopenharmony_ci		for (i = 0; i < range->npins; i++)
6718c2ecf20Sopenharmony_ci			if (range->pins[i] == offset)
6728c2ecf20Sopenharmony_ci				break;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci		if (WARN_ON(i == range->npins))
6758c2ecf20Sopenharmony_ci			return -EINVAL;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci		gpio_offset = i;
6788c2ecf20Sopenharmony_ci	} else {
6798c2ecf20Sopenharmony_ci		gpio_offset = offset - range->pin_base;
6808c2ecf20Sopenharmony_ci	}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	gpio_offset += range->id;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	muxval = priv->socdata->get_gpio_muxval(offset, gpio_offset);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	return uniphier_pmx_set_one_mux(pctldev, offset, muxval);
6878c2ecf20Sopenharmony_ci}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_cistatic const struct pinmux_ops uniphier_pmxops = {
6908c2ecf20Sopenharmony_ci	.get_functions_count = uniphier_pmx_get_functions_count,
6918c2ecf20Sopenharmony_ci	.get_function_name = uniphier_pmx_get_function_name,
6928c2ecf20Sopenharmony_ci	.get_function_groups = uniphier_pmx_get_function_groups,
6938c2ecf20Sopenharmony_ci	.set_mux = uniphier_pmx_set_mux,
6948c2ecf20Sopenharmony_ci	.gpio_request_enable = uniphier_pmx_gpio_request_enable,
6958c2ecf20Sopenharmony_ci	.strict = true,
6968c2ecf20Sopenharmony_ci};
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
6998c2ecf20Sopenharmony_cistatic int uniphier_pinctrl_suspend(struct device *dev)
7008c2ecf20Sopenharmony_ci{
7018c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv = dev_get_drvdata(dev);
7028c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_reg_region *r;
7038c2ecf20Sopenharmony_ci	int ret;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	list_for_each_entry(r, &priv->reg_regions, node) {
7068c2ecf20Sopenharmony_ci		ret = regmap_bulk_read(priv->regmap, r->base, r->vals,
7078c2ecf20Sopenharmony_ci				       r->nregs);
7088c2ecf20Sopenharmony_ci		if (ret)
7098c2ecf20Sopenharmony_ci			return ret;
7108c2ecf20Sopenharmony_ci	}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	return 0;
7138c2ecf20Sopenharmony_ci}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_cistatic int uniphier_pinctrl_resume(struct device *dev)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv = dev_get_drvdata(dev);
7188c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_reg_region *r;
7198c2ecf20Sopenharmony_ci	int ret;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	list_for_each_entry(r, &priv->reg_regions, node) {
7228c2ecf20Sopenharmony_ci		ret = regmap_bulk_write(priv->regmap, r->base, r->vals,
7238c2ecf20Sopenharmony_ci					r->nregs);
7248c2ecf20Sopenharmony_ci		if (ret)
7258c2ecf20Sopenharmony_ci			return ret;
7268c2ecf20Sopenharmony_ci	}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	if (priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_DBGMUX_SEPARATE) {
7298c2ecf20Sopenharmony_ci		ret = regmap_write(priv->regmap,
7308c2ecf20Sopenharmony_ci				   UNIPHIER_PINCTRL_LOAD_PINMUX, 1);
7318c2ecf20Sopenharmony_ci		if (ret)
7328c2ecf20Sopenharmony_ci			return ret;
7338c2ecf20Sopenharmony_ci	}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	return 0;
7368c2ecf20Sopenharmony_ci}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_cistatic int uniphier_pinctrl_add_reg_region(struct device *dev,
7398c2ecf20Sopenharmony_ci					   struct uniphier_pinctrl_priv *priv,
7408c2ecf20Sopenharmony_ci					   unsigned int base,
7418c2ecf20Sopenharmony_ci					   unsigned int count,
7428c2ecf20Sopenharmony_ci					   unsigned int width)
7438c2ecf20Sopenharmony_ci{
7448c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_reg_region *region;
7458c2ecf20Sopenharmony_ci	unsigned int nregs;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	if (!count)
7488c2ecf20Sopenharmony_ci		return 0;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	nregs = DIV_ROUND_UP(count * width, 32);
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	region = devm_kzalloc(dev, struct_size(region, vals, nregs),
7538c2ecf20Sopenharmony_ci			      GFP_KERNEL);
7548c2ecf20Sopenharmony_ci	if (!region)
7558c2ecf20Sopenharmony_ci		return -ENOMEM;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	region->base = base;
7588c2ecf20Sopenharmony_ci	region->nregs = nregs;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	list_add_tail(&region->node, &priv->reg_regions);
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	return 0;
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ci#endif
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_cistatic int uniphier_pinctrl_pm_init(struct device *dev,
7678c2ecf20Sopenharmony_ci				    struct uniphier_pinctrl_priv *priv)
7688c2ecf20Sopenharmony_ci{
7698c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
7708c2ecf20Sopenharmony_ci	const struct uniphier_pinctrl_socdata *socdata = priv->socdata;
7718c2ecf20Sopenharmony_ci	unsigned int num_drvctrl = 0;
7728c2ecf20Sopenharmony_ci	unsigned int num_drv2ctrl = 0;
7738c2ecf20Sopenharmony_ci	unsigned int num_drv3ctrl = 0;
7748c2ecf20Sopenharmony_ci	unsigned int num_pupdctrl = 0;
7758c2ecf20Sopenharmony_ci	unsigned int num_iectrl = 0;
7768c2ecf20Sopenharmony_ci	unsigned int iectrl, drvctrl, pupdctrl;
7778c2ecf20Sopenharmony_ci	enum uniphier_pin_drv_type drv_type;
7788c2ecf20Sopenharmony_ci	enum uniphier_pin_pull_dir pull_dir;
7798c2ecf20Sopenharmony_ci	int i, ret;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	for (i = 0; i < socdata->npins; i++) {
7828c2ecf20Sopenharmony_ci		void *drv_data = socdata->pins[i].drv_data;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci		drvctrl = uniphier_pin_get_drvctrl(drv_data);
7858c2ecf20Sopenharmony_ci		drv_type = uniphier_pin_get_drv_type(drv_data);
7868c2ecf20Sopenharmony_ci		pupdctrl = uniphier_pin_get_pupdctrl(drv_data);
7878c2ecf20Sopenharmony_ci		pull_dir = uniphier_pin_get_pull_dir(drv_data);
7888c2ecf20Sopenharmony_ci		iectrl = uniphier_pin_get_iectrl(drv_data);
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci		switch (drv_type) {
7918c2ecf20Sopenharmony_ci		case UNIPHIER_PIN_DRV_1BIT:
7928c2ecf20Sopenharmony_ci			num_drvctrl = max(num_drvctrl, drvctrl + 1);
7938c2ecf20Sopenharmony_ci			break;
7948c2ecf20Sopenharmony_ci		case UNIPHIER_PIN_DRV_2BIT:
7958c2ecf20Sopenharmony_ci			num_drv2ctrl = max(num_drv2ctrl, drvctrl + 1);
7968c2ecf20Sopenharmony_ci			break;
7978c2ecf20Sopenharmony_ci		case UNIPHIER_PIN_DRV_3BIT:
7988c2ecf20Sopenharmony_ci			num_drv3ctrl = max(num_drv3ctrl, drvctrl + 1);
7998c2ecf20Sopenharmony_ci			break;
8008c2ecf20Sopenharmony_ci		default:
8018c2ecf20Sopenharmony_ci			break;
8028c2ecf20Sopenharmony_ci		}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci		if (pull_dir == UNIPHIER_PIN_PULL_UP ||
8058c2ecf20Sopenharmony_ci		    pull_dir == UNIPHIER_PIN_PULL_DOWN)
8068c2ecf20Sopenharmony_ci			num_pupdctrl = max(num_pupdctrl, pupdctrl + 1);
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci		if (iectrl != UNIPHIER_PIN_IECTRL_NONE) {
8098c2ecf20Sopenharmony_ci			if (socdata->caps & UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL)
8108c2ecf20Sopenharmony_ci				iectrl = i;
8118c2ecf20Sopenharmony_ci			num_iectrl = max(num_iectrl, iectrl + 1);
8128c2ecf20Sopenharmony_ci		}
8138c2ecf20Sopenharmony_ci	}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&priv->reg_regions);
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	ret = uniphier_pinctrl_add_reg_region(dev, priv,
8188c2ecf20Sopenharmony_ci					      UNIPHIER_PINCTRL_PINMUX_BASE,
8198c2ecf20Sopenharmony_ci					      socdata->npins, 8);
8208c2ecf20Sopenharmony_ci	if (ret)
8218c2ecf20Sopenharmony_ci		return ret;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	ret = uniphier_pinctrl_add_reg_region(dev, priv,
8248c2ecf20Sopenharmony_ci					      UNIPHIER_PINCTRL_DRVCTRL_BASE,
8258c2ecf20Sopenharmony_ci					      num_drvctrl, 1);
8268c2ecf20Sopenharmony_ci	if (ret)
8278c2ecf20Sopenharmony_ci		return ret;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	ret = uniphier_pinctrl_add_reg_region(dev, priv,
8308c2ecf20Sopenharmony_ci					      UNIPHIER_PINCTRL_DRV2CTRL_BASE,
8318c2ecf20Sopenharmony_ci					      num_drv2ctrl, 2);
8328c2ecf20Sopenharmony_ci	if (ret)
8338c2ecf20Sopenharmony_ci		return ret;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	ret = uniphier_pinctrl_add_reg_region(dev, priv,
8368c2ecf20Sopenharmony_ci					      UNIPHIER_PINCTRL_DRV3CTRL_BASE,
8378c2ecf20Sopenharmony_ci					      num_drv3ctrl, 3);
8388c2ecf20Sopenharmony_ci	if (ret)
8398c2ecf20Sopenharmony_ci		return ret;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	ret = uniphier_pinctrl_add_reg_region(dev, priv,
8428c2ecf20Sopenharmony_ci					      UNIPHIER_PINCTRL_PUPDCTRL_BASE,
8438c2ecf20Sopenharmony_ci					      num_pupdctrl, 1);
8448c2ecf20Sopenharmony_ci	if (ret)
8458c2ecf20Sopenharmony_ci		return ret;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	ret = uniphier_pinctrl_add_reg_region(dev, priv,
8488c2ecf20Sopenharmony_ci					      UNIPHIER_PINCTRL_IECTRL_BASE,
8498c2ecf20Sopenharmony_ci					      num_iectrl, 1);
8508c2ecf20Sopenharmony_ci	if (ret)
8518c2ecf20Sopenharmony_ci		return ret;
8528c2ecf20Sopenharmony_ci#endif
8538c2ecf20Sopenharmony_ci	return 0;
8548c2ecf20Sopenharmony_ci}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ciconst struct dev_pm_ops uniphier_pinctrl_pm_ops = {
8578c2ecf20Sopenharmony_ci	SET_LATE_SYSTEM_SLEEP_PM_OPS(uniphier_pinctrl_suspend,
8588c2ecf20Sopenharmony_ci				     uniphier_pinctrl_resume)
8598c2ecf20Sopenharmony_ci};
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ciint uniphier_pinctrl_probe(struct platform_device *pdev,
8628c2ecf20Sopenharmony_ci			   const struct uniphier_pinctrl_socdata *socdata)
8638c2ecf20Sopenharmony_ci{
8648c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
8658c2ecf20Sopenharmony_ci	struct uniphier_pinctrl_priv *priv;
8668c2ecf20Sopenharmony_ci	struct device_node *parent;
8678c2ecf20Sopenharmony_ci	int ret;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	if (!socdata ||
8708c2ecf20Sopenharmony_ci	    !socdata->pins || !socdata->npins ||
8718c2ecf20Sopenharmony_ci	    !socdata->groups || !socdata->groups_count ||
8728c2ecf20Sopenharmony_ci	    !socdata->functions || !socdata->functions_count) {
8738c2ecf20Sopenharmony_ci		dev_err(dev, "pinctrl socdata lacks necessary members\n");
8748c2ecf20Sopenharmony_ci		return -EINVAL;
8758c2ecf20Sopenharmony_ci	}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
8788c2ecf20Sopenharmony_ci	if (!priv)
8798c2ecf20Sopenharmony_ci		return -ENOMEM;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	parent = of_get_parent(dev->of_node);
8828c2ecf20Sopenharmony_ci	priv->regmap = syscon_node_to_regmap(parent);
8838c2ecf20Sopenharmony_ci	of_node_put(parent);
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	if (IS_ERR(priv->regmap)) {
8868c2ecf20Sopenharmony_ci		dev_err(dev, "failed to get regmap\n");
8878c2ecf20Sopenharmony_ci		return PTR_ERR(priv->regmap);
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	priv->socdata = socdata;
8918c2ecf20Sopenharmony_ci	priv->pctldesc.name = dev->driver->name;
8928c2ecf20Sopenharmony_ci	priv->pctldesc.pins = socdata->pins;
8938c2ecf20Sopenharmony_ci	priv->pctldesc.npins = socdata->npins;
8948c2ecf20Sopenharmony_ci	priv->pctldesc.pctlops = &uniphier_pctlops;
8958c2ecf20Sopenharmony_ci	priv->pctldesc.pmxops = &uniphier_pmxops;
8968c2ecf20Sopenharmony_ci	priv->pctldesc.confops = &uniphier_confops;
8978c2ecf20Sopenharmony_ci	priv->pctldesc.owner = dev->driver->owner;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	ret = uniphier_pinctrl_pm_init(dev, priv);
9008c2ecf20Sopenharmony_ci	if (ret)
9018c2ecf20Sopenharmony_ci		return ret;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	priv->pctldev = devm_pinctrl_register(dev, &priv->pctldesc, priv);
9048c2ecf20Sopenharmony_ci	if (IS_ERR(priv->pctldev)) {
9058c2ecf20Sopenharmony_ci		dev_err(dev, "failed to register UniPhier pinctrl driver\n");
9068c2ecf20Sopenharmony_ci		return PTR_ERR(priv->pctldev);
9078c2ecf20Sopenharmony_ci	}
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, priv);
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	return 0;
9128c2ecf20Sopenharmony_ci}
913