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

Commit 7c926492 authored by Maxime Ripard's avatar Maxime Ripard Committed by Linus Walleij
Browse files

pinctrl: sunxi: Add support for interrupt debouncing



The pin controller found in the Allwinner SoCs has support for interrupts
debouncing.

However, this is not done per-pin, preventing us from using the generic
pinconf binding for that, but per irq bank, which, depending on the SoC,
ranges from one to five.

Introduce a device-wide property to deal with this using a microsecond
resolution. We can re-use the per-pin input-debounce property for that, so
let's do it!

Signed-off-by: default avatarMaxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: default avatarRob Herring <robh@kernel.org>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent 51814827
Loading
Loading
Loading
Loading
+14 −0
Original line number Original line Diff line number Diff line
@@ -28,6 +28,20 @@ Required properties:
- reg: Should contain the register physical address and length for the
- reg: Should contain the register physical address and length for the
  pin controller.
  pin controller.


- clocks: phandle to the clocks feeding the pin controller:
  - "apb": the gated APB parent clock
  - "hosc": the high frequency oscillator in the system
  - "losc": the low frequency oscillator in the system

Note: For backward compatibility reasons, the hosc and losc clocks are only
required if you need to use the optional input-debounce property. Any new
device tree should set them.

Optional properties:
  - input-debounce: Array of debouncing periods in microseconds. One period per
    irq bank found in the controller. 0 if no setup required.


Please refer to pinctrl-bindings.txt in this directory for details of the
Please refer to pinctrl-bindings.txt in this directory for details of the
common pinctrl bindings used by client devices.
common pinctrl bindings used by client devices.


+84 −0
Original line number Original line Diff line number Diff line
@@ -1122,6 +1122,88 @@ static int sunxi_pinctrl_build_state(struct platform_device *pdev)
	return 0;
	return 0;
}
}


static int sunxi_pinctrl_get_debounce_div(struct clk *clk, int freq, int *diff)
{
	unsigned long clock = clk_get_rate(clk);
	unsigned int best_diff = ~0, best_div;
	int i;

	for (i = 0; i < 8; i++) {
		int cur_diff = abs(freq - (clock >> i));

		if (cur_diff < best_diff) {
			best_diff = cur_diff;
			best_div = i;
		}
	}

	*diff = best_diff;
	return best_div;
}

static int sunxi_pinctrl_setup_debounce(struct sunxi_pinctrl *pctl,
					struct device_node *node)
{
	unsigned int hosc_diff, losc_diff;
	unsigned int hosc_div, losc_div;
	struct clk *hosc, *losc;
	u8 div, src;
	int i, ret;

	/* Deal with old DTs that didn't have the oscillators */
	if (of_count_phandle_with_args(node, "clocks", "#clock-cells") != 3)
		return 0;

	/* If we don't have any setup, bail out */
	if (!of_find_property(node, "input-debounce", NULL))
		return 0;

	losc = devm_clk_get(pctl->dev, "losc");
	if (IS_ERR(losc))
		return PTR_ERR(losc);

	hosc = devm_clk_get(pctl->dev, "hosc");
	if (IS_ERR(hosc))
		return PTR_ERR(hosc);

	for (i = 0; i < pctl->desc->irq_banks; i++) {
		unsigned long debounce_freq;
		u32 debounce;

		ret = of_property_read_u32_index(node, "input-debounce",
						 i, &debounce);
		if (ret)
			return ret;

		if (!debounce)
			continue;

		debounce_freq = DIV_ROUND_CLOSEST(USEC_PER_SEC, debounce);
		losc_div = sunxi_pinctrl_get_debounce_div(losc,
							  debounce_freq,
							  &losc_diff);

		hosc_div = sunxi_pinctrl_get_debounce_div(hosc,
							  debounce_freq,
							  &hosc_diff);

		if (hosc_diff < losc_diff) {
			div = hosc_div;
			src = 1;
		} else {
			div = losc_div;
			src = 0;
		}

		writel(src | div << 4,
		       pctl->membase +
		       sunxi_irq_debounce_reg_from_bank(i,
							pctl->desc->irq_bank_base));
	}

	return 0;
}

int sunxi_pinctrl_init(struct platform_device *pdev,
int sunxi_pinctrl_init(struct platform_device *pdev,
		       const struct sunxi_pinctrl_desc *desc)
		       const struct sunxi_pinctrl_desc *desc)
{
{
@@ -1284,6 +1366,8 @@ int sunxi_pinctrl_init(struct platform_device *pdev,
						 pctl);
						 pctl);
	}
	}


	sunxi_pinctrl_setup_debounce(pctl, node);

	dev_info(&pdev->dev, "initialized sunXi PIO driver\n");
	dev_info(&pdev->dev, "initialized sunXi PIO driver\n");


	return 0;
	return 0;
+7 −0
Original line number Original line Diff line number Diff line
@@ -69,6 +69,8 @@
#define IRQ_STATUS_IRQ_BITS		1
#define IRQ_STATUS_IRQ_BITS		1
#define IRQ_STATUS_IRQ_MASK		((1 << IRQ_STATUS_IRQ_BITS) - 1)
#define IRQ_STATUS_IRQ_MASK		((1 << IRQ_STATUS_IRQ_BITS) - 1)


#define IRQ_DEBOUNCE_REG	0x218

#define IRQ_MEM_SIZE		0x20
#define IRQ_MEM_SIZE		0x20


#define IRQ_EDGE_RISING		0x00
#define IRQ_EDGE_RISING		0x00
@@ -265,6 +267,11 @@ static inline u32 sunxi_irq_ctrl_offset(u16 irq)
	return irq_num * IRQ_CTRL_IRQ_BITS;
	return irq_num * IRQ_CTRL_IRQ_BITS;
}
}


static inline u32 sunxi_irq_debounce_reg_from_bank(u8 bank, unsigned bank_base)
{
	return IRQ_DEBOUNCE_REG + (bank_base + bank) * IRQ_MEM_SIZE;
}

static inline u32 sunxi_irq_status_reg_from_bank(u8 bank, unsigned bank_base)
static inline u32 sunxi_irq_status_reg_from_bank(u8 bank, unsigned bank_base)
{
{
	return IRQ_STATUS_REG + (bank_base + bank) * IRQ_MEM_SIZE;
	return IRQ_STATUS_REG + (bank_base + bank) * IRQ_MEM_SIZE;