blob: 06db361881d99f717b7aa006994d6671190b8346 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Synaptics AS390 GPIO driver
*
* Copyright (C) 2018 Synaptics Incorporated
*
* Author: Jisheng Zhang <jszhang@kernel.org>
*/
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#define AS390_GPIO_OUTDIR 0x00
#define AS390_GPIO_OUTDIR_SET 0x04
#define AS390_GPIO_OUTDIR_CLR 0x08
#define AS390_GPIO_OUTLVL 0x0c
#define AS390_GPIO_OUTLVL_SET 0x10
#define AS390_GPIO_OUTLVL_CLR 0x14
#define AS390_GPIO_INT_POLARITY 0x18
#define AS390_GPIO_INT_POLARITY_SET 0x1c
#define AS390_GPIO_INT_POLARITY_CLR 0x20
#define AS390_GPIO_INT_ENABLE 0x24
#define AS390_GPIO_INT_ENABLE_SET 0x28
#define AS390_GPIO_INT_ENABLE_CLR 0x2c
#define AS390_GPIO_IN_STATUS 0x30
#define AS390_GPIO_NR 8
struct as390_gpio {
void __iomem *base;
struct gpio_chip gc;
};
static inline u32 as390_read(struct as390_gpio *chip, unsigned int regoff)
{
void __iomem *reg_base = chip->base;
return readl_relaxed(reg_base + regoff);
}
static inline void as390_write(struct as390_gpio *chip, unsigned int regoff,
u32 val)
{
void __iomem *reg_base = chip->base;
writel_relaxed(val, reg_base + regoff);
}
static int as390_direction_input(struct gpio_chip *gc, unsigned offset)
{
struct as390_gpio *chip = gpiochip_get_data(gc);
as390_write(chip, AS390_GPIO_OUTDIR_CLR, BIT(offset));
return 0;
}
static int as390_direction_output(struct gpio_chip *gc, unsigned offset,
int value)
{
struct as390_gpio *chip = gpiochip_get_data(gc);
as390_write(chip, AS390_GPIO_OUTDIR_SET, BIT(offset));
if (value)
as390_write(chip, AS390_GPIO_OUTLVL_SET, BIT(offset));
else
as390_write(chip, AS390_GPIO_OUTLVL_CLR, BIT(offset));
return 0;
}
static int as390_get_value(struct gpio_chip *gc, unsigned offset)
{
struct as390_gpio *chip = gpiochip_get_data(gc);
return !!(as390_read(chip, AS390_GPIO_IN_STATUS) & BIT(offset));
}
static void as390_set_value(struct gpio_chip *gc, unsigned offset, int value)
{
struct as390_gpio *chip = gpiochip_get_data(gc);
if (value)
as390_write(chip, AS390_GPIO_OUTLVL_SET, BIT(offset));
else
as390_write(chip, AS390_GPIO_OUTLVL_CLR, BIT(offset));
}
static int as390_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct as390_gpio *chip;
struct resource *res;
int ret;
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
chip->base = devm_ioremap_resource(dev, res);
if (IS_ERR(chip->base))
return PTR_ERR(chip->base);
chip->gc.direction_input = as390_direction_input;
chip->gc.direction_output = as390_direction_output;
chip->gc.get = as390_get_value;
chip->gc.set = as390_set_value;
chip->gc.base = -1;
chip->gc.ngpio = AS390_GPIO_NR;
chip->gc.label = dev_name(dev);
chip->gc.parent = dev;
chip->gc.owner = THIS_MODULE;
ret = gpiochip_add_data(&chip->gc, chip);
if (ret)
return ret;
platform_set_drvdata(pdev, chip);
return 0;
}
static const struct of_device_id as390_gpio_match[] = {
{
.compatible = "syna,as390-gpio",
},
{},
};
static struct platform_driver as390_gpio_driver = {
.probe = as390_gpio_probe,
.driver = {
.name = "as390_gpio",
.of_match_table = of_match_ptr(as390_gpio_match),
},
};
module_platform_driver(as390_gpio_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>");
MODULE_DESCRIPTION("Synaptics AS390 GPIO driver");