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

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
......@@ -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.
......
......@@ -1122,6 +1122,88 @@ static int sunxi_pinctrl_build_state(struct platform_device *pdev)
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_device *pdev,
pctl);
}
sunxi_pinctrl_setup_debounce(pctl, node);
dev_info(&pdev->dev, "initialized sunXi PIO driver\n");
return 0;
......
......@@ -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(u16 irq)
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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment