162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * TI DaVinci GPIO Support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2006-2007 David Brownell 662306a36Sopenharmony_ci * Copyright (c) 2007, MontaVista Software, Inc. <source@mvista.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/gpio/driver.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/clk.h> 1362306a36Sopenharmony_ci#include <linux/err.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/irq.h> 1662306a36Sopenharmony_ci#include <linux/irqdomain.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/of_device.h> 2062306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci#include <linux/platform_data/gpio-davinci.h> 2362306a36Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 2462306a36Sopenharmony_ci#include <linux/spinlock.h> 2562306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define MAX_REGS_BANKS 5 2862306a36Sopenharmony_ci#define MAX_INT_PER_BANK 32 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct davinci_gpio_regs { 3162306a36Sopenharmony_ci u32 dir; 3262306a36Sopenharmony_ci u32 out_data; 3362306a36Sopenharmony_ci u32 set_data; 3462306a36Sopenharmony_ci u32 clr_data; 3562306a36Sopenharmony_ci u32 in_data; 3662306a36Sopenharmony_ci u32 set_rising; 3762306a36Sopenharmony_ci u32 clr_rising; 3862306a36Sopenharmony_ci u32 set_falling; 3962306a36Sopenharmony_ci u32 clr_falling; 4062306a36Sopenharmony_ci u32 intstat; 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_citypedef struct irq_chip *(*gpio_get_irq_chip_cb_t)(unsigned int irq); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define BINTEN 0x8 /* GPIO Interrupt Per-Bank Enable Register */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic void __iomem *gpio_base; 4862306a36Sopenharmony_cistatic unsigned int offset_array[5] = {0x10, 0x38, 0x60, 0x88, 0xb0}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct davinci_gpio_irq_data { 5162306a36Sopenharmony_ci void __iomem *regs; 5262306a36Sopenharmony_ci struct davinci_gpio_controller *chip; 5362306a36Sopenharmony_ci int bank_num; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistruct davinci_gpio_controller { 5762306a36Sopenharmony_ci struct gpio_chip chip; 5862306a36Sopenharmony_ci struct irq_domain *irq_domain; 5962306a36Sopenharmony_ci /* Serialize access to GPIO registers */ 6062306a36Sopenharmony_ci spinlock_t lock; 6162306a36Sopenharmony_ci void __iomem *regs[MAX_REGS_BANKS]; 6262306a36Sopenharmony_ci int gpio_unbanked; 6362306a36Sopenharmony_ci int irqs[MAX_INT_PER_BANK]; 6462306a36Sopenharmony_ci struct davinci_gpio_regs context[MAX_REGS_BANKS]; 6562306a36Sopenharmony_ci u32 binten_context; 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic inline u32 __gpio_mask(unsigned gpio) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci return 1 << (gpio % 32); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic inline struct davinci_gpio_regs __iomem *irq2regs(struct irq_data *d) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct davinci_gpio_regs __iomem *g; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci g = (__force struct davinci_gpio_regs __iomem *)irq_data_get_irq_chip_data(d); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return g; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int davinci_gpio_irq_setup(struct platform_device *pdev); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/*--------------------------------------------------------------------------*/ 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* board setup code *MUST* setup pinmux and enable the GPIO clock. */ 8762306a36Sopenharmony_cistatic inline int __davinci_direction(struct gpio_chip *chip, 8862306a36Sopenharmony_ci unsigned offset, bool out, int value) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct davinci_gpio_controller *d = gpiochip_get_data(chip); 9162306a36Sopenharmony_ci struct davinci_gpio_regs __iomem *g; 9262306a36Sopenharmony_ci unsigned long flags; 9362306a36Sopenharmony_ci u32 temp; 9462306a36Sopenharmony_ci int bank = offset / 32; 9562306a36Sopenharmony_ci u32 mask = __gpio_mask(offset); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci g = d->regs[bank]; 9862306a36Sopenharmony_ci spin_lock_irqsave(&d->lock, flags); 9962306a36Sopenharmony_ci temp = readl_relaxed(&g->dir); 10062306a36Sopenharmony_ci if (out) { 10162306a36Sopenharmony_ci temp &= ~mask; 10262306a36Sopenharmony_ci writel_relaxed(mask, value ? &g->set_data : &g->clr_data); 10362306a36Sopenharmony_ci } else { 10462306a36Sopenharmony_ci temp |= mask; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci writel_relaxed(temp, &g->dir); 10762306a36Sopenharmony_ci spin_unlock_irqrestore(&d->lock, flags); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return 0; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int davinci_direction_in(struct gpio_chip *chip, unsigned offset) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci return __davinci_direction(chip, offset, false, 0); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int 11862306a36Sopenharmony_cidavinci_direction_out(struct gpio_chip *chip, unsigned offset, int value) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci return __davinci_direction(chip, offset, true, value); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* 12462306a36Sopenharmony_ci * Read the pin's value (works even if it's set up as output); 12562306a36Sopenharmony_ci * returns zero/nonzero. 12662306a36Sopenharmony_ci * 12762306a36Sopenharmony_ci * Note that changes are synched to the GPIO clock, so reading values back 12862306a36Sopenharmony_ci * right after you've set them may give old values. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_cistatic int davinci_gpio_get(struct gpio_chip *chip, unsigned offset) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct davinci_gpio_controller *d = gpiochip_get_data(chip); 13362306a36Sopenharmony_ci struct davinci_gpio_regs __iomem *g; 13462306a36Sopenharmony_ci int bank = offset / 32; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci g = d->regs[bank]; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return !!(__gpio_mask(offset) & readl_relaxed(&g->in_data)); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* 14262306a36Sopenharmony_ci * Assuming the pin is muxed as a gpio output, set its output value. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_cistatic void 14562306a36Sopenharmony_cidavinci_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct davinci_gpio_controller *d = gpiochip_get_data(chip); 14862306a36Sopenharmony_ci struct davinci_gpio_regs __iomem *g; 14962306a36Sopenharmony_ci int bank = offset / 32; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci g = d->regs[bank]; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci writel_relaxed(__gpio_mask(offset), 15462306a36Sopenharmony_ci value ? &g->set_data : &g->clr_data); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic struct davinci_gpio_platform_data * 15862306a36Sopenharmony_cidavinci_gpio_get_pdata(struct platform_device *pdev) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct device_node *dn = pdev->dev.of_node; 16162306a36Sopenharmony_ci struct davinci_gpio_platform_data *pdata; 16262306a36Sopenharmony_ci int ret; 16362306a36Sopenharmony_ci u32 val; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node) 16662306a36Sopenharmony_ci return dev_get_platdata(&pdev->dev); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 16962306a36Sopenharmony_ci if (!pdata) 17062306a36Sopenharmony_ci return NULL; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci ret = of_property_read_u32(dn, "ti,ngpio", &val); 17362306a36Sopenharmony_ci if (ret) 17462306a36Sopenharmony_ci goto of_err; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci pdata->ngpio = val; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ret = of_property_read_u32(dn, "ti,davinci-gpio-unbanked", &val); 17962306a36Sopenharmony_ci if (ret) 18062306a36Sopenharmony_ci goto of_err; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci pdata->gpio_unbanked = val; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return pdata; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ciof_err: 18762306a36Sopenharmony_ci dev_err(&pdev->dev, "Populating pdata from DT failed: err %d\n", ret); 18862306a36Sopenharmony_ci return NULL; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int davinci_gpio_probe(struct platform_device *pdev) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci int bank, i, ret = 0; 19462306a36Sopenharmony_ci unsigned int ngpio, nbank, nirq; 19562306a36Sopenharmony_ci struct davinci_gpio_controller *chips; 19662306a36Sopenharmony_ci struct davinci_gpio_platform_data *pdata; 19762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci pdata = davinci_gpio_get_pdata(pdev); 20062306a36Sopenharmony_ci if (!pdata) { 20162306a36Sopenharmony_ci dev_err(dev, "No platform data found\n"); 20262306a36Sopenharmony_ci return -EINVAL; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci dev->platform_data = pdata; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* 20862306a36Sopenharmony_ci * The gpio banks conceptually expose a segmented bitmap, 20962306a36Sopenharmony_ci * and "ngpio" is one more than the largest zero-based 21062306a36Sopenharmony_ci * bit index that's valid. 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci ngpio = pdata->ngpio; 21362306a36Sopenharmony_ci if (ngpio == 0) { 21462306a36Sopenharmony_ci dev_err(dev, "How many GPIOs?\n"); 21562306a36Sopenharmony_ci return -EINVAL; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* 21962306a36Sopenharmony_ci * If there are unbanked interrupts then the number of 22062306a36Sopenharmony_ci * interrupts is equal to number of gpios else all are banked so 22162306a36Sopenharmony_ci * number of interrupts is equal to number of banks(each with 16 gpios) 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_ci if (pdata->gpio_unbanked) 22462306a36Sopenharmony_ci nirq = pdata->gpio_unbanked; 22562306a36Sopenharmony_ci else 22662306a36Sopenharmony_ci nirq = DIV_ROUND_UP(ngpio, 16); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci chips = devm_kzalloc(dev, sizeof(*chips), GFP_KERNEL); 22962306a36Sopenharmony_ci if (!chips) 23062306a36Sopenharmony_ci return -ENOMEM; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci gpio_base = devm_platform_ioremap_resource(pdev, 0); 23362306a36Sopenharmony_ci if (IS_ERR(gpio_base)) 23462306a36Sopenharmony_ci return PTR_ERR(gpio_base); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci for (i = 0; i < nirq; i++) { 23762306a36Sopenharmony_ci chips->irqs[i] = platform_get_irq(pdev, i); 23862306a36Sopenharmony_ci if (chips->irqs[i] < 0) 23962306a36Sopenharmony_ci return chips->irqs[i]; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci chips->chip.label = dev_name(dev); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci chips->chip.direction_input = davinci_direction_in; 24562306a36Sopenharmony_ci chips->chip.get = davinci_gpio_get; 24662306a36Sopenharmony_ci chips->chip.direction_output = davinci_direction_out; 24762306a36Sopenharmony_ci chips->chip.set = davinci_gpio_set; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci chips->chip.ngpio = ngpio; 25062306a36Sopenharmony_ci chips->chip.base = pdata->no_auto_base ? pdata->base : -1; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci#ifdef CONFIG_OF_GPIO 25362306a36Sopenharmony_ci chips->chip.parent = dev; 25462306a36Sopenharmony_ci chips->chip.request = gpiochip_generic_request; 25562306a36Sopenharmony_ci chips->chip.free = gpiochip_generic_free; 25662306a36Sopenharmony_ci#endif 25762306a36Sopenharmony_ci spin_lock_init(&chips->lock); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci nbank = DIV_ROUND_UP(ngpio, 32); 26062306a36Sopenharmony_ci for (bank = 0; bank < nbank; bank++) 26162306a36Sopenharmony_ci chips->regs[bank] = gpio_base + offset_array[bank]; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci ret = devm_gpiochip_add_data(dev, &chips->chip, chips); 26462306a36Sopenharmony_ci if (ret) 26562306a36Sopenharmony_ci return ret; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci platform_set_drvdata(pdev, chips); 26862306a36Sopenharmony_ci ret = davinci_gpio_irq_setup(pdev); 26962306a36Sopenharmony_ci if (ret) 27062306a36Sopenharmony_ci return ret; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return 0; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci/*--------------------------------------------------------------------------*/ 27662306a36Sopenharmony_ci/* 27762306a36Sopenharmony_ci * We expect irqs will normally be set up as input pins, but they can also be 27862306a36Sopenharmony_ci * used as output pins ... which is convenient for testing. 27962306a36Sopenharmony_ci * 28062306a36Sopenharmony_ci * NOTE: The first few GPIOs also have direct INTC hookups in addition 28162306a36Sopenharmony_ci * to their GPIOBNK0 irq, with a bit less overhead. 28262306a36Sopenharmony_ci * 28362306a36Sopenharmony_ci * All those INTC hookups (direct, plus several IRQ banks) can also 28462306a36Sopenharmony_ci * serve as EDMA event triggers. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic void gpio_irq_disable(struct irq_data *d) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct davinci_gpio_regs __iomem *g = irq2regs(d); 29062306a36Sopenharmony_ci uintptr_t mask = (uintptr_t)irq_data_get_irq_handler_data(d); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci writel_relaxed(mask, &g->clr_falling); 29362306a36Sopenharmony_ci writel_relaxed(mask, &g->clr_rising); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic void gpio_irq_enable(struct irq_data *d) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct davinci_gpio_regs __iomem *g = irq2regs(d); 29962306a36Sopenharmony_ci uintptr_t mask = (uintptr_t)irq_data_get_irq_handler_data(d); 30062306a36Sopenharmony_ci unsigned status = irqd_get_trigger_type(d); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci status &= IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING; 30362306a36Sopenharmony_ci if (!status) 30462306a36Sopenharmony_ci status = IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (status & IRQ_TYPE_EDGE_FALLING) 30762306a36Sopenharmony_ci writel_relaxed(mask, &g->set_falling); 30862306a36Sopenharmony_ci if (status & IRQ_TYPE_EDGE_RISING) 30962306a36Sopenharmony_ci writel_relaxed(mask, &g->set_rising); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int gpio_irq_type(struct irq_data *d, unsigned trigger) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) 31562306a36Sopenharmony_ci return -EINVAL; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic struct irq_chip gpio_irqchip = { 32162306a36Sopenharmony_ci .name = "GPIO", 32262306a36Sopenharmony_ci .irq_enable = gpio_irq_enable, 32362306a36Sopenharmony_ci .irq_disable = gpio_irq_disable, 32462306a36Sopenharmony_ci .irq_set_type = gpio_irq_type, 32562306a36Sopenharmony_ci .flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_SKIP_SET_WAKE, 32662306a36Sopenharmony_ci}; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic void gpio_irq_handler(struct irq_desc *desc) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct davinci_gpio_regs __iomem *g; 33162306a36Sopenharmony_ci u32 mask = 0xffff; 33262306a36Sopenharmony_ci int bank_num; 33362306a36Sopenharmony_ci struct davinci_gpio_controller *d; 33462306a36Sopenharmony_ci struct davinci_gpio_irq_data *irqdata; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci irqdata = (struct davinci_gpio_irq_data *)irq_desc_get_handler_data(desc); 33762306a36Sopenharmony_ci bank_num = irqdata->bank_num; 33862306a36Sopenharmony_ci g = irqdata->regs; 33962306a36Sopenharmony_ci d = irqdata->chip; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* we only care about one bank */ 34262306a36Sopenharmony_ci if ((bank_num % 2) == 1) 34362306a36Sopenharmony_ci mask <<= 16; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* temporarily mask (level sensitive) parent IRQ */ 34662306a36Sopenharmony_ci chained_irq_enter(irq_desc_get_chip(desc), desc); 34762306a36Sopenharmony_ci while (1) { 34862306a36Sopenharmony_ci u32 status; 34962306a36Sopenharmony_ci int bit; 35062306a36Sopenharmony_ci irq_hw_number_t hw_irq; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci /* ack any irqs */ 35362306a36Sopenharmony_ci status = readl_relaxed(&g->intstat) & mask; 35462306a36Sopenharmony_ci if (!status) 35562306a36Sopenharmony_ci break; 35662306a36Sopenharmony_ci writel_relaxed(status, &g->intstat); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* now demux them to the right lowlevel handler */ 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci while (status) { 36162306a36Sopenharmony_ci bit = __ffs(status); 36262306a36Sopenharmony_ci status &= ~BIT(bit); 36362306a36Sopenharmony_ci /* Max number of gpios per controller is 144 so 36462306a36Sopenharmony_ci * hw_irq will be in [0..143] 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_ci hw_irq = (bank_num / 2) * 32 + bit; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci generic_handle_domain_irq(d->irq_domain, hw_irq); 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci chained_irq_exit(irq_desc_get_chip(desc), desc); 37262306a36Sopenharmony_ci /* now it may re-trigger */ 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic int gpio_to_irq_banked(struct gpio_chip *chip, unsigned offset) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct davinci_gpio_controller *d = gpiochip_get_data(chip); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (d->irq_domain) 38062306a36Sopenharmony_ci return irq_create_mapping(d->irq_domain, offset); 38162306a36Sopenharmony_ci else 38262306a36Sopenharmony_ci return -ENXIO; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic int gpio_to_irq_unbanked(struct gpio_chip *chip, unsigned offset) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct davinci_gpio_controller *d = gpiochip_get_data(chip); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* 39062306a36Sopenharmony_ci * NOTE: we assume for now that only irqs in the first gpio_chip 39162306a36Sopenharmony_ci * can provide direct-mapped IRQs to AINTC (up to 32 GPIOs). 39262306a36Sopenharmony_ci */ 39362306a36Sopenharmony_ci if (offset < d->gpio_unbanked) 39462306a36Sopenharmony_ci return d->irqs[offset]; 39562306a36Sopenharmony_ci else 39662306a36Sopenharmony_ci return -ENODEV; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic int gpio_irq_type_unbanked(struct irq_data *data, unsigned trigger) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct davinci_gpio_controller *d; 40262306a36Sopenharmony_ci struct davinci_gpio_regs __iomem *g; 40362306a36Sopenharmony_ci u32 mask, i; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci d = (struct davinci_gpio_controller *)irq_data_get_irq_handler_data(data); 40662306a36Sopenharmony_ci g = (struct davinci_gpio_regs __iomem *)d->regs[0]; 40762306a36Sopenharmony_ci for (i = 0; i < MAX_INT_PER_BANK; i++) 40862306a36Sopenharmony_ci if (data->irq == d->irqs[i]) 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (i == MAX_INT_PER_BANK) 41262306a36Sopenharmony_ci return -EINVAL; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci mask = __gpio_mask(i); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) 41762306a36Sopenharmony_ci return -EINVAL; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci writel_relaxed(mask, (trigger & IRQ_TYPE_EDGE_FALLING) 42062306a36Sopenharmony_ci ? &g->set_falling : &g->clr_falling); 42162306a36Sopenharmony_ci writel_relaxed(mask, (trigger & IRQ_TYPE_EDGE_RISING) 42262306a36Sopenharmony_ci ? &g->set_rising : &g->clr_rising); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic int 42862306a36Sopenharmony_cidavinci_gpio_irq_map(struct irq_domain *d, unsigned int irq, 42962306a36Sopenharmony_ci irq_hw_number_t hw) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct davinci_gpio_controller *chips = 43262306a36Sopenharmony_ci (struct davinci_gpio_controller *)d->host_data; 43362306a36Sopenharmony_ci struct davinci_gpio_regs __iomem *g = chips->regs[hw / 32]; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci irq_set_chip_and_handler_name(irq, &gpio_irqchip, handle_simple_irq, 43662306a36Sopenharmony_ci "davinci_gpio"); 43762306a36Sopenharmony_ci irq_set_irq_type(irq, IRQ_TYPE_NONE); 43862306a36Sopenharmony_ci irq_set_chip_data(irq, (__force void *)g); 43962306a36Sopenharmony_ci irq_set_handler_data(irq, (void *)(uintptr_t)__gpio_mask(hw)); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic const struct irq_domain_ops davinci_gpio_irq_ops = { 44562306a36Sopenharmony_ci .map = davinci_gpio_irq_map, 44662306a36Sopenharmony_ci .xlate = irq_domain_xlate_onetwocell, 44762306a36Sopenharmony_ci}; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic struct irq_chip *davinci_gpio_get_irq_chip(unsigned int irq) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci static struct irq_chip_type gpio_unbanked; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci gpio_unbanked = *irq_data_get_chip_type(irq_get_irq_data(irq)); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci return &gpio_unbanked.chip; 45662306a36Sopenharmony_ci}; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic struct irq_chip *keystone_gpio_get_irq_chip(unsigned int irq) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci static struct irq_chip gpio_unbanked; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci gpio_unbanked = *irq_get_chip(irq); 46362306a36Sopenharmony_ci return &gpio_unbanked; 46462306a36Sopenharmony_ci}; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic const struct of_device_id davinci_gpio_ids[]; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci/* 46962306a36Sopenharmony_ci * NOTE: for suspend/resume, probably best to make a platform_device with 47062306a36Sopenharmony_ci * suspend_late/resume_resume calls hooking into results of the set_wake() 47162306a36Sopenharmony_ci * calls ... so if no gpios are wakeup events the clock can be disabled, 47262306a36Sopenharmony_ci * with outputs left at previously set levels, and so that VDD3P3V.IOPWDN0 47362306a36Sopenharmony_ci * (dm6446) can be set appropriately for GPIOV33 pins. 47462306a36Sopenharmony_ci */ 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic int davinci_gpio_irq_setup(struct platform_device *pdev) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci unsigned gpio, bank; 47962306a36Sopenharmony_ci int irq; 48062306a36Sopenharmony_ci int ret; 48162306a36Sopenharmony_ci struct clk *clk; 48262306a36Sopenharmony_ci u32 binten = 0; 48362306a36Sopenharmony_ci unsigned ngpio; 48462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 48562306a36Sopenharmony_ci struct davinci_gpio_controller *chips = platform_get_drvdata(pdev); 48662306a36Sopenharmony_ci struct davinci_gpio_platform_data *pdata = dev->platform_data; 48762306a36Sopenharmony_ci struct davinci_gpio_regs __iomem *g; 48862306a36Sopenharmony_ci struct irq_domain *irq_domain = NULL; 48962306a36Sopenharmony_ci const struct of_device_id *match; 49062306a36Sopenharmony_ci struct irq_chip *irq_chip; 49162306a36Sopenharmony_ci struct davinci_gpio_irq_data *irqdata; 49262306a36Sopenharmony_ci gpio_get_irq_chip_cb_t gpio_get_irq_chip; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* 49562306a36Sopenharmony_ci * Use davinci_gpio_get_irq_chip by default to handle non DT cases 49662306a36Sopenharmony_ci */ 49762306a36Sopenharmony_ci gpio_get_irq_chip = davinci_gpio_get_irq_chip; 49862306a36Sopenharmony_ci match = of_match_device(of_match_ptr(davinci_gpio_ids), 49962306a36Sopenharmony_ci dev); 50062306a36Sopenharmony_ci if (match) 50162306a36Sopenharmony_ci gpio_get_irq_chip = (gpio_get_irq_chip_cb_t)match->data; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci ngpio = pdata->ngpio; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci clk = devm_clk_get(dev, "gpio"); 50662306a36Sopenharmony_ci if (IS_ERR(clk)) { 50762306a36Sopenharmony_ci dev_err(dev, "Error %ld getting gpio clock\n", PTR_ERR(clk)); 50862306a36Sopenharmony_ci return PTR_ERR(clk); 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci ret = clk_prepare_enable(clk); 51262306a36Sopenharmony_ci if (ret) 51362306a36Sopenharmony_ci return ret; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (!pdata->gpio_unbanked) { 51662306a36Sopenharmony_ci irq = devm_irq_alloc_descs(dev, -1, 0, ngpio, 0); 51762306a36Sopenharmony_ci if (irq < 0) { 51862306a36Sopenharmony_ci dev_err(dev, "Couldn't allocate IRQ numbers\n"); 51962306a36Sopenharmony_ci clk_disable_unprepare(clk); 52062306a36Sopenharmony_ci return irq; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci irq_domain = irq_domain_add_legacy(dev->of_node, ngpio, irq, 0, 52462306a36Sopenharmony_ci &davinci_gpio_irq_ops, 52562306a36Sopenharmony_ci chips); 52662306a36Sopenharmony_ci if (!irq_domain) { 52762306a36Sopenharmony_ci dev_err(dev, "Couldn't register an IRQ domain\n"); 52862306a36Sopenharmony_ci clk_disable_unprepare(clk); 52962306a36Sopenharmony_ci return -ENODEV; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* 53462306a36Sopenharmony_ci * Arrange gpiod_to_irq() support, handling either direct IRQs or 53562306a36Sopenharmony_ci * banked IRQs. Having GPIOs in the first GPIO bank use direct 53662306a36Sopenharmony_ci * IRQs, while the others use banked IRQs, would need some setup 53762306a36Sopenharmony_ci * tweaks to recognize hardware which can do that. 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ci chips->chip.to_irq = gpio_to_irq_banked; 54062306a36Sopenharmony_ci chips->irq_domain = irq_domain; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* 54362306a36Sopenharmony_ci * AINTC can handle direct/unbanked IRQs for GPIOs, with the GPIO 54462306a36Sopenharmony_ci * controller only handling trigger modes. We currently assume no 54562306a36Sopenharmony_ci * IRQ mux conflicts; gpio_irq_type_unbanked() is only for GPIOs. 54662306a36Sopenharmony_ci */ 54762306a36Sopenharmony_ci if (pdata->gpio_unbanked) { 54862306a36Sopenharmony_ci /* pass "bank 0" GPIO IRQs to AINTC */ 54962306a36Sopenharmony_ci chips->chip.to_irq = gpio_to_irq_unbanked; 55062306a36Sopenharmony_ci chips->gpio_unbanked = pdata->gpio_unbanked; 55162306a36Sopenharmony_ci binten = GENMASK(pdata->gpio_unbanked / 16, 0); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* AINTC handles mask/unmask; GPIO handles triggering */ 55462306a36Sopenharmony_ci irq = chips->irqs[0]; 55562306a36Sopenharmony_ci irq_chip = gpio_get_irq_chip(irq); 55662306a36Sopenharmony_ci irq_chip->name = "GPIO-AINTC"; 55762306a36Sopenharmony_ci irq_chip->irq_set_type = gpio_irq_type_unbanked; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* default trigger: both edges */ 56062306a36Sopenharmony_ci g = chips->regs[0]; 56162306a36Sopenharmony_ci writel_relaxed(~0, &g->set_falling); 56262306a36Sopenharmony_ci writel_relaxed(~0, &g->set_rising); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* set the direct IRQs up to use that irqchip */ 56562306a36Sopenharmony_ci for (gpio = 0; gpio < pdata->gpio_unbanked; gpio++) { 56662306a36Sopenharmony_ci irq_set_chip(chips->irqs[gpio], irq_chip); 56762306a36Sopenharmony_ci irq_set_handler_data(chips->irqs[gpio], chips); 56862306a36Sopenharmony_ci irq_set_status_flags(chips->irqs[gpio], 56962306a36Sopenharmony_ci IRQ_TYPE_EDGE_BOTH); 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci goto done; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* 57662306a36Sopenharmony_ci * Or, AINTC can handle IRQs for banks of 16 GPIO IRQs, which we 57762306a36Sopenharmony_ci * then chain through our own handler. 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_ci for (gpio = 0, bank = 0; gpio < ngpio; bank++, gpio += 16) { 58062306a36Sopenharmony_ci /* disabled by default, enabled only as needed 58162306a36Sopenharmony_ci * There are register sets for 32 GPIOs. 2 banks of 16 58262306a36Sopenharmony_ci * GPIOs are covered by each set of registers hence divide by 2 58362306a36Sopenharmony_ci */ 58462306a36Sopenharmony_ci g = chips->regs[bank / 2]; 58562306a36Sopenharmony_ci writel_relaxed(~0, &g->clr_falling); 58662306a36Sopenharmony_ci writel_relaxed(~0, &g->clr_rising); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* 58962306a36Sopenharmony_ci * Each chip handles 32 gpios, and each irq bank consists of 16 59062306a36Sopenharmony_ci * gpio irqs. Pass the irq bank's corresponding controller to 59162306a36Sopenharmony_ci * the chained irq handler. 59262306a36Sopenharmony_ci */ 59362306a36Sopenharmony_ci irqdata = devm_kzalloc(&pdev->dev, 59462306a36Sopenharmony_ci sizeof(struct 59562306a36Sopenharmony_ci davinci_gpio_irq_data), 59662306a36Sopenharmony_ci GFP_KERNEL); 59762306a36Sopenharmony_ci if (!irqdata) { 59862306a36Sopenharmony_ci clk_disable_unprepare(clk); 59962306a36Sopenharmony_ci return -ENOMEM; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci irqdata->regs = g; 60362306a36Sopenharmony_ci irqdata->bank_num = bank; 60462306a36Sopenharmony_ci irqdata->chip = chips; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci irq_set_chained_handler_and_data(chips->irqs[bank], 60762306a36Sopenharmony_ci gpio_irq_handler, irqdata); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci binten |= BIT(bank); 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cidone: 61362306a36Sopenharmony_ci /* 61462306a36Sopenharmony_ci * BINTEN -- per-bank interrupt enable. genirq would also let these 61562306a36Sopenharmony_ci * bits be set/cleared dynamically. 61662306a36Sopenharmony_ci */ 61762306a36Sopenharmony_ci writel_relaxed(binten, gpio_base + BINTEN); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci return 0; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic void davinci_gpio_save_context(struct davinci_gpio_controller *chips, 62362306a36Sopenharmony_ci u32 nbank) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct davinci_gpio_regs __iomem *g; 62662306a36Sopenharmony_ci struct davinci_gpio_regs *context; 62762306a36Sopenharmony_ci u32 bank; 62862306a36Sopenharmony_ci void __iomem *base; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci base = chips->regs[0] - offset_array[0]; 63162306a36Sopenharmony_ci chips->binten_context = readl_relaxed(base + BINTEN); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci for (bank = 0; bank < nbank; bank++) { 63462306a36Sopenharmony_ci g = chips->regs[bank]; 63562306a36Sopenharmony_ci context = &chips->context[bank]; 63662306a36Sopenharmony_ci context->dir = readl_relaxed(&g->dir); 63762306a36Sopenharmony_ci context->set_data = readl_relaxed(&g->set_data); 63862306a36Sopenharmony_ci context->set_rising = readl_relaxed(&g->set_rising); 63962306a36Sopenharmony_ci context->set_falling = readl_relaxed(&g->set_falling); 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci /* Clear all interrupt status registers */ 64362306a36Sopenharmony_ci writel_relaxed(GENMASK(31, 0), &g->intstat); 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic void davinci_gpio_restore_context(struct davinci_gpio_controller *chips, 64762306a36Sopenharmony_ci u32 nbank) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci struct davinci_gpio_regs __iomem *g; 65062306a36Sopenharmony_ci struct davinci_gpio_regs *context; 65162306a36Sopenharmony_ci u32 bank; 65262306a36Sopenharmony_ci void __iomem *base; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci base = chips->regs[0] - offset_array[0]; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (readl_relaxed(base + BINTEN) != chips->binten_context) 65762306a36Sopenharmony_ci writel_relaxed(chips->binten_context, base + BINTEN); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci for (bank = 0; bank < nbank; bank++) { 66062306a36Sopenharmony_ci g = chips->regs[bank]; 66162306a36Sopenharmony_ci context = &chips->context[bank]; 66262306a36Sopenharmony_ci if (readl_relaxed(&g->dir) != context->dir) 66362306a36Sopenharmony_ci writel_relaxed(context->dir, &g->dir); 66462306a36Sopenharmony_ci if (readl_relaxed(&g->set_data) != context->set_data) 66562306a36Sopenharmony_ci writel_relaxed(context->set_data, &g->set_data); 66662306a36Sopenharmony_ci if (readl_relaxed(&g->set_rising) != context->set_rising) 66762306a36Sopenharmony_ci writel_relaxed(context->set_rising, &g->set_rising); 66862306a36Sopenharmony_ci if (readl_relaxed(&g->set_falling) != context->set_falling) 66962306a36Sopenharmony_ci writel_relaxed(context->set_falling, &g->set_falling); 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic int davinci_gpio_suspend(struct device *dev) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci struct davinci_gpio_controller *chips = dev_get_drvdata(dev); 67662306a36Sopenharmony_ci struct davinci_gpio_platform_data *pdata = dev_get_platdata(dev); 67762306a36Sopenharmony_ci u32 nbank = DIV_ROUND_UP(pdata->ngpio, 32); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci davinci_gpio_save_context(chips, nbank); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci return 0; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic int davinci_gpio_resume(struct device *dev) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci struct davinci_gpio_controller *chips = dev_get_drvdata(dev); 68762306a36Sopenharmony_ci struct davinci_gpio_platform_data *pdata = dev_get_platdata(dev); 68862306a36Sopenharmony_ci u32 nbank = DIV_ROUND_UP(pdata->ngpio, 32); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci davinci_gpio_restore_context(chips, nbank); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci return 0; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(davinci_gpio_dev_pm_ops, davinci_gpio_suspend, 69662306a36Sopenharmony_ci davinci_gpio_resume); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic const struct of_device_id davinci_gpio_ids[] = { 69962306a36Sopenharmony_ci { .compatible = "ti,keystone-gpio", keystone_gpio_get_irq_chip}, 70062306a36Sopenharmony_ci { .compatible = "ti,am654-gpio", keystone_gpio_get_irq_chip}, 70162306a36Sopenharmony_ci { .compatible = "ti,dm6441-gpio", davinci_gpio_get_irq_chip}, 70262306a36Sopenharmony_ci { /* sentinel */ }, 70362306a36Sopenharmony_ci}; 70462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, davinci_gpio_ids); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic struct platform_driver davinci_gpio_driver = { 70762306a36Sopenharmony_ci .probe = davinci_gpio_probe, 70862306a36Sopenharmony_ci .driver = { 70962306a36Sopenharmony_ci .name = "davinci_gpio", 71062306a36Sopenharmony_ci .pm = pm_sleep_ptr(&davinci_gpio_dev_pm_ops), 71162306a36Sopenharmony_ci .of_match_table = of_match_ptr(davinci_gpio_ids), 71262306a36Sopenharmony_ci }, 71362306a36Sopenharmony_ci}; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci/* 71662306a36Sopenharmony_ci * GPIO driver registration needs to be done before machine_init functions 71762306a36Sopenharmony_ci * access GPIO. Hence davinci_gpio_drv_reg() is a postcore_initcall. 71862306a36Sopenharmony_ci */ 71962306a36Sopenharmony_cistatic int __init davinci_gpio_drv_reg(void) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci return platform_driver_register(&davinci_gpio_driver); 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_cipostcore_initcall(davinci_gpio_drv_reg); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic void __exit davinci_gpio_exit(void) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci platform_driver_unregister(&davinci_gpio_driver); 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_cimodule_exit(davinci_gpio_exit); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ciMODULE_AUTHOR("Jan Kotas <jank@cadence.com>"); 73262306a36Sopenharmony_ciMODULE_DESCRIPTION("DAVINCI GPIO driver"); 73362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 73462306a36Sopenharmony_ciMODULE_ALIAS("platform:gpio-davinci"); 735