// SPDX-License-Identifier: GPL-2.0-only /* * GPIO driver for the MikroTik RouterBoard 4xx series * * Copyright (C) 2008-2011 Gabor Juhos * Copyright (C) 2008 Imre Kaloz * Copyright (C) 2015 Bert Vermeulen * Copyright (C) 2020 Christopher Hill * * This file was based on the driver for Linux 2.6.22 published by * MikroTik for their RouterBoard 4xx series devices. * * N.B. driver probe reports "DMA mask not set" warnings which are * an artifact of using a platform_driver as an MFD device child. * See conversation here https://lkml.org/lkml/2020/4/28/675 */ #include #include #include #include #include struct rb4xx_gpio { struct rb4xx_cpld *cpld; struct device *dev; struct gpio_chip chip; struct mutex lock; u16 values; /* bitfield of GPIO 0-8 current values */ }; static int rb4xx_gpio_cpld_set(struct rb4xx_gpio *gpio, unsigned int offset, int value) { struct rb4xx_cpld *cpld = gpio->cpld; u16 values; int ret; mutex_lock(&gpio->lock); values = gpio->values; if (value) values |= BIT(offset); else values &= ~(BIT(offset)); if (values == gpio->values) { ret = 0; goto unlock; } if (offset < 8) { ret = cpld->gpio_set_0_7(cpld, values & 0xff); } else if (offset == 8) { ret = cpld->gpio_set_8(cpld, values >> 8); } if(likely(!ret)) gpio->values = values; unlock: mutex_unlock(&gpio->lock); return ret; } static int rb4xx_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { return 0; /* All 9 GPIOs are out */ } static int rb4xx_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { return -EOPNOTSUPP; } static int rb4xx_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { return rb4xx_gpio_cpld_set(gpiochip_get_data(chip), offset, value); } static int rb4xx_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct rb4xx_gpio *gpio = gpiochip_get_data(chip); int ret; mutex_lock(&gpio->lock); ret = (gpio->values >> offset) & 0x1; mutex_unlock(&gpio->lock); return ret; } static void rb4xx_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { rb4xx_gpio_cpld_set(gpiochip_get_data(chip), offset, value); } static int rb4xx_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device *parent = dev->parent; struct rb4xx_gpio *gpio; u32 val; if (!parent) return -ENODEV; gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL); if (!gpio) return -ENOMEM; platform_set_drvdata(pdev, gpio); gpio->cpld = dev_get_drvdata(parent); gpio->dev = dev; gpio->values = 0; mutex_init(&gpio->lock); gpio->chip.label = "rb4xx-gpio"; gpio->chip.parent = dev; gpio->chip.owner = THIS_MODULE; gpio->chip.get_direction = rb4xx_gpio_get_direction; gpio->chip.direction_input = rb4xx_gpio_direction_input; gpio->chip.direction_output = rb4xx_gpio_direction_output; gpio->chip.get = rb4xx_gpio_get; gpio->chip.set = rb4xx_gpio_set; gpio->chip.ngpio = 9; gpio->chip.base = -1; gpio->chip.can_sleep = 1; if (!of_property_read_u32(dev->of_node, "base", &val)) gpio->chip.base = val; return gpiochip_add_data(&gpio->chip, gpio); } static int rb4xx_gpio_remove(struct platform_device *pdev) { struct rb4xx_gpio *gpio = platform_get_drvdata(pdev); gpiochip_remove(&gpio->chip); mutex_destroy(&gpio->lock); return 0; } static const struct platform_device_id rb4xx_gpio_id_table[] = { { "mikrotik,rb4xx-gpio", }, { }, }; MODULE_DEVICE_TABLE(platform, rb4xx_gpio_id_table); static struct platform_driver rb4xx_gpio_driver = { .probe = rb4xx_gpio_probe, .remove = rb4xx_gpio_remove, .id_table = rb4xx_gpio_id_table, .driver = { .name = "rb4xx-gpio", }, }; module_platform_driver(rb4xx_gpio_driver); MODULE_DESCRIPTION("Mikrotik RB4xx GPIO driver"); MODULE_AUTHOR("Gabor Juhos "); MODULE_AUTHOR("Imre Kaloz "); MODULE_AUTHOR("Bert Vermeulen "); MODULE_AUTHOR("Christopher Hill