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

Commit 3caa7d8c authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Geert Uytterhoeven
Browse files

pinctrl: sh-pfc: Add drive strength support



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

Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
parent 93d2185d
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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.


+15 −0
Original line number Diff line number Diff line
@@ -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,
+3 −0
Original line number Diff line number Diff line
@@ -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);
+111 −0
Original line number Diff line number Diff line
@@ -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]);
+17 −0
Original line number Diff line number Diff line
@@ -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;