Skip to content

Commit a25f48f

Browse files
Alban BedelBartosz Golaszewski
authored andcommitted
gpio: kempld: Implement the interrupt controller
Add a GPIO IRQ chip implementation for the kempld GPIO controller. Of note is only how the parent IRQ is obtained. The IRQ for the GPIO controller can be configured in the BIOS, along with the IRQ for the I2C controller. These IRQ are returned by ACPI but this information is only usable if both IRQ are configured. When only one is configured, only one is returned making it impossible to know which one it is. Luckily the BIOS will set the configured IRQ in the PLD registers, so it can be read from there instead, and that also work on platforms without ACPI. The vendor driver allowed to override the IRQ using a module parameters, so there are boards in field which used this parameter instead of properly configuring the BIOS. This implementation provides this as well for compatibility. Signed-off-by: Alban Bedel <alban.bedel@lht.dlh.de> Link: https://patch.msgid.link/20260311143120.2179347-5-alban.bedel@lht.dlh.de Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
1 parent 2443c2e commit a25f48f

3 files changed

Lines changed: 194 additions & 0 deletions

File tree

drivers/gpio/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,6 +1440,7 @@ config GPIO_JANZ_TTL
14401440
config GPIO_KEMPLD
14411441
tristate "Kontron ETX / COMexpress GPIO"
14421442
depends on MFD_KEMPLD
1443+
select GPIOLIB_IRQCHIP
14431444
help
14441445
This enables support for the PLD GPIO interface on some Kontron ETX
14451446
and COMexpress (ETXexpress) modules.

drivers/gpio/gpio-kempld.c

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <linux/module.h>
1212
#include <linux/bitops.h>
1313
#include <linux/errno.h>
14+
#include <linux/interrupt.h>
1415
#include <linux/platform_device.h>
1516
#include <linux/gpio/driver.h>
1617
#include <linux/mfd/kempld.h>
@@ -19,13 +20,26 @@
1920
#define KEMPLD_GPIO_MASK(x) (BIT((x) % 8))
2021
#define KEMPLD_GPIO_DIR 0x40
2122
#define KEMPLD_GPIO_LVL 0x42
23+
#define KEMPLD_GPIO_STS 0x44
2224
#define KEMPLD_GPIO_EVT_LVL_EDGE 0x46
25+
#define KEMPLD_GPIO_EVT_LOW_HIGH 0x48
2326
#define KEMPLD_GPIO_IEN 0x4A
27+
#define KEMPLD_GPIO_OUT_LVL 0x4E
28+
29+
/* The IRQ to use if none was configured in the BIOS */
30+
static unsigned int gpio_irq;
31+
module_param_hw(gpio_irq, uint, irq, 0444);
32+
MODULE_PARM_DESC(gpio_irq, "Set legacy GPIO IRQ (1-15)");
2433

2534
struct kempld_gpio_data {
2635
struct gpio_chip chip;
2736
struct kempld_device_data *pld;
2837
u8 out_lvl_reg;
38+
39+
struct mutex irq_lock;
40+
u16 ien;
41+
u16 evt_low_high;
42+
u16 evt_lvl_edge;
2943
};
3044

3145
/*
@@ -193,6 +207,180 @@ static int kempld_gpio_pincount(struct kempld_device_data *pld)
193207
return evt ? __ffs(evt) : 16;
194208
}
195209

210+
static void kempld_irq_mask(struct irq_data *data)
211+
{
212+
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
213+
struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
214+
215+
gpio->ien &= ~BIT(irqd_to_hwirq(data));
216+
gpiochip_disable_irq(chip, irqd_to_hwirq(data));
217+
}
218+
219+
static void kempld_irq_unmask(struct irq_data *data)
220+
{
221+
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
222+
struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
223+
224+
gpiochip_enable_irq(chip, irqd_to_hwirq(data));
225+
gpio->ien |= BIT(irqd_to_hwirq(data));
226+
}
227+
228+
static int kempld_irq_set_type(struct irq_data *data, unsigned int type)
229+
{
230+
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
231+
struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
232+
233+
switch (type) {
234+
case IRQ_TYPE_EDGE_RISING:
235+
gpio->evt_low_high |= BIT(data->hwirq);
236+
gpio->evt_lvl_edge |= BIT(data->hwirq);
237+
break;
238+
case IRQ_TYPE_EDGE_FALLING:
239+
gpio->evt_low_high &= ~BIT(data->hwirq);
240+
gpio->evt_lvl_edge |= BIT(data->hwirq);
241+
break;
242+
case IRQ_TYPE_LEVEL_HIGH:
243+
gpio->evt_low_high |= BIT(data->hwirq);
244+
gpio->evt_lvl_edge &= ~BIT(data->hwirq);
245+
break;
246+
case IRQ_TYPE_LEVEL_LOW:
247+
gpio->evt_low_high &= ~BIT(data->hwirq);
248+
gpio->evt_lvl_edge &= ~BIT(data->hwirq);
249+
break;
250+
default:
251+
return -EINVAL;
252+
}
253+
254+
return 0;
255+
}
256+
257+
static void kempld_irq_bus_lock(struct irq_data *data)
258+
{
259+
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
260+
struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
261+
262+
mutex_lock(&gpio->irq_lock);
263+
}
264+
265+
static void kempld_irq_bus_sync_unlock(struct irq_data *data)
266+
{
267+
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
268+
struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
269+
struct kempld_device_data *pld = gpio->pld;
270+
271+
kempld_get_mutex(pld);
272+
kempld_write16(pld, KEMPLD_GPIO_EVT_LVL_EDGE, gpio->evt_lvl_edge);
273+
kempld_write16(pld, KEMPLD_GPIO_EVT_LOW_HIGH, gpio->evt_low_high);
274+
kempld_write16(pld, KEMPLD_GPIO_IEN, gpio->ien);
275+
kempld_release_mutex(pld);
276+
277+
mutex_unlock(&gpio->irq_lock);
278+
}
279+
280+
static const struct irq_chip kempld_irqchip = {
281+
.name = "kempld-gpio",
282+
.irq_mask = kempld_irq_mask,
283+
.irq_unmask = kempld_irq_unmask,
284+
.irq_set_type = kempld_irq_set_type,
285+
.irq_bus_lock = kempld_irq_bus_lock,
286+
.irq_bus_sync_unlock = kempld_irq_bus_sync_unlock,
287+
.flags = IRQCHIP_IMMUTABLE,
288+
GPIOCHIP_IRQ_RESOURCE_HELPERS,
289+
};
290+
291+
static irqreturn_t kempld_gpio_irq_handler(int irq, void *data)
292+
{
293+
struct kempld_gpio_data *gpio = data;
294+
struct gpio_chip *chip = &gpio->chip;
295+
unsigned int pin, child_irq;
296+
unsigned long status;
297+
298+
kempld_get_mutex(gpio->pld);
299+
300+
status = kempld_read16(gpio->pld, KEMPLD_GPIO_STS);
301+
if (status)
302+
kempld_write16(gpio->pld, KEMPLD_GPIO_STS, status);
303+
304+
kempld_release_mutex(gpio->pld);
305+
306+
status &= gpio->ien;
307+
if (!status)
308+
return IRQ_NONE;
309+
310+
for_each_set_bit(pin, &status, chip->ngpio) {
311+
child_irq = irq_find_mapping(chip->irq.domain, pin);
312+
handle_nested_irq(child_irq);
313+
}
314+
315+
return IRQ_HANDLED;
316+
}
317+
318+
static int kempld_gpio_irq_init(struct device *dev,
319+
struct kempld_gpio_data *gpio)
320+
{
321+
struct kempld_device_data *pld = gpio->pld;
322+
struct gpio_chip *chip = &gpio->chip;
323+
struct gpio_irq_chip *girq;
324+
unsigned int irq;
325+
int ret;
326+
327+
/* Get the IRQ configured by the BIOS in the PLD */
328+
kempld_get_mutex(pld);
329+
irq = kempld_read8(pld, KEMPLD_IRQ_GPIO);
330+
kempld_release_mutex(pld);
331+
332+
if (irq == 0xff) {
333+
dev_info(dev, "GPIO controller has no IRQ support\n");
334+
return 0;
335+
}
336+
337+
/* Allow overriding the IRQ with the module parameter */
338+
if (gpio_irq > 0) {
339+
dev_warn(dev, "Forcing IRQ to %d\n", gpio_irq);
340+
irq &= ~KEMPLD_IRQ_GPIO_MASK;
341+
irq |= gpio_irq & KEMPLD_IRQ_GPIO_MASK;
342+
}
343+
344+
if (!(irq & KEMPLD_IRQ_GPIO_MASK)) {
345+
dev_warn(dev, "No IRQ configured\n");
346+
return 0;
347+
}
348+
349+
/* Get the current config, disable all child interrupts, clear them
350+
* and set the parent IRQ
351+
*/
352+
kempld_get_mutex(pld);
353+
gpio->evt_low_high = kempld_read16(pld, KEMPLD_GPIO_EVT_LOW_HIGH);
354+
gpio->evt_lvl_edge = kempld_read16(pld, KEMPLD_GPIO_EVT_LVL_EDGE);
355+
kempld_write16(pld, KEMPLD_GPIO_IEN, 0);
356+
kempld_write16(pld, KEMPLD_GPIO_STS, 0xFFFF);
357+
kempld_write16(pld, KEMPLD_IRQ_GPIO, irq);
358+
kempld_release_mutex(pld);
359+
360+
girq = &chip->irq;
361+
gpio_irq_chip_set_chip(girq, &kempld_irqchip);
362+
363+
girq->parent_handler = NULL;
364+
girq->num_parents = 0;
365+
girq->parents = NULL;
366+
girq->default_type = IRQ_TYPE_NONE;
367+
girq->handler = handle_simple_irq;
368+
girq->threaded = true;
369+
370+
mutex_init(&gpio->irq_lock);
371+
372+
ret = devm_request_threaded_irq(dev, irq & KEMPLD_IRQ_GPIO_MASK,
373+
NULL, kempld_gpio_irq_handler,
374+
IRQF_ONESHOT, chip->label,
375+
gpio);
376+
if (ret) {
377+
dev_err(dev, "failed to request irq %d\n", irq);
378+
return ret;
379+
}
380+
381+
return 0;
382+
}
383+
196384
static int kempld_gpio_probe(struct platform_device *pdev)
197385
{
198386
struct device *dev = &pdev->dev;
@@ -247,6 +435,10 @@ static int kempld_gpio_probe(struct platform_device *pdev)
247435
return -ENODEV;
248436
}
249437

438+
ret = kempld_gpio_irq_init(dev, gpio);
439+
if (ret)
440+
return ret;
441+
250442
ret = devm_gpiochip_add_data(dev, chip, gpio);
251443
if (ret) {
252444
dev_err(dev, "Could not register GPIO chip\n");

include/linux/mfd/kempld.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#define KEMPLD_SPEC_GET_MINOR(x) (x & 0x0f)
3838
#define KEMPLD_SPEC_GET_MAJOR(x) ((x >> 4) & 0x0f)
3939
#define KEMPLD_IRQ_GPIO 0x35
40+
#define KEMPLD_IRQ_GPIO_MASK 0x0f
4041
#define KEMPLD_IRQ_I2C 0x36
4142
#define KEMPLD_CFG 0x37
4243
#define KEMPLD_CFG_GPIO_I2C_MUX (1 << 0)

0 commit comments

Comments
 (0)