From 7c926492d38a3feef4b4b29c91b7c03eb1b8b546 Mon Sep 17 00:00:00 2001 From: Maxime Ripard 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 Acked-by: Rob Herring Signed-off-by: Linus Walleij --- .../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;