pinctrl: pinctrl-microchip-sgpio: Add irq support (for sparx5)
This adds 'interrupt-controller' features for the signals available on the Microchip SGPIO controller, however only for controller versions on the Sparx5 platform (or later). Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com> Link: https://lore.kernel.org/r/20201209142753.683208-2-lars.povlsen@microchip.com [Select GPIOLIB_IRQCHIP in Kconfig] Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
parent
6e261d1090
commit
be2dc859ab
|
@ -380,6 +380,7 @@ config PINCTRL_MICROCHIP_SGPIO
|
|||
depends on OF
|
||||
depends on HAS_IOMEM
|
||||
select GPIOLIB
|
||||
select GPIOLIB_IRQCHIP
|
||||
select GENERIC_PINCONF
|
||||
select GENERIC_PINCTRL_GROUPS
|
||||
select GENERIC_PINMUX_FUNCTIONS
|
||||
|
|
|
@ -31,6 +31,11 @@ enum {
|
|||
REG_PORT_ENABLE,
|
||||
REG_SIO_CONFIG,
|
||||
REG_SIO_CLOCK,
|
||||
REG_INT_POLARITY,
|
||||
REG_INT_TRIGGER,
|
||||
REG_INT_ACK,
|
||||
REG_INT_ENABLE,
|
||||
REG_INT_IDENT,
|
||||
MAXREG
|
||||
};
|
||||
|
||||
|
@ -40,8 +45,13 @@ enum {
|
|||
SGPIO_ARCH_SPARX5,
|
||||
};
|
||||
|
||||
enum {
|
||||
SGPIO_FLAGS_HAS_IRQ = BIT(0),
|
||||
};
|
||||
|
||||
struct sgpio_properties {
|
||||
int arch;
|
||||
int flags;
|
||||
u8 regoff[MAXREG];
|
||||
};
|
||||
|
||||
|
@ -60,6 +70,16 @@ struct sgpio_properties {
|
|||
#define SGPIO_SPARX5_CLK_FREQ GENMASK(19, 8)
|
||||
#define SGPIO_SPARX5_BIT_SOURCE GENMASK(23, 12)
|
||||
|
||||
#define SGPIO_MASTER_INTR_ENA BIT(0)
|
||||
|
||||
#define SGPIO_INT_TRG_LEVEL 0
|
||||
#define SGPIO_INT_TRG_EDGE 1
|
||||
#define SGPIO_INT_TRG_EDGE_FALL 2
|
||||
#define SGPIO_INT_TRG_EDGE_RISE 3
|
||||
|
||||
#define SGPIO_TRG_LEVEL_HIGH 0
|
||||
#define SGPIO_TRG_LEVEL_LOW 1
|
||||
|
||||
static const struct sgpio_properties properties_luton = {
|
||||
.arch = SGPIO_ARCH_LUTON,
|
||||
.regoff = { 0x00, 0x09, 0x29, 0x2a, 0x2b },
|
||||
|
@ -72,7 +92,8 @@ static const struct sgpio_properties properties_ocelot = {
|
|||
|
||||
static const struct sgpio_properties properties_sparx5 = {
|
||||
.arch = SGPIO_ARCH_SPARX5,
|
||||
.regoff = { 0x00, 0x06, 0x26, 0x04, 0x05 },
|
||||
.flags = SGPIO_FLAGS_HAS_IRQ,
|
||||
.regoff = { 0x00, 0x06, 0x26, 0x04, 0x05, 0x2a, 0x32, 0x3a, 0x3e, 0x42 },
|
||||
};
|
||||
|
||||
static const char * const functions[] = { "gpio" };
|
||||
|
@ -107,6 +128,11 @@ static inline void sgpio_pin_to_addr(struct sgpio_priv *priv, int pin,
|
|||
addr->bit = pin % priv->bitcount;
|
||||
}
|
||||
|
||||
static inline int sgpio_addr_to_pin(struct sgpio_priv *priv, int port, int bit)
|
||||
{
|
||||
return bit + port * priv->bitcount;
|
||||
}
|
||||
|
||||
static inline u32 sgpio_readl(struct sgpio_priv *priv, u32 rno, u32 off)
|
||||
{
|
||||
u32 __iomem *reg = &priv->regs[priv->properties->regoff[rno] + off];
|
||||
|
@ -478,7 +504,7 @@ static int microchip_sgpio_of_xlate(struct gpio_chip *gc,
|
|||
gpiospec->args[1] > priv->bitcount)
|
||||
return -EINVAL;
|
||||
|
||||
pin = gpiospec->args[1] + gpiospec->args[0] * priv->bitcount;
|
||||
pin = sgpio_addr_to_pin(priv, gpiospec->args[0], gpiospec->args[1]);
|
||||
|
||||
if (pin > gc->ngpio)
|
||||
return -EINVAL;
|
||||
|
@ -527,6 +553,133 @@ static int microchip_sgpio_get_ports(struct sgpio_priv *priv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void microchip_sgpio_irq_settype(struct irq_data *data,
|
||||
int type,
|
||||
int polarity)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
struct sgpio_bank *bank = gpiochip_get_data(chip);
|
||||
unsigned int gpio = irqd_to_hwirq(data);
|
||||
struct sgpio_port_addr addr;
|
||||
u32 ena;
|
||||
|
||||
sgpio_pin_to_addr(bank->priv, gpio, &addr);
|
||||
|
||||
/* Disable interrupt while changing type */
|
||||
ena = sgpio_readl(bank->priv, REG_INT_ENABLE, addr.bit);
|
||||
sgpio_writel(bank->priv, ena & ~BIT(addr.port), REG_INT_ENABLE, addr.bit);
|
||||
|
||||
/* Type value spread over 2 registers sets: low, high bit */
|
||||
sgpio_clrsetbits(bank->priv, REG_INT_TRIGGER, addr.bit,
|
||||
BIT(addr.port), (!!(type & 0x1)) << addr.port);
|
||||
sgpio_clrsetbits(bank->priv, REG_INT_TRIGGER + SGPIO_MAX_BITS, addr.bit,
|
||||
BIT(addr.port), (!!(type & 0x2)) << addr.port);
|
||||
|
||||
if (type == SGPIO_INT_TRG_LEVEL)
|
||||
sgpio_clrsetbits(bank->priv, REG_INT_POLARITY, addr.bit,
|
||||
BIT(addr.port), polarity << addr.port);
|
||||
|
||||
/* Possibly re-enable interrupts */
|
||||
sgpio_writel(bank->priv, ena, REG_INT_ENABLE, addr.bit);
|
||||
}
|
||||
|
||||
static void microchip_sgpio_irq_setreg(struct irq_data *data,
|
||||
int reg,
|
||||
bool clear)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
struct sgpio_bank *bank = gpiochip_get_data(chip);
|
||||
unsigned int gpio = irqd_to_hwirq(data);
|
||||
struct sgpio_port_addr addr;
|
||||
|
||||
sgpio_pin_to_addr(bank->priv, gpio, &addr);
|
||||
|
||||
if (clear)
|
||||
sgpio_clrsetbits(bank->priv, reg, addr.bit, BIT(addr.port), 0);
|
||||
else
|
||||
sgpio_clrsetbits(bank->priv, reg, addr.bit, 0, BIT(addr.port));
|
||||
}
|
||||
|
||||
static void microchip_sgpio_irq_mask(struct irq_data *data)
|
||||
{
|
||||
microchip_sgpio_irq_setreg(data, REG_INT_ENABLE, true);
|
||||
}
|
||||
|
||||
static void microchip_sgpio_irq_unmask(struct irq_data *data)
|
||||
{
|
||||
microchip_sgpio_irq_setreg(data, REG_INT_ENABLE, false);
|
||||
}
|
||||
|
||||
static void microchip_sgpio_irq_ack(struct irq_data *data)
|
||||
{
|
||||
microchip_sgpio_irq_setreg(data, REG_INT_ACK, false);
|
||||
}
|
||||
|
||||
static int microchip_sgpio_irq_set_type(struct irq_data *data, unsigned int type)
|
||||
{
|
||||
type &= IRQ_TYPE_SENSE_MASK;
|
||||
|
||||
switch (type) {
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
irq_set_handler_locked(data, handle_edge_irq);
|
||||
microchip_sgpio_irq_settype(data, SGPIO_INT_TRG_EDGE, 0);
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
irq_set_handler_locked(data, handle_edge_irq);
|
||||
microchip_sgpio_irq_settype(data, SGPIO_INT_TRG_EDGE_RISE, 0);
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
irq_set_handler_locked(data, handle_edge_irq);
|
||||
microchip_sgpio_irq_settype(data, SGPIO_INT_TRG_EDGE_FALL, 0);
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
irq_set_handler_locked(data, handle_level_irq);
|
||||
microchip_sgpio_irq_settype(data, SGPIO_INT_TRG_LEVEL, SGPIO_TRG_LEVEL_HIGH);
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
irq_set_handler_locked(data, handle_level_irq);
|
||||
microchip_sgpio_irq_settype(data, SGPIO_INT_TRG_LEVEL, SGPIO_TRG_LEVEL_LOW);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_chip microchip_sgpio_irqchip = {
|
||||
.name = "gpio",
|
||||
.irq_mask = microchip_sgpio_irq_mask,
|
||||
.irq_ack = microchip_sgpio_irq_ack,
|
||||
.irq_unmask = microchip_sgpio_irq_unmask,
|
||||
.irq_set_type = microchip_sgpio_irq_set_type,
|
||||
};
|
||||
|
||||
static void sgpio_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct irq_chip *parent_chip = irq_desc_get_chip(desc);
|
||||
struct gpio_chip *chip = irq_desc_get_handler_data(desc);
|
||||
struct sgpio_bank *bank = gpiochip_get_data(chip);
|
||||
struct sgpio_priv *priv = bank->priv;
|
||||
int bit, port, gpio;
|
||||
long val;
|
||||
|
||||
for (bit = 0; bit < priv->bitcount; bit++) {
|
||||
val = sgpio_readl(priv, REG_INT_IDENT, bit);
|
||||
if (!val)
|
||||
continue;
|
||||
|
||||
chained_irq_enter(parent_chip, desc);
|
||||
|
||||
for_each_set_bit(port, &val, SGPIO_BITS_PER_WORD) {
|
||||
gpio = sgpio_addr_to_pin(priv, port, bit);
|
||||
generic_handle_irq(irq_linear_revmap(chip->irq.domain, gpio));
|
||||
}
|
||||
|
||||
chained_irq_exit(parent_chip, desc);
|
||||
}
|
||||
}
|
||||
|
||||
static int microchip_sgpio_register_bank(struct device *dev,
|
||||
struct sgpio_priv *priv,
|
||||
struct fwnode_handle *fwnode,
|
||||
|
@ -608,6 +761,36 @@ static int microchip_sgpio_register_bank(struct device *dev,
|
|||
gc->base = -1;
|
||||
gc->ngpio = ngpios;
|
||||
|
||||
if (bank->is_input && priv->properties->flags & SGPIO_FLAGS_HAS_IRQ) {
|
||||
int irq = fwnode_irq_get(fwnode, 0);
|
||||
|
||||
if (irq) {
|
||||
struct gpio_irq_chip *girq = &gc->irq;
|
||||
|
||||
girq->chip = devm_kmemdup(dev, µchip_sgpio_irqchip,
|
||||
sizeof(microchip_sgpio_irqchip),
|
||||
GFP_KERNEL);
|
||||
if (!girq->chip)
|
||||
return -ENOMEM;
|
||||
girq->parent_handler = sgpio_irq_handler;
|
||||
girq->num_parents = 1;
|
||||
girq->parents = devm_kcalloc(dev, 1,
|
||||
sizeof(*girq->parents),
|
||||
GFP_KERNEL);
|
||||
if (!girq->parents)
|
||||
return -ENOMEM;
|
||||
girq->parents[0] = irq;
|
||||
girq->default_type = IRQ_TYPE_NONE;
|
||||
girq->handler = handle_bad_irq;
|
||||
|
||||
/* Disable all individual pins */
|
||||
for (i = 0; i < SGPIO_MAX_BITS; i++)
|
||||
sgpio_writel(priv, 0, REG_INT_ENABLE, i);
|
||||
/* Master enable */
|
||||
sgpio_clrsetbits(priv, REG_SIO_CONFIG, 0, 0, SGPIO_MASTER_INTR_ENA);
|
||||
}
|
||||
}
|
||||
|
||||
ret = devm_gpiochip_add_data(dev, gc, bank);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to register: ret %d\n", ret);
|
||||
|
|
Loading…
Reference in New Issue