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:
parent
93d2185dca
commit
3caa7d8c3f
|
@ -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.
|
||||
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 = ®->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]);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue