Commit bc7c3efe authored by Federico Vaga's avatar Federico Vaga

drv:gn412x-gpio: move IRQ support in here

Signed-off-by: Federico Vaga's avatarFederico Vaga <federico.vaga@cern.ch>
parent b1ca3364
......@@ -31,7 +31,6 @@ obj-m := spec.o
spec-objs := spec-core.o
spec-objs += spec-fpga.o
spec-objs += spec-irq.o
spec-objs += spec-dbg.o
spec-objs += spec-fmc.o
spec-objs += spec-compat.o
......
......@@ -9,6 +9,8 @@
#include "spec.h"
#include "spec-compat.h"
#define GN412X_INT_CFG_MAX 7
enum gn412x_gpio_versions {
GN412X_VER = 0,
};
......@@ -107,9 +109,285 @@ static void gn412x_gpio_set(struct gpio_chip *chip,
gn412x_gpio_reg_write(chip, GNGPIO_OUTPUT_VALUE, offset, value);
}
int gn412x_gpio_init(struct gn412x_dev *gn412x)
/**
* (disable)
*/
static void gn412x_gpio_irq_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct gn412x_dev *gn412x = to_gn412x_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_dev *gn412x = to_gn412x_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);
/*
* detect errors:
* - level and edge together cannot work
*/
if ((flow_type & IRQ_TYPE_LEVEL_MASK) &&
(flow_type & IRQ_TYPE_EDGE_BOTH)) {
dev_err(gc->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 (default)? */
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
*/
}
static struct irq_chip gn412x_gpio_irq_chip = {
.name = "GN412X-GPIO",
.irq_startup = gn412x_gpio_irq_startup,
.irq_disable = gn412x_gpio_irq_disable,
.irq_mask = gn412x_gpio_irq_mask,
.irq_unmask = gn412x_gpio_irq_unmask,
.irq_set_type = gn412x_gpio_irq_set_type,
.irq_ack = gn412x_gpio_irq_ack,
};
/**
* 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_dev *gn412x = arg;
/* Ack the interrupts */
gn412x_ioread32(gn412x, GNINT_STAT);
gn412x_iowrite32(gn412x, 0x0000, GNINT_STAT);
complete(&gn412x->compl);
return IRQ_HANDLED;
}
/**
* Enable GPIO interrupts
* @gn412x gn412x device
* @cfg_n interrupt configuration register number
*
* Return: 0 on success, otherwise a negative error number
*/
int gn412x_int_gpio_enable(struct gn412x_dev *gn412x, unsigned int cfg_n)
{
uint32_t int_cfg;
if (WARN(cfg_n > GN412X_INT_CFG_MAX, "Unexistent GN412X INT_CFG(%d)",
cfg_n))
return -EINVAL;
gn412x->int_cfg_gpio = cfg_n;
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(gn412x->int_cfg_gpio));
int_cfg |= GNINT_STAT_GPIO;
gn412x_iowrite32(gn412x, int_cfg, GNINT_CFG(gn412x->int_cfg_gpio));
return 0;
}
/**
* Disable GPIO interrupts from a single configuration space
* @gn412x gn412x device
*
* Return: 0 on success, otherwise a negative error number
*/
void gn412x_int_gpio_disable(struct gn412x_dev *gn412x)
{
uint32_t int_cfg;
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(gn412x->int_cfg_gpio));
int_cfg &= ~GNINT_STAT_GPIO;
gn412x_iowrite32(gn412x, int_cfg, GNINT_CFG(gn412x->int_cfg_gpio));
}
/**
* Disable GPIO interrupts from all configuration spaces
* @gn412x gn412x device
*/
static void gn412x_int_gpio_disable_all(struct gn412x_dev *gn412x)
{
int i;
for (i = 0; i <= GN412X_INT_CFG_MAX; ++i) {
uint32_t int_cfg;
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(i));
int_cfg &= ~GNINT_STAT_GPIO;
gn412x_iowrite32(gn412x, int_cfg, GNINT_CFG(i));
}
}
/**
* Handle IRQ from the GPIO block
*/
static irqreturn_t gn412x_gpio_irq_handler_t(int irq, void *arg)
{
int err;
struct gn412x_dev *gn412x = arg;
struct gpio_chip *gc = &gn412x->gpiochip;
unsigned int cascade_irq;
uint32_t gpio_int_status, int_cfg;
unsigned long loop;
irqreturn_t ret = IRQ_NONE;
int i;
gpio_int_status = gn412x_ioread32(gn412x, GNGPIO_INT_STATUS);
if (!gpio_int_status)
goto out_enable_irq;
loop = gpio_int_status;
for_each_set_bit(i, &loop, GN4124_GPIO_MAX) {
cascade_irq = irq_find_mapping(gc->irqdomain, i);
/*
* 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);
}
ret = IRQ_HANDLED;
out_enable_irq:
/* Re-enable the GPIO interrupts, we are done here */
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(gn412x->int_cfg_gpio));
int_cfg |= GNINT_STAT_GPIO;
gn412x_iowrite32(gn412x, int_cfg, GNINT_CFG(gn412x->int_cfg_gpio));
return ret;
}
static irqreturn_t gn412x_gpio_irq_handler_h(int irq, void *arg)
{
struct gn412x_dev *gn412x = arg;
uint32_t int_stat, int_cfg;
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(gn412x->int_cfg_gpio));
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);
/*
* 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).
*/
int_cfg &= ~GNINT_STAT_GPIO;
gn412x_iowrite32(gn412x, int_cfg, GNINT_CFG(gn412x->int_cfg_gpio));
return IRQ_WAKE_THREAD;
}
static void gn412x_gpio_irq_set_nested_thread(struct gn412x_dev *gn412x,
unsigned int gpio, bool nest)
{
int irq;
irq= irq_find_mapping(gn412x->gpiochip.irqdomain, gpio);
irq_set_nested_thread(irq, nest);
}
static void gn412x_gpio_irq_set_nested_thread_all(struct gn412x_dev *gn412x,
bool nest)
{
int i;
for (i = 0; i < GN4124_GPIO_MAX; ++i)
gn412x_gpio_irq_set_nested_thread(gn412x, i, nest);
}
int gn412x_gpio_init(struct device *parent, struct gn412x_dev *gn412x)
{
int err, irq;
memset(&gn412x->gpiochip, 0, sizeof(gn412x->gpiochip));
gn412x->gpiochip.dev = parent;
......@@ -124,14 +402,54 @@ int gn412x_gpio_init(struct gn412x_dev *gn412x)
gn412x->gpiochip.set = gn412x_gpio_set,
gn412x->gpiochip.base = -1,
gn412x->gpiochip.ngpio = GN4124_GPIO_MAX,
gn412x->gpiochip.can_sleep = 0;
err = gpiochip_add(&gn412x->gpiochip);
if (err)
return err;
goto err_add;
gn412x_int_gpio_disable_all(gn412x);
gn412x_iowrite32(gn412x, 0xFFFF, GNGPIO_INT_MASK_SET);
err = gpiochip_irqchip_add(&gn412x->gpiochip,
&gn412x_gpio_irq_chip,
0, handle_simple_irq,
IRQ_TYPE_NONE);
if (err)
goto err_add_irq;
gn412x_gpio_irq_set_nested_thread_all(gn412x, true);
irq = to_pci_dev(gn412x->gpiochip.dev->parent)->irq;
err = request_threaded_irq(irq,
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",
irq, err);
goto err_req;
}
return 0;
err_req:
gn412x_gpio_irq_set_nested_thread_all(gn412x, false);
gpiochip_irqchip_remove(&gn412x->gpiochip);
err_add_irq:
gpiochip_remove(&gn412x->gpiochip);
err_add:
return err;
}
void gn412x_gpio_exit(struct gn412x_dev *gn412x)
{
int irq;
gn412x_int_gpio_disable_all(gn412x);
irq = to_pci_dev(gn412x->gpiochip.dev->parent)->irq;
free_irq(irq, gn412x);
gn412x_gpio_irq_set_nested_thread_all(gn412x, false);
gpiochip_irqchip_remove(&gn412x->gpiochip);
gpiochip_remove(&gn412x->gpiochip);
}
......@@ -216,4 +216,17 @@ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table)
mutex_unlock(gpio_lookup_lock_p);
}
void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
{
void (*gpiochip_irqchip_remove_p)(struct gpio_chip *gpiochip);
gpiochip_irqchip_remove_p = (void *) kallsyms_lookup_name("gpiochip_irqchip_remove");
if (gpiochip_irqchip_remove_p)
gpiochip_irqchip_remove_p(gpiochip);
else
WARN(1, "Cannot find 'gpiochip_irqchip_remove'");
}
#endif
......@@ -81,4 +81,5 @@ extern int compat_gpiod_add_lookup_table(struct gpiod_lookup_table *table);
#if KERNEL_VERSION(4, 3, 0) > LINUX_VERSION_CODE
extern void gpiod_remove_lookup_table(struct gpiod_lookup_table *table);
extern void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);
#endif
......@@ -145,7 +145,7 @@ static int spec_probe(struct pci_dev *pdev,
spec->dev.driver = pdev->dev.driver;
spec->gn412x.mem = spec->remap[2];
err = gn412x_gpio_init(&spec->gn412x);
err = gn412x_gpio_init(&spec->dev, &spec->gn412x);
if (err)
goto err_ggpio;
......@@ -157,10 +157,6 @@ static int spec_probe(struct pci_dev *pdev,
if (err)
goto err_fpga;
err = spec_irq_init(spec);
if (err)
goto err_irq;
err = spec_fw_load_init(spec);
if (err)
goto err_fw;
......@@ -178,8 +174,6 @@ static int spec_probe(struct pci_dev *pdev,
err_fmc:
err_fw:
spec_irq_exit(spec);
err_irq:
spec_fpga_exit(spec);
err_fpga:
spec_gpio_exit(spec);
......@@ -208,7 +202,6 @@ static void spec_remove(struct pci_dev *pdev)
spec_dbg_exit(spec);
spec_fmc_exit(spec);
spec_irq_exit(spec);
spec_fpga_exit(spec);
spec_gpio_exit(spec);
gn412x_gpio_exit(&spec->gn412x);
......
......@@ -6,25 +6,25 @@
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/gpio/consumer.h>
#include "spec.h"
#include "spec-compat.h"
static int spec_irq_dbg_info(struct seq_file *s, void *offset)
{
struct spec_dev *spec = s->private;
int i;
seq_printf(s, "'%s':\n",dev_name(spec->dev.parent));
seq_printf(s, " redirect: %d\n", to_pci_dev(spec->dev.parent)->irq);
seq_printf(s, " irq-mapping:\n");
for (i = 0; i < GN4124_GPIO_IRQ_MAX; ++i) {
seq_printf(s, " - hardware: %d\n", i);
seq_printf(s, " linux: %d\n",
irq_find_mapping(spec->gpio_domain, i));
}
seq_printf(s, " - hardware: 8\n");
seq_printf(s, " linux: %d\n",
gpiod_to_irq(spec->gpiod[GN4124_GPIO_IRQ0]));
seq_printf(s, " - hardware: 9\n");
seq_printf(s, " linux: %d\n",
gpiod_to_irq(spec->gpiod[GN4124_GPIO_IRQ1]));
return 0;
}
......
......@@ -105,8 +105,39 @@ int spec_gpio_init(struct spec_dev *spec)
if (err)
goto err_out1;
spec->gpiod[GN4124_GPIO_IRQ0] = gpiod_get_index(&spec->dev,
"irq", 0,
GPIOD_IN);
if (IS_ERR(spec->gpiod[GN4124_GPIO_IRQ0])) {
err = PTR_ERR(spec->gpiod[GN4124_GPIO_IRQ0]);
goto err_irq0;
}
spec->gpiod[GN4124_GPIO_IRQ1] = gpiod_get_index(&spec->dev,
"irq", 1,
GPIOD_IN);
if (IS_ERR(spec->gpiod[GN4124_GPIO_IRQ1])) {
err = PTR_ERR(spec->gpiod[GN4124_GPIO_IRQ1]);
goto err_irq1;
}
/* Because of a BUG in RedHat kernel 3.10 we re-set direction */
err = gpiod_direction_input(spec->gpiod[GN4124_GPIO_IRQ0]);
if (err)
goto err_in0;
err = gpiod_direction_input(spec->gpiod[GN4124_GPIO_IRQ1]);
if (err)
goto err_in1;
gn412x_int_gpio_enable(&spec->gn412x, 0);
return 0;
err_in1:
err_in0:
gpiod_put(spec->gpiod[GN4124_GPIO_IRQ1]);
err_irq1:
gpiod_put(spec->gpiod[GN4124_GPIO_IRQ0]);
err_irq0:
err_out1:
err_out0:
gpiod_put(spec->gpiod[GN4124_GPIO_BOOTSEL1]);
......@@ -125,6 +156,10 @@ err_dup:
void spec_gpio_exit(struct spec_dev *spec)
{
gn412x_int_gpio_disable(&spec->gn412x);
gpiod_put(spec->gpiod[GN4124_GPIO_IRQ0]);
gpiod_put(spec->gpiod[GN4124_GPIO_IRQ1]);
gpiod_put(spec->gpiod[GN4124_GPIO_BOOTSEL0]);
gpiod_put(spec->gpiod[GN4124_GPIO_BOOTSEL1]);
......
This diff is collapsed.
......@@ -58,8 +58,6 @@ enum spec_fpga_select {
SPEC_FPGA_SELECT_SPI,
};
#define GN4124_GPIO_IRQ_MAX 16
/* Registers for GN4124 access */
enum {
/* page 106 */
......@@ -126,10 +124,17 @@ enum {
#define GNINT_STAT_SW1 BIT(3)
#define GNINT_STAT_SW_ALL (GNINT_STAT_SW0 | GNINT_STAT_SW1)
/**
* struct gn412x_dev GN412X device descriptor
* @compl: for IRQ testing
* @int_cfg_gpio: INT_CFG used for GPIO interrupts
*/
struct gn412x_dev {
void __iomem *mem;
struct gpio_chip gpiochip;
struct completion compl;
int int_cfg_gpio;
};
static inline struct gn412x_dev *to_gn412x_dev_gpio(struct gpio_chip *chip)
......@@ -141,19 +146,15 @@ static inline struct gn412x_dev *to_gn412x_dev_gpio(struct gpio_chip *chip)
* struct spec_dev - SPEC instance
* It describes a SPEC device instance.
* @dev Linux device instance descriptor
* @gpio_domain: IRQ domain for GN4124 chip
* @flags collection of bit flags
* @remap ioremap of PCI bar 0, 2, 4
* @slot_info: information about FMC slot
* @i2c_pdev: platform device for I2C master
* @i2c_adapter: the I2C master device to be used
* @compl: for IRQ testing
*/
struct spec_dev {
struct device dev;
struct irq_domain *gpio_domain;
struct fpga_manager *mgr;
DECLARE_BITMAP(flags, SPEC_FLAG_BITS);
......@@ -169,8 +170,6 @@ struct spec_dev {
#define SPEC_DBG_FW_NAME "fpga_firmware"
struct dentry *dbg_fw;
struct completion compl;
struct gn412x_dev gn412x;
struct gpiod_lookup_table *gpiod_table;
......@@ -223,8 +222,11 @@ static inline void gennum_mask_val(struct spec_dev *spec,
gennum_writel(spec, v, reg);
}
extern int gn412x_gpio_init(struct gn412x_dev *spec);
extern int gn412x_gpio_init(struct device *parent, struct gn412x_dev *spec);
extern void gn412x_gpio_exit(struct gn412x_dev *spec);
extern int gn412x_int_gpio_enable(struct gn412x_dev *gn412x,
unsigned int cfg_n);
extern void gn412x_int_gpio_disable(struct gn412x_dev *gn412x);
extern int spec_fpga_init(struct spec_dev *spec);
extern void spec_fpga_exit(struct spec_dev *spec);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment