|
@@ -161,6 +161,73 @@ static void ftgpio_gpio_irq_handler(struct irq_desc *desc)
|
|
|
chained_irq_exit(irqchip, desc);
|
|
|
}
|
|
|
|
|
|
+static int ftgpio_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
|
|
|
+ unsigned long config)
|
|
|
+{
|
|
|
+ enum pin_config_param param = pinconf_to_config_param(config);
|
|
|
+ u32 arg = pinconf_to_config_argument(config);
|
|
|
+ struct ftgpio_gpio *g = gpiochip_get_data(gc);
|
|
|
+ unsigned long pclk_freq;
|
|
|
+ u32 deb_div;
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ if (param != PIN_CONFIG_INPUT_DEBOUNCE)
|
|
|
+ return -ENOTSUPP;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Debounce only works if interrupts are enabled. The manual
|
|
|
+ * states that if PCLK is 66 MHz, and this is set to 0x7D0, then
|
|
|
+ * PCLK is divided down to 33 kHz for the debounce timer. 0x7D0 is
|
|
|
+ * 2000 decimal, so what they mean is simply that the PCLK is
|
|
|
+ * divided by this value.
|
|
|
+ *
|
|
|
+ * As we get a debounce setting in microseconds, we calculate the
|
|
|
+ * desired period time and see if we can get a suitable debounce
|
|
|
+ * time.
|
|
|
+ */
|
|
|
+ pclk_freq = clk_get_rate(g->clk);
|
|
|
+ deb_div = DIV_ROUND_CLOSEST(pclk_freq, arg);
|
|
|
+
|
|
|
+ /* This register is only 24 bits wide */
|
|
|
+ if (deb_div > (1 << 24))
|
|
|
+ return -ENOTSUPP;
|
|
|
+
|
|
|
+ dev_dbg(g->dev, "prescale divisor: %08x, resulting frequency %lu Hz\n",
|
|
|
+ deb_div, (pclk_freq/deb_div));
|
|
|
+
|
|
|
+ val = readl(g->base + GPIO_DEBOUNCE_PRESCALE);
|
|
|
+ if (val == deb_div) {
|
|
|
+ /*
|
|
|
+ * The debounce timer happens to already be set to the
|
|
|
+ * desireable value, what a coincidence! We can just enable
|
|
|
+ * debounce on this GPIO line and return. This happens more
|
|
|
+ * often than you think, for example when all GPIO keys
|
|
|
+ * on a system are requesting the same debounce interval.
|
|
|
+ */
|
|
|
+ val = readl(g->base + GPIO_DEBOUNCE_EN);
|
|
|
+ val |= BIT(offset);
|
|
|
+ writel(val, g->base + GPIO_DEBOUNCE_EN);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ val = readl(g->base + GPIO_DEBOUNCE_EN);
|
|
|
+ if (val) {
|
|
|
+ /*
|
|
|
+ * Oh no! Someone is already using the debounce with
|
|
|
+ * another setting than what we need. Bummer.
|
|
|
+ */
|
|
|
+ return -ENOTSUPP;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* First come, first serve */
|
|
|
+ writel(deb_div, g->base + GPIO_DEBOUNCE_PRESCALE);
|
|
|
+ /* Enable debounce */
|
|
|
+ val |= BIT(offset);
|
|
|
+ writel(val, g->base + GPIO_DEBOUNCE_EN);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int ftgpio_gpio_probe(struct platform_device *pdev)
|
|
|
{
|
|
|
struct device *dev = &pdev->dev;
|
|
@@ -214,6 +281,10 @@ static int ftgpio_gpio_probe(struct platform_device *pdev)
|
|
|
g->gc.owner = THIS_MODULE;
|
|
|
/* ngpio is set by bgpio_init() */
|
|
|
|
|
|
+ /* We need a silicon clock to do debounce */
|
|
|
+ if (!IS_ERR(g->clk))
|
|
|
+ g->gc.set_config = ftgpio_gpio_set_config;
|
|
|
+
|
|
|
ret = devm_gpiochip_add_data(dev, &g->gc, g);
|
|
|
if (ret)
|
|
|
goto dis_clk;
|
|
@@ -223,6 +294,9 @@ static int ftgpio_gpio_probe(struct platform_device *pdev)
|
|
|
writel(0x0, g->base + GPIO_INT_MASK);
|
|
|
writel(~0x0, g->base + GPIO_INT_CLR);
|
|
|
|
|
|
+ /* Clear any use of debounce */
|
|
|
+ writel(0x0, g->base + GPIO_DEBOUNCE_EN);
|
|
|
+
|
|
|
ret = gpiochip_irqchip_add(&g->gc, &ftgpio_gpio_irqchip,
|
|
|
0, handle_bad_irq,
|
|
|
IRQ_TYPE_NONE);
|