pinctrl: sh-pfc: Add drive strength support

Add support for the drive-strengh pin configuration using the generic
pinconf DT bindings.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
This commit is contained in:
Laurent Pinchart 2016-03-23 16:06:00 +02:00 committed by Geert Uytterhoeven
parent 93d2185dca
commit 3caa7d8c3f
5 changed files with 148 additions and 2 deletions

View File

@ -72,8 +72,8 @@ Pin Configuration Node Properties:
The pin configuration parameters use the generic pinconf bindings defined in
pinctrl-bindings.txt in this directory. The supported parameters are
bias-disable, bias-pull-up, bias-pull-down and power-source. For pins that
have a configurable I/O voltage, the power-source value should be the
bias-disable, bias-pull-up, bias-pull-down, drive strength and power-source. For
pins that have a configurable I/O voltage, the power-source value should be the
nominal I/O voltage in millivolts.

View File

@ -175,6 +175,21 @@ void sh_pfc_write_raw_reg(void __iomem *mapped_reg, unsigned int reg_width,
BUG();
}
u32 sh_pfc_read_reg(struct sh_pfc *pfc, u32 reg, unsigned int width)
{
return sh_pfc_read_raw_reg(sh_pfc_phys_to_virt(pfc, reg), width);
}
void sh_pfc_write_reg(struct sh_pfc *pfc, u32 reg, unsigned int width, u32 data)
{
if (pfc->info->unlock_reg)
sh_pfc_write_raw_reg(
sh_pfc_phys_to_virt(pfc, pfc->info->unlock_reg), 32,
~data);
sh_pfc_write_raw_reg(sh_pfc_phys_to_virt(pfc, reg), width, data);
}
static void sh_pfc_config_reg_helper(struct sh_pfc *pfc,
const struct pinmux_cfg_reg *crp,
unsigned int in_pos,

View File

@ -62,6 +62,9 @@ int sh_pfc_unregister_pinctrl(struct sh_pfc *pfc);
u32 sh_pfc_read_raw_reg(void __iomem *mapped_reg, unsigned int reg_width);
void sh_pfc_write_raw_reg(void __iomem *mapped_reg, unsigned int reg_width,
u32 data);
u32 sh_pfc_read_reg(struct sh_pfc *pfc, u32 reg, unsigned int width);
void sh_pfc_write_reg(struct sh_pfc *pfc, u32 reg, unsigned int width,
u32 data);
int sh_pfc_get_pin_index(struct sh_pfc *pfc, unsigned int pin);
int sh_pfc_config_mux(struct sh_pfc *pfc, unsigned mark, int pinmux_type);

View File

@ -476,6 +476,91 @@ static const struct pinmux_ops sh_pfc_pinmux_ops = {
.gpio_set_direction = sh_pfc_gpio_set_direction,
};
static u32 sh_pfc_pinconf_find_drive_strength_reg(struct sh_pfc *pfc,
unsigned int pin, unsigned int *offset, unsigned int *size)
{
const struct pinmux_drive_reg_field *field;
const struct pinmux_drive_reg *reg;
unsigned int i;
for (reg = pfc->info->drive_regs; reg->reg; ++reg) {
for (i = 0; i < ARRAY_SIZE(reg->fields); ++i) {
field = &reg->fields[i];
if (field->size && field->pin == pin) {
*offset = field->offset;
*size = field->size;
return reg->reg;
}
}
}
return 0;
}
static int sh_pfc_pinconf_get_drive_strength(struct sh_pfc *pfc,
unsigned int pin)
{
unsigned long flags;
unsigned int offset;
unsigned int size;
u32 reg;
u32 val;
reg = sh_pfc_pinconf_find_drive_strength_reg(pfc, pin, &offset, &size);
if (!reg)
return -EINVAL;
spin_lock_irqsave(&pfc->lock, flags);
val = sh_pfc_read_reg(pfc, reg, 32);
spin_unlock_irqrestore(&pfc->lock, flags);
val = (val >> offset) & GENMASK(size - 1, 0);
/* Convert the value to mA based on a full drive strength value of 24mA.
* We can make the full value configurable later if needed.
*/
return (val + 1) * (size == 2 ? 6 : 3);
}
static int sh_pfc_pinconf_set_drive_strength(struct sh_pfc *pfc,
unsigned int pin, u16 strength)
{
unsigned long flags;
unsigned int offset;
unsigned int size;
unsigned int step;
u32 reg;
u32 val;
reg = sh_pfc_pinconf_find_drive_strength_reg(pfc, pin, &offset, &size);
if (!reg)
return -EINVAL;
step = size == 2 ? 6 : 3;
if (strength < step || strength > 24)
return -EINVAL;
/* Convert the value from mA based on a full drive strength value of
* 24mA. We can make the full value configurable later if needed.
*/
strength = strength / step - 1;
spin_lock_irqsave(&pfc->lock, flags);
val = sh_pfc_read_reg(pfc, reg, 32);
val &= ~GENMASK(offset + size - 1, offset);
val |= strength << offset;
sh_pfc_write_reg(pfc, reg, 32, val);
spin_unlock_irqrestore(&pfc->lock, flags);
return 0;
}
/* 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)
@ -493,6 +578,9 @@ static bool sh_pfc_pinconf_validate(struct sh_pfc *pfc, unsigned int _pin,
case PIN_CONFIG_BIAS_PULL_DOWN:
return pin->configs & SH_PFC_PIN_CFG_PULL_DOWN;
case PIN_CONFIG_DRIVE_STRENGTH:
return pin->configs & SH_PFC_PIN_CFG_DRIVE_STRENGTH;
case PIN_CONFIG_POWER_SOURCE:
return pin->configs & SH_PFC_PIN_CFG_IO_VOLTAGE;
@ -532,6 +620,17 @@ static int sh_pfc_pinconf_get(struct pinctrl_dev *pctldev, unsigned _pin,
break;
}
case PIN_CONFIG_DRIVE_STRENGTH: {
int ret;
ret = sh_pfc_pinconf_get_drive_strength(pfc, _pin);
if (ret < 0)
return ret;
*config = ret;
break;
}
case PIN_CONFIG_POWER_SOURCE: {
int ret;
@ -584,6 +683,18 @@ static int sh_pfc_pinconf_set(struct pinctrl_dev *pctldev, unsigned _pin,
break;
case PIN_CONFIG_DRIVE_STRENGTH: {
unsigned int arg =
pinconf_to_config_argument(configs[i]);
int ret;
ret = sh_pfc_pinconf_set_drive_strength(pfc, _pin, arg);
if (ret < 0)
return ret;
break;
}
case PIN_CONFIG_POWER_SOURCE: {
unsigned int arg =
pinconf_to_config_argument(configs[i]);

View File

@ -28,6 +28,7 @@ enum {
#define SH_PFC_PIN_CFG_PULL_UP (1 << 2)
#define SH_PFC_PIN_CFG_PULL_DOWN (1 << 3)
#define SH_PFC_PIN_CFG_IO_VOLTAGE (1 << 4)
#define SH_PFC_PIN_CFG_DRIVE_STRENGTH (1 << 5)
#define SH_PFC_PIN_CFG_NO_GPIO (1 << 31)
struct sh_pfc_pin {
@ -131,6 +132,21 @@ struct pinmux_cfg_reg {
{ var_fw0, var_fwn, 0 }, \
.enum_ids = (const u16 [])
struct pinmux_drive_reg_field {
u16 pin;
u8 offset;
u8 size;
};
struct pinmux_drive_reg {
u32 reg;
const struct pinmux_drive_reg_field fields[8];
};
#define PINMUX_DRIVE_REG(name, r) \
.reg = r, \
.fields =
struct pinmux_data_reg {
u32 reg;
u8 reg_width;
@ -199,6 +215,7 @@ struct sh_pfc_soc_info {
#endif
const struct pinmux_cfg_reg *cfg_regs;
const struct pinmux_drive_reg *drive_regs;
const struct pinmux_data_reg *data_regs;
const u16 *pinmux_data;