// SPDX-License-Identifier: GPL-2.0+
//
// Copyright (C) 2015-2017 Socionext Inc.
// Author: Masahiro Yamada <yamada.masahiro@socionext.com>
#include <linux/list.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include "../core.h"
#include "../pinctrl-utils.h"
#include "pinctrl-uniphier.h"
#define UNIPHIER_PINCTRL_PINMUX_BASE 0x1000
#define UNIPHIER_PINCTRL_LOAD_PINMUX 0x1700
#define UNIPHIER_PINCTRL_DRVCTRL_BASE 0x1800
#define UNIPHIER_PINCTRL_DRV2CTRL_BASE 0x1900
#define UNIPHIER_PINCTRL_DRV3CTRL_BASE 0x1980
#define UNIPHIER_PINCTRL_PUPDCTRL_BASE 0x1a00
#define UNIPHIER_PINCTRL_IECTRL_BASE 0x1d00
struct uniphier_pinctrl_reg_region {
struct list_head node;
unsigned int base;
unsigned int nregs;
u32 vals[0];
};
struct uniphier_pinctrl_priv {
struct pinctrl_desc pctldesc;
struct pinctrl_dev *pctldev;
struct regmap *regmap;
const struct uniphier_pinctrl_socdata *socdata;
struct list_head reg_regions;
};
static int uniphier_pctl_get_groups_count(struct pinctrl_dev *pctldev)
{
struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
return priv->socdata->groups_count;
}
static const char *uniphier_pctl_get_group_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
return priv->socdata->groups[selector].name;
}
static int uniphier_pctl_get_group_pins(struct pinctrl_dev *pctldev,
unsigned selector,
const unsigned **pins,
unsigned *num_pins)
{
struct uniphier_pinctrl_priv *priv = pinctrl_dev_get_drvdata(pctldev);
*pins = priv->socdata->groups[selector].pins;
*num_pins = priv->socdata->groups[selector].num_pins;
return 0;
}
#ifdef CONFIG_DEBUG_FS
static void uniphier_pctl_pin_dbg_show(struct pinctrl_dev *pctldev,
struct seq_file *s, unsigned offset)
{
const struct pin_desc *desc = pin_desc_get(pctldev, offset);
const char *pull_dir, *drv_type;
switch (uniphier_pin_get_pull_dir(desc->drv_data)) {
case UNIPHIER_PIN_PULL_UP:
pull_dir = "UP";
break;
case UNIPHIER_PIN_PULL_DOWN:
pull_dir = "DOWN";
break;
case UNIPHIER_PIN_PULL_UP_FIXED:
pull_dir = "UP(FIXED)";
break;
case UNIPHIER_PIN_PULL_DOWN_FIXED:
pull_dir = "DOWN(FIXED)";
break;
case UNIPHIER_PIN_PULL_NONE:
pull_dir = "NONE";
break;
default:
BUG();
}
switch (uniphier_pin_get_drv_type(desc->drv_data)) {
case UNIPHIER_PIN_DRV_1BIT:
drv_type = "4/8(mA)";
break;
case UNIPHIER_PIN_DRV_2BIT:
drv_type = "8/12/16/20(mA)";
break;
case UNIPHIER_PIN_DRV_3BIT:
drv_type = "4/5/7/9/11/12/14/16(mA)";
break;
case UNIPHIER_PIN_DRV_FIXED4:
drv_type = "4(mA)";
break;
case UNIPHIER_PIN_DRV_FIXED5:
drv_type = "5(mA)";
break;
case UNIPHIER_PIN_DRV_FIXED8:
drv_type = "8(mA)";
break;
case UNIPHIER_PIN_DRV_NONE:
drv_type = "NONE";
break;
default:
BUG();
}
seq_printf(s, " PULL_DIR=%s DRV_TYPE=%s", pull_dir, drv_type);
}
#endif
static const struct pinctrl_ops uniphier_pctlops = {
.get_groups_count = uniphier_pctl_get_groups_count,
.get_group_name = uniphier_pctl_get_group_name,
.get_group_pins = uniphier_pctl_get_group_pins,
#ifdef CONFIG_DEBUG_FS
.pin_dbg_show = uniphier_pctl_pin_dbg_show,
#endif
.dt_node_to_map = pinconf_generic_dt_node_to_map_all,
.dt_free_map = pinctrl_utils_free_map,
};
static const unsigned int uniphier_conf_drv_strengths_1bit[] = {4, 8};
static const unsigned int uniphier_conf_drv_strengths_2bit[] = {8, 12, 16, 20};
static const unsigned int uniphier_conf_drv_strengths_3bit[] = {4, 5, 7, 9, 11,
12, 14, 16};
static const unsigned int uniphier_conf_drv_strengths_fixed4[] = {4};
static const unsigned int uniphier_conf_drv_strengths_fixed5[] = {5};
static const unsigned int uniphier_conf_drv_strengths_fixed8[] = {8};
static int uniphier_conf_get_drvctrl_data(struct pinctrl_dev *pctldev,
unsigned int pin, unsigned int *reg,
unsigned int *shift,
unsigned int *mask,
const unsigned int **strengths)
{
const struct pin_desc *desc = pin_desc_get(pctldev, pin);
enum uniphier_pin_drv_type type =
uniphier_pin_get_drv_type(desc->drv_data);
unsigned int base = 0;
unsigned int stride = 0;
unsigned int width =