Skip to content

Commit e57b53f

Browse files
committed
gpio: mpfs: Add interrupt support
Add support for interrupts to the PolarFire SoC GPIO driver. Each GPIO has an independent interrupt that is wired to an interrupt mux that sits between the controllers and the PLIC. The SoC has more GPIO lines than connections from the mux to the PLIC, so some GPIOs must share PLIC interrupts. The configuration is not static and is set at runtime, conventionally by the platform's firmware. CoreGPIO, the version intended for use in the FPGA fabric has two interrupt output ports, one is IO_NUM bits wide, as is used in the hardened cores, and the other is a single bit with all lines ORed together. Acked-by: Bartosz Golaszewski <[email protected]> Reviewed-by: Linus Walleij <[email protected]> Signed-off-by: Conor Dooley <[email protected]>
1 parent 63b5305 commit e57b53f

2 files changed

Lines changed: 120 additions & 3 deletions

File tree

drivers/gpio/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,7 @@ config GPIO_PL061
572572
config GPIO_POLARFIRE_SOC
573573
bool "Microchip FPGA GPIO support"
574574
select REGMAP_MMIO
575+
select GPIOLIB_IRQCHIP
575576
help
576577
Say yes here to support the GPIO controllers on Microchip FPGAs.
577578

drivers/gpio/gpio-mpfs.c

Lines changed: 119 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,17 @@
99
#include <linux/device.h>
1010
#include <linux/errno.h>
1111
#include <linux/gpio/driver.h>
12-
#include <linux/init.h>
12+
#include <linux/interrupt.h>
1313
#include <linux/mod_devicetable.h>
14+
#include <linux/of_irq.h>
1415
#include <linux/platform_device.h>
1516
#include <linux/property.h>
1617
#include <linux/regmap.h>
1718
#include <linux/spinlock.h>
1819

1920
#define MPFS_GPIO_CTRL(i) (0x4 * (i))
2021
#define MPFS_MAX_NUM_GPIO 32
21-
#define MPFS_GPIO_EN_INT 3
22+
#define MPFS_GPIO_EN_INT BIT(3)
2223
#define MPFS_GPIO_EN_OUT_BUF BIT(2)
2324
#define MPFS_GPIO_EN_IN BIT(1)
2425
#define MPFS_GPIO_EN_OUT BIT(0)
@@ -52,6 +53,7 @@ static const struct regmap_config mpfs_gpio_regmap_config = {
5253
.reg_bits = 32,
5354
.reg_stride = 4,
5455
.val_bits = 32,
56+
.use_raw_spinlock = true,
5557
};
5658

5759
static int mpfs_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio_index)
@@ -114,13 +116,98 @@ static int mpfs_gpio_set(struct gpio_chip *gc, unsigned int gpio_index, int valu
114116
return ret;
115117
}
116118

119+
static int mpfs_gpio_irq_set_type(struct irq_data *data, unsigned int type)
120+
{
121+
struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
122+
struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
123+
int gpio_index = irqd_to_hwirq(data) % 32;
124+
u32 interrupt_type;
125+
126+
switch (type) {
127+
case IRQ_TYPE_EDGE_BOTH:
128+
interrupt_type = MPFS_GPIO_TYPE_INT_EDGE_BOTH;
129+
break;
130+
case IRQ_TYPE_EDGE_FALLING:
131+
interrupt_type = MPFS_GPIO_TYPE_INT_EDGE_NEG;
132+
break;
133+
case IRQ_TYPE_EDGE_RISING:
134+
interrupt_type = MPFS_GPIO_TYPE_INT_EDGE_POS;
135+
break;
136+
case IRQ_TYPE_LEVEL_HIGH:
137+
interrupt_type = MPFS_GPIO_TYPE_INT_LEVEL_HIGH;
138+
break;
139+
case IRQ_TYPE_LEVEL_LOW:
140+
interrupt_type = MPFS_GPIO_TYPE_INT_LEVEL_LOW;
141+
break;
142+
}
143+
144+
regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index),
145+
MPFS_GPIO_TYPE_INT_MASK, interrupt_type);
146+
147+
return 0;
148+
}
149+
150+
static void mpfs_gpio_irq_unmask(struct irq_data *data)
151+
{
152+
struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
153+
struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
154+
int gpio_index = irqd_to_hwirq(data) % 32;
155+
156+
gpiochip_enable_irq(gc, gpio_index);
157+
mpfs_gpio_direction_input(gc, gpio_index);
158+
regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index),
159+
MPFS_GPIO_EN_INT, MPFS_GPIO_EN_INT);
160+
}
161+
162+
static void mpfs_gpio_irq_mask(struct irq_data *data)
163+
{
164+
struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
165+
struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
166+
int gpio_index = irqd_to_hwirq(data) % 32;
167+
168+
regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index),
169+
MPFS_GPIO_EN_INT, 0);
170+
gpiochip_disable_irq(gc, gpio_index);
171+
}
172+
173+
static const struct irq_chip mpfs_gpio_irqchip = {
174+
.name = "MPFS GPIO",
175+
.irq_set_type = mpfs_gpio_irq_set_type,
176+
.irq_mask = mpfs_gpio_irq_mask,
177+
.irq_unmask = mpfs_gpio_irq_unmask,
178+
.flags = IRQCHIP_IMMUTABLE | IRQCHIP_MASK_ON_SUSPEND,
179+
GPIOCHIP_IRQ_RESOURCE_HELPERS,
180+
};
181+
182+
static void mpfs_gpio_irq_handler(struct irq_desc *desc)
183+
{
184+
struct irq_chip *irqchip = irq_desc_get_chip(desc);
185+
struct mpfs_gpio_chip *mpfs_gpio = irq_desc_get_handler_data(desc);
186+
unsigned long status;
187+
u32 val;
188+
int i;
189+
190+
chained_irq_enter(irqchip, desc);
191+
192+
regmap_read(mpfs_gpio->regs, MPFS_IRQ_REG, &val);
193+
status = val;
194+
for_each_set_bit(i, &status, MPFS_MAX_NUM_GPIO) {
195+
regmap_write(mpfs_gpio->regs, MPFS_IRQ_REG, BIT(i));
196+
generic_handle_domain_irq(mpfs_gpio->gc.irq.domain, i);
197+
}
198+
199+
chained_irq_exit(irqchip, desc);
200+
}
201+
117202
static int mpfs_gpio_probe(struct platform_device *pdev)
118203
{
119204
struct device *dev = &pdev->dev;
205+
struct device_node *node = dev->of_node;
120206
struct mpfs_gpio_chip *mpfs_gpio;
207+
struct gpio_irq_chip *girq;
121208
struct clk *clk;
122209
void __iomem *base;
123-
int ngpios;
210+
int ngpios, nirqs, ret;
124211

125212
mpfs_gpio = devm_kzalloc(dev, sizeof(*mpfs_gpio), GFP_KERNEL);
126213
if (!mpfs_gpio)
@@ -157,6 +244,35 @@ static int mpfs_gpio_probe(struct platform_device *pdev)
157244
mpfs_gpio->gc.parent = dev;
158245
mpfs_gpio->gc.owner = THIS_MODULE;
159246

247+
nirqs = of_irq_count(node);
248+
if (nirqs > MPFS_MAX_NUM_GPIO)
249+
return -ENXIO;
250+
251+
if (nirqs) {
252+
girq = &mpfs_gpio->gc.irq;
253+
254+
gpio_irq_chip_set_chip(girq, &mpfs_gpio_irqchip);
255+
256+
girq->num_parents = nirqs;
257+
girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents,
258+
sizeof(*girq->parents), GFP_KERNEL);
259+
if (!girq->parents)
260+
return -ENOMEM;
261+
262+
for (int i = 0; i < nirqs; i++) {
263+
ret = platform_get_irq(pdev, i);
264+
if (ret < 0)
265+
return ret;
266+
267+
girq->parents[i] = ret;
268+
girq->parent_handler_data = mpfs_gpio;
269+
girq->parent_handler = mpfs_gpio_irq_handler;
270+
}
271+
272+
girq->handler = handle_level_irq;
273+
girq->default_type = IRQ_TYPE_NONE;
274+
}
275+
160276
return devm_gpiochip_add_data(dev, &mpfs_gpio->gc, mpfs_gpio);
161277
}
162278

0 commit comments

Comments
 (0)