Newer
Older
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <linux/module.h>
#include <linux/gpio/driver.h>
#include "gn412x.h"
#include "platform_data/gn412x-gpio.h"
/**
* struct gn412x_gpio_dev GN412X device descriptor
* @compl: for IRQ testing
* @int_cfg_gpio: INT_CFG used for GPIO interrupts
*/
struct gn412x_gpio_dev {
void __iomem *mem;
struct gpio_chip gpiochip;
struct irq_chip irqchip;
struct completion compl;
struct gn412x_platform_data *pdata;
struct dentry *dbg_dir;
#define GN412X_DBG_REG_NAME "regs"
struct dentry *dbg_reg;
struct debugfs_regset32 dbg_reg32;
};
static struct gn412x_platform_data gn412x_gpio_pdata_default = {
.int_cfg = 0,
};
static inline struct gn412x_gpio_dev *to_gn412x_gpio_dev_gpio(struct gpio_chip *chip)
{
return container_of(chip, struct gn412x_gpio_dev, gpiochip);
}
enum gn412x_gpio_versions {
GN412X_VER = 0,
};
enum htvic_mem_resources {
GN412X_MEM_BASE = 0,
};
#define REG32(_name, _offset) {.name = _name, .offset = _offset}
static const struct debugfs_reg32 gn412x_debugfs_reg32[] = {
REG32("INT_CTRL", GNINT_CTRL),
REG32("INT_STAT", GNINT_STAT),
REG32("INT_CFG0", GNINT_CFG_0),
REG32("INT_CFG1", GNINT_CFG_1),
REG32("INT_CFG2", GNINT_CFG_2),
REG32("INT_CFG3", GNINT_CFG_3),
REG32("INT_CFG4", GNINT_CFG_4),
REG32("INT_CFG5", GNINT_CFG_5),
REG32("INT_CFG6", GNINT_CFG_6),
REG32("INT_CFG7", GNINT_CFG_7),
REG32("GPIO_BYPASS_MODE", GNGPIO_BYPASS_MODE),
REG32("GPIO_DIRECTION_MODE", GNGPIO_DIRECTION_MODE),
REG32("GPIO_OUTPUT_ENABLE", GNGPIO_OUTPUT_ENABLE),
REG32("GPIO_OUTPUT_VALUE", GNGPIO_OUTPUT_VALUE),
REG32("GPIO_INPUT_VALUE", GNGPIO_INPUT_VALUE),
REG32("GPIO_INT_MASK", GNGPIO_INT_MASK),
REG32("GPIO_INT_MASK_CLR", GNGPIO_INT_MASK_CLR),
REG32("GPIO_INT_MASK_SET", GNGPIO_INT_MASK_SET),
REG32("GPIO_INT_STATUS", GNGPIO_INT_STATUS),
REG32("GPIO_INT_TYPE", GNGPIO_INT_TYPE),
REG32("GPIO_INT_VALUE", GNGPIO_INT_VALUE),
REG32("GPIO_INT_ON_ANY", GNGPIO_INT_ON_ANY),
};
static int gn412x_dbg_init(struct gn412x_gpio_dev *gn412x)
{
struct dentry *dir;
#if KERNEL_VERSION(5, 6, 0) > LINUX_VERSION_CODE
struct dentry *file;
#endif
#if KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE
struct device *dev = gn412x->gpiochip.dev;
#else
struct device *dev = gn412x->gpiochip.parent;
#endif
dir = debugfs_create_dir(dev_name(dev), NULL);
if (IS_ERR_OR_NULL(dir)) {
err = PTR_ERR(dir);
dev_warn(dev,
"Cannot create debugfs directory \"%s\" (%d)\n",
dev_name(dev), err);
goto err_dir;
}
gn412x->dbg_reg32.regs = gn412x_debugfs_reg32;
gn412x->dbg_reg32.nregs = ARRAY_SIZE(gn412x_debugfs_reg32);
gn412x->dbg_reg32.base = gn412x->mem;
#if KERNEL_VERSION(5, 6, 0) <= LINUX_VERSION_CODE
debugfs_create_regset32(GN412X_DBG_REG_NAME, 0200,
dir, &gn412x->dbg_reg32);
#else
file = debugfs_create_regset32(GN412X_DBG_REG_NAME, 0200,
dir, &gn412x->dbg_reg32);
if (IS_ERR_OR_NULL(file)) {
err = PTR_ERR(file);
dev_warn(dev,
"Cannot create debugfs file \"%s\" (%d)\n",
GN412X_DBG_REG_NAME, err);
goto err_reg32;
}
gn412x->dbg_reg = file;
#endif
gn412x->dbg_dir = dir;
return 0;
#if KERNEL_VERSION(5, 6, 0) > LINUX_VERSION_CODE
err_reg32:
debugfs_remove_recursive(dir);
err_dir:
return err;
}
static void gn412x_dbg_exit(struct gn412x_gpio_dev *gn412x)
{
debugfs_remove_recursive(gn412x->dbg_dir);
}
static uint32_t gn412x_ioread32(struct gn412x_gpio_dev *gn412x,
int reg)
{
return ioread32(gn412x->mem + reg);
}
static void gn412x_iowrite32(struct gn412x_gpio_dev *gn412x,
uint32_t val, int reg)
{
return iowrite32(val, gn412x->mem + reg);
}
static int gn412x_gpio_reg_read(struct gpio_chip *chip,
struct gn412x_gpio_dev *gn412x = to_gn412x_gpio_dev_gpio(chip);
return gn412x_ioread32(gn412x, reg) & BIT(offset);
}
static void gn412x_gpio_reg_write(struct gpio_chip *chip,
struct gn412x_gpio_dev *gn412x = to_gn412x_gpio_dev_gpio(chip);
regval = gn412x_ioread32(gn412x, reg);
if (value)
regval |= BIT(offset);
else
regval &= ~BIT(offset);
gn412x_iowrite32(gn412x, regval, reg);
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/**
* Enable Internal Gennum error's interrupts
* @gn412x gn412x device
*
* Return: 0 on success, otherwise a negative error number
*/
static void gn412x_gpio_int_cfg_enable_err(struct gn412x_gpio_dev *gn412x)
{
uint32_t int_cfg;
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(gn412x->pdata->int_cfg));
int_cfg |= GNINT_STAT_ERR_ALL;
gn412x_iowrite32(gn412x, int_cfg, GNINT_CFG(gn412x->pdata->int_cfg));
}
/**
* disable Internal Gennum error's interrupts
* @gn412x gn412x device
*
* Return: 0 on success, otherwise a negative error number
*/
static void gn412x_gpio_int_cfg_disable_err(struct gn412x_gpio_dev *gn412x)
{
uint32_t int_cfg;
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(gn412x->pdata->int_cfg));
int_cfg &= ~GNINT_STAT_ERR_ALL;
gn412x_iowrite32(gn412x, int_cfg, GNINT_CFG(gn412x->pdata->int_cfg));
}
/**
* Enable GPIO interrupts
* @gn412x gn412x device
*
* Return: 0 on success, otherwise a negative error number
*/
static void gn412x_gpio_int_cfg_enable_gpio(struct gn412x_gpio_dev *gn412x)
{
uint32_t int_cfg;
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(gn412x->pdata->int_cfg));
int_cfg |= GNINT_STAT_GPIO;
gn412x_iowrite32(gn412x, int_cfg, GNINT_CFG(gn412x->pdata->int_cfg));
}
/**
* Disable GPIO interrupts from a single configuration space
* @gn412x gn412x device
*/
static void gn412x_gpio_int_cfg_disable_gpio(struct gn412x_gpio_dev *gn412x)
{
uint32_t int_cfg;
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(gn412x->pdata->int_cfg));
int_cfg &= ~GNINT_STAT_GPIO;
gn412x_iowrite32(gn412x, int_cfg, GNINT_CFG(gn412x->pdata->int_cfg));
}
static int gn412x_gpio_request(struct gpio_chip *chip, unsigned int offset)
#if KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE
struct device *dev = chip->dev;
#else
struct device *dev = chip->parent;
#endif
val = gn412x_gpio_reg_read(chip, GNGPIO_BYPASS_MODE, offset);
if (val) {
dev_err(dev, "%s GPIO %d is in BYPASS mode\n",
chip->label, offset);
return -EBUSY;
}
return 0;
}
static void gn412x_gpio_free(struct gpio_chip *chip, unsigned int offset)
{
/* set it as input to avoid to drive anything */
gn412x_gpio_reg_write(chip, GNGPIO_DIRECTION_MODE, offset, 1);
}
static int gn412x_gpio_get_direction(struct gpio_chip *chip,
return !!gn412x_gpio_reg_read(chip, GNGPIO_DIRECTION_MODE, offset);
}
static int gn412x_gpio_direction_input(struct gpio_chip *chip,
gn412x_gpio_reg_write(chip, GNGPIO_DIRECTION_MODE, offset, 1);
gn412x_gpio_reg_write(chip, GNGPIO_OUTPUT_ENABLE, offset, 0);
return 0;
}
static int gn412x_gpio_direction_output(struct gpio_chip *chip,
gn412x_gpio_reg_write(chip, GNGPIO_DIRECTION_MODE, offset, 0);
gn412x_gpio_reg_write(chip, GNGPIO_OUTPUT_ENABLE, offset, 1);
gn412x_gpio_reg_write(chip, GNGPIO_OUTPUT_VALUE, offset, value);
return 0;
}
static int gn412x_gpio_get(struct gpio_chip *chip,
{
return gn412x_gpio_reg_read(chip, GNGPIO_INPUT_VALUE, offset);
}
static void gn412x_gpio_set(struct gpio_chip *chip,
{
gn412x_gpio_reg_write(chip, GNGPIO_OUTPUT_VALUE, offset, value);
}
/**
* (disable)
*/
static void gn412x_gpio_irq_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct gn412x_gpio_dev *gn412x = to_gn412x_gpio_dev_gpio(gc);
gn412x_iowrite32(gn412x, BIT(d->hwirq), GNGPIO_INT_MASK_SET);
}
/**
* (enable)
*/
static void gn412x_gpio_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct gn412x_gpio_dev *gn412x = to_gn412x_gpio_dev_gpio(gc);
gn412x_iowrite32(gn412x, BIT(d->hwirq), GNGPIO_INT_MASK_CLR);
}
static int gn412x_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
#if KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE
struct device *dev = gc->dev;
#else
struct device *dev = gc->parent;
#endif
/*
* detect errors:
* - level and edge together cannot work
*/
if ((flow_type & IRQ_TYPE_LEVEL_MASK) &&
(flow_type & IRQ_TYPE_EDGE_BOTH)) {
dev_err(dev,
"Impossible to set GPIO IRQ %ld to both LEVEL and EDGE (0x%x)\n",
d->hwirq, flow_type);
return -EINVAL;
}
/* Configure: level or edge (default)? */
if (flow_type & IRQ_TYPE_LEVEL_MASK) {
gn412x_gpio_reg_write(gc, GNGPIO_INT_TYPE, d->hwirq, 1);
} else {
gn412x_gpio_reg_write(gc, GNGPIO_INT_TYPE, d->hwirq, 0);
/* if we want to trigger on any edge */
if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
gn412x_gpio_reg_write(gc, GNGPIO_INT_ON_ANY,
d->hwirq, 1);
}
/* Configure: level-low or falling-edge, level-high or raising-edge */
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING))
gn412x_gpio_reg_write(gc, GNGPIO_INT_VALUE, d->hwirq, 0);
else
gn412x_gpio_reg_write(gc, GNGPIO_INT_VALUE, d->hwirq, 1);
return IRQ_SET_MASK_OK;
}
/**
* A new IRQ interrupt has been requested
* @d IRQ related data
*
* We need to set the GPIO line to be input and disable completely any
* kind of output. We do not want any alternative function (bypass mode).
*/
static unsigned int gn412x_gpio_irq_startup(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
gn412x_gpio_reg_write(gc, GNGPIO_BYPASS_MODE, d->hwirq, 0);
gn412x_gpio_reg_write(gc, GNGPIO_DIRECTION_MODE, d->hwirq, 1);
gn412x_gpio_reg_write(gc, GNGPIO_OUTPUT_ENABLE, d->hwirq, 0);
gn412x_gpio_direction_input(gc, d->hwirq);
/* FIXME in the original code we had this? What is it? */
/* !!(gennum_readl(spec, GNGPIO_INPUT_VALUE) & bit); */
gn412x_gpio_irq_unmask(d);
return 0;
}
/**
* It disables the GPIO interrupt by masking it
*/
static void gn412x_gpio_irq_disable(struct irq_data *d)
{
gn412x_gpio_irq_mask(d);
}
static void gn412x_gpio_irq_ack(struct irq_data *d)
{
/*
* based on HW design,there is no need to ack HW
* before handle current irq. But this routine is
* necessary for handle_edge_irq
}
/**
* This will run in hard-IRQ context since we do not have much to do
*/
static irqreturn_t spec_irq_sw_handler(int irq, void *arg)
{
struct gn412x_gpio_dev *gn412x = arg;
/* Ack the interrupts */
gn412x_ioread32(gn412x, GNINT_STAT);
gn412x_iowrite32(gn412x, 0x0000, GNINT_STAT);
complete(&gn412x->compl);
return IRQ_HANDLED;
}
/**
* Handle IRQ from the GPIO block
*/
static irqreturn_t gn412x_gpio_irq_handler_t(int irq, void *arg)
struct gn412x_gpio_dev *gn412x = arg;
struct gpio_chip *gc = &gn412x->gpiochip;
unsigned int cascade_irq;
uint32_t gpio_int_status, gpio_int_type;
unsigned long loop;
irqreturn_t ret = IRQ_NONE;
int i;
#if KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE
struct device *dev = gc->dev;
#else
struct device *dev = gc->parent;
#endif
gpio_int_status = gn412x_ioread32(gn412x, GNGPIO_INT_STATUS);
gpio_int_status &= ~gn412x_ioread32(gn412x, GNGPIO_INT_MASK);
if (!gpio_int_status)
goto out_enable_irq;
loop = gpio_int_status;
for_each_set_bit(i, &loop, GN4124_GPIO_MAX) {
#if KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE
cascade_irq = irq_find_mapping(gc->irqdomain, i);
#else
cascade_irq = irq_find_mapping(gc->irq.domain, i);
#endif
dev_dbg(dev, "GPIO: %d, IRQ: %d\n", i, cascade_irq);
/*
* Ok, now we execute the handler for the given IRQ. Please
* note that this is not the action requested by the device
* driver but it is the handler defined during the IRQ mapping
*/
handle_nested_irq(cascade_irq);
}
/*
* Level interrupts are cleared only when they are processed. This
* means that at this point (interrupts have been processed) the
* GPIO_INT_STATUS is still set because it is a sticky bit that got
* set again after our first read because we did not handle yet
* the IRQ. For this reason we have to clear it (defentivelly) by
* reading again the register ...
*/
gpio_int_status = gn412x_ioread32(gn412x, GNGPIO_INT_STATUS);
/*
* ... but like this edge interrupts are lost. So write back
* in the register the status of all pending edge interrupts.
* (NOTE: not yet prooven that it works)
*/
gpio_int_type = gn412x_ioread32(gn412x, GNGPIO_INT_TYPE);
gn412x_iowrite32(gn412x, (gpio_int_status & (~gpio_int_type)),
GNGPIO_INT_STATUS);
ret = IRQ_HANDLED;
out_enable_irq:
/* Re-enable the GPIO interrupts, we are done here */
gn412x_gpio_int_cfg_enable_gpio(gn412x);
static bool gn412x_gpio_fpga_is_programmed(struct gn412x_gpio_dev *gn412x)
{
bool done;
done = gn412x_ioread32(gn412x, FCL_STATUS) & FCL_SPRI_DONE;
return done;
}
static irqreturn_t gn412x_gpio_irq_handler_h(int irq, void *arg)
{
struct gn412x_gpio_dev *gn412x = arg;
uint32_t int_stat, int_cfg;
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(gn412x->pdata->int_cfg));
int_stat = gn412x_ioread32(gn412x, GNINT_STAT);
if (unlikely(!(int_stat & int_cfg)))
return IRQ_NONE;
if (unlikely(int_stat & GNINT_STAT_SW_ALL)) /* only for testing */
return spec_irq_sw_handler(irq, gn412x);
if (unlikely(int_stat & GNINT_STAT_ERR_ALL)) {
gn412x_iowrite32(gn412x, int_stat & GNINT_STAT_ERR_ALL,
GNINT_STAT);
if (!gn412x_gpio_fpga_is_programmed(gn412x))
return IRQ_NONE; /* spurious interrupt if FPGA is not programmed */
#if KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE
dev_err(gn412x->gpiochip.dev, "Local bus error 0x%08x\n", int_stat);
#else
dev_err(gn412x->gpiochip.parent, "Local bus error 0x%08x\n", int_stat);
#endif
/*
* Do not listen to new interrupts while handling the current GPIOs.
* This may take a while since the chain behind each GPIO can be long.
* If the IRQ behind is level, we do not want this IRQ handeler to be
* called continuously. But on the other hand we do not want other
* devices sharing the same IRQ to wait for us; just to play safe,
* let's disable interrupts. Within the thread we will re-enable them
* when we are ready (like IRQF_ONESHOT).
*
* We keep the error interrupts enabled
gn412x_gpio_int_cfg_disable_gpio(gn412x);
return IRQ_WAKE_THREAD;
}
static void gn412x_gpio_irq_set_nested_thread(struct gn412x_gpio_dev *gn412x,
unsigned int gpio, bool nest)
{
int irq;
#if KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE
irq = irq_find_mapping(gn412x->gpiochip.irqdomain, gpio);
#else
irq = irq_find_mapping(gn412x->gpiochip.irq.domain, gpio);
#endif
irq_set_nested_thread(irq, nest);
}
static void gn412x_gpio_irq_set_nested_thread_all(struct gn412x_gpio_dev *gn412x,
bool nest)
{
int i;
for (i = 0; i < GN4124_GPIO_MAX; ++i)
gn412x_gpio_irq_set_nested_thread(gn412x, i, nest);
}
static int gn412x_gpio_probe(struct platform_device *pdev)
struct gn412x_gpio_dev *gn412x;
struct resource *r;
int err;
gn412x = kzalloc(sizeof(*gn412x), GFP_KERNEL);
if (!gn412x)
return -ENOMEM;
platform_set_drvdata(pdev, gn412x);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
dev_err(&pdev->dev, "Missing memory resource\n");
err = -EINVAL;
goto err_res_mem;
}
gn412x->mem = ioremap(r->start, resource_size(r));
if (!gn412x->mem) {
err = -EADDRNOTAVAIL;
goto err_map;
}
gn412x->pdata = dev_get_platdata(&pdev->dev);
if (!gn412x->pdata) {
dev_warn(&pdev->dev, "Missing platform data, use default\n");
gn412x->pdata = &gn412x_gpio_pdata_default;
}
gn412x_iowrite32(gn412x, 0, GNGPIO_BYPASS_MODE);
gn412x_gpio_int_cfg_disable_err(gn412x);
gn412x_gpio_int_cfg_disable_gpio(gn412x);
gn412x_iowrite32(gn412x, 0xFFFF, GNGPIO_INT_MASK_SET);
gn412x->irqchip.name = "GN412X-GPIO",
gn412x->irqchip.irq_startup = gn412x_gpio_irq_startup,
gn412x->irqchip.irq_disable = gn412x_gpio_irq_disable,
gn412x->irqchip.irq_mask = gn412x_gpio_irq_mask,
gn412x->irqchip.irq_unmask = gn412x_gpio_irq_unmask,
gn412x->irqchip.irq_set_type = gn412x_gpio_irq_set_type,
gn412x->irqchip.irq_ack = gn412x_gpio_irq_ack,
#if KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE
#else
gn412x->gpiochip.parent = &pdev->dev;
#endif
gn412x->gpiochip.label = "gn412x-gpio";
gn412x->gpiochip.owner = THIS_MODULE;
gn412x->gpiochip.request = gn412x_gpio_request;
gn412x->gpiochip.free = gn412x_gpio_free;
gn412x->gpiochip.get_direction = gn412x_gpio_get_direction;
gn412x->gpiochip.direction_input = gn412x_gpio_direction_input;
gn412x->gpiochip.direction_output = gn412x_gpio_direction_output;
gn412x->gpiochip.get = gn412x_gpio_get;
gn412x->gpiochip.set = gn412x_gpio_set;
gn412x->gpiochip.base = -1;
gn412x->gpiochip.ngpio = GN4124_GPIO_MAX;
gn412x->gpiochip.can_sleep = 0;
#if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE
gn412x->gpiochip.irq.chip = &gn412x->irqchip;
gn412x->gpiochip.irq.first = 0;
gn412x->gpiochip.irq.handler = handle_simple_irq;
gn412x->gpiochip.irq.default_type = IRQ_TYPE_NONE;
gn412x->gpiochip.irq.threaded = true;
#endif
err = gpiochip_add(&gn412x->gpiochip);
if (err)
#if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE
err = gpiochip_irqchip_add(&gn412x->gpiochip,
&gn412x->irqchip,
0, handle_simple_irq,
IRQ_TYPE_NONE);
if (err)
goto err_add_irq;
gn412x_gpio_irq_set_nested_thread_all(gn412x, true);
#if KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE
err = request_threaded_irq(platform_get_irq(pdev, 0),
gn412x_gpio_irq_handler_h,
gn412x_gpio_irq_handler_t,
IRQF_SHARED,
dev_name(gn412x->gpiochip.dev),
gn412x);
if (err) {
dev_err(gn412x->gpiochip.dev, "Can't request IRQ %d (%d)\n",
#else
err = request_threaded_irq(platform_get_irq(pdev, 0),
gn412x_gpio_irq_handler_h,
gn412x_gpio_irq_handler_t,
IRQF_SHARED | IRQF_ONESHOT,
dev_name(gn412x->gpiochip.parent),
gn412x);
if (err) {
dev_err(gn412x->gpiochip.parent, "Can't request IRQ %d (%d)\n",
#endif
gn412x_gpio_int_cfg_enable_err(gn412x);
gn412x_gpio_int_cfg_enable_gpio(gn412x);
err_req:
gn412x_gpio_irq_set_nested_thread_all(gn412x, false);
#if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE
err_add_irq:
gpiochip_remove(&gn412x->gpiochip);
static int gn412x_gpio_remove(struct platform_device *pdev)
struct gn412x_gpio_dev *gn412x = platform_get_drvdata(pdev);
gn412x_gpio_int_cfg_disable_gpio(gn412x);
gn412x_gpio_int_cfg_disable_err(gn412x);
free_irq(platform_get_irq(pdev, 0), gn412x);
gn412x_gpio_irq_set_nested_thread_all(gn412x, false);
dev_dbg(&pdev->dev, "%s\n", __func__);
static const struct platform_device_id gn412x_gpio_id[] = {
{
.name = "gn412x-gpio",
},
};
static struct platform_driver gn412x_gpio_platform_driver = {
.driver = {
.name = KBUILD_MODNAME,
},
.probe = gn412x_gpio_probe,
.remove = gn412x_gpio_remove,
.id_table = gn412x_gpio_id,
};
module_platform_driver(gn412x_gpio_platform_driver);
MODULE_AUTHOR("Federico Vaga <federico.vaga@cern.ch>");
MODULE_DESCRIPTION("GN412X GPIO driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(VERSION);
MODULE_DEVICE_TABLE(platform, gn412x_gpio_id);