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: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
Laurent Pinchart 2013-03-10 16:44:02 +01:00
parent b705c05425
commit c58d9c1b26
3 changed files with 108 additions and 28 deletions

View file

@ -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

View file

@ -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;
*config = cfg->type;
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;
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;
return sh_pfc_reconfig_pin(pmx, pin, config);
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 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 = {
.pin_config_get = sh_pfc_pinconf_get,
.pin_config_set = sh_pfc_pinconf_set,
.pin_config_dbg_show = sh_pfc_pinconf_dbg_show,
.is_generic = true,
.pin_config_get = sh_pfc_pinconf_get,
.pin_config_set = sh_pfc_pinconf_set,
.pin_config_group_set = sh_pfc_pinconf_group_set,
.pin_config_config_dbg_show = pinconf_generic_dump_config,
};
/* PFC ranges -> pinctrl pin descs */

View file

@ -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;