172 lines
4.9 KiB
Diff
172 lines
4.9 KiB
Diff
|
From 7c926492d38a3feef4b4b29c91b7c03eb1b8b546 Mon Sep 17 00:00:00 2001
|
||
|
From: Maxime Ripard <maxime.ripard@free-electrons.com>
|
||
|
Date: Mon, 14 Nov 2016 21:53:03 +0100
|
||
|
Subject: 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: Maxime Ripard <maxime.ripard@free-electrons.com>
|
||
|
Acked-by: Rob Herring <robh@kernel.org>
|
||
|
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
|
||
|
---
|
||
|
.../bindings/pinctrl/allwinner,sunxi-pinctrl.txt | 14 ++++
|
||
|
drivers/pinctrl/sunxi/pinctrl-sunxi.c | 84 ++++++++++++++++++++++
|
||
|
drivers/pinctrl/sunxi/pinctrl-sunxi.h | 7 ++
|
||
|
3 files changed, 105 insertions(+)
|
||
|
|
||
|
--- a/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt
|
||
|
+++ b/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt
|
||
|
@@ -28,6 +28,20 @@ Required properties:
|
||
|
- reg: Should contain the register physical address and length for the
|
||
|
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
|
||
|
common pinctrl bindings used by client devices.
|
||
|
|
||
|
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
|
||
|
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
|
||
|
@@ -1122,6 +1122,88 @@ static int sunxi_pinctrl_build_state(str
|
||
|
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,
|
||
|
const struct sunxi_pinctrl_desc *desc)
|
||
|
{
|
||
|
@@ -1284,6 +1366,8 @@ int sunxi_pinctrl_init(struct platform_d
|
||
|
pctl);
|
||
|
}
|
||
|
|
||
|
+ sunxi_pinctrl_setup_debounce(pctl, node);
|
||
|
+
|
||
|
dev_info(&pdev->dev, "initialized sunXi PIO driver\n");
|
||
|
|
||
|
return 0;
|
||
|
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h
|
||
|
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
|
||
|
@@ -69,6 +69,8 @@
|
||
|
#define 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_EDGE_RISING 0x00
|
||
|
@@ -265,6 +267,11 @@ static inline u32 sunxi_irq_ctrl_offset(
|
||
|
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)
|
||
|
{
|
||
|
return IRQ_STATUS_REG + (bank_base + bank) * IRQ_MEM_SIZE;
|