// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2019 Intel Corporation */
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/platform_device.h>
#include "core.h"
#include "pinconf.h"
#include "pinmux.h"
#include "pinctrl-equilibrium.h"
#define PIN_NAME_FMT "io-%d"
#define PIN_NAME_LEN 10
#define PAD_REG_OFF 0x100
static void eqbr_gpio_disable_irq(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct eqbr_gpio_ctrl *gctrl = gpiochip_get_data(gc);
unsigned int offset = irqd_to_hwirq(d);
unsigned long flags;
raw_spin_lock_irqsave(&gctrl->lock, flags);
writel(BIT(offset), gctrl->membase + GPIO_IRNENCLR);
raw_spin_unlock_irqrestore(&gctrl->lock, flags);
}
static void eqbr_gpio_enable_irq(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct eqbr_gpio_ctrl *gctrl = gpiochip_get_data(gc);
unsigned int offset = irqd_to_hwirq(d);
unsigned long flags;
gc->direction_input(gc, offset);
raw_spin_lock_irqsave(&gctrl->lock, flags);
writel(BIT(offset), gctrl->membase + GPIO_IRNRNSET);
raw_spin_unlock_irqrestore(&gctrl->lock, flags);
}
static void eqbr_gpio_ack_irq(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct eqbr_gpio_ctrl *gctrl = gpiochip_get_data(gc);
unsigned int offset = irqd_to_hwirq(d);
unsigned long flags;
raw_spin_lock_irqsave(&gctrl->lock, flags);
writel(BIT(offset), gctrl->membase + GPIO_IRNCR);
raw_spin_unlock_irqrestore(&gctrl->lock, flags);
}
static void eqbr_gpio_mask_ack_irq(struct irq_data *d)
{
eqbr_gpio_disable_irq(d);
eqbr_gpio_ack_irq(d);
}
static inline void eqbr_cfg_bit(void __iomem *addr,
unsigned int offset, unsigned int set)
{
if (set)
writel(readl(addr) | BIT(offset), addr);
else
writel(readl(addr) & ~BIT(offset), addr);
}
static int eqbr_irq_type_cfg(struct gpio_irq_type *type,
struct eqbr_gpio_ctrl *gctrl,
unsigned int offset)
{
unsigned long flags;
raw_spin_lock_irqsave(&gctrl->lock, flags);
eqbr_cfg_bit(gctrl->membase + GPIO_IRNCFG, offset, type->trig_type);
eqbr_cfg_bit(gctrl->membase + GPIO_EXINTCR1, offset, type->trig_type);
eqbr_cfg_bit(gctrl->membase + GPIO_EXINTCR0, offset, type->logic_type);
raw_spin_unlock_irqrestore(&gctrl->lock, flags);
return 0;
}
static int eqbr_gpio_set_irq_type(struct irq_data *d, unsigned int type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct eqbr_gpio_ctrl *gctrl = gpiochip_get_data(gc);
unsigned int offset = irqd_to_hwirq(d);
struct gpio_irq_type it;
memset(&it, 0, sizeof(it));
if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_NONE)
return 0;
switch (type) {
case IRQ_TYPE_EDGE_RISING:
it.trig_type = GPIO_EDGE_TRIG;
it.edge_type = GPIO_SINGLE_EDGE;
it.logic_type = GPIO_POSITIVE_TRIG;
break;
case IRQ_TYPE_EDGE_FALLING:
it.trig_type = GPIO_EDGE_TRIG;
it.edge_type = GPIO_SINGLE_EDGE;
it.logic_type = GPIO_NEGATIVE_TRIG;
break;
case IRQ_TYPE_EDGE_BOTH:
it.trig_type = GPIO_EDGE_TRIG;
it.edge_type = GPIO_BOTH_EDGE;
it.logic_type = GPIO_POSITIVE_TRIG;
break;
case IRQ_TYPE_LEVEL_HIGH:
it.trig_type = GPIO_LEVEL_TRIG;
it.edge_type = GPIO_SINGLE_EDGE;
it.logic_type = GPIO_POSITIVE_TRIG;
break;
case IRQ_TYPE_LEVEL_LOW:
it.trig_type = GPIO_LEVEL_TRIG;
it.edge_type = GPIO_SINGLE_EDGE;
it.logic_type = GPIO_NEGATIVE_TRIG;
break;
default:
return -EINVAL;
}
eqbr_irq_type_cfg(&it, gctrl, offset);
if (it.trig_type == GPIO_EDGE_TRIG)
irq_set_handler_locked(d, handle_edge_irq);
else
irq_set_handler_locked(d, handle_level_irq);
return 0;
}
static void eqbr_irq_handler(struct irq_desc *desc)
{
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
struct eqbr_gpio_ctrl *gctrl = gpiochip_get_data(gc);
struct irq_chip *ic = irq_desc_get_chip(desc);
unsigned long pins, offset;
chained_irq_enter(ic, desc);
pins = readl(gctrl->membase + GPIO_IRNCR);
for_each_set_bit(offset, &pins, gc->ngpio)
generic_handle_irq(irq_find_mapping(gc->irq.domain, offset));
chained_irq_exit(ic, desc);
}
static int gpiochip_setup(struct device *dev, struct eqbr_gpio_ctrl *gctrl)
{
struct gpio_irq_chip *girq;
struct gpio_chip *gc;
gc = &gctrl->chip;
gc->label = gctrl->name;
#if defined(CONFIG_OF_GPIO)
gc->of_node = gctrl->node;
#endif
if (!of_property_read_bool(gctrl->node, "interrupt-controller")) {
dev_dbg(dev, "gc %s: doesn't act as interrupt controller!\n",
gctrl->name);
return 0;
}
gctrl