Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c58d9c1b authored by Laurent Pinchart's avatar Laurent Pinchart
Browse files

sh-pfc: Implement generic pinconf support



The existing PFC pinconf implementation, tied to the PFC-specific pin
types, isn't used by drivers or boards. Replace it with the generic
pinconf types to implement bias (pull-up/down) setup. Other pin
configuration options can be implemented later if needed.

Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Acked-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent b705c054
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ config PINCTRL_SH_PFC
	select GPIO_SH_PFC if ARCH_REQUIRE_GPIOLIB
	select PINMUX
	select PINCONF
	select GENERIC_PINCONF
	def_bool y
	help
	  This enables pin control drivers for SH and SH Mobile platforms
+93 −30
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@
#include <linux/spinlock.h>

#include "core.h"
#include "../core.h"
#include "../pinconf.h"

struct sh_pfc_pin_config {
	u32 type;
@@ -230,57 +232,118 @@ static const struct pinmux_ops sh_pfc_pinmux_ops = {
	.gpio_set_direction	= sh_pfc_gpio_set_direction,
};

/* Check whether the requested parameter is supported for a pin. */
static bool sh_pfc_pinconf_validate(struct sh_pfc *pfc, unsigned int _pin,
				    enum pin_config_param param)
{
	int idx = sh_pfc_get_pin_index(pfc, _pin);
	const struct sh_pfc_pin *pin = &pfc->info->pins[idx];

	switch (param) {
	case PIN_CONFIG_BIAS_DISABLE:
		return true;

	case PIN_CONFIG_BIAS_PULL_UP:
		return pin->configs & SH_PFC_PIN_CFG_PULL_UP;

	case PIN_CONFIG_BIAS_PULL_DOWN:
		return pin->configs & SH_PFC_PIN_CFG_PULL_DOWN;

	default:
		return false;
	}
}

static int sh_pfc_pinconf_get(struct pinctrl_dev *pctldev, unsigned _pin,
			      unsigned long *config)
{
	struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
	struct sh_pfc *pfc = pmx->pfc;
	int idx = sh_pfc_get_pin_index(pfc, _pin);
	struct sh_pfc_pin_config *cfg = &pmx->configs[idx];
	enum pin_config_param param = pinconf_to_config_param(*config);
	unsigned long flags;
	unsigned int bias;

	if (!sh_pfc_pinconf_validate(pfc, _pin, param))
		return -ENOTSUPP;

	switch (param) {
	case PIN_CONFIG_BIAS_DISABLE:
	case PIN_CONFIG_BIAS_PULL_UP:
	case PIN_CONFIG_BIAS_PULL_DOWN:
		if (!pfc->info->ops || !pfc->info->ops->get_bias)
			return -ENOTSUPP;

		spin_lock_irqsave(&pfc->lock, flags);
		bias = pfc->info->ops->get_bias(pfc, _pin);
		spin_unlock_irqrestore(&pfc->lock, flags);

		if (bias != param)
			return -EINVAL;

		*config = 0;
		break;

	*config = cfg->type;
	default:
		return -ENOTSUPP;
	}

	return 0;
}

static int sh_pfc_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin,
static int sh_pfc_pinconf_set(struct pinctrl_dev *pctldev, unsigned _pin,
			      unsigned long config)
{
	struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
	struct sh_pfc *pfc = pmx->pfc;
	enum pin_config_param param = pinconf_to_config_param(config);
	unsigned long flags;

	/* Validate the new type */
	if (config >= PINMUX_FLAG_TYPE)
		return -EINVAL;
	if (!sh_pfc_pinconf_validate(pfc, _pin, param))
		return -ENOTSUPP;

	switch (param) {
	case PIN_CONFIG_BIAS_PULL_UP:
	case PIN_CONFIG_BIAS_PULL_DOWN:
	case PIN_CONFIG_BIAS_DISABLE:
		if (!pfc->info->ops || !pfc->info->ops->set_bias)
			return -ENOTSUPP;

		spin_lock_irqsave(&pfc->lock, flags);
		pfc->info->ops->set_bias(pfc, _pin, param);
		spin_unlock_irqrestore(&pfc->lock, flags);

		break;

	default:
		return -ENOTSUPP;
	}

	return sh_pfc_reconfig_pin(pmx, pin, config);
	return 0;
}

static void sh_pfc_pinconf_dbg_show(struct pinctrl_dev *pctldev,
				    struct seq_file *s, unsigned pin)
static int sh_pfc_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned group,
				    unsigned long config)
{
	const char *pinmux_type_str[] = {
		[PINMUX_TYPE_NONE]		= "none",
		[PINMUX_TYPE_FUNCTION]		= "function",
		[PINMUX_TYPE_GPIO]		= "gpio",
		[PINMUX_TYPE_OUTPUT]		= "output",
		[PINMUX_TYPE_INPUT]		= "input",
		[PINMUX_TYPE_INPUT_PULLUP]	= "input bias pull up",
		[PINMUX_TYPE_INPUT_PULLDOWN]	= "input bias pull down",
	};
	unsigned long config;
	int rc;
	struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
	const unsigned int *pins;
	unsigned int num_pins;
	unsigned int i;

	rc = sh_pfc_pinconf_get(pctldev, pin, &config);
	if (unlikely(rc != 0))
		return;
	pins = pmx->pfc->info->groups[group].pins;
	num_pins = pmx->pfc->info->groups[group].nr_pins;

	seq_printf(s, " %s", pinmux_type_str[config]);
	for (i = 0; i < num_pins; ++i)
		sh_pfc_pinconf_set(pctldev, pins[i], config);

	return 0;
}

static const struct pinconf_ops sh_pfc_pinconf_ops = {
	.is_generic			= true,
	.pin_config_get			= sh_pfc_pinconf_get,
	.pin_config_set			= sh_pfc_pinconf_set,
	.pin_config_dbg_show	= sh_pfc_pinconf_dbg_show,
	.pin_config_group_set		= sh_pfc_pinconf_group_set,
	.pin_config_config_dbg_show	= pinconf_generic_dump_config,
};

/* PFC ranges -> pinctrl pin descs */
+16 −0
Original line number Diff line number Diff line
@@ -31,9 +31,15 @@ enum {
	PINMUX_FLAG_TYPE,	/* must be last */
};

#define SH_PFC_PIN_CFG_INPUT		(1 << 0)
#define SH_PFC_PIN_CFG_OUTPUT		(1 << 1)
#define SH_PFC_PIN_CFG_PULL_UP		(1 << 2)
#define SH_PFC_PIN_CFG_PULL_DOWN	(1 << 3)

struct sh_pfc_pin {
	const pinmux_enum_t enum_id;
	const char *name;
	unsigned int configs;
};

#define SH_PFC_PIN_GROUP(n)				\
@@ -120,8 +126,18 @@ struct pinmux_range {
	pinmux_enum_t force;
};

struct sh_pfc;

struct sh_pfc_soc_operations {
	unsigned int (*get_bias)(struct sh_pfc *pfc, unsigned int pin);
	void (*set_bias)(struct sh_pfc *pfc, unsigned int pin,
			 unsigned int bias);
};

struct sh_pfc_soc_info {
	const char *name;
	const struct sh_pfc_soc_operations *ops;

	struct pinmux_range input;
	struct pinmux_range input_pd;
	struct pinmux_range input_pu;