ec5aa31bbe
The platform_set_drvdata() call is only useful if we need to retrieve back the private information. Since the driver doesn't do that, it's not useful to have it. If this is removed, we can also just do a direct return on devm_gpiochip_add_data(). We don't need to print that this call failed as there are other ways to log/see this during probe. Signed-off-by: Alexandru Ardelean <aardelean@deviqon.com> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
211 lines
5.1 KiB
C
211 lines
5.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Toshiba Visconti GPIO Support
|
|
*
|
|
* (C) Copyright 2020 Toshiba Electronic Devices & Storage Corporation
|
|
* (C) Copyright 2020 TOSHIBA CORPORATION
|
|
*
|
|
* Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
|
|
*/
|
|
|
|
#include <linux/gpio/driver.h>
|
|
#include <linux/init.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/module.h>
|
|
#include <linux/io.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/bitops.h>
|
|
|
|
/* register offset */
|
|
#define GPIO_DIR 0x00
|
|
#define GPIO_IDATA 0x08
|
|
#define GPIO_ODATA 0x10
|
|
#define GPIO_OSET 0x18
|
|
#define GPIO_OCLR 0x20
|
|
#define GPIO_INTMODE 0x30
|
|
|
|
#define BASE_HW_IRQ 24
|
|
|
|
struct visconti_gpio {
|
|
void __iomem *base;
|
|
spinlock_t lock; /* protect gpio register */
|
|
struct gpio_chip gpio_chip;
|
|
struct irq_chip irq_chip;
|
|
};
|
|
|
|
static int visconti_gpio_irq_set_type(struct irq_data *d, unsigned int type)
|
|
{
|
|
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
|
struct visconti_gpio *priv = gpiochip_get_data(gc);
|
|
u32 offset = irqd_to_hwirq(d);
|
|
u32 bit = BIT(offset);
|
|
u32 intc_type = IRQ_TYPE_EDGE_RISING;
|
|
u32 intmode, odata;
|
|
int ret = 0;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&priv->lock, flags);
|
|
|
|
odata = readl(priv->base + GPIO_ODATA);
|
|
intmode = readl(priv->base + GPIO_INTMODE);
|
|
|
|
switch (type) {
|
|
case IRQ_TYPE_EDGE_RISING:
|
|
odata &= ~bit;
|
|
intmode &= ~bit;
|
|
break;
|
|
case IRQ_TYPE_EDGE_FALLING:
|
|
odata |= bit;
|
|
intmode &= ~bit;
|
|
break;
|
|
case IRQ_TYPE_EDGE_BOTH:
|
|
intmode |= bit;
|
|
break;
|
|
case IRQ_TYPE_LEVEL_HIGH:
|
|
intc_type = IRQ_TYPE_LEVEL_HIGH;
|
|
odata &= ~bit;
|
|
intmode &= ~bit;
|
|
break;
|
|
case IRQ_TYPE_LEVEL_LOW:
|
|
intc_type = IRQ_TYPE_LEVEL_HIGH;
|
|
odata |= bit;
|
|
intmode &= ~bit;
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
writel(odata, priv->base + GPIO_ODATA);
|
|
writel(intmode, priv->base + GPIO_INTMODE);
|
|
irq_set_irq_type(offset, intc_type);
|
|
|
|
ret = irq_chip_set_type_parent(d, type);
|
|
err:
|
|
spin_unlock_irqrestore(&priv->lock, flags);
|
|
return ret;
|
|
}
|
|
|
|
static int visconti_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
|
|
unsigned int child,
|
|
unsigned int child_type,
|
|
unsigned int *parent,
|
|
unsigned int *parent_type)
|
|
{
|
|
/* Interrupts 0..15 mapped to interrupts 24..39 on the GIC */
|
|
if (child < 16) {
|
|
/* All these interrupts are level high in the CPU */
|
|
*parent_type = IRQ_TYPE_LEVEL_HIGH;
|
|
*parent = child + BASE_HW_IRQ;
|
|
return 0;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static void *visconti_gpio_populate_parent_fwspec(struct gpio_chip *chip,
|
|
unsigned int parent_hwirq,
|
|
unsigned int parent_type)
|
|
{
|
|
struct irq_fwspec *fwspec;
|
|
|
|
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
|
|
if (!fwspec)
|
|
return NULL;
|
|
|
|
fwspec->fwnode = chip->irq.parent_domain->fwnode;
|
|
fwspec->param_count = 3;
|
|
fwspec->param[0] = 0;
|
|
fwspec->param[1] = parent_hwirq;
|
|
fwspec->param[2] = parent_type;
|
|
|
|
return fwspec;
|
|
}
|
|
|
|
static int visconti_gpio_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct visconti_gpio *priv;
|
|
struct irq_chip *irq_chip;
|
|
struct gpio_irq_chip *girq;
|
|
struct irq_domain *parent;
|
|
struct device_node *irq_parent;
|
|
struct fwnode_handle *fwnode;
|
|
int ret;
|
|
|
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
|
if (!priv)
|
|
return -ENOMEM;
|
|
|
|
spin_lock_init(&priv->lock);
|
|
|
|
priv->base = devm_platform_ioremap_resource(pdev, 0);
|
|
if (IS_ERR(priv->base))
|
|
return PTR_ERR(priv->base);
|
|
|
|
irq_parent = of_irq_find_parent(dev->of_node);
|
|
if (!irq_parent) {
|
|
dev_err(dev, "No IRQ parent node\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
parent = irq_find_host(irq_parent);
|
|
if (!parent) {
|
|
dev_err(dev, "No IRQ parent domain\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
fwnode = of_node_to_fwnode(irq_parent);
|
|
of_node_put(irq_parent);
|
|
|
|
ret = bgpio_init(&priv->gpio_chip, dev, 4,
|
|
priv->base + GPIO_IDATA,
|
|
priv->base + GPIO_OSET,
|
|
priv->base + GPIO_OCLR,
|
|
priv->base + GPIO_DIR,
|
|
NULL,
|
|
0);
|
|
if (ret) {
|
|
dev_err(dev, "unable to init generic GPIO\n");
|
|
return ret;
|
|
}
|
|
|
|
irq_chip = &priv->irq_chip;
|
|
irq_chip->name = dev_name(dev);
|
|
irq_chip->irq_mask = irq_chip_mask_parent;
|
|
irq_chip->irq_unmask = irq_chip_unmask_parent;
|
|
irq_chip->irq_eoi = irq_chip_eoi_parent;
|
|
irq_chip->irq_set_type = visconti_gpio_irq_set_type;
|
|
irq_chip->flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND;
|
|
|
|
girq = &priv->gpio_chip.irq;
|
|
girq->chip = irq_chip;
|
|
girq->fwnode = fwnode;
|
|
girq->parent_domain = parent;
|
|
girq->child_to_parent_hwirq = visconti_gpio_child_to_parent_hwirq;
|
|
girq->populate_parent_alloc_arg = visconti_gpio_populate_parent_fwspec;
|
|
girq->default_type = IRQ_TYPE_NONE;
|
|
girq->handler = handle_level_irq;
|
|
|
|
return devm_gpiochip_add_data(dev, &priv->gpio_chip, priv);
|
|
}
|
|
|
|
static const struct of_device_id visconti_gpio_of_match[] = {
|
|
{ .compatible = "toshiba,gpio-tmpv7708", },
|
|
{ /* end of table */ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, visconti_gpio_of_match);
|
|
|
|
static struct platform_driver visconti_gpio_driver = {
|
|
.probe = visconti_gpio_probe,
|
|
.driver = {
|
|
.name = "visconti_gpio",
|
|
.of_match_table = of_match_ptr(visconti_gpio_of_match),
|
|
}
|
|
};
|
|
module_platform_driver(visconti_gpio_driver);
|
|
|
|
MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>");
|
|
MODULE_DESCRIPTION("Toshiba Visconti GPIO Driver");
|
|
MODULE_LICENSE("GPL v2");
|