diff options
Diffstat (limited to 'drivers/gpio/gpio-tegra.c')
| -rw-r--r-- | drivers/gpio/gpio-tegra.c | 125 |
1 files changed, 81 insertions, 44 deletions
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index d982593d756..4e8fb8261a8 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -17,6 +17,7 @@ * */ +#include <linux/err.h> #include <linux/init.h> #include <linux/irq.h> #include <linux/interrupt.h> @@ -26,9 +27,9 @@ #include <linux/platform_device.h> #include <linux/module.h> #include <linux/irqdomain.h> +#include <linux/irqchip/chained_irq.h> #include <linux/pinctrl/consumer.h> - -#include <asm/mach/irq.h> +#include <linux/pm.h> #define GPIO_BANK(x) ((x) >> 5) #define GPIO_PORT(x) (((x) >> 3) & 0x3) @@ -64,15 +65,17 @@ struct tegra_gpio_bank { int bank; int irq; spinlock_t lvl_lock[4]; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP u32 cnf[4]; u32 out[4]; u32 oe[4]; u32 int_enb[4]; u32 int_lvl[4]; + u32 wake_enb[4]; #endif }; +static struct device *dev; static struct irq_domain *irq_domain; static void __iomem *regs; static u32 tegra_gpio_bank_count; @@ -109,20 +112,18 @@ static void tegra_gpio_enable(int gpio) { tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 1); } -EXPORT_SYMBOL_GPL(tegra_gpio_enable); static void tegra_gpio_disable(int gpio) { tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 0); } -EXPORT_SYMBOL_GPL(tegra_gpio_disable); -int tegra_gpio_request(struct gpio_chip *chip, unsigned offset) +static int tegra_gpio_request(struct gpio_chip *chip, unsigned offset) { return pinctrl_request_gpio(offset); } -void tegra_gpio_free(struct gpio_chip *chip, unsigned offset) +static void tegra_gpio_free(struct gpio_chip *chip, unsigned offset) { pinctrl_free_gpio(offset); tegra_gpio_disable(offset); @@ -135,6 +136,11 @@ static void tegra_gpio_set(struct gpio_chip *chip, unsigned offset, int value) static int tegra_gpio_get(struct gpio_chip *chip, unsigned offset) { + /* If gpio is in output mode then read from the out value */ + if ((tegra_gpio_readl(GPIO_OE(offset)) >> GPIO_BIT(offset)) & 1) + return (tegra_gpio_readl(GPIO_OUT(offset)) >> + GPIO_BIT(offset)) & 0x1; + return (tegra_gpio_readl(GPIO_IN(offset)) >> GPIO_BIT(offset)) & 0x1; } @@ -200,6 +206,7 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) int lvl_type; int val; unsigned long flags; + int ret; switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_EDGE_RISING: @@ -226,6 +233,12 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) return -EINVAL; } + ret = gpio_lock_as_irq(&tegra_gpio_chip, gpio); + if (ret) { + dev_err(dev, "unable to lock Tegra GPIO %d as IRQ\n", gpio); + return ret; + } + spin_lock_irqsave(&bank->lvl_lock[port], flags); val = tegra_gpio_readl(GPIO_INT_LVL(gpio)); @@ -246,6 +259,13 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) return 0; } +static void tegra_gpio_irq_shutdown(struct irq_data *d) +{ + int gpio = d->hwirq; + + gpio_unlock_as_irq(&tegra_gpio_chip, gpio); +} + static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) { struct tegra_gpio_bank *bank; @@ -285,8 +305,8 @@ static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) } -#ifdef CONFIG_PM -void tegra_gpio_resume(void) +#ifdef CONFIG_PM_SLEEP +static int tegra_gpio_resume(struct device *dev) { unsigned long flags; int b; @@ -308,9 +328,10 @@ void tegra_gpio_resume(void) } local_irq_restore(flags); + return 0; } -void tegra_gpio_suspend(void) +static int tegra_gpio_suspend(struct device *dev) { unsigned long flags; int b; @@ -327,14 +348,31 @@ void tegra_gpio_suspend(void) bank->oe[p] = tegra_gpio_readl(GPIO_OE(gpio)); bank->int_enb[p] = tegra_gpio_readl(GPIO_INT_ENB(gpio)); bank->int_lvl[p] = tegra_gpio_readl(GPIO_INT_LVL(gpio)); + + /* Enable gpio irq for wake up source */ + tegra_gpio_writel(bank->wake_enb[p], + GPIO_INT_ENB(gpio)); } } local_irq_restore(flags); + return 0; } -static int tegra_gpio_wake_enable(struct irq_data *d, unsigned int enable) +static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) { struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); + int gpio = d->hwirq; + u32 port, bit, mask; + + port = GPIO_PORT(gpio); + bit = GPIO_BIT(gpio); + mask = BIT(bit); + + if (enable) + bank->wake_enb[port] |= mask; + else + bank->wake_enb[port] &= ~mask; + return irq_set_irq_wake(bank->irq, enable); } #endif @@ -345,11 +383,16 @@ static struct irq_chip tegra_gpio_irq_chip = { .irq_mask = tegra_gpio_irq_mask, .irq_unmask = tegra_gpio_irq_unmask, .irq_set_type = tegra_gpio_irq_set_type, -#ifdef CONFIG_PM - .irq_set_wake = tegra_gpio_wake_enable, + .irq_shutdown = tegra_gpio_irq_shutdown, +#ifdef CONFIG_PM_SLEEP + .irq_set_wake = tegra_gpio_irq_set_wake, #endif }; +static const struct dev_pm_ops tegra_gpio_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume) +}; + struct tegra_gpio_soc_config { u32 bank_stride; u32 upper_offset; @@ -365,7 +408,7 @@ static struct tegra_gpio_soc_config tegra30_gpio_config = { .upper_offset = 0x80, }; -static struct of_device_id tegra_gpio_of_match[] __devinitdata = { +static const struct of_device_id tegra_gpio_of_match[] = { { .compatible = "nvidia,tegra30-gpio", .data = &tegra30_gpio_config }, { .compatible = "nvidia,tegra20-gpio", .data = &tegra20_gpio_config }, { }, @@ -376,22 +419,25 @@ static struct of_device_id tegra_gpio_of_match[] __devinitdata = { */ static struct lock_class_key gpio_lock_class; -static int __devinit tegra_gpio_probe(struct platform_device *pdev) +static int tegra_gpio_probe(struct platform_device *pdev) { const struct of_device_id *match; struct tegra_gpio_soc_config *config; - int irq_base; struct resource *res; struct tegra_gpio_bank *bank; + int ret; int gpio; int i; int j; + dev = &pdev->dev; + match = of_match_device(tegra_gpio_of_match, &pdev->dev); - if (match) - config = (struct tegra_gpio_soc_config *)match->data; - else - config = &tegra20_gpio_config; + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + config = (struct tegra_gpio_soc_config *)match->data; tegra_gpio_bank_stride = config->bank_stride; tegra_gpio_upper_offset = config->upper_offset; @@ -412,19 +458,14 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) tegra_gpio_banks = devm_kzalloc(&pdev->dev, tegra_gpio_bank_count * sizeof(*tegra_gpio_banks), GFP_KERNEL); - if (!tegra_gpio_banks) { - dev_err(&pdev->dev, "Couldn't allocate bank structure\n"); + if (!tegra_gpio_banks) return -ENODEV; - } - irq_base = irq_alloc_descs(-1, 0, tegra_gpio_chip.ngpio, 0); - if (irq_base < 0) { - dev_err(&pdev->dev, "Couldn't allocate IRQ numbers\n"); - return -ENODEV; - } - irq_domain = irq_domain_add_legacy(pdev->dev.of_node, - tegra_gpio_chip.ngpio, irq_base, 0, + irq_domain = irq_domain_add_linear(pdev->dev.of_node, + tegra_gpio_chip.ngpio, &irq_domain_simple_ops, NULL); + if (!irq_domain) + return -ENODEV; for (i = 0; i < tegra_gpio_bank_count; i++) { res = platform_get_resource(pdev, IORESOURCE_IRQ, i); @@ -439,16 +480,9 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Missing MEM resource\n"); - return -ENODEV; - } - - regs = devm_request_and_ioremap(&pdev->dev, res); - if (!regs) { - dev_err(&pdev->dev, "Couldn't ioremap regs\n"); - return -ENODEV; - } + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); for (i = 0; i < tegra_gpio_bank_count; i++) { for (j = 0; j < 4; j++) { @@ -457,14 +491,16 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev) } } -#ifdef CONFIG_OF_GPIO tegra_gpio_chip.of_node = pdev->dev.of_node; -#endif - gpiochip_add(&tegra_gpio_chip); + ret = gpiochip_add(&tegra_gpio_chip); + if (ret < 0) { + irq_domain_remove(irq_domain); + return ret; + } for (gpio = 0; gpio < tegra_gpio_chip.ngpio; gpio++) { - int irq = irq_find_mapping(irq_domain, gpio); + int irq = irq_create_mapping(irq_domain, gpio); /* No validity check; all Tegra GPIOs are valid IRQs */ bank = &tegra_gpio_banks[GPIO_BANK(gpio)]; @@ -493,6 +529,7 @@ static struct platform_driver tegra_gpio_driver = { .driver = { .name = "tegra-gpio", .owner = THIS_MODULE, + .pm = &tegra_gpio_pm_ops, .of_match_table = tegra_gpio_of_match, }, .probe = tegra_gpio_probe, |
