Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpio: tegra: Add support for gpio debounce

NVIDIA's Tegra210 support the HW debounce in the GPIO controller
for all its GPIO pins.

Add support for setting debounce timing by implementing the
set_debounce callback of gpiochip.

Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-by: Stephen Warren <swarren@nvidia.com>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>

authored by

Laxman Dewangan and committed by
Linus Walleij
3737de42 b546be0d

+68 -1
+68 -1
drivers/gpio/gpio-tegra.c
··· 46 46 #define GPIO_INT_ENB(t, x) (GPIO_REG(t, x) + 0x50) 47 47 #define GPIO_INT_LVL(t, x) (GPIO_REG(t, x) + 0x60) 48 48 #define GPIO_INT_CLR(t, x) (GPIO_REG(t, x) + 0x70) 49 + #define GPIO_DBC_CNT(t, x) (GPIO_REG(t, x) + 0xF0) 50 + 49 51 50 52 #define GPIO_MSK_CNF(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x00) 51 53 #define GPIO_MSK_OE(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x10) 52 54 #define GPIO_MSK_OUT(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0X20) 55 + #define GPIO_MSK_DBC_EN(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x30) 53 56 #define GPIO_MSK_INT_STA(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x40) 54 57 #define GPIO_MSK_INT_ENB(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x50) 55 58 #define GPIO_MSK_INT_LVL(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x60) ··· 70 67 int bank; 71 68 int irq; 72 69 spinlock_t lvl_lock[4]; 70 + spinlock_t dbc_lock[4]; /* Lock for updating debounce count register */ 73 71 #ifdef CONFIG_PM_SLEEP 74 72 u32 cnf[4]; 75 73 u32 out[4]; ··· 78 74 u32 int_enb[4]; 79 75 u32 int_lvl[4]; 80 76 u32 wake_enb[4]; 77 + u32 dbc_enb[4]; 81 78 #endif 79 + u32 dbc_cnt[4]; 82 80 struct tegra_gpio_info *tgi; 83 81 }; 84 82 85 83 struct tegra_gpio_soc_config { 84 + bool debounce_supported; 86 85 u32 bank_stride; 87 86 u32 upper_offset; 88 87 }; ··· 188 181 tegra_gpio_set(chip, offset, value); 189 182 tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 1); 190 183 tegra_gpio_enable(tgi, offset); 184 + return 0; 185 + } 186 + 187 + static int tegra_gpio_set_debounce(struct gpio_chip *chip, unsigned int offset, 188 + unsigned int debounce) 189 + { 190 + struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 191 + struct tegra_gpio_bank *bank = &tgi->bank_info[GPIO_BANK(offset)]; 192 + unsigned int debounce_ms = DIV_ROUND_UP(debounce, 1000); 193 + unsigned long flags; 194 + int port; 195 + 196 + if (!debounce_ms) { 197 + tegra_gpio_mask_write(tgi, GPIO_MSK_DBC_EN(tgi, offset), 198 + offset, 0); 199 + return 0; 200 + } 201 + 202 + debounce_ms = min(debounce_ms, 255U); 203 + port = GPIO_PORT(offset); 204 + 205 + /* There is only one debounce count register per port and hence 206 + * set the maximum of current and requested debounce time. 207 + */ 208 + spin_lock_irqsave(&bank->dbc_lock[port], flags); 209 + if (bank->dbc_cnt[port] < debounce_ms) { 210 + tegra_gpio_writel(tgi, debounce_ms, GPIO_DBC_CNT(tgi, offset)); 211 + bank->dbc_cnt[port] = debounce_ms; 212 + } 213 + spin_unlock_irqrestore(&bank->dbc_lock[port], flags); 214 + 215 + tegra_gpio_mask_write(tgi, GPIO_MSK_DBC_EN(tgi, offset), offset, 1); 216 + 191 217 return 0; 192 218 } 193 219 ··· 390 350 unsigned int gpio = (b<<5) | (p<<3); 391 351 tegra_gpio_writel(tgi, bank->cnf[p], 392 352 GPIO_CNF(tgi, gpio)); 353 + 354 + if (tgi->soc->debounce_supported) { 355 + tegra_gpio_writel(tgi, bank->dbc_cnt[p], 356 + GPIO_DBC_CNT(tgi, gpio)); 357 + tegra_gpio_writel(tgi, bank->dbc_enb[p], 358 + GPIO_MSK_DBC_EN(tgi, gpio)); 359 + } 360 + 393 361 tegra_gpio_writel(tgi, bank->out[p], 394 362 GPIO_OUT(tgi, gpio)); 395 363 tegra_gpio_writel(tgi, bank->oe[p], ··· 433 385 GPIO_OUT(tgi, gpio)); 434 386 bank->oe[p] = tegra_gpio_readl(tgi, 435 387 GPIO_OE(tgi, gpio)); 388 + if (tgi->soc->debounce_supported) { 389 + bank->dbc_enb[p] = tegra_gpio_readl(tgi, 390 + GPIO_MSK_DBC_EN(tgi, gpio)); 391 + bank->dbc_enb[p] = (bank->dbc_enb[p] << 8) | 392 + bank->dbc_enb[p]; 393 + } 394 + 436 395 bank->int_enb[p] = tegra_gpio_readl(tgi, 437 396 GPIO_INT_ENB(tgi, gpio)); 438 397 bank->int_lvl[p] = tegra_gpio_readl(tgi, ··· 593 538 594 539 platform_set_drvdata(pdev, tgi); 595 540 541 + if (config->debounce_supported) 542 + tgi->gc.set_debounce = tegra_gpio_set_debounce; 543 + 596 544 tgi->bank_info = devm_kzalloc(&pdev->dev, tgi->bank_count * 597 545 sizeof(*tgi->bank_info), GFP_KERNEL); 598 546 if (!tgi->bank_info) ··· 655 597 irq_set_chained_handler_and_data(bank->irq, 656 598 tegra_gpio_irq_handler, bank); 657 599 658 - for (j = 0; j < 4; j++) 600 + for (j = 0; j < 4; j++) { 659 601 spin_lock_init(&bank->lvl_lock[j]); 602 + spin_lock_init(&bank->dbc_lock[j]); 603 + } 660 604 } 661 605 662 606 tegra_gpio_debuginit(tgi); ··· 676 616 .upper_offset = 0x80, 677 617 }; 678 618 619 + static const struct tegra_gpio_soc_config tegra210_gpio_config = { 620 + .debounce_supported = true, 621 + .bank_stride = 0x100, 622 + .upper_offset = 0x80, 623 + }; 624 + 679 625 static const struct of_device_id tegra_gpio_of_match[] = { 626 + { .compatible = "nvidia,tegra210-gpio", .data = &tegra210_gpio_config }, 680 627 { .compatible = "nvidia,tegra30-gpio", .data = &tegra30_gpio_config }, 681 628 { .compatible = "nvidia,tegra20-gpio", .data = &tegra20_gpio_config }, 682 629 { },