From: Michael Walle <michael@walle.cc> To: linux-gpio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-pwm@vger.kernel.org, linux-watchdog@vger.kernel.org, linux-arm-kernel@lists.infradead.org Cc: "Linus Walleij" <linus.walleij@linaro.org>, "Bartosz Golaszewski" <bgolaszewski@baylibre.com>, "Rob Herring" <robh+dt@kernel.org>, "Jean Delvare" <jdelvare@suse.com>, "Guenter Roeck" <linux@roeck-us.net>, "Lee Jones" <lee.jones@linaro.org>, "Thierry Reding" <thierry.reding@gmail.com>, "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>, "Wim Van Sebroeck" <wim@linux-watchdog.org>, "Shawn Guo" <shawnguo@kernel.org>, "Li Yang" <leoyang.li@nxp.com>, "Thomas Gleixner" <tglx@linutronix.de>, "Jason Cooper" <jason@lakedaemon.net>, "Marc Zyngier" <maz@kernel.org>, "Michael Walle" <michael@walle.cc> Subject: [PATCH 12/18] gpio: add support for the sl28cpld GPIO controller Date: Tue, 17 Mar 2020 21:50:11 +0100 Message-ID: <20200317205017.28280-13-michael@walle.cc> (raw) In-Reply-To: <20200317205017.28280-1-michael@walle.cc> This adds support for the GPIO controller of the sl28 board management controller. This driver is part of a multi-function device. Signed-off-by: Michael Walle <michael@walle.cc> --- drivers/gpio/Kconfig | 11 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-sl28cpld.c | 332 +++++++++++++++++++++++++++++++++++ 3 files changed, 344 insertions(+) create mode 100644 drivers/gpio/gpio-sl28cpld.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3cbf8882a0dd..516e47017ef5 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1211,6 +1211,17 @@ config GPIO_RC5T583 This driver provides the support for driving/reading the gpio pins of RC5T583 device through standard gpio library. +config GPIO_SL28CPLD + tristate "Kontron sl28 GPIO" + depends on MFD_SL28CPLD + depends on OF_GPIO + select GPIOLIB_IRQCHIP + help + This enables support for the GPIOs found on the Kontron sl28 CPLD. + + This driver can also be built as a module. If so, the module will be + called gpio-sl28cpld. + config GPIO_STMPE bool "STMPE GPIOs" depends on MFD_STMPE diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 0b571264ddbc..0ca2d52c78e8 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -127,6 +127,7 @@ obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o obj-$(CONFIG_GPIO_SCH) += gpio-sch.o obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o +obj-$(CONFIG_GPIO_SL28CPLD) += gpio-sl28cpld.o obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o diff --git a/drivers/gpio/gpio-sl28cpld.c b/drivers/gpio/gpio-sl28cpld.c new file mode 100644 index 000000000000..94f82013882f --- /dev/null +++ b/drivers/gpio/gpio-sl28cpld.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SMARC-sAL28 GPIO driver. + * + * Copyright 2019 Kontron Europe GmbH + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/interrupt.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include <linux/gpio/driver.h> + +#define GPIO_REG_DIR 0 +#define GPIO_REG_OUT 1 +#define GPIO_REG_IN 2 +#define GPIO_REG_IE 3 +#define GPIO_REG_IP 4 + +#define GPI_REG_IN 0 + +#define GPO_REG_OUT 0 + +enum sl28cpld_gpio_type { + sl28cpld_gpio, + sl28cpld_gpi, + sl28cpld_gpo, +}; + +struct sl28cpld_gpio { + struct gpio_chip gpio_chip; + struct irq_chip irq_chip; + struct regmap *regmap; + u32 offset; + struct mutex lock; + u8 ie; +}; + +static void sl28cpld_gpio_set_reg(struct gpio_chip *chip, unsigned int reg, + unsigned int offset, int value) +{ + struct sl28cpld_gpio *gpio = gpiochip_get_data(chip); + unsigned int mask = 1 << offset; + unsigned int val = value << offset; + + regmap_update_bits(gpio->regmap, gpio->offset + reg, mask, val); +} + +static void sl28cpld_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + sl28cpld_gpio_set_reg(chip, GPIO_REG_OUT, offset, value); +} + +static void sl28cpld_gpo_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + sl28cpld_gpio_set_reg(chip, GPO_REG_OUT, offset, value); +} + +static int sl28cpld_gpio_get_reg(struct gpio_chip *chip, unsigned int reg, + unsigned int offset) +{ + struct sl28cpld_gpio *gpio = gpiochip_get_data(chip); + unsigned int mask = 1 << offset; + unsigned int val; + int ret; + + ret = regmap_read(gpio->regmap, gpio->offset + reg, &val); + if (ret) + return ret; + + return (val & mask) ? 1 : 0; +} + +static int sl28cpld_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + return sl28cpld_gpio_get_reg(chip, GPIO_REG_IN, offset); +} + +static int sl28cpld_gpi_get(struct gpio_chip *chip, unsigned int offset) +{ + return sl28cpld_gpio_get_reg(chip, GPI_REG_IN, offset); +} + +static int sl28cpld_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + struct sl28cpld_gpio *gpio = gpiochip_get_data(chip); + unsigned int reg; + int ret; + + ret = regmap_read(gpio->regmap, gpio->offset + GPIO_REG_DIR, ®); + if (ret) + return ret; + + if (reg & (1 << offset)) + return GPIO_LINE_DIRECTION_OUT; + else + return GPIO_LINE_DIRECTION_IN; +} + +static int sl28cpld_gpio_set_direction(struct gpio_chip *chip, + unsigned int offset, + bool output) +{ + struct sl28cpld_gpio *gpio = gpiochip_get_data(chip); + unsigned int mask = 1 << offset; + unsigned int val = (output) ? mask : 0; + + return regmap_update_bits(gpio->regmap, gpio->offset + GPIO_REG_DIR, + mask, val); + +} + +static int sl28cpld_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + return sl28cpld_gpio_set_direction(chip, offset, false); +} + +static int sl28cpld_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + sl28cpld_gpio_set_reg(chip, GPIO_REG_OUT, offset, value); + return sl28cpld_gpio_set_direction(chip, offset, true); +} + +static void sl28cpld_gpio_irq_lock(struct irq_data *data) +{ + struct sl28cpld_gpio *gpio = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + + mutex_lock(&gpio->lock); +} + +static void sl28cpld_gpio_irq_sync_unlock(struct irq_data *data) +{ + struct sl28cpld_gpio *gpio = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + + regmap_write(gpio->regmap, gpio->offset + GPIO_REG_IE, gpio->ie); + mutex_unlock(&gpio->lock); +} + +static void sl28cpld_gpio_irq_disable(struct irq_data *data) +{ + struct sl28cpld_gpio *gpio = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + + if (data->hwirq >= 8) + return; + + gpio->ie &= ~(1 << data->hwirq); +} + +static void sl28cpld_gpio_irq_enable(struct irq_data *data) +{ + struct sl28cpld_gpio *gpio = + gpiochip_get_data(irq_data_get_irq_chip_data(data)); + + if (data->hwirq >= 8) + return; + + gpio->ie |= (1 << data->hwirq); +} + +static int sl28cpld_gpio_irq_set_type(struct irq_data *data, unsigned int type) +{ + /* only edge triggered interrupts on both edges are supported */ + return (type == IRQ_TYPE_EDGE_BOTH) ? 0 : -EINVAL; +} + +static irqreturn_t sl28cpld_gpio_irq_thread(int irq, void *data) +{ + struct sl28cpld_gpio *gpio = data; + unsigned int ip; + unsigned int virq; + int pin; + int ret; + + ret = regmap_read(gpio->regmap, gpio->offset + GPIO_REG_IP, &ip); + if (ret) + return IRQ_NONE; + + /* mask other pending interrupts which are not enabled */ + ip &= gpio->ie; + + /* ack the interrupts */ + regmap_write(gpio->regmap, gpio->offset + GPIO_REG_IP, ip); + + /* and handle them */ + while (ip) { + pin = __ffs(ip); + ip &= ~BIT(pin); + + virq = irq_find_mapping(gpio->gpio_chip.irq.domain, pin); + if (virq) + handle_nested_irq(virq); + } + + return IRQ_HANDLED; +} + +static int sl28_cpld_gpio_irq_init(struct platform_device *pdev, int irq) +{ + struct sl28cpld_gpio *gpio = platform_get_drvdata(pdev); + struct irq_chip *irq_chip = &gpio->irq_chip; + int ret; + + irq_chip->name = "sl28cpld-gpio-irq", + irq_chip->irq_bus_lock = sl28cpld_gpio_irq_lock, + irq_chip->irq_bus_sync_unlock = sl28cpld_gpio_irq_sync_unlock, + irq_chip->irq_disable = sl28cpld_gpio_irq_disable, + irq_chip->irq_enable = sl28cpld_gpio_irq_enable, + irq_chip->irq_set_type = sl28cpld_gpio_irq_set_type, + irq_chip->flags = IRQCHIP_SKIP_SET_WAKE, + + ret = gpiochip_irqchip_add_nested(&gpio->gpio_chip, irq_chip, 0, + handle_simple_irq, IRQ_TYPE_NONE); + if (ret) + return ret; + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + sl28cpld_gpio_irq_thread, + IRQF_SHARED | IRQF_ONESHOT, + pdev->name, gpio); + if (ret) + return ret; + + gpiochip_set_nested_irqchip(&gpio->gpio_chip, irq_chip, irq); + + return 0; +} + +static int sl28cpld_gpio_probe(struct platform_device *pdev) +{ + enum sl28cpld_gpio_type type = + platform_get_device_id(pdev)->driver_data; + struct device_node *np = pdev->dev.of_node; + struct sl28cpld_gpio *gpio; + struct gpio_chip *chip; + struct resource *res; + bool irq_support = false; + int ret; + int irq; + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + if (!pdev->dev.parent) + return -ENODEV; + + gpio->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!gpio->regmap) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_REG, 0); + if (!res) + return -EINVAL; + gpio->offset = res->start; + + /* initialize struct gpio_chip */ + mutex_init(&gpio->lock); + chip = &gpio->gpio_chip; + chip->parent = &pdev->dev; + chip->label = dev_name(&pdev->dev); + chip->owner = THIS_MODULE; + chip->can_sleep = true; + chip->base = -1; + chip->ngpio = 8; + + switch (type) { + case sl28cpld_gpio: + chip->get_direction = sl28cpld_gpio_get_direction; + chip->direction_input = sl28cpld_gpio_direction_input; + chip->direction_output = sl28cpld_gpio_direction_output; + chip->get = sl28cpld_gpio_get; + chip->set = sl28cpld_gpio_set; + irq_support = true; + break; + case sl28cpld_gpo: + chip->set = sl28cpld_gpo_set; + chip->get = sl28cpld_gpi_get; + break; + case sl28cpld_gpi: + chip->get = sl28cpld_gpi_get; + break; + } + + ret = devm_gpiochip_add_data(&pdev->dev, chip, gpio); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, gpio); + + if (irq_support && of_property_read_bool(np, "interrupt-controller")) { + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return ret; + + ret = sl28_cpld_gpio_irq_init(pdev, irq); + if (ret) + return ret; + } + + return 0; +} + +static const struct platform_device_id sl28cpld_gpio_id_table[] = { + {"sl28cpld-gpio", sl28cpld_gpio}, + {"sl28cpld-gpi", sl28cpld_gpi}, + {"sl28cpld-gpo", sl28cpld_gpo}, +}; +MODULE_DEVICE_TABLE(platform, sl28cpld_gpio_id_table); + +static struct platform_driver sl28cpld_gpio_driver = { + .probe = sl28cpld_gpio_probe, + .id_table = sl28cpld_gpio_id_table, + .driver = { + .name = "sl28cpld-gpio", + }, +}; +module_platform_driver(sl28cpld_gpio_driver); + +MODULE_DESCRIPTION("sl28cpld GPIO Driver"); +MODULE_LICENSE("GPL"); -- 2.20.1
next prev parent reply index Thread overview: 40+ messages / expand[flat|nested] mbox.gz Atom feed top 2020-03-17 20:49 [PATCH 00/18] Add support for Kontron sl28cpld Michael Walle 2020-03-17 20:50 ` [PATCH 01/18] include/linux/ioport.h: add helper to define REG resource constructs Michael Walle 2020-03-17 20:50 ` [PATCH 02/18] mfd: mfd-core: Don't overwrite the dma_mask of the child device Michael Walle 2020-03-17 20:50 ` [PATCH 03/18] mfd: mfd-core: match device tree node against reg property Michael Walle 2020-03-17 20:50 ` [PATCH 04/18] dt-bindings: mfd: Add bindings for sl28cpld Michael Walle 2020-03-30 22:35 ` Rob Herring 2020-03-31 7:40 ` Michael Walle 2020-03-17 20:50 ` [PATCH 05/18] mfd: Add support for Kontron sl28cpld management controller Michael Walle 2020-03-18 3:28 ` Guenter Roeck 2020-03-18 16:38 ` Michael Walle 2020-03-17 20:50 ` [PATCH 06/18] irqchip: add sl28cpld interrupt controller support Michael Walle 2020-03-18 16:53 ` Guenter Roeck 2020-03-18 17:06 ` Michael Walle 2020-03-18 20:35 ` Guenter Roeck 2020-03-17 20:50 ` [PATCH 07/18] dt-bindings: watchdog: Add bindings for sl28cpld watchdog Michael Walle 2020-03-17 20:50 ` [PATCH 08/18] watchdog: add support " Michael Walle 2020-03-18 3:17 ` Guenter Roeck 2020-03-17 20:50 ` [PATCH 09/18] dt-bindings: pwm: Add bindings for sl28cpld PWM controller Michael Walle 2020-03-17 20:50 ` [PATCH 10/18] pwm: add support " Michael Walle 2020-03-17 20:50 ` [PATCH 11/18] dt-bindings: gpio: Add bindings for sl28cpld GPIO controller Michael Walle 2020-03-17 20:50 ` Michael Walle [this message] 2020-03-18 9:14 ` [PATCH 12/18] gpio: add support for the " Bartosz Golaszewski 2020-03-18 12:45 ` Michael Walle 2020-03-25 11:50 ` Bartosz Golaszewski 2020-03-26 20:05 ` Michael Walle 2020-03-27 10:20 ` Linus Walleij 2020-03-27 15:28 ` Michael Walle 2020-03-27 19:01 ` Linus Walleij 2020-03-30 11:21 ` Bartosz Golaszewski 2020-03-30 11:48 ` Michael Walle 2020-03-28 12:04 ` Michael Walle 2020-03-28 17:20 ` Michael Walle 2020-03-17 20:50 ` [PATCH 13/18] dt-bindings: hwmon: Add bindings for sl28cpld hardware monitoring Michael Walle 2020-03-17 20:50 ` [PATCH 14/18] hwmon: add support for the sl28cpld hardware monitoring controller Michael Walle 2020-03-18 3:27 ` Guenter Roeck 2020-03-18 16:32 ` Michael Walle 2020-03-17 20:50 ` [PATCH 15/18] arm64: dts: freescale: sl28: enable sl28cpld Michael Walle 2020-03-17 20:50 ` [PATCH 16/18] arm64: dts: freescale: sl28: map GPIOs to input events Michael Walle 2020-03-17 20:50 ` [PATCH 17/18] arm64: dts: freescale: sl28: enable LED support Michael Walle 2020-03-17 20:50 ` [PATCH 18/18] arm64: dts: freescale: sl28: enable fan support Michael Walle
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20200317205017.28280-13-michael@walle.cc \ --to=michael@walle.cc \ --cc=bgolaszewski@baylibre.com \ --cc=devicetree@vger.kernel.org \ --cc=jason@lakedaemon.net \ --cc=jdelvare@suse.com \ --cc=lee.jones@linaro.org \ --cc=leoyang.li@nxp.com \ --cc=linus.walleij@linaro.org \ --cc=linux-arm-kernel@lists.infradead.org \ --cc=linux-gpio@vger.kernel.org \ --cc=linux-hwmon@vger.kernel.org \ --cc=linux-kernel@vger.kernel.org \ --cc=linux-pwm@vger.kernel.org \ --cc=linux-watchdog@vger.kernel.org \ --cc=linux@roeck-us.net \ --cc=maz@kernel.org \ --cc=robh+dt@kernel.org \ --cc=shawnguo@kernel.org \ --cc=tglx@linutronix.de \ --cc=thierry.reding@gmail.com \ --cc=u.kleine-koenig@pengutronix.de \ --cc=wim@linux-watchdog.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Linux-Watchdog Archive on lore.kernel.org Archives are clonable: git clone --mirror https://lore.kernel.org/linux-watchdog/0 linux-watchdog/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 linux-watchdog linux-watchdog/ https://lore.kernel.org/linux-watchdog \ linux-watchdog@vger.kernel.org public-inbox-index linux-watchdog Example config snippet for mirrors Newsgroup available over NNTP: nntp://nntp.lore.kernel.org/org.kernel.vger.linux-watchdog AGPL code for this site: git clone https://public-inbox.org/public-inbox.git