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
2 changed files with 186 additions and 2 deletions
|
@ -380,6 +380,7 @@ config PINCTRL_MICROCHIP_SGPIO
|
||||||
depends on OF
|
depends on OF
|
||||||
depends on HAS_IOMEM
|
depends on HAS_IOMEM
|
||||||
select GPIOLIB
|
select GPIOLIB
|
||||||
|
select GPIOLIB_IRQCHIP
|
||||||
select GENERIC_PINCONF
|
select GENERIC_PINCONF
|
||||||
select GENERIC_PINCTRL_GROUPS
|
select GENERIC_PINCTRL_GROUPS
|
||||||
select GENERIC_PINMUX_FUNCTIONS
|
select GENERIC_PINMUX_FUNCTIONS
|
||||||
|
|
|
@ -31,6 +31,11 @@ enum {
|
||||||
REG_PORT_ENABLE,
|
REG_PORT_ENABLE,
|
||||||
REG_SIO_CONFIG,
|
REG_SIO_CONFIG,
|
||||||
REG_SIO_CLOCK,
|
REG_SIO_CLOCK,
|
||||||
|
REG_INT_POLARITY,
|
||||||
|
REG_INT_TRIGGER,
|
||||||
|
REG_INT_ACK,
|
||||||
|
REG_INT_ENABLE,
|
||||||
|
REG_INT_IDENT,
|
||||||
MAXREG
|
MAXREG
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -40,8 +45,13 @@ enum {
|
||||||
SGPIO_ARCH_SPARX5,
|
SGPIO_ARCH_SPARX5,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SGPIO_FLAGS_HAS_IRQ = BIT(0),
|
||||||
|
};
|
||||||
|
|
||||||
struct sgpio_properties {
|
struct sgpio_properties {
|
||||||
int arch;
|
int arch;
|
||||||
|
int flags;
|
||||||
u8 regoff[MAXREG];
|
u8 regoff[MAXREG];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -60,6 +70,16 @@ struct sgpio_properties {
|
||||||
#define SGPIO_SPARX5_CLK_FREQ GENMASK(19, 8)
|
#define SGPIO_SPARX5_CLK_FREQ GENMASK(19, 8)
|
||||||
#define SGPIO_SPARX5_BIT_SOURCE GENMASK(23, 12)
|
#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 = {
|
static const struct sgpio_properties properties_luton = {
|
||||||
.arch = SGPIO_ARCH_LUTON,
|
.arch = SGPIO_ARCH_LUTON,
|
||||||
.regoff = { 0x00, 0x09, 0x29, 0x2a, 0x2b },
|
.regoff = { 0x00, 0x09, 0x29, 0x2a, 0x2b },
|
||||||
|
@ -72,7 +92,8 @@ static const struct sgpio_properties properties_ocelot = {
|
||||||
|
|
||||||
static const struct sgpio_properties properties_sparx5 = {
|
static const struct sgpio_properties properties_sparx5 = {
|
||||||
.arch = SGPIO_ARCH_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" };
|
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;
|
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)
|
static inline u32 sgpio_readl(struct sgpio_priv *priv, u32 rno, u32 off)
|
||||||
{
|
{
|
||||||
u32 __iomem *reg = &priv->regs[priv->properties->regoff[rno] + 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)
|
gpiospec->args[1] > priv->bitcount)
|
||||||
return -EINVAL;
|
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)
|
if (pin > gc->ngpio)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -527,6 +553,133 @@ static int microchip_sgpio_get_ports(struct sgpio_priv *priv)
|
||||||
return 0;
|
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,
|
static int microchip_sgpio_register_bank(struct device *dev,
|
||||||
struct sgpio_priv *priv,
|
struct sgpio_priv *priv,
|
||||||
struct fwnode_handle *fwnode,
|
struct fwnode_handle *fwnode,
|
||||||
|
@ -608,6 +761,36 @@ static int microchip_sgpio_register_bank(struct device *dev,
|
||||||
gc->base = -1;
|
gc->base = -1;
|
||||||
gc->ngpio = ngpios;
|
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);
|
ret = devm_gpiochip_add_data(dev, gc, bank);
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_err(dev, "Failed to register: ret %d\n", ret);
|
dev_err(dev, "Failed to register: ret %d\n", ret);
|
||||||
|
|
Loading…
Add table
Reference in a new issue