| // 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"); |