// SPDX-License-Identifier: GPL-2.0
/*
* Faraday Technolog FTGPIO010 gpiochip and interrupt routines
* Copyright ( C ) 2017 Linus Walleij < linus . walleij @ linaro . org >
*
* Based on arch / arm / mach - gemini / gpio . c :
* Copyright ( C ) 2008 - 2009 Paulius Zaleckas < paulius . zaleckas @ teltonika . lt >
*
* Based on plat - mxc / gpio . c :
* MXC GPIO support . ( c ) 2008 Daniel Mack < daniel @ caiaq . de >
* Copyright 2008 Juergen Beisert , kernel @ pengutronix . de
*/
#include <linux/gpio/driver.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/bitops.h>
#include <linux/clk.h>
/* GPIO registers definition */
#define GPIO_DATA_OUT 0 x00
#define GPIO_DATA_IN 0 x04
#define GPIO_DIR 0 x08
#define GPIO_BYPASS_IN 0 x0C
#define GPIO_DATA_SET 0 x10
#define GPIO_DATA_CLR 0 x14
#define GPIO_PULL_EN 0 x18
#define GPIO_PULL_TYPE 0 x1C
#define GPIO_INT_EN 0 x20
#define GPIO_INT_STAT_RAW 0 x24
#define GPIO_INT_STAT_MASKED 0 x28
#define GPIO_INT_MASK 0 x2C
#define GPIO_INT_CLR 0 x30
#define GPIO_INT_TYPE 0 x34
#define GPIO_INT_BOTH_EDGE 0 x38
#define GPIO_INT_LEVEL 0 x3C
#define GPIO_DEBOUNCE_EN 0 x40
#define GPIO_DEBOUNCE_PRESCALE 0 x44
/**
* struct ftgpio_gpio - Gemini GPIO state container
* @ dev : containing device for this instance
* @ gc : gpiochip for this instance
* @ base : remapped I / O - memory base
* @ clk : silicon clock
*/
struct ftgpio_gpio {
struct device *dev;
struct gpio_chip gc;
void __iomem *base;
struct clk *clk;
};
static void ftgpio_gpio_ack_irq(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct ftgpio_gpio *g = gpiochip_get_data(gc);
writel(BIT(irqd_to_hwirq(d)), g->base + GPIO_INT_CLR);
}
static void ftgpio_gpio_mask_irq(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct ftgpio_gpio *g = gpiochip_get_data(gc);
u32 val;
val = readl(g->base + GPIO_INT_EN);
val &= ~BIT(irqd_to_hwirq(d));
writel(val, g->base + GPIO_INT_EN);
gpiochip_disable_irq(gc, irqd_to_hwirq(d));
}
static void ftgpio_gpio_unmask_irq(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct ftgpio_gpio *g = gpiochip_get_data(gc);
u32 val;
gpiochip_enable_irq(gc, irqd_to_hwirq(d));
val = readl(g->base + GPIO_INT_EN);
val |= BIT(irqd_to_hwirq(d));
writel(val, g->base + GPIO_INT_EN);
}
static int ftgpio_gpio_set_irq_type(struct irq_data *d, unsigned int type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct ftgpio_gpio *g = gpiochip_get_data(gc);
u32 mask = BIT(irqd_to_hwirq(d));
u32 reg_both, reg_level, reg_type;
reg_type = readl(g->base + GPIO_INT_TYPE);
reg_level = readl(g->base + GPIO_INT_LEVEL);
reg_both = readl(g->base + GPIO_INT_BOTH_EDGE);
switch (type) {
case IRQ_TYPE_EDGE_BOTH:
irq_set_handler_locked(d, handle_edge_irq);
reg_type &= ~mask;
reg_both |= mask;
break ;
case IRQ_TYPE_EDGE_RISING:
irq_set_handler_locked(d, handle_edge_irq);
reg_type &= ~mask;
reg_both &= ~mask;
reg_level &= ~mask;
break ;
case IRQ_TYPE_EDGE_FALLING:
irq_set_handler_locked(d, handle_edge_irq);
reg_type &= ~mask;
reg_both &= ~mask;
reg_level |= mask;
break ;
case IRQ_TYPE_LEVEL_HIGH:
irq_set_handler_locked(d, handle_level_irq);
reg_type |= mask;
reg_level &= ~mask;
break ;
case IRQ_TYPE_LEVEL_LOW:
irq_set_handler_locked(d, handle_level_irq);
reg_type |= mask;
reg_level |= mask;
break ;
default :
irq_set_handler_locked(d, handle_bad_irq);
return -EINVAL;
}
writel(reg_type, g->base + GPIO_INT_TYPE);
writel(reg_level, g->base + GPIO_INT_LEVEL);
writel(reg_both, g->base + GPIO_INT_BOTH_EDGE);
ftgpio_gpio_ack_irq(d);
return 0 ;
}
static void ftgpio_gpio_irq_handler(struct irq_desc *desc)
{
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
struct ftgpio_gpio *g = gpiochip_get_data(gc);
struct irq_chip *irqchip = irq_desc_get_chip(desc);
int offset;
unsigned long stat;
chained_irq_enter(irqchip, desc);
stat = readl(g->base + GPIO_INT_STAT_RAW);
if (stat)
for_each_set_bit(offset, &stat, gc->ngpio)
generic_handle_domain_irq(gc->irq.domain, offset);
chained_irq_exit(irqchip, desc);
}
static int ftgpio_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
unsigned long config)
{
enum pin_config_param param = pinconf_to_config_param(config);
u32 arg = pinconf_to_config_argument(config);
struct ftgpio_gpio *g = gpiochip_get_data(gc);
unsigned long pclk_freq;
u32 deb_div;
u32 val;
if (param != PIN_CONFIG_INPUT_DEBOUNCE)
return -ENOTSUPP;
/*
* Debounce only works if interrupts are enabled . The manual
* states that if PCLK is 66 MHz , and this is set to 0 x7D0 , then
* PCLK is divided down to 33 kHz for the debounce timer . 0 x7D0 is
* 2000 decimal , so what they mean is simply that the PCLK is
* divided by this value .
*
* As we get a debounce setting in microseconds , we calculate the
* desired period time and see if we can get a suitable debounce
* time .
*/
pclk_freq = clk_get_rate(g->clk);
deb_div = DIV_ROUND_CLOSEST(pclk_freq, arg);
/* This register is only 24 bits wide */
if (deb_div > (1 << 24 ))
return -ENOTSUPP;
dev_dbg(g->dev, "prescale divisor: %08x, resulting frequency %lu Hz\n" ,
deb_div, (pclk_freq/deb_div));
val = readl(g->base + GPIO_DEBOUNCE_PRESCALE);
if (val == deb_div) {
/*
* The debounce timer happens to already be set to the
* desirable value , what a coincidence ! We can just enable
* debounce on this GPIO line and return . This happens more
* often than you think , for example when all GPIO keys
* on a system are requesting the same debounce interval .
*/
val = readl(g->base + GPIO_DEBOUNCE_EN);
val |= BIT(offset);
writel(val, g->base + GPIO_DEBOUNCE_EN);
return 0 ;
}
val = readl(g->base + GPIO_DEBOUNCE_EN);
if (val) {
/*
* Oh no ! Someone is already using the debounce with
* another setting than what we need . Bummer .
*/
return -ENOTSUPP;
}
/* First come, first serve */
writel(deb_div, g->base + GPIO_DEBOUNCE_PRESCALE);
/* Enable debounce */
val |= BIT(offset);
writel(val, g->base + GPIO_DEBOUNCE_EN);
return 0 ;
}
static const struct irq_chip ftgpio_irq_chip = {
.name = "FTGPIO010" ,
.irq_ack = ftgpio_gpio_ack_irq,
.irq_mask = ftgpio_gpio_mask_irq,
.irq_unmask = ftgpio_gpio_unmask_irq,
.irq_set_type = ftgpio_gpio_set_irq_type,
.flags = IRQCHIP_IMMUTABLE,
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static int ftgpio_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ftgpio_gpio *g;
struct gpio_irq_chip *girq;
int irq;
int ret;
g = devm_kzalloc(dev, sizeof (*g), GFP_KERNEL);
if (!g)
return -ENOMEM;
g->dev = dev;
g->base = devm_platform_ioremap_resource(pdev, 0 );
if (IS_ERR(g->base))
return PTR_ERR(g->base);
irq = platform_get_irq(pdev, 0 );
if (irq < 0 )
return irq;
g->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(g->clk) && PTR_ERR(g->clk) == -EPROBE_DEFER)
/*
* Percolate deferrals , for anything else ,
* just live without the clocking .
*/
return PTR_ERR(g->clk);
ret = bgpio_init(&g->gc, dev, 4 ,
g->base + GPIO_DATA_IN,
g->base + GPIO_DATA_SET,
g->base + GPIO_DATA_CLR,
g->base + GPIO_DIR,
NULL,
0 );
if (ret)
return dev_err_probe(dev, ret, "unable to init generic GPIO\n" );
g->gc.label = dev_name(dev);
g->gc.base = -1 ;
g->gc.parent = dev;
g->gc.owner = THIS_MODULE;
/* ngpio is set by bgpio_init() */
/* We need a silicon clock to do debounce */
if (!IS_ERR(g->clk))
g->gc.set_config = ftgpio_gpio_set_config;
girq = &g->gc.irq;
gpio_irq_chip_set_chip(girq, &ftgpio_irq_chip);
girq->parent_handler = ftgpio_gpio_irq_handler;
girq->num_parents = 1 ;
girq->parents = devm_kcalloc(dev, 1 , sizeof (*girq->parents),
GFP_KERNEL);
if (!girq->parents)
return -ENOMEM;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_bad_irq;
girq->parents[0 ] = irq;
/* Disable, unmask and clear all interrupts */
writel(0 x0, g->base + GPIO_INT_EN);
writel(0 x0, g->base + GPIO_INT_MASK);
writel(~0 x0, g->base + GPIO_INT_CLR);
/* Clear any use of debounce */
writel(0 x0, g->base + GPIO_DEBOUNCE_EN);
return devm_gpiochip_add_data(dev, &g->gc, g);
}
static const struct of_device_id ftgpio_gpio_of_match[] = {
{
.compatible = "cortina,gemini-gpio" ,
},
{
.compatible = "moxa,moxart-gpio" ,
},
{
.compatible = "faraday,ftgpio010" ,
},
{},
};
static struct platform_driver ftgpio_gpio_driver = {
.driver = {
.name = "ftgpio010-gpio" ,
.of_match_table = ftgpio_gpio_of_match,
},
.probe = ftgpio_gpio_probe,
};
builtin_platform_driver(ftgpio_gpio_driver);
Messung V0.5 in Prozent C=92 H=100 G=95
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-09-27)
¤
*© Formatika GbR, Deutschland