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

Commit 18c28caa authored by Tomasz Figa's avatar Tomasz Figa Committed by Linus Walleij
Browse files

pinctrl: samsung: Decouple direction setting from pinctrl



This patch makes the pinctrl-samsung driver configure GPIO direction on
its own, without using the pinctrl_gpio_direction_*() "helpers". The
rationale behind this change is as follows:
 - pinctrl-samsung does not need translation from GPIO namespace to
   pinctrl namespace to handle GPIO operations - GPIO chip and offset
   therein are enough to calculate necessary offsets and bit masks in
   constant time,
 - the pinctrl_gpio_direction_*() functions do not do anything useful
   other than translating the pin into pinctrl namespace and calling the
   .gpio_set_direction() from pinmux_ops of the controller,
 - the undesirable side effect of using those helpers is losing the
   ability to change GPIO direction in atomic context, because they
   explicitly use a mutex for synchronization,

Results of this patch are:
 - fixed warnings about scheduling while atomic in code that needs to
   set GPIO direction in atomic context (e.g. interrupt handler),
 - reduced overhead of bitbanging drivers that use gpio_direction_*(),
   e.g. i2c-gpio.

Signed-off-by: default avatarTomasz Figa <t.figa@samsung.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent 0e938675
Loading
Loading
Loading
Loading
+44 −55
Original line number Diff line number Diff line
@@ -333,57 +333,12 @@ static int samsung_pinmux_enable(struct pinctrl_dev *pctldev, unsigned selector,
	return 0;
}

/*
 * The calls to gpio_direction_output() and gpio_direction_input()
 * leads to this function call (via the pinctrl_gpio_direction_{input|output}()
 * function called from the gpiolib interface).
 */
static int samsung_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev,
		struct pinctrl_gpio_range *range, unsigned offset, bool input)
{
	struct samsung_pin_bank_type *type;
	struct samsung_pin_bank *bank;
	struct samsung_pinctrl_drv_data *drvdata;
	void __iomem *reg;
	u32 data, pin_offset, mask, shift;
	unsigned long flags;

	bank = gc_to_pin_bank(range->gc);
	type = bank->type;
	drvdata = pinctrl_dev_get_drvdata(pctldev);

	pin_offset = offset - bank->pin_base;
	reg = drvdata->virt_base + bank->pctl_offset +
					type->reg_offset[PINCFG_TYPE_FUNC];

	mask = (1 << type->fld_width[PINCFG_TYPE_FUNC]) - 1;
	shift = pin_offset * type->fld_width[PINCFG_TYPE_FUNC];
	if (shift >= 32) {
		/* Some banks have two config registers */
		shift -= 32;
		reg += 4;
	}

	spin_lock_irqsave(&bank->slock, flags);

	data = readl(reg);
	data &= ~(mask << shift);
	if (!input)
		data |= FUNC_OUTPUT << shift;
	writel(data, reg);

	spin_unlock_irqrestore(&bank->slock, flags);

	return 0;
}

/* list of pinmux callbacks for the pinmux vertical in pinctrl core */
static const struct pinmux_ops samsung_pinmux_ops = {
	.get_functions_count	= samsung_get_functions_count,
	.get_function_name	= samsung_pinmux_get_fname,
	.get_function_groups	= samsung_pinmux_get_groups,
	.enable			= samsung_pinmux_enable,
	.gpio_set_direction	= samsung_pinmux_gpio_set_direction,
};

/* set or get the pin config settings for a specified pin */
@@ -532,25 +487,59 @@ static int samsung_gpio_get(struct gpio_chip *gc, unsigned offset)
}

/*
 * gpiolib gpio_direction_input callback function. The setting of the pin
 * mux function as 'gpio input' will be handled by the pinctrl susbsystem
 * interface.
 * The calls to gpio_direction_output() and gpio_direction_input()
 * leads to this function call.
 */
static int samsung_gpio_set_direction(struct gpio_chip *gc,
					     unsigned offset, bool input)
{
	struct samsung_pin_bank_type *type;
	struct samsung_pin_bank *bank;
	struct samsung_pinctrl_drv_data *drvdata;
	void __iomem *reg;
	u32 data, mask, shift;
	unsigned long flags;

	bank = gc_to_pin_bank(gc);
	type = bank->type;
	drvdata = bank->drvdata;

	reg = drvdata->virt_base + bank->pctl_offset +
					type->reg_offset[PINCFG_TYPE_FUNC];

	mask = (1 << type->fld_width[PINCFG_TYPE_FUNC]) - 1;
	shift = offset * type->fld_width[PINCFG_TYPE_FUNC];
	if (shift >= 32) {
		/* Some banks have two config registers */
		shift -= 32;
		reg += 4;
	}

	spin_lock_irqsave(&bank->slock, flags);

	data = readl(reg);
	data &= ~(mask << shift);
	if (!input)
		data |= FUNC_OUTPUT << shift;
	writel(data, reg);

	spin_unlock_irqrestore(&bank->slock, flags);

	return 0;
}

/* gpiolib gpio_direction_input callback function. */
static int samsung_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
{
	return pinctrl_gpio_direction_input(gc->base + offset);
	return samsung_gpio_set_direction(gc, offset, true);
}

/*
 * gpiolib gpio_direction_output callback function. The setting of the pin
 * mux function as 'gpio output' will be handled by the pinctrl susbsystem
 * interface.
 */
/* gpiolib gpio_direction_output callback function. */
static int samsung_gpio_direction_output(struct gpio_chip *gc, unsigned offset,
							int value)
{
	samsung_gpio_set(gc, offset, value);
	return pinctrl_gpio_direction_output(gc->base + offset);
	return samsung_gpio_set_direction(gc, offset, false);
}

/*