sh: pfc: pin config get/set support.

This implements simple support for adjusting the pin config value via the
pinctrl API. The pinconf-generic code is abandoned for now until we've
got a chance to revamp the pinmux_type state tracking that's needed by
legacy code.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
This commit is contained in:
Paul Mundt 2012-07-20 16:39:09 +09:00
parent 5440711073
commit fdd85ec3eb
2 changed files with 97 additions and 51 deletions

View File

@ -17,7 +17,6 @@ config PINCTRL_SH_PFC
select PINCTRL select PINCTRL
select PINMUX select PINMUX
select PINCONF select PINCONF
select GENERIC_PINCONF
config GPIO_SH_PFC config GPIO_SH_PFC
tristate "SuperH PFC GPIO support" tristate "SuperH PFC GPIO support"

View File

@ -65,10 +65,17 @@ static int sh_pfc_get_group_pins(struct pinctrl_dev *pctldev, unsigned group,
return 0; return 0;
} }
static void sh_pfc_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,
unsigned offset)
{
seq_printf(s, "%s", DRV_NAME);
}
static struct pinctrl_ops sh_pfc_pinctrl_ops = { static struct pinctrl_ops sh_pfc_pinctrl_ops = {
.get_groups_count = sh_pfc_get_groups_count, .get_groups_count = sh_pfc_get_groups_count,
.get_group_name = sh_pfc_get_group_name, .get_group_name = sh_pfc_get_group_name,
.get_group_pins = sh_pfc_get_group_pins, .get_group_pins = sh_pfc_get_group_pins,
.pin_dbg_show = sh_pfc_pin_dbg_show,
}; };
static int sh_pfc_get_functions_count(struct pinctrl_dev *pctldev) static int sh_pfc_get_functions_count(struct pinctrl_dev *pctldev)
@ -124,6 +131,59 @@ static inline int sh_pfc_config_function(struct sh_pfc *pfc, unsigned offset)
return 0; return 0;
} }
static int sh_pfc_reconfig_pin(struct sh_pfc *pfc, unsigned offset,
int new_type)
{
unsigned long flags;
int pinmux_type;
int ret = -EINVAL;
spin_lock_irqsave(&pfc->lock, flags);
pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE;
/*
* See if the present config needs to first be de-configured.
*/
switch (pinmux_type) {
case PINMUX_TYPE_GPIO:
break;
case PINMUX_TYPE_OUTPUT:
case PINMUX_TYPE_INPUT:
case PINMUX_TYPE_INPUT_PULLUP:
case PINMUX_TYPE_INPUT_PULLDOWN:
sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE);
break;
default:
goto err;
}
/*
* Dry run
*/
if (sh_pfc_config_gpio(pfc, offset, new_type,
GPIO_CFG_DRYRUN) != 0)
goto err;
/*
* Request
*/
if (sh_pfc_config_gpio(pfc, offset, new_type,
GPIO_CFG_REQ) != 0)
goto err;
pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE;
pfc->gpios[offset].flags |= new_type;
ret = 0;
err:
spin_unlock_irqrestore(&pfc->lock, flags);
return ret;
}
static int sh_pfc_gpio_request_enable(struct pinctrl_dev *pctldev, static int sh_pfc_gpio_request_enable(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range, struct pinctrl_gpio_range *range,
unsigned offset) unsigned offset)
@ -185,49 +245,9 @@ static int sh_pfc_gpio_set_direction(struct pinctrl_dev *pctldev,
unsigned offset, bool input) unsigned offset, bool input)
{ {
struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
struct sh_pfc *pfc = pmx->pfc; int type = input ? PINMUX_TYPE_INPUT : PINMUX_TYPE_OUTPUT;
unsigned long flags;
int pinmux_type, new_pinmux_type;
int ret = -EINVAL;
new_pinmux_type = input ? PINMUX_TYPE_INPUT : PINMUX_TYPE_OUTPUT; return sh_pfc_reconfig_pin(pmx->pfc, offset, type);
spin_lock_irqsave(&pfc->lock, flags);
pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE;
switch (pinmux_type) {
case PINMUX_TYPE_GPIO:
break;
case PINMUX_TYPE_OUTPUT:
case PINMUX_TYPE_INPUT:
case PINMUX_TYPE_INPUT_PULLUP:
case PINMUX_TYPE_INPUT_PULLDOWN:
sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE);
break;
default:
goto err;
}
if (sh_pfc_config_gpio(pfc, offset,
new_pinmux_type,
GPIO_CFG_DRYRUN) != 0)
goto err;
if (sh_pfc_config_gpio(pfc, offset,
new_pinmux_type,
GPIO_CFG_REQ) != 0)
BUG();
pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE;
pfc->gpios[offset].flags |= new_pinmux_type;
ret = 0;
err:
spin_unlock_irqrestore(&pfc->lock, flags);
return ret;
} }
static struct pinmux_ops sh_pfc_pinmux_ops = { static struct pinmux_ops sh_pfc_pinmux_ops = {
@ -244,26 +264,53 @@ static struct pinmux_ops sh_pfc_pinmux_ops = {
static int sh_pfc_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, static int sh_pfc_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin,
unsigned long *config) unsigned long *config)
{ {
enum pin_config_param param = (enum pin_config_param)(*config); struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
struct sh_pfc *pfc = pmx->pfc;
switch (param) { *config = pfc->gpios[pin].flags & PINMUX_FLAG_TYPE;
default:
break;
}
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) unsigned long config)
{ {
return -EINVAL; struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
struct sh_pfc *pfc = pmx->pfc;
/* Validate the new type */
if (config >= PINMUX_FLAG_TYPE)
return -EINVAL;
return sh_pfc_reconfig_pin(pmx->pfc, pin, config);
}
static void sh_pfc_pinconf_dbg_show(struct pinctrl_dev *pctldev,
struct seq_file *s, unsigned pin)
{
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;
rc = sh_pfc_pinconf_get(pctldev, pin, &config);
if (unlikely(rc != 0))
return;
seq_printf(s, " %s", pinmux_type_str[config]);
} }
static struct pinconf_ops sh_pfc_pinconf_ops = { static struct pinconf_ops sh_pfc_pinconf_ops = {
.is_generic = true,
.pin_config_get = sh_pfc_pinconf_get, .pin_config_get = sh_pfc_pinconf_get,
.pin_config_set = sh_pfc_pinconf_set, .pin_config_set = sh_pfc_pinconf_set,
.pin_config_dbg_show = sh_pfc_pinconf_dbg_show,
}; };
static struct pinctrl_gpio_range sh_pfc_gpio_range = { static struct pinctrl_gpio_range sh_pfc_gpio_range = {