diff options
Diffstat (limited to 'drivers/gpio')
33 files changed, 3311 insertions, 788 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 0f924d135f..d9bb68e06b 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config GPIOLIB bool select GENERIC_GPIO @@ -11,7 +12,7 @@ config GPIO_GENERIC config GPIO_DIGIC bool "GPIO support for Canon DIGIC" - depends on ARCH_DIGIC + depends on ARCH_DIGIC || COMPILE_TEST config GPIO_74164 bool "Generic SPI attached shift register" @@ -21,17 +22,31 @@ config GPIO_74164 shift registers. This driver can be used to provide access to more gpio outputs. +config GPIO_74XX_MMIO + tristate "GPIO driver for 74xx-ICs with MMIO access" + depends on OFDEVICE + select GPIO_GENERIC + help + Say yes here to support GPIO functionality for 74xx-compatible ICs + with MMIO access. Compatible models include: + 1 bit: 741G125 (Input), 741G74 (Output) + 2 bits: 742G125 (Input), 7474 (Output) + 4 bits: 74125 (Input), 74175 (Output) + 6 bits: 74365 (Input), 74174 (Output) + 8 bits: 74244 (Input), 74273 (Output) + 16 bits: 741624 (Input), 7416374 (Output) + config GPIO_CLPS711X bool "GPIO support for CLPS711X" - depends on ARCH_CLPS711X + depends on ARCH_CLPS711X || COMPILE_TEST select GPIO_GENERIC help Say yes here to enable the GPIO driver for the CLPS711X CPUs config GPIO_DAVINCI bool "TI Davinci/Keystone GPIO support" - default y if ARCH_DAVINCI - depends on ARM && ARCH_DAVINCI + default y if ARCH_DAVINCI || ARCH_K3 + depends on (ARM && (ARCH_DAVINCI || ARCH_K3)) || COMPILE_TEST help Say yes here to enable GPIO support for TI Davinci/Keystone SoCs. @@ -43,23 +58,27 @@ config GPIO_GENERIC_PLATFORM GPIO controllers config GPIO_IMX - def_bool ARCH_IMX + bool "i.MX GPIO controller" if COMPILE_TEST + default y if ARCH_IMX config GPIO_VF610 - def_bool ARCH_VF610 + bool "VF610 GPIO controller" if COMPILE_TEST + depends on ARCH_IMX || ARCH_VF610 || COMPILE_TEST + default y if ARCH_VF610 || ARCH_IMX93 config GPIO_MXS - def_bool ARCH_MXS + bool "MXS GPIO controller" if COMPILE_TEST + default y if ARCH_MXS config GPIO_JZ4740 bool "GPIO support for Ingenic SoCs" - depends on MACH_MIPS_XBURST + depends on MACH_MIPS_XBURST || COMPILE_TEST help Say yes here to enable the GPIO driver for the Ingenic SoCs. config GPIO_MALTA_FPGA_I2C bool "Malta CBUS FPGA I2C GPIO" - depends on MACH_MIPS_MALTA + depends on MACH_MIPS_MALTA || COMPILE_TEST help Support access to the CBUS FPGA I2C lines through the gpio library. @@ -69,18 +88,19 @@ config GPIO_MALTA_FPGA_I2C config GPIO_MPC8XXX bool "MPC512x/MPC8xxx/QorIQ GPIO support" - depends on ARCH_LAYERSCAPE + depends on ARCH_LAYERSCAPE || COMPILE_TEST select GPIO_GENERIC help Say Y here if you're going to use hardware that connects to the MPC512x/831x/834x/837x/8572/8610/QorIQ GPIOs. config GPIO_OMAP - def_bool ARCH_OMAP + bool "OMAP GPIO controller" if COMPILE_TEST + default y if ARCH_OMAP config GPIO_ORION bool "GPIO support for Marvell Orion/MVEBU SoCs" - depends on ARCH_MVEBU + depends on ARCH_MVEBU || COMPILE_TEST help Say yes here to add the driver for the GPIO controller found on Marvell Orion and MVEBU SoCs (Armada 370/XP, @@ -129,13 +149,27 @@ config GPIO_PL061 help Say yes here to support the PrimeCell PL061 GPIO device +config GPIO_RASPBERRYPI_EXP + bool "Raspberry Pi 3 GPIO Expander" + depends on ARCH_BCM283X + help + Turn on GPIO support for the expander on Raspberry Pi 3 boards, using + the firmware mailbox to communicate with VideoCore on BCM283x chips. + +config GPIO_ROCKCHIP + bool "Rockchip GPIO support" + depends on ARCH_ROCKCHIP + help + Say yes here to include the driver for the GPIO controller found on + Rockchip SoCs. + config GPIO_STMPE depends on MFD_STMPE bool "STMPE GPIO Expander" config GPIO_TEGRA bool "GPIO support for the Tegra SoCs" - depends on ARCH_TEGRA + depends on ARCH_TEGRA || COMPILE_TEST help Say yes here to include the driver for the GPIO controller found on the Tegra line of SoCs. @@ -153,10 +187,44 @@ config GPIO_SX150X Say Y here to build support for the Semtec Sx150x I2C GPIO expander chip. +config GPIO_SIFIVE + bool "SiFive GPIO support" + depends on OF_GPIO + select GPIO_GENERIC + help + Say yes here to support the GPIO device on SiFive SoCs. + +config GPIO_STARFIVE + bool "StarFive GPIO support" + depends on SOC_STARFIVE || CROSS_COMPILE + depends on OF_GPIO + select GPIO_GENERIC + help + Say yes here to support the GPIO device on StarFive SoCs. + config GPIO_LIBFTDI1 bool "libftdi1 driver" depends on SANDBOX +config GPIO_ZYNQ + tristate "Xilinx Zynq GPIO support" + depends on ARCH_ZYNQ || ARCH_ZYNQMP || CROSS_COMPILE + depends on OFDEVICE + help + Say yes here to support Xilinx Zynq GPIO controller. + +config GPIO_LATCH + tristate "GPIO latch driver" + help + Say yes here to enable a driver for GPIO multiplexers based on latches + connected to other GPIOs. + +config GPIO_INTEL + bool "Intel GPIO driver" + depends on X86 || COMPILE_TEST + help + Say Y here to build support for the Intel GPIO driver. + endmenu endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index bc5c500e4d..b0575ccf8c 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -1,6 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_GPIOLIB) += gpiolib.o obj-$(CONFIG_GPIO_74164) += gpio-74164.o +obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o obj-$(CONFIG_MACH_MIPS_ATH79) += gpio-ath79.o obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o @@ -18,7 +20,14 @@ obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o +obj-$(CONFIG_GPIO_ROCKCHIP) += gpio-rockchip.o obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o obj-$(CONFIG_GPIO_DESIGNWARE) += gpio-dw.o obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o +obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o +obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o +obj-$(CONFIG_GPIO_STARFIVE) += gpio-starfive-vic.o +obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o +obj-$(CONFIG_GPIO_LATCH) += gpio-latch.o +obj-$(CONFIG_GPIO_INTEL) += gpio-intel.o diff --git a/drivers/gpio/gpio-74164.c b/drivers/gpio/gpio-74164.c index e4d413b689..fb96e281b2 100644 --- a/drivers/gpio/gpio-74164.c +++ b/drivers/gpio/gpio-74164.c @@ -1,16 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2010 Gabor Juhos <juhosg@openwrt.org> +// SPDX-FileCopyrightText: 2010 Miguel Gaio <miguel.gaio@efixo.com> + /* * Generic serial-in/parallel-out 8-bits shift register GPIO driver * e.g. for 74x164 * - * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> - * - * Based on Linux driver - * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org> - * Copyright (C) 2010 Miguel Gaio <miguel.gaio@efixo.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. + * Created by Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * based on the Linux driver for this hardware. */ #include <common.h> @@ -97,16 +94,16 @@ static struct platform_device_id gpio_74164_ids[] = { { } }; -static int gpio_74164_probe(struct device_d *dev) +static int gpio_74164_probe(struct device *dev) { struct spi_device *spi = (struct spi_device *)dev->type_data; struct gpio_74164 *priv; u32 num_regs = 1; dev->id = DEVICE_ID_DYNAMIC; - if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node) { - dev->id = of_alias_get_id(dev->device_node, "gpio"); - of_property_read_u32(dev->device_node, "registers-number", + if (IS_ENABLED(CONFIG_OFDEVICE) && dev->of_node) { + dev->id = of_alias_get_id(dev->of_node, "gpio"); + of_property_read_u32(dev->of_node, "registers-number", &num_regs); } @@ -124,7 +121,7 @@ static int gpio_74164_probe(struct device_d *dev) return gpiochip_add(&priv->chip); } -static struct driver_d gpio_74164_driver = { +static struct driver gpio_74164_driver = { .name = "gpio-74164", .probe = gpio_74164_probe, .id_table = gpio_74164_ids, diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c new file mode 100644 index 0000000000..513d071c79 --- /dev/null +++ b/drivers/gpio/gpio-74xx-mmio.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 74xx MMIO GPIO driver + * + * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru> + * + * Ported to barebox from linux-v5.4-rc6 + * Copyright (C) 2019-2021 Antony Pavlov <antonynpavlov@gmail.com> + * + */ + +#include <common.h> +#include <driver.h> +#include <errno.h> +#include <gpio.h> +#include <init.h> +#include <io.h> +#include <malloc.h> +#include <of_device.h> + +#include <linux/err.h> +#include <linux/basic_mmio_gpio.h> + +#define MMIO_74XX_DIR_IN (0 << 8) +#define MMIO_74XX_DIR_OUT (1 << 8) +#define MMIO_74XX_BIT_CNT(x) ((x) & 0xff) + +struct mmio_74xx_gpio_priv { + struct bgpio_chip bgc; + unsigned int flags; +}; + +static const struct of_device_id mmio_74xx_gpio_ids[] = { + { + .compatible = "ti,741g125", + .data = (const void *)(MMIO_74XX_DIR_IN | 1), + }, + { + .compatible = "ti,742g125", + .data = (const void *)(MMIO_74XX_DIR_IN | 2), + }, + { + .compatible = "ti,74125", + .data = (const void *)(MMIO_74XX_DIR_IN | 4), + }, + { + .compatible = "ti,74365", + .data = (const void *)(MMIO_74XX_DIR_IN | 6), + }, + { + .compatible = "ti,74244", + .data = (const void *)(MMIO_74XX_DIR_IN | 8), + }, + { + .compatible = "ti,741624", + .data = (const void *)(MMIO_74XX_DIR_IN | 16), + }, + { + .compatible = "ti,741g74", + .data = (const void *)(MMIO_74XX_DIR_OUT | 1), + }, + { + .compatible = "ti,7474", + .data = (const void *)(MMIO_74XX_DIR_OUT | 2), + }, + { + .compatible = "ti,74175", + .data = (const void *)(MMIO_74XX_DIR_OUT | 4), + }, + { + .compatible = "ti,74174", + .data = (const void *)(MMIO_74XX_DIR_OUT | 6), + }, + { + .compatible = "ti,74273", + .data = (const void *)(MMIO_74XX_DIR_OUT | 8), + }, + { + .compatible = "ti,7416374", + .data = (const void *)(MMIO_74XX_DIR_OUT | 16), + }, + { } +}; +MODULE_DEVICE_TABLE(of, mmio_74xx_gpio_ids); + +static inline +struct mmio_74xx_gpio_priv *to_mmio_74xx_gpio_priv(struct gpio_chip *gc) +{ + struct bgpio_chip *bgc = + container_of(gc, struct bgpio_chip, gc); + + return container_of(bgc, struct mmio_74xx_gpio_priv, bgc); +} + +static int mmio_74xx_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct mmio_74xx_gpio_priv *priv = to_mmio_74xx_gpio_priv(gc); + + if (priv->flags & MMIO_74XX_DIR_OUT) + return GPIOF_DIR_OUT; + + return GPIOF_DIR_IN; +} + +static int mmio_74xx_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + struct mmio_74xx_gpio_priv *priv = to_mmio_74xx_gpio_priv(gc); + + return (priv->flags & MMIO_74XX_DIR_OUT) ? -ENOTSUPP : 0; +} + +static int mmio_74xx_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct mmio_74xx_gpio_priv *priv = to_mmio_74xx_gpio_priv(gc); + + if (priv->flags & MMIO_74XX_DIR_OUT) { + gc->ops->set(gc, gpio, val); + return 0; + } + + return -ENOTSUPP; +} + +static int mmio_74xx_gpio_probe(struct device *dev) +{ + struct mmio_74xx_gpio_priv *priv; + void __iomem *dat; + int err; + struct gpio_chip *gc; + + priv = xzalloc(sizeof(*priv)); + + priv->flags = (uintptr_t)of_device_get_match_data(dev); + + dat = dev_request_mem_region(dev, 0); + if (IS_ERR(dat)) + return PTR_ERR(dat); + + err = bgpio_init(&priv->bgc, dev, + DIV_ROUND_UP(MMIO_74XX_BIT_CNT(priv->flags), 8), + dat, NULL, NULL, NULL, NULL, 0); + if (err) + return err; + + gc = &priv->bgc.gc; + gc->ops->direction_input = mmio_74xx_dir_in; + gc->ops->direction_output = mmio_74xx_dir_out; + gc->ops->get_direction = mmio_74xx_get_direction; + gc->ngpio = MMIO_74XX_BIT_CNT(priv->flags); + + dev->priv = priv; + + return gpiochip_add(gc); +} + +static struct driver mmio_74xx_gpio_driver = { + .name = "74xx-mmio-gpio", + .of_compatible = DRV_OF_COMPAT(mmio_74xx_gpio_ids), + .probe = mmio_74xx_gpio_probe, +}; + +coredevice_platform_driver(mmio_74xx_gpio_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); +MODULE_DESCRIPTION("74xx MMIO GPIO driver"); diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c index d08d743b54..b32e9552ce 100644 --- a/drivers/gpio/gpio-ath79.c +++ b/drivers/gpio/gpio-ath79.c @@ -1,16 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com> +// SPDX-FileCopyrightText: 2008-2011 Gabor Juhos <juhosg@openwrt.org> +// SPDX-FileCopyrightText: 2008 Imre Kaloz <kaloz@openwrt.org> +// SPDX-FileCopyrightText: 2015 Antony Pavlov <antonynpavlov@gmail.com> + /* * Atheros AR71XX/AR724X/AR913X GPIO API support * - * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com> - * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org> - * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> - * Copyright (C) 2015 Antony Pavlov <antonynpavlov@gmail.com> - * * Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. */ #include <common.h> @@ -104,11 +102,12 @@ static const struct of_device_id ath79_gpio_of_match[] = { { .compatible = "qca,ar7100-gpio" }, {}, }; +MODULE_DEVICE_TABLE(of, ath79_gpio_of_match); -static int ath79_gpio_probe(struct device_d *dev) +static int ath79_gpio_probe(struct device *dev) { struct resource *iores; - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; int err; if (!np) { @@ -145,14 +144,10 @@ static int ath79_gpio_probe(struct device_d *dev) return 0; } -static struct driver_d ath79_gpio_driver = { +static struct driver ath79_gpio_driver = { .name = "ath79-gpio", .probe = ath79_gpio_probe, .of_compatible = DRV_OF_COMPAT(ath79_gpio_of_match), }; -static int ath79_gpio_init(void) -{ - return platform_driver_register(&ath79_gpio_driver); -} -coredevice_initcall(ath79_gpio_init); +coredevice_platform_driver(ath79_gpio_driver); diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c index 44f40c36e4..a59bdd212a 100644 --- a/drivers/gpio/gpio-clps711x.c +++ b/drivers/gpio/gpio-clps711x.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-or-later /* Author: Alexander Shiyan <shc_work@mail.ru> */ #include <init.h> @@ -7,16 +7,13 @@ #include <linux/err.h> #include <linux/basic_mmio_gpio.h> -static int clps711x_gpio_probe(struct device_d *dev) +static int clps711x_gpio_probe(struct device *dev) { struct resource *iores; - int err, id = dev->id; + int err, id = of_alias_get_id(dev->of_node, "gpio"); void __iomem *dat, *dir = NULL, *dir_inv = NULL; struct bgpio_chip *bgc; - if (dev->device_node) - id = of_alias_get_id(dev->device_node, "gpio"); - if (id < 0 || id > 4) return -ENODEV; @@ -25,17 +22,15 @@ static int clps711x_gpio_probe(struct device_d *dev) return PTR_ERR(iores); dat = IOMEM(iores->start); + iores = dev_request_mem_resource(dev, 1); + if (IS_ERR(iores)) + return PTR_ERR(iores); + switch (id) { case 3: - iores = dev_request_mem_resource(dev, 1); - if (IS_ERR(iores)) - return PTR_ERR(iores); dir_inv = IOMEM(iores->start); break; default: - iores = dev_request_mem_resource(dev, 1); - if (IS_ERR(iores)) - return PTR_ERR(iores); dir = IOMEM(iores->start); break; } @@ -64,12 +59,13 @@ out_err: return err; } -static struct of_device_id __maybe_unused clps711x_gpio_dt_ids[] = { +static const struct of_device_id __maybe_unused clps711x_gpio_dt_ids[] = { { .compatible = "cirrus,ep7209-gpio", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, clps711x_gpio_dt_ids); -static struct driver_d clps711x_gpio_driver = { +static struct driver clps711x_gpio_driver = { .name = "clps711x-gpio", .probe = clps711x_gpio_probe, .of_compatible = DRV_OF_COMPAT(clps711x_gpio_dt_ids), diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index 7c060a09b1..831da2fc7f 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2006-2007 David Brownell +// SPDX-FileCopyrightText: 2007 MontaVista Software, Inc. <source@mvista.com> +// SPDX-FileCopyrightText: 2014 Antony Pavlov <antonynpavlov@gmail.com> + /* * TI DaVinci GPIO Support - * - * Copyright (c) 2006-2007 David Brownell - * Copyright (c) 2007, MontaVista Software, Inc. <source@mvista.com> - * Copyright (c) 2014 Antony Pavlov <antonynpavlov@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <common.h> @@ -17,9 +13,6 @@ #include <io.h> #include <linux/err.h> -#define readl_relaxed readl -#define writel_relaxed writel - struct davinci_gpio_regs { u32 dir; u32 out_data; @@ -37,40 +30,42 @@ struct davinci_gpio_controller { struct gpio_chip chip; /* Serialize access to GPIO registers */ void __iomem *regs; - void __iomem *set_data; - void __iomem *clr_data; - void __iomem *in_data; }; #define chip2controller(chip) \ container_of(chip, struct davinci_gpio_controller, chip) -static struct davinci_gpio_regs __iomem *gpio2regs(void __iomem *gpio_base, - unsigned gpio) +static struct davinci_gpio_regs __iomem *gpio2regs(struct davinci_gpio_controller *d, + unsigned gpio) { void __iomem *ptr; if (gpio < 32 * 1) - ptr = gpio_base + 0x10; + ptr = d->regs + 0x10; else if (gpio < 32 * 2) - ptr = gpio_base + 0x38; + ptr = d->regs + 0x38; else if (gpio < 32 * 3) - ptr = gpio_base + 0x60; + ptr = d->regs + 0x60; else if (gpio < 32 * 4) - ptr = gpio_base + 0x88; + ptr = d->regs + 0x88; else if (gpio < 32 * 5) - ptr = gpio_base + 0xb0; + ptr = d->regs + 0xb0; else ptr = NULL; return ptr; } +static inline u32 __gpio_mask(unsigned gpio) +{ + return 1 << (gpio % 32); +} + static int davinci_get_direction(struct gpio_chip *chip, unsigned offset) { struct davinci_gpio_controller *d = chip2controller(chip); - struct davinci_gpio_regs __iomem *g = d->regs; + struct davinci_gpio_regs __iomem *g = gpio2regs(d, offset); - return ((readl_relaxed(&g->dir)) & (1 << offset)) ? + return ((readl_relaxed(&g->dir)) & __gpio_mask(offset)) ? GPIOF_DIR_IN : GPIOF_DIR_OUT; } @@ -78,9 +73,9 @@ static inline int __davinci_direction(struct gpio_chip *chip, unsigned offset, bool out, int value) { struct davinci_gpio_controller *d = chip2controller(chip); - struct davinci_gpio_regs __iomem *g = d->regs; + struct davinci_gpio_regs __iomem *g = gpio2regs(d, offset); u32 temp; - u32 mask = 1 << offset; + u32 mask = __gpio_mask(offset); temp = readl_relaxed(&g->dir); if (out) { @@ -115,9 +110,9 @@ davinci_direction_out(struct gpio_chip *chip, unsigned offset, int value) static int davinci_gpio_get(struct gpio_chip *chip, unsigned offset) { struct davinci_gpio_controller *d = chip2controller(chip); - struct davinci_gpio_regs __iomem *g = d->regs; + struct davinci_gpio_regs __iomem *g = gpio2regs(d, offset); - return ((1 << offset) & readl_relaxed(&g->in_data)) ? 1 : 0; + return (__gpio_mask(offset) & readl_relaxed(&g->in_data)) ? 1 : 0; } /* @@ -127,9 +122,9 @@ static void davinci_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct davinci_gpio_controller *d = chip2controller(chip); - struct davinci_gpio_regs __iomem *g = d->regs; + struct davinci_gpio_regs __iomem *g = gpio2regs(d, offset); - writel_relaxed((1 << offset), value ? &g->set_data : &g->clr_data); + writel_relaxed(__gpio_mask(offset), value ? &g->set_data : &g->clr_data); } static struct gpio_ops davinci_gpio_ops = { @@ -140,17 +135,17 @@ static struct gpio_ops davinci_gpio_ops = { .set = davinci_gpio_set, }; -static int davinci_gpio_probe(struct device_d *dev) +static int davinci_gpio_probe(struct device *dev) { struct resource *iores; void __iomem *gpio_base; int ret; u32 val; - int i, base; unsigned ngpio; struct davinci_gpio_controller *chips; + struct gpio_chip *gc; - ret = of_property_read_u32(dev->device_node, "ti,ngpio", &val); + ret = of_property_read_u32(dev->of_node, "ti,ngpio", &val); if (ret) { dev_err(dev, "could not read 'ti,ngpio' property\n"); return -EINVAL; @@ -161,7 +156,7 @@ static int davinci_gpio_probe(struct device_d *dev) if (WARN_ON(ARCH_NR_GPIOS < ngpio)) ngpio = ARCH_NR_GPIOS; - chips = xzalloc((ngpio / 32 + 1) * sizeof(*chips)); + chips = xzalloc(sizeof(*chips)); iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) { @@ -170,44 +165,31 @@ static int davinci_gpio_probe(struct device_d *dev) } gpio_base = IOMEM(iores->start); - for (i = 0, base = 0; base < ngpio; i++, base += 32) { - struct davinci_gpio_regs __iomem *regs; - struct gpio_chip *gc; - - gc = &chips[i].chip; - gc->ops = &davinci_gpio_ops; - - gc->dev = dev; - gc->base = base; - gc->ngpio = ngpio - base; - if (gc->ngpio > 32) - gc->ngpio = 32; + gc = &chips->chip; + gc->ops = &davinci_gpio_ops; + gc->dev = dev; + gc->ngpio = ngpio; + gc->base = -1; - regs = gpio2regs(gpio_base, base); - chips[i].regs = regs; - chips[i].set_data = ®s->set_data; - chips[i].clr_data = ®s->clr_data; - chips[i].in_data = ®s->in_data; + chips->regs = gpio_base; - gpiochip_add(gc); - } + gpiochip_add(gc); return 0; } static struct of_device_id davinci_gpio_ids[] = { + { .compatible = "ti,keystone-gpio", }, + { .compatible = "ti,am654-gpio", }, { .compatible = "ti,dm6441-gpio", }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, davinci_gpio_ids); -static struct driver_d davinci_gpio_driver = { +static struct driver davinci_gpio_driver = { .name = "davinci_gpio", .probe = davinci_gpio_probe, .of_compatible = DRV_OF_COMPAT(davinci_gpio_ids), }; -static int davinci_gpio_drv_reg(void) -{ - return platform_driver_register(&davinci_gpio_driver); -} -coredevice_initcall(davinci_gpio_drv_reg); +coredevice_platform_driver(davinci_gpio_driver); diff --git a/drivers/gpio/gpio-digic.c b/drivers/gpio/gpio-digic.c index 714e3b4a1d..7e2db85efe 100644 --- a/drivers/gpio/gpio-digic.c +++ b/drivers/gpio/gpio-digic.c @@ -1,19 +1,5 @@ -/* - * Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com> - * - * This file is part of barebox. - * See file CREDITS for list of people who contributed to this project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2013 Antony Pavlov <antonynpavlov@gmail.com> #include <common.h> #include <malloc.h> @@ -120,7 +106,7 @@ static struct gpio_ops digic_gpio_ops = { .set = digic_gpio_set_value, }; -static int digic_gpio_probe(struct device_d *dev) +static int digic_gpio_probe(struct device *dev) { struct resource *iores; struct digic_gpio_chip *chip; @@ -170,15 +156,12 @@ static __maybe_unused struct of_device_id digic_gpio_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, digic_gpio_dt_ids); -static struct driver_d digic_gpio_driver = { +static struct driver digic_gpio_driver = { .name = "digic-gpio", .probe = digic_gpio_probe, .of_compatible = DRV_OF_COMPAT(digic_gpio_dt_ids), }; -static int digic_gpio_init(void) -{ - return platform_driver_register(&digic_gpio_driver); -} -coredevice_initcall(digic_gpio_init); +coredevice_platform_driver(digic_gpio_driver); diff --git a/drivers/gpio/gpio-dw.c b/drivers/gpio/gpio-dw.c index b81e6a75c5..e6eba6b423 100644 --- a/drivers/gpio/gpio-dw.c +++ b/drivers/gpio/gpio-dw.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2012 Altera + /* * Designware GPIO support functions - * - * Copyright (C) 2012 Altera - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <common.h> @@ -116,7 +104,7 @@ static struct gpio_ops dw_gpio_ops = { .set = dw_gpio_set, }; -static int dw_gpio_add_port(struct device_d *dev, struct device_node *node, +static int dw_gpio_add_port(struct device *dev, struct device_node *node, struct dw_gpio *parent) { struct dw_gpio_instance *chip; @@ -150,7 +138,7 @@ static int dw_gpio_add_port(struct device_d *dev, struct device_node *node, return -ENODEV; } - chip->chip.dev->device_node = node; + chip->chip.dev->of_node = node; ret = gpiochip_add(&chip->chip); if (ret) @@ -162,7 +150,7 @@ static int dw_gpio_add_port(struct device_d *dev, struct device_node *node, return 0; } -static int dw_gpio_probe(struct device_d *dev) +static int dw_gpio_probe(struct device *dev) { struct resource *iores; struct dw_gpio *gpio; @@ -175,7 +163,7 @@ static int dw_gpio_probe(struct device_d *dev) return PTR_ERR(iores); gpio->regs = IOMEM(iores->start); - for_each_child_of_node(dev->device_node, node) + for_each_child_of_node(dev->of_node, node) dw_gpio_add_port(dev, node, gpio); return 0; @@ -188,15 +176,12 @@ static __maybe_unused struct of_device_id dwgpio_match[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, dwgpio_match); -static struct driver_d dwgpio_driver = { +static struct driver dwgpio_driver = { .name = "dw-apb-gpio", .probe = dw_gpio_probe, .of_compatible = DRV_OF_COMPAT(dwgpio_match), }; -static int __init dwgpio_init(void) -{ - return platform_driver_register(&dwgpio_driver); -} -postcore_initcall(dwgpio_init); +postcore_platform_driver(dwgpio_driver); diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c index e4c4f39b62..1902c83960 100644 --- a/drivers/gpio/gpio-generic.c +++ b/drivers/gpio/gpio-generic.c @@ -1,74 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2008 MontaVista Software, Inc. +// SPDX-FileCopyrightText: 2008,2010 Anton Vorontsov <cbouatmailru@gmail.com> + /* - * Generic driver for memory-mapped GPIO controllers. - * - * Based on linux driver by: - * Copyright 2008 MontaVista Software, Inc. - * Copyright 2008,2010 Anton Vorontsov <cbouatmailru@gmail.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. + * Generic driver for memory-mapped GPIO controllers, based on the Linux driver. */ #include <init.h> -#include <malloc.h> #include <linux/err.h> +#include <linux/bug.h> +#include <linux/kernel.h> +#include <linux/compiler.h> +#include <linux/types.h> +#include <errno.h> #include <linux/log2.h> -#include <linux/err.h> +#include <linux/ioport.h> +#include <io.h> #include <linux/basic_mmio_gpio.h> +#include <linux/slab.h> +#include <linux/bitops.h> +#include <driver.h> +#include <of.h> +#include <of_device.h> -static void bgpio_write8(void __iomem *reg, unsigned int data) +static void bgpio_write8(void __iomem *reg, unsigned long data) { writeb(data, reg); } -static unsigned int bgpio_read8(void __iomem *reg) +static unsigned long bgpio_read8(void __iomem *reg) { return readb(reg); } -static void bgpio_write16(void __iomem *reg, unsigned int data) +static void bgpio_write16(void __iomem *reg, unsigned long data) { writew(data, reg); } -static unsigned int bgpio_read16(void __iomem *reg) +static unsigned long bgpio_read16(void __iomem *reg) { return readw(reg); } -static void bgpio_write32(void __iomem *reg, unsigned int data) +static void bgpio_write32(void __iomem *reg, unsigned long data) { writel(data, reg); } -static unsigned int bgpio_read32(void __iomem *reg) +static unsigned long bgpio_read32(void __iomem *reg) { return readl(reg); } -static unsigned int bgpio_pin2mask(struct bgpio_chip *bgc, unsigned int pin) +#if BITS_PER_LONG >= 64 +static void bgpio_write64(void __iomem *reg, unsigned long data) +{ + writeq(data, reg); +} + +static unsigned long bgpio_read64(void __iomem *reg) +{ + return readq(reg); +} +#endif /* BITS_PER_LONG >= 64 */ + +static void bgpio_write16be(void __iomem *reg, unsigned long data) +{ + iowrite16be(data, reg); +} + +static unsigned long bgpio_read16be(void __iomem *reg) { - return 1 << pin; + return ioread16be(reg); } -static unsigned int bgpio_pin2mask_be(struct bgpio_chip *bgc, unsigned int pin) +static void bgpio_write32be(void __iomem *reg, unsigned long data) { - return 1 << (bgc->bits - 1 - pin); + iowrite32be(data, reg); +} + +static unsigned long bgpio_read32be(void __iomem *reg) +{ + return ioread32be(reg); +} + +static unsigned long bgpio_line2mask(struct bgpio_chip *bgc, unsigned int line) +{ + if (bgc->be_bits) + return BIT(bgc->bits - 1 - line); + return BIT(line); +} + +static int bgpio_get_set(struct gpio_chip *gc, unsigned int gpio) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + unsigned long pinmask = bgpio_line2mask(bgc, gpio); + bool dir = !!(bgc->dir & pinmask); + + if (dir) + return !!(bgc->read_reg(bgc->reg_set) & pinmask); + else + return !!(bgc->read_reg(bgc->reg_dat) & pinmask); } static int bgpio_get(struct gpio_chip *gc, unsigned int gpio) { struct bgpio_chip *bgc = to_bgpio_chip(gc); + return !!(bgc->read_reg(bgc->reg_dat) & bgpio_line2mask(bgc, gpio)); +} - return bgc->read_reg(bgc->reg_dat) & bgc->pin2mask(bgc, gpio); +static void bgpio_set_none(struct gpio_chip *gc, unsigned int gpio, int val) +{ } static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) { struct bgpio_chip *bgc = to_bgpio_chip(gc); - unsigned int mask = bgc->pin2mask(bgc, gpio); + unsigned long mask = bgpio_line2mask(bgc, gpio); if (val) bgc->data |= mask; @@ -82,7 +131,7 @@ static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, int val) { struct bgpio_chip *bgc = to_bgpio_chip(gc); - unsigned int mask = bgc->pin2mask(bgc, gpio); + unsigned long mask = bgpio_line2mask(bgc, gpio); if (val) bgc->write_reg(bgc->reg_set, mask); @@ -93,7 +142,7 @@ static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) { struct bgpio_chip *bgc = to_bgpio_chip(gc); - unsigned int mask = bgc->pin2mask(bgc, gpio); + unsigned long mask = bgpio_line2mask(bgc, gpio); if (val) bgc->data |= mask; @@ -108,6 +157,12 @@ static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio) return 0; } +static int bgpio_dir_out_err(struct gpio_chip *gc, unsigned int gpio, + int val) +{ + return -EINVAL; +} + static int bgpio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) { @@ -120,69 +175,115 @@ static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) { struct bgpio_chip *bgc = to_bgpio_chip(gc); - bgc->dir &= ~bgc->pin2mask(bgc, gpio); - bgc->write_reg(bgc->reg_dir, bgc->dir); + bgc->dir &= ~bgpio_line2mask(bgc, gpio); + + if (bgc->reg_dir_in) + bgc->write_reg(bgc->reg_dir_in, ~bgc->dir); + if (bgc->reg_dir_out) + bgc->write_reg(bgc->reg_dir_out, bgc->dir); return 0; } -static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio) { struct bgpio_chip *bgc = to_bgpio_chip(gc); - gc->ops->set(gc, gpio, val); + /* Return 0 if output, 1 if input */ + if (bgc->dir_unreadable) { + if (bgc->dir & bgpio_line2mask(bgc, gpio)) + return GPIOF_DIR_OUT; + return GPIOF_DIR_IN; + } - bgc->dir |= bgc->pin2mask(bgc, gpio); - bgc->write_reg(bgc->reg_dir, bgc->dir); + if (bgc->reg_dir_out) { + if (bgc->read_reg(bgc->reg_dir_out) & bgpio_line2mask(bgc, gpio)) + return GPIOF_DIR_OUT; + return GPIOF_DIR_IN; + } - return 0; + if (bgc->reg_dir_in) + if (!(bgc->read_reg(bgc->reg_dir_in) & bgpio_line2mask(bgc, gpio))) + return GPIOF_DIR_OUT; + + return GPIOF_DIR_IN; } -static int bgpio_dir_in_inv(struct gpio_chip *gc, unsigned int gpio) +static void bgpio_dir_out(struct bgpio_chip *bgc, unsigned int gpio, int val) { - struct bgpio_chip *bgc = to_bgpio_chip(gc); - - bgc->dir |= bgc->pin2mask(bgc, gpio); - bgc->write_reg(bgc->reg_dir, bgc->dir); + bgc->dir |= bgpio_line2mask(bgc, gpio); - return 0; + if (bgc->reg_dir_in) + bgc->write_reg(bgc->reg_dir_in, ~bgc->dir); + if (bgc->reg_dir_out) + bgc->write_reg(bgc->reg_dir_out, bgc->dir); } -static int bgpio_dir_out_inv(struct gpio_chip *gc, unsigned int gpio, int val) +static int bgpio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio, + int val) { struct bgpio_chip *bgc = to_bgpio_chip(gc); + bgpio_dir_out(bgc, gpio, val); gc->ops->set(gc, gpio, val); + return 0; +} - bgc->dir &= ~bgc->pin2mask(bgc, gpio); - bgc->write_reg(bgc->reg_dir, bgc->dir); +static int bgpio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio, + int val) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + gc->ops->set(gc, gpio, val); + bgpio_dir_out(bgc, gpio, val); return 0; } -static int bgpio_setup_accessors(struct device_d *dev, struct bgpio_chip *bgc, - bool be) +static int bgpio_setup_accessors(struct device *dev, + struct bgpio_chip *bgc, + bool byte_be) { + switch (bgc->bits) { case 8: bgc->read_reg = bgpio_read8; bgc->write_reg = bgpio_write8; break; case 16: - bgc->read_reg = bgpio_read16; - bgc->write_reg = bgpio_write16; + if (byte_be) { + bgc->read_reg = bgpio_read16be; + bgc->write_reg = bgpio_write16be; + } else { + bgc->read_reg = bgpio_read16; + bgc->write_reg = bgpio_write16; + } break; case 32: - bgc->read_reg = bgpio_read32; - bgc->write_reg = bgpio_write32; + if (byte_be) { + bgc->read_reg = bgpio_read32be; + bgc->write_reg = bgpio_write32be; + } else { + bgc->read_reg = bgpio_read32; + bgc->write_reg = bgpio_write32; + } + break; +#if BITS_PER_LONG >= 64 + case 64: + if (byte_be) { + dev_err(dev, + "64 bit big endian byte order unsupported\n"); + return -EINVAL; + } else { + bgc->read_reg = bgpio_read64; + bgc->write_reg = bgpio_write64; + } break; +#endif /* BITS_PER_LONG >= 64 */ default: - dev_err(dev, "Unsupported data width %u bits\n", bgc->bits); + dev_err(dev, "unsupported data width %u bits\n", bgc->bits); return -EINVAL; } - bgc->pin2mask = be ? bgpio_pin2mask_be : bgpio_pin2mask; - return 0; } @@ -193,7 +294,7 @@ static int bgpio_setup_accessors(struct device_d *dev, struct bgpio_chip *bgc, * - single input/output register resource (named "dat"). * - set/clear pair (named "set" and "clr"). * - single output register resource and single input resource ("set" and - * dat"). + * dat"). * * For the single output register, this drives a 1 by setting a bit and a zero * by clearing a bit. For the set clr pair, this drives a 1 by setting a bit @@ -211,126 +312,214 @@ static int bgpio_setup_accessors(struct device_d *dev, struct bgpio_chip *bgc, static int bgpio_setup_io(struct bgpio_chip *bgc, void __iomem *dat, void __iomem *set, - void __iomem *clr) + void __iomem *clr, + unsigned long flags) { - if (!dat) - return -EINVAL; + struct gpio_ops *ops = bgc->gc.ops; bgc->reg_dat = dat; + if (!bgc->reg_dat) + return -EINVAL; if (set && clr) { bgc->reg_set = set; bgc->reg_clr = clr; - bgc->gc.ops->set = bgpio_set_with_clear; + ops->set = bgpio_set_with_clear; } else if (set && !clr) { bgc->reg_set = set; - bgc->gc.ops->set = bgpio_set_set; - } else - bgc->gc.ops->set = bgpio_set; + ops->set = bgpio_set_set; + } else if (flags & BGPIOF_NO_OUTPUT) { + ops->set = bgpio_set_none; + } else { + ops->set = bgpio_set; + } - bgc->gc.ops->get = bgpio_get; + if (!(flags & BGPIOF_UNREADABLE_REG_SET) && (flags & BGPIOF_READ_OUTPUT_REG_SET)) + ops->get = bgpio_get_set; + else + ops->get = bgpio_get; return 0; } static int bgpio_setup_direction(struct bgpio_chip *bgc, void __iomem *dirout, - void __iomem *dirin) + void __iomem *dirin, + unsigned long flags) { - if (dirout && dirin) - return -EINVAL; - - if (dirout) { - bgc->reg_dir = dirout; - bgc->gc.ops->direction_output = bgpio_dir_out; - bgc->gc.ops->direction_input = bgpio_dir_in; - } else if (dirin) { - bgc->reg_dir = dirin; - bgc->gc.ops->direction_output = bgpio_dir_out_inv; - bgc->gc.ops->direction_input = bgpio_dir_in_inv; + struct gpio_ops *ops = bgc->gc.ops; + + if (dirout || dirin) { + bgc->reg_dir_out = dirout; + bgc->reg_dir_in = dirin; + if (flags & BGPIOF_NO_SET_ON_INPUT) + ops->direction_output = bgpio_dir_out_dir_first; + else + ops->direction_output = bgpio_dir_out_val_first; + ops->direction_input = bgpio_dir_in; + ops->get_direction = bgpio_get_dir; } else { - bgc->gc.ops->direction_output = bgpio_simple_dir_out; - bgc->gc.ops->direction_input = bgpio_simple_dir_in; + if (flags & BGPIOF_NO_OUTPUT) + ops->direction_output = bgpio_dir_out_err; + else + ops->direction_output = bgpio_simple_dir_out; + ops->direction_input = bgpio_simple_dir_in; } return 0; } -int bgpio_init(struct bgpio_chip *bgc, struct device_d *dev, +static int bgpio_request(struct gpio_chip *chip, unsigned gpio_pin) +{ + if (gpio_pin < chip->ngpio) + return 0; + + return -EINVAL; +} + +/** + * bgpio_init() - Initialize generic GPIO accessor functions + * @bgc: the GPIO chip to set up + * @dev: the parent device of the new GPIO chip (compulsory) + * @sz: the size (width) of the MMIO registers in bytes, typically 1, 2 or 4 + * @dat: MMIO address for the register to READ the value of the GPIO lines, it + * is expected that a 1 in the corresponding bit in this register means the + * line is asserted + * @set: MMIO address for the register to SET the value of the GPIO lines, it is + * expected that we write the line with 1 in this register to drive the GPIO line + * high. + * @clr: MMIO address for the register to CLEAR the value of the GPIO lines, it is + * expected that we write the line with 1 in this register to drive the GPIO line + * low. It is allowed to leave this address as NULL, in that case the SET register + * will be assumed to also clear the GPIO lines, by actively writing the line + * with 0. + * @dirout: MMIO address for the register to set the line as OUTPUT. It is assumed + * that setting a line to 1 in this register will turn that line into an + * output line. Conversely, setting the line to 0 will turn that line into + * an input. + * @dirin: MMIO address for the register to set this line as INPUT. It is assumed + * that setting a line to 1 in this register will turn that line into an + * input line. Conversely, setting the line to 0 will turn that line into + * an output. + * @flags: Different flags that will affect the behaviour of the device, such as + * endianness etc. + */ +int bgpio_init(struct bgpio_chip *bgc, struct device *dev, unsigned int sz, void __iomem *dat, void __iomem *set, void __iomem *clr, void __iomem *dirout, void __iomem *dirin, unsigned long flags) { + struct gpio_ops *ops = &bgc->ops; int ret; - if ((sz > 4) || !is_power_of_2(sz)) + if (!is_power_of_2(sz)) return -EINVAL; bgc->bits = sz * 8; - bgc->gc.ngpio = bgc->bits; + if (bgc->bits > BITS_PER_LONG) + return -EINVAL; + bgc->gc.base = -1; + bgc->gc.ngpio = bgc->bits; bgc->gc.dev = dev; - bgc->gc.ops = &bgc->ops; + bgc->gc.ops = ops; + ops->request = bgpio_request; + bgc->be_bits = !!(flags & BGPIOF_BIG_ENDIAN); - ret = bgpio_setup_io(bgc, dat, set, clr); + ret = bgpio_setup_io(bgc, dat, set, clr, flags); if (ret) return ret; - ret = bgpio_setup_accessors(dev, bgc, flags & BGPIOF_BIG_ENDIAN); + ret = bgpio_setup_accessors(dev, bgc, flags & BGPIOF_BIG_ENDIAN_BYTE_ORDER); if (ret) return ret; - ret = bgpio_setup_direction(bgc, dirout, dirin); + ret = bgpio_setup_direction(bgc, dirout, dirin, flags); if (ret) return ret; bgc->data = bgc->read_reg(bgc->reg_dat); - - if (bgc->gc.ops->set == bgpio_set_set && !(flags & - BGPIOF_UNREADABLE_REG_SET)) + if (ops->set == bgpio_set_set && + !(flags & BGPIOF_UNREADABLE_REG_SET)) bgc->data = bgc->read_reg(bgc->reg_set); - if (bgc->reg_dir && !(flags & BGPIOF_UNREADABLE_REG_DIR)) - bgc->dir = bgc->read_reg(bgc->reg_dir); + if (flags & BGPIOF_UNREADABLE_REG_DIR) + bgc->dir_unreadable = true; + + /* + * Inspect hardware to find initial direction setting. + */ + if ((bgc->reg_dir_out || bgc->reg_dir_in) && + !(flags & BGPIOF_UNREADABLE_REG_DIR)) { + if (bgc->reg_dir_out) + bgc->dir = bgc->read_reg(bgc->reg_dir_out); + else if (bgc->reg_dir_in) + bgc->dir = ~bgc->read_reg(bgc->reg_dir_in); + /* + * If we have two direction registers, synchronise + * input setting to output setting, the library + * can not handle a line being input and output at + * the same time. + */ + if (bgc->reg_dir_out && bgc->reg_dir_in) + bgc->write_reg(bgc->reg_dir_in, ~bgc->dir); + } return ret; } +EXPORT_SYMBOL_GPL(bgpio_init); void bgpio_remove(struct bgpio_chip *bgc) { gpiochip_remove(&bgc->gc); free(bgc); } +EXPORT_SYMBOL_GPL(bgpio_remove); #ifdef CONFIG_GPIO_GENERIC_PLATFORM -static void __iomem *bgpio_map(struct device_d *dev, const char *name, - resource_size_t sane_sz, int *err) +static void __iomem *bgpio_map(struct device *dev, + const char *name, + resource_size_t sane_sz) { struct resource *r; - struct resource *ret; - - *err = 0; + resource_size_t sz; - r = dev_get_resource_by_name(dev, IORESOURCE_MEM, name); + r = dev_request_mem_resource_by_name(dev, name); if (IS_ERR(r)) return NULL; - if (resource_size(r) != sane_sz) { - *err = -EINVAL; - return NULL; - } + sz = resource_size(r); + if (sz != sane_sz) + return IOMEM_ERR_PTR(-EINVAL); - ret = request_iomem_region(dev_name(dev), r->start, r->end); - if (IS_ERR(ret)) { - *err = PTR_ERR(ret); + return IOMEM(r->start); +} + +static const struct of_device_id bgpio_of_match[]; + +static struct bgpio_pdata *bgpio_parse_dt(struct device *dev, + unsigned long *flags) +{ + struct bgpio_pdata *pdata; + + if (!of_match_device(bgpio_of_match, dev)) return NULL; - } - return IOMEM(ret->start); + pdata = xzalloc(sizeof(struct bgpio_pdata)); + + pdata->base = -1; + + if (of_device_is_big_endian(dev->of_node)) + *flags |= BGPIOF_BIG_ENDIAN_BYTE_ORDER; + + if (of_property_read_bool(dev->of_node, "no-output")) + *flags |= BGPIOF_NO_OUTPUT; + + return pdata; } -static int bgpio_dev_probe(struct device_d *dev) +static int bgpio_dev_probe(struct device *dev) { struct resource *r; void __iomem *dat; @@ -342,35 +531,37 @@ static int bgpio_dev_probe(struct device_d *dev) unsigned long flags = 0; int err; struct bgpio_chip *bgc; - struct bgpio_pdata *pdata = dev->platform_data; + struct bgpio_pdata *pdata; + + pdata = bgpio_parse_dt(dev, &flags); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); r = dev_get_resource_by_name(dev, IORESOURCE_MEM, "dat"); - if (IS_ERR(r)) - return PTR_ERR(r); + if (!r) + return -EINVAL; sz = resource_size(r); - dat = bgpio_map(dev, "dat", sz, &err); - if (!dat) - return err ? err : -EINVAL; + dat = bgpio_map(dev, "dat", sz); + if (IS_ERR(dat)) + return PTR_ERR(dat); - set = bgpio_map(dev, "set", sz, &err); - if (err) - return err; + set = bgpio_map(dev, "set", sz); + if (IS_ERR(set)) + return PTR_ERR(set); - clr = bgpio_map(dev, "clr", sz, &err); - if (err) - return err; + clr = bgpio_map(dev, "clr", sz); + if (IS_ERR(clr)) + return PTR_ERR(clr); - dirout = bgpio_map(dev, "dirout", sz, &err); - if (err) - return err; + dirout = bgpio_map(dev, "dirout", sz); + if (IS_ERR(dirout)) + return PTR_ERR(dirout); - dirin = bgpio_map(dev, "dirin", sz, &err); - if (err) - return err; - - dev_get_drvdata(dev, (const void **)&flags); + dirin = bgpio_map(dev, "dirin", sz); + if (IS_ERR(dirin)) + return PTR_ERR(dirin); bgc = xzalloc(sizeof(struct bgpio_chip)); @@ -378,57 +569,47 @@ static int bgpio_dev_probe(struct device_d *dev) if (err) return err; - if (pdata) { - bgc->gc.base = pdata->base; - if (pdata->ngpio > 0) - bgc->gc.ngpio = pdata->ngpio; - } + bgc->gc.base = pdata->base; + bgc->gc.dev = dev; + bgc->gc.ops = &bgc->ops; + if (pdata->ngpio > 0) + bgc->gc.ngpio = pdata->ngpio; dev->priv = bgc; return gpiochip_add(&bgc->gc); } -static void bgpio_dev_remove(struct device_d *dev) +static void bgpio_dev_remove(struct device *dev) { struct bgpio_chip *bgc = dev->priv; bgpio_remove(bgc); } -static struct platform_device_id bgpio_id_table[] = { +static const struct of_device_id bgpio_of_match[] = { { - .name = "basic-mmio-gpio", - .driver_data = 0, + .compatible = "wd,mbl-gpio", }, { - .name = "basic-mmio-gpio-be", - .driver_data = BGPIOF_BIG_ENDIAN, + .compatible = "brcm,bcm6345-gpio" }, - { } -}; - -static struct of_device_id __maybe_unused bgpio_of_match[] = { { - .compatible = "wd,mbl-gpio", + .compatible = "ni,169445-nand-gpio" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, bgpio_of_match); -static struct driver_d bgpio_driver = { +static struct driver bgpio_driver = { .name = "basic-mmio-gpio", - .id_table = bgpio_id_table, - .of_compatible = DRV_OF_COMPAT(bgpio_of_match), + .of_compatible = bgpio_of_match, .probe = bgpio_dev_probe, .remove = bgpio_dev_remove, }; -static int bgpio_register(void) -{ - return platform_driver_register(&bgpio_driver); -} -coredevice_initcall(bgpio_register); +coredevice_platform_driver(bgpio_driver); #endif diff --git a/drivers/gpio/gpio-imx.c b/drivers/gpio/gpio-imx.c index 2827e11e73..49c5555b98 100644 --- a/drivers/gpio/gpio-imx.c +++ b/drivers/gpio/gpio-imx.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2004 Sascha Hauer, Synertronixx GmbH + /* - * arch/arm/mach-imx/gpio.c - * - * author: Sascha Hauer - * Created: april 20th, 2004 - * Copyright: Synertronixx GmbH - * - * Common code for i.MX machines - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * + * arch/arm/mach-imx/gpio.c * + * Common code for i.MX machines */ #include <common.h> @@ -130,7 +117,7 @@ static struct gpio_ops imx_gpio_ops = { .get_direction = imx_get_direction, }; -static int imx_gpio_probe(struct device_d *dev) +static int imx_gpio_probe(struct device *dev) { struct resource *iores; struct imx_gpio_chip *imxgpio; @@ -148,7 +135,7 @@ static int imx_gpio_probe(struct device_d *dev) imxgpio->base = IOMEM(iores->start); imxgpio->chip.ops = &imx_gpio_ops; if (dev->id < 0) { - imxgpio->chip.base = of_alias_get_id(dev->device_node, "gpio"); + imxgpio->chip.base = of_alias_get_id(dev->of_node, "gpio"); if (imxgpio->chip.base < 0) return imxgpio->chip.base; imxgpio->chip.base *= 32; @@ -197,6 +184,7 @@ static __maybe_unused struct of_device_id imx_gpio_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, imx_gpio_dt_ids); static struct platform_device_id imx_gpio_ids[] = { { @@ -210,16 +198,11 @@ static struct platform_device_id imx_gpio_ids[] = { }, }; -static struct driver_d imx_gpio_driver = { +static struct driver imx_gpio_driver = { .name = "imx-gpio", .probe = imx_gpio_probe, .of_compatible = DRV_OF_COMPAT(imx_gpio_dt_ids), .id_table = imx_gpio_ids, }; -static int imx_gpio_add(void) -{ - platform_driver_register(&imx_gpio_driver); - return 0; -} -postcore_initcall(imx_gpio_add); +postcore_platform_driver(imx_gpio_driver); diff --git a/drivers/gpio/gpio-intel.c b/drivers/gpio/gpio-intel.c new file mode 100644 index 0000000000..ebec220f46 --- /dev/null +++ b/drivers/gpio/gpio-intel.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2004 Tomas Marek, Elrest Solutions Company s.r.o. + +/* + * Based on the Linux kernel v6.8 drivers/pinctrl/intel/pinctrl-intel.c. + */ + +#include <common.h> +#include <errno.h> +#include <io.h> +#include <gpio.h> + +#include <platform_data/gpio-intel.h> + +#define PADBAR 0x00c + +/* Offset from pad_regs */ +#define PADCFG0 0x000 +#define PADCFG0_RXEVCFG_MASK GENMASK(26, 25) +#define PADCFG0_RXEVCFG_LEVEL (0 << 25) +#define PADCFG0_RXEVCFG_EDGE (1 << 25) +#define PADCFG0_RXEVCFG_DISABLED (2 << 25) +#define PADCFG0_RXEVCFG_EDGE_BOTH (3 << 25) +#define PADCFG0_PREGFRXSEL BIT(24) +#define PADCFG0_RXINV BIT(23) +#define PADCFG0_GPIROUTIOXAPIC BIT(20) +#define PADCFG0_GPIROUTSCI BIT(19) +#define PADCFG0_GPIROUTSMI BIT(18) +#define PADCFG0_GPIROUTNMI BIT(17) +#define PADCFG0_PMODE_SHIFT 10 +#define PADCFG0_PMODE_MASK GENMASK(13, 10) +#define PADCFG0_PMODE_GPIO 0 +#define PADCFG0_GPIORXDIS BIT(9) +#define PADCFG0_GPIOTXDIS BIT(8) +#define PADCFG0_GPIORXSTATE BIT(1) +#define PADCFG0_GPIOTXSTATE BIT(0) + +struct intel_gpio_chip { + struct gpio_chip chip; + void __iomem *community_pad_base; +}; + +static inline struct intel_gpio_chip *to_intel_gpio(struct gpio_chip *gc) +{ + return container_of(gc, struct intel_gpio_chip, chip); +} + +static void __iomem *intel_gpio_padcfg0_reg(const struct intel_gpio_chip *chip, + const unsigned int gpio) +{ + const u32 pad_cfg_offset = 16; + + return chip->community_pad_base + pad_cfg_offset * gpio; +} + +static u32 intel_gpio_padcfg0_value(const struct intel_gpio_chip *chip, + const unsigned int gpio) +{ + return readl(intel_gpio_padcfg0_reg(chip, gpio)); +} + +static void intel_gpio_padcfg0_write(const struct intel_gpio_chip *chip, + const unsigned int gpio, u32 value) +{ + writel(value, intel_gpio_padcfg0_reg(chip, gpio)); +} + +static void intel_gpio_set_value(struct gpio_chip *gc, unsigned int gpio, + int value) +{ + struct intel_gpio_chip *chip = to_intel_gpio(gc); + u32 padcfg0; + + padcfg0 = intel_gpio_padcfg0_value(chip, gpio); + if (value) + padcfg0 |= PADCFG0_GPIOTXSTATE; + else + padcfg0 &= ~PADCFG0_GPIOTXSTATE; + intel_gpio_padcfg0_write(chip, gpio, padcfg0); +} + +static int intel_gpio_get_value(struct gpio_chip *gc, unsigned int gpio) +{ + struct intel_gpio_chip *chip = to_intel_gpio(gc); + u32 padcfg0; + + padcfg0 = intel_gpio_padcfg0_value(chip, gpio); + if (!(padcfg0 & PADCFG0_GPIOTXDIS)) + return !!(padcfg0 & PADCFG0_GPIOTXSTATE); + + return !!(padcfg0 & PADCFG0_GPIORXSTATE); +} + +static int intel_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + struct intel_gpio_chip *chip = to_intel_gpio(gc); + u32 padcfg0; + + padcfg0 = intel_gpio_padcfg0_value(chip, gpio); + + if (padcfg0 & PADCFG0_PMODE_MASK) + return -EINVAL; + + if (padcfg0 & PADCFG0_GPIOTXDIS) + return GPIOF_DIR_IN; + + return GPIOF_DIR_OUT; +} + +static int intel_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio) +{ + struct intel_gpio_chip *chip = to_intel_gpio(gc); + u32 padcfg0; + + padcfg0 = intel_gpio_padcfg0_value(chip, gpio); + padcfg0 &= ~PADCFG0_GPIORXDIS; + padcfg0 |= PADCFG0_GPIOTXDIS; + intel_gpio_padcfg0_write(chip, gpio, padcfg0); + + return 0; +} + +static int intel_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio, + int value) +{ + struct intel_gpio_chip *chip = to_intel_gpio(gc); + u32 padcfg0; + + padcfg0 = intel_gpio_padcfg0_value(chip, gpio); + padcfg0 &= ~PADCFG0_GPIOTXDIS; + padcfg0 |= PADCFG0_GPIORXDIS; + if (value) + padcfg0 |= PADCFG0_GPIOTXSTATE; + else + padcfg0 &= ~PADCFG0_GPIOTXSTATE; + intel_gpio_padcfg0_write(chip, gpio, padcfg0); + + return 0; +} + +static struct gpio_ops intel_gpio_ops = { + .direction_input = intel_gpio_direction_input, + .direction_output = intel_gpio_direction_output, + .get_direction = intel_gpio_get_direction, + .get = intel_gpio_get_value, + .set = intel_gpio_set_value, +}; + +static int intel_gpio_probe(struct device *dev) +{ + const struct gpio_intel_platform_data *pdata; + struct intel_gpio_chip *intel_gpio; + void __iomem *community_pad_base; + void __iomem *community_base; + struct resource *iores; + int ret; + + pdata = (struct gpio_intel_platform_data *)dev->platform_data; + if (!pdata) { + dev_err(dev, "Configuration missing!\n"); + return -EINVAL; + } + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) { + dev_err(dev, "Memory resource request failed: %ld\n", + PTR_ERR(iores)); + return PTR_ERR(iores); + } + + intel_gpio = xzalloc(sizeof(*intel_gpio)); + + intel_gpio->chip.ops = &intel_gpio_ops; + intel_gpio->chip.ngpio = pdata->ngpios; + intel_gpio->chip.dev = dev; + + community_base = IOMEM(iores->start); + community_pad_base = IOMEM(community_base + readl(community_base + PADBAR)); + + intel_gpio->community_pad_base = community_pad_base; + + ret = gpiochip_add(&intel_gpio->chip); + + if (ret) { + dev_err(dev, "Couldn't add gpiochip: %pe\n", ERR_PTR(ret)); + kfree(intel_gpio); + return ret; + } + + return 0; +} + +static struct driver_d intel_gpio_driver = { + .name = "intel-gpio", + .probe = intel_gpio_probe, +}; + +coredevice_platform_driver(intel_gpio_driver); diff --git a/drivers/gpio/gpio-jz4740.c b/drivers/gpio/gpio-jz4740.c index 87e0716b06..911f9f2470 100644 --- a/drivers/gpio/gpio-jz4740.c +++ b/drivers/gpio/gpio-jz4740.c @@ -1,19 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2013,2014 Antony Pavlov <antonynpavlov@gmail.com> +// SPDX-FileCopyrightText: 2009-2010 Lars-Peter Clausen <lars@metafoo.de> + /* - * Copyright (C) 2013, 2014 Antony Pavlov <antonynpavlov@gmail.com> - * * Based on Linux JZ4740 platform GPIO support: - * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> @@ -88,7 +78,7 @@ static struct gpio_ops jz4740_gpio_ops = { .set = jz4740_gpio_set_value, }; -static int jz4740_gpio_probe(struct device_d *dev) +static int jz4740_gpio_probe(struct device *dev) { struct resource *iores; void __iomem *base; @@ -129,15 +119,12 @@ static __maybe_unused struct of_device_id jz4740_gpio_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, jz4740_gpio_dt_ids); -static struct driver_d jz4740_gpio_driver = { +static struct driver jz4740_gpio_driver = { .name = "jz4740-gpio", .probe = jz4740_gpio_probe, .of_compatible = DRV_OF_COMPAT(jz4740_gpio_dt_ids), }; -static int jz4740_gpio_init(void) -{ - return platform_driver_register(&jz4740_gpio_driver); -} -coredevice_initcall(jz4740_gpio_init); +coredevice_platform_driver(jz4740_gpio_driver); diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c new file mode 100644 index 0000000000..9a1a87250e --- /dev/null +++ b/drivers/gpio/gpio-latch.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * GPIO latch driver + * + * Copyright (C) 2022 Sascha Hauer <s.hauer@pengutronix.de> + * + * This driver implements a GPIO (or better GPO as there is no input) + * multiplexer based on latches like this: + * + * CLK0 ----------------------. ,--------. + * CLK1 -------------------. `--------|> #0 | + * | | | + * OUT0 ----------------+--|-----------|D0 Q0|-----|< + * OUT1 --------------+-|--|-----------|D1 Q1|-----|< + * OUT2 ------------+-|-|--|-----------|D2 Q2|-----|< + * OUT3 ----------+-|-|-|--|-----------|D3 Q3|-----|< + * OUT4 --------+-|-|-|-|--|-----------|D4 Q4|-----|< + * OUT5 ------+-|-|-|-|-|--|-----------|D5 Q5|-----|< + * OUT6 ----+-|-|-|-|-|-|--|-----------|D6 Q6|-----|< + * OUT7 --+-|-|-|-|-|-|-|--|-----------|D7 Q7|-----|< + * | | | | | | | | | `--------' + * | | | | | | | | | + * | | | | | | | | | ,--------. + * | | | | | | | | `-----------|> #1 | + * | | | | | | | | | | + * | | | | | | | `--------------|D0 Q0|-----|< + * | | | | | | `----------------|D1 Q1|-----|< + * | | | | | `------------------|D2 Q2|-----|< + * | | | | `--------------------|D3 Q3|-----|< + * | | | `----------------------|D4 Q4|-----|< + * | | `------------------------|D5 Q5|-----|< + * | `--------------------------|D6 Q6|-----|< + * `----------------------------|D7 Q7|-----|< + * `--------' + * + * The above is just an example. The actual number of number of latches and + * the number of inputs per latch is derived from the number of GPIOs given + * in the corresponding device tree properties. + */ + +#include <common.h> +#include <errno.h> +#include <io.h> +#include <of.h> +#include <gpio.h> +#include <init.h> +#include <of_gpio.h> +#include <linux/bitmap.h> + +struct gpio_latch_priv { + struct gpio_chip gc; + int *clk_gpios; + int *latched_gpios; + int n_latched_gpios; + unsigned int setup_duration_ns; + unsigned int clock_duration_ns; + unsigned long *shadow; +}; + +static int gpio_latch_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + return 0; +} + +static void gpio_latch_set(struct gpio_chip *gc, unsigned int offset, int val) +{ + struct gpio_latch_priv *priv = container_of(gc, struct gpio_latch_priv, gc); + int latch = offset / priv->n_latched_gpios; + int i; + + assign_bit(offset, priv->shadow, val); + + for (i = 0; i < priv->n_latched_gpios; i++) + gpio_set_value(priv->latched_gpios[i], + test_bit(latch * priv->n_latched_gpios + i, priv->shadow)); + + ndelay(priv->setup_duration_ns); + gpio_set_value(priv->clk_gpios[latch], 1); + ndelay(priv->clock_duration_ns); + gpio_set_value(priv->clk_gpios[latch], 0); +} +static int gpio_latch_direction_output(struct gpio_chip *gc, unsigned gpio, int val) +{ + gpio_latch_set(gc, gpio, val); + + return 0; +} + +#define DURATION_NS_MAX 5000 + +static struct gpio_ops gpio_latch_gpio_ops = { + .direction_output = gpio_latch_direction_output, + .set = gpio_latch_set, + .get_direction = gpio_latch_get_direction, +}; + +static int gpio_latch_probe(struct device *dev) +{ + struct gpio_latch_priv *priv; + int n_latches, i, ret; + struct device_node *np = dev->of_node; + enum of_gpio_flags flags; + + priv = xzalloc(sizeof(*priv)); + + n_latches = of_gpio_named_count(np, "clk-gpios"); + if (n_latches < 0) { + dev_err(dev, "invalid or missing clk-gpios"); + ret = -EINVAL; + goto err_gpio; + } + + priv->n_latched_gpios = of_gpio_named_count(np, "latched-gpios"); + if (priv->n_latched_gpios < 0) { + dev_err(dev, "invalid or missing latched-gpios"); + ret = -EINVAL; + goto err_gpio; + } + + priv->clk_gpios = xzalloc(sizeof(int) * n_latches); + priv->latched_gpios = xzalloc(sizeof(int) * priv->n_latched_gpios); + + for (i = 0; i < n_latches; i++) { + priv->clk_gpios[i] = of_get_named_gpio_flags(np, "clk-gpios", + i, &flags); + ret = gpio_request_one(priv->clk_gpios[i], + flags & OF_GPIO_ACTIVE_LOW ? GPIOF_ACTIVE_LOW : 0, + dev_name(dev)); + if (ret) { + dev_err(dev, "Cannot request gpio %d: %s\n", priv->clk_gpios[i], + strerror(-ret)); + goto err_gpio; + } + + gpio_direction_output(priv->clk_gpios[i], 0); + } + + for (i = 0; i < priv->n_latched_gpios; i++) { + priv->latched_gpios[i] = of_get_named_gpio_flags(np, "latched-gpios", + i, &flags); + ret = gpio_request_one(priv->latched_gpios[i], + flags & OF_GPIO_ACTIVE_LOW ? GPIOF_ACTIVE_LOW : 0, + dev_name(dev)); + if (ret) { + dev_err(dev, "Cannot request gpio %d: %s\n", priv->latched_gpios[i], + strerror(-ret)); + goto err_gpio; + } + } + + priv->shadow = bitmap_zalloc(n_latches * priv->n_latched_gpios); + + of_property_read_u32(np, "setup-duration-ns", &priv->setup_duration_ns); + if (priv->setup_duration_ns > DURATION_NS_MAX) { + dev_warn(dev, "setup-duration-ns too high, limit to %d\n", + DURATION_NS_MAX); + priv->setup_duration_ns = DURATION_NS_MAX; + } + + of_property_read_u32(np, "clock-duration-ns", &priv->clock_duration_ns); + if (priv->clock_duration_ns > DURATION_NS_MAX) { + dev_warn(dev, "clock-duration-ns too high, limit to %d\n", + DURATION_NS_MAX); + priv->clock_duration_ns = DURATION_NS_MAX; + } + + priv->gc.ops = &gpio_latch_gpio_ops; + priv->gc.ngpio = n_latches * priv->n_latched_gpios; + priv->gc.base = -1; + priv->gc.dev = dev; + + return gpiochip_add(&priv->gc); + +err_gpio: + return ret; +} + +static const struct of_device_id gpio_latch_ids[] = { + { + .compatible = "gpio-latch", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, gpio_latch_ids); + +static struct driver gpio_latch_driver = { + .name = "gpio-latch", + .probe = gpio_latch_probe, + .of_compatible = DRV_OF_COMPAT(gpio_latch_ids), +}; +device_platform_driver(gpio_latch_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); +MODULE_DESCRIPTION("GPIO latch driver"); diff --git a/drivers/gpio/gpio-libftdi1.c b/drivers/gpio/gpio-libftdi1.c index cd36b08ca6..b7c94b573d 100644 --- a/drivers/gpio/gpio-libftdi1.c +++ b/drivers/gpio/gpio-libftdi1.c @@ -1,18 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2016,2017 Antony Pavlov <antonynpavlov@gmail.com> + /* * libftdi1 sandbox barebox GPIO driver - * - * Copyright (C) 2016, 2017 Antony Pavlov <antonynpavlov@gmail.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> @@ -76,7 +66,7 @@ static struct gpio_ops libftdi1_gpio_ops = { .set = libftdi1_gpio_set_value, }; -static int libftdi1_gpio_probe(struct device_d *dev) +static int libftdi1_gpio_probe(struct device *dev) { struct libftdi1_gpio_chip *gpio; struct ft2232_bitbang *ftbb; @@ -84,13 +74,13 @@ static int libftdi1_gpio_probe(struct device_d *dev) uint32_t id_vendor, id_product; const char *i_serial_number = NULL; - of_property_read_u32(dev->device_node, "usb,id_vendor", + of_property_read_u32(dev->of_node, "usb,id_vendor", &id_vendor); - of_property_read_u32(dev->device_node, "usb,id_product", + of_property_read_u32(dev->of_node, "usb,id_product", &id_product); - of_property_read_string(dev->device_node, "usb,i_serial_number", + of_property_read_string(dev->of_node, "usb,i_serial_number", &i_serial_number); ftbb = barebox_libftdi1_open(id_vendor, id_product, @@ -122,13 +112,14 @@ static __maybe_unused const struct of_device_id libftdi1_gpio_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, libftdi1_gpio_dt_ids); -static void libftdi1_gpio_remove(struct device_d *dev) +static void libftdi1_gpio_remove(struct device *dev) { barebox_libftdi1_close(); } -static struct driver_d libftdi1_gpio_driver = { +static struct driver libftdi1_gpio_driver = { .name = "libftdi1-gpio", .probe = libftdi1_gpio_probe, .remove = libftdi1_gpio_remove, diff --git a/drivers/gpio/gpio-malta-fpga-i2c.c b/drivers/gpio/gpio-malta-fpga-i2c.c index 0188e51af2..d0e454015c 100644 --- a/drivers/gpio/gpio-malta-fpga-i2c.c +++ b/drivers/gpio/gpio-malta-fpga-i2c.c @@ -1,20 +1,5 @@ -/* - * Copyright (C) 2014 Antony Pavlov <antonynpavlov@gmail.com> - * - * This file is part of barebox. - * See file CREDITS for list of people who contributed to this project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2014 Antony Pavlov <antonynpavlov@gmail.com> #include <common.h> #include <init.h> @@ -131,7 +116,7 @@ static struct gpio_ops malta_i2c_gpio_ops = { .set = malta_i2c_gpio_set_value, }; -static int malta_i2c_gpio_probe(struct device_d *dev) +static int malta_i2c_gpio_probe(struct device *dev) { struct resource *iores; void __iomem *gpio_base; @@ -174,15 +159,12 @@ static __maybe_unused struct of_device_id malta_i2c_gpio_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, malta_i2c_gpio_dt_ids); -static struct driver_d malta_i2c_gpio_driver = { +static struct driver malta_i2c_gpio_driver = { .name = "malta-fpga-i2c-gpio", .probe = malta_i2c_gpio_probe, .of_compatible = DRV_OF_COMPAT(malta_i2c_gpio_dt_ids), }; -static int malta_i2c_gpio_driver_init(void) -{ - return platform_driver_register(&malta_i2c_gpio_driver); -} -coredevice_initcall(malta_i2c_gpio_driver_init); +coredevice_platform_driver(malta_i2c_gpio_driver); diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 979f92ad30..bf855958a3 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2008 Peter Korsgaard <jacmet@sunsite.dk> +// SPDX-FileCopyrightText: 2016 Freescale Semiconductor Inc. + /* * GPIOs on MPC512x/8349/8572/8610/QorIQ and compatible - * - * Copyright (C) 2008 Peter Korsgaard <jacmet@sunsite.dk> - * Copyright (C) 2016 Freescale Semiconductor Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include <common.h> @@ -37,7 +34,7 @@ struct mpc8xxx_gpio_devtype { int (*gpio_get)(struct bgpio_chip *, unsigned int); }; -static int mpc8xxx_probe(struct device_d *dev) +static int mpc8xxx_probe(struct device *dev) { struct device_node *np; struct resource *iores; @@ -47,8 +44,8 @@ static int mpc8xxx_probe(struct device_d *dev) mpc8xxx_gc = xzalloc(sizeof(*mpc8xxx_gc)); - if (dev->device_node) { - np = dev->device_node; + if (dev->of_node) { + np = dev->of_node; } else { dev_err(dev, "no device_node\n"); return -ENODEV; @@ -68,7 +65,8 @@ static int mpc8xxx_probe(struct device_d *dev) ret = bgpio_init(bgc, dev, 4, mpc8xxx_gc->regs + GPIO_DAT, NULL, NULL, - mpc8xxx_gc->regs + GPIO_DIR, NULL, 0); + mpc8xxx_gc->regs + GPIO_DIR, NULL, + BGPIOF_BIG_ENDIAN); if (ret) goto err; dev_dbg(dev, "GPIO registers are LITTLE endian\n"); @@ -77,7 +75,8 @@ static int mpc8xxx_probe(struct device_d *dev) mpc8xxx_gc->regs + GPIO_DAT, NULL, NULL, mpc8xxx_gc->regs + GPIO_DIR, NULL, - BGPIOF_BIG_ENDIAN); + BGPIOF_BIG_ENDIAN + | BGPIOF_BIG_ENDIAN_BYTE_ORDER); if (ret) goto err; dev_dbg(dev, "GPIO registers are BIG endian\n"); @@ -108,15 +107,12 @@ static __maybe_unused struct of_device_id mpc8xxx_gpio_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, mpc8xxx_gpio_ids); -static struct driver_d mpc8xxx_driver = { +static struct driver mpc8xxx_driver = { .name = "mpc8xxx-gpio", .probe = mpc8xxx_probe, .of_compatible = DRV_OF_COMPAT(mpc8xxx_gpio_ids), }; -static int __init mpc8xxx_init(void) -{ - return platform_driver_register(&mpc8xxx_driver); -} -postcore_initcall(mpc8xxx_init); +postcore_platform_driver(mpc8xxx_driver); diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index ef78873ad2..770acb61c8 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -1,15 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + /* * Freescale MXS gpio support - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <common.h> @@ -106,7 +98,7 @@ static struct gpio_ops mxs_gpio_ops = { .get_direction = mxs_get_direction, }; -static int mxs_gpio_probe(struct device_d *dev) +static int mxs_gpio_probe(struct device *dev) { struct mxs_gpio_chip *mxsgpio; struct mxs_gpio_regs *regs; @@ -119,7 +111,7 @@ static int mxs_gpio_probe(struct device_d *dev) mxsgpio = xzalloc(sizeof(*mxsgpio)); mxsgpio->chip.ops = &mxs_gpio_ops; if (dev->id < 0) { - id = of_alias_get_id(dev->device_node, "gpio"); + id = of_alias_get_id(dev->of_node, "gpio"); if (id < 0) return id; mxsgpio->base = dev_get_mem_region(dev->parent, 0); @@ -158,6 +150,7 @@ static __maybe_unused struct of_device_id mxs_gpio_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, mxs_gpio_dt_ids); static struct platform_device_id mxs_gpio_ids[] = { { @@ -171,16 +164,11 @@ static struct platform_device_id mxs_gpio_ids[] = { }, }; -static struct driver_d mxs_gpio_driver = { +static struct driver mxs_gpio_driver = { .name = "gpio-mxs", .probe = mxs_gpio_probe, .of_compatible = DRV_OF_COMPAT(mxs_gpio_dt_ids), .id_table = mxs_gpio_ids, }; -static int mxs_gpio_add(void) -{ - platform_driver_register(&mxs_gpio_driver); - return 0; -} -postcore_initcall(mxs_gpio_add); +postcore_platform_driver(mxs_gpio_driver); diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index b00766a6aa..3fcb7387e3 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1,35 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2009 Wind River Systems, Inc. (Tom Rix <Tom.Rix@windriver.com>) +// SPDX-FileCopyrightText: 2003-2005 Nokia Corporation (Juha Yrjölä <juha.yrjola@nokia.com>) + /* - * Copyright (c) 2009 Wind River Systems, Inc. - * Tom Rix <Tom.Rix@windriver.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * This work is derived from the linux 2.6.27 kernel source - * To fetch, use the kernel repository - * git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git - * Use the v2.6.27 tag. - * - * Below is the original's header including its copyright - * - * linux/arch/arm/plat-omap/gpio.c - * * Support functions for OMAP GPIO * - * Copyright (C) 2003-2005 Nokia Corporation - * Written by Juha Yrjölä <juha.yrjola@nokia.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. + * This work is derived from the omap GPIO driver in Linux 2.6.27. */ #include <common.h> #include <io.h> @@ -139,7 +115,7 @@ static struct gpio_ops omap_gpio_ops = { .set = omap_gpio_set_value, }; -static int omap_gpio_probe(struct device_d *dev) +static int omap_gpio_probe(struct device *dev) { struct resource *iores; struct omap_gpio_chip *omapgpio; @@ -158,7 +134,7 @@ static int omap_gpio_probe(struct device_d *dev) omapgpio->chip.ops = &omap_gpio_ops; if (dev->id < 0) { - omapgpio->chip.base = of_alias_get_id(dev->device_node, "gpio"); + omapgpio->chip.base = of_alias_get_id(dev->of_node, "gpio"); if (omapgpio->chip.base < 0) return omapgpio->chip.base; omapgpio->chip.base *= 32; @@ -185,15 +161,12 @@ static __maybe_unused struct of_device_id omap_gpio_dt_ids[] = { }, { } }; +MODULE_DEVICE_TABLE(of, omap_gpio_dt_ids); -static struct driver_d omap_gpio_driver = { +static struct driver omap_gpio_driver = { .name = "omap-gpio", .probe = omap_gpio_probe, .of_compatible = DRV_OF_COMPAT(omap_gpio_dt_ids), }; -static int omap_gpio_add(void) -{ - return platform_driver_register(&omap_gpio_driver); -} -coredevice_initcall(omap_gpio_add); +coredevice_platform_driver(omap_gpio_driver); diff --git a/drivers/gpio/gpio-orion.c b/drivers/gpio/gpio-orion.c index 63ef966edf..0a1b50069b 100644 --- a/drivers/gpio/gpio-orion.c +++ b/drivers/gpio/gpio-orion.c @@ -1,18 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + /* * Marvell Orion/MVEBU SoC GPIO driver * * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> @@ -87,14 +78,11 @@ static struct gpio_ops orion_gpio_ops = { .set = orion_gpio_set_value, }; -static int orion_gpio_probe(struct device_d *dev) +static int orion_gpio_probe(struct device *dev) { struct resource *iores; struct orion_gpio_chip *gpio; - - dev->id = of_alias_get_id(dev->device_node, "gpio"); - if (dev->id < 0) - return dev->id; + int id; gpio = xzalloc(sizeof(*gpio)); iores = dev_request_mem_resource(dev, 0); @@ -105,9 +93,14 @@ static int orion_gpio_probe(struct device_d *dev) gpio->regs = IOMEM(iores->start); gpio->chip.dev = dev; gpio->chip.ops = &orion_gpio_ops; - gpio->chip.base = dev->id * 32; + + id = of_alias_get_id(dev->of_node, "gpio"); + if (id < 0) + return id; + + gpio->chip.base = id * 32; gpio->chip.ngpio = 32; - of_property_read_u32(dev->device_node, "ngpios", &gpio->chip.ngpio); + of_property_read_u32(dev->of_node, "ngpios", &gpio->chip.ngpio); gpiochip_add(&gpio->chip); @@ -120,8 +113,9 @@ static struct of_device_id orion_gpio_dt_ids[] = { { .compatible = "marvell,orion-gpio", }, { } }; +MODULE_DEVICE_TABLE(of, orion_gpio_dt_ids); -static struct driver_d orion_gpio_driver = { +static struct driver orion_gpio_driver = { .name = "orion-gpio", .probe = orion_gpio_probe, .of_compatible = DRV_OF_COMPAT(orion_gpio_dt_ids), diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 98a7a6c97c..e303f6b21f 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -1,21 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2005 Ben Gardner <bgardner@wabtec.com> +// SPDX-FileCopyrightText: 2007 Marvell International Ltd. + /* * PCA953x 4/8/16/24/40 bit I/O ports * * This code was ported from linux-3.15 kernel by Antony Pavlov. - * - * Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com> - * Copyright (C) 2007 Marvell International Ltd. - * * Derived from drivers/i2c/chips/pca9539.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. */ #include <common.h> #include <malloc.h> #include <driver.h> +#include <linux/gpio/consumer.h> +#include <regulator.h> #include <xfuncs.h> #include <errno.h> #include <i2c/i2c.h> @@ -41,6 +39,8 @@ #define PCA_GPIO_MASK 0x00FF #define PCA_INT 0x0100 +#define PCA_PCAL 0x0200 +#define PCA_LATCH_INT (PCA_PCAL | PCA_INT) #define PCA953X_TYPE 0x1000 #define PCA957X_TYPE 0x2000 @@ -60,6 +60,8 @@ static struct platform_device_id pca953x_id[] = { { "pca9575", 16 | PCA957X_TYPE | PCA_INT, }, { "pca9698", 40 | PCA953X_TYPE, }, + { "pcal6408", 8 | PCA953X_TYPE | PCA_LATCH_INT, }, + { "max7310", 8 | PCA953X_TYPE, }, { "max7312", 16 | PCA953X_TYPE | PCA_INT, }, { "max7313", 16 | PCA953X_TYPE | PCA_INT, }, @@ -410,12 +412,14 @@ out: return ret; } -static int pca953x_probe(struct device_d *dev) +static int pca953x_probe(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); unsigned long driver_data; struct pca953x_platform_data *pdata; struct pca953x_chip *chip; + struct regulator *reg; + struct gpio_desc *reset_gpio; int ret; u32 invert = 0; @@ -439,6 +443,20 @@ static int pca953x_probe(struct device_d *dev) chip->client = client; + reset_gpio = gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(reset_gpio)) + dev_warn(dev, "Failed to get 'reset' GPIO (ignored)\n"); + + reg = regulator_get(dev, "vcc"); + if (IS_ERR(reg)) { + dev_warn(dev, "Failed to get 'vcc' regulator (ignored).\n"); + reg = NULL; + } + + ret = regulator_enable(reg); + if (ret) + return dev_err_probe(dev, ret, "failed to enable register\n"); + chip->chip_type = driver_data & (PCA953X_TYPE | PCA957X_TYPE); /* initialize cached registers from their original values. @@ -457,6 +475,8 @@ static int pca953x_probe(struct device_d *dev) if (ret) return ret; + slice_depends_on(gpiochip_slice(&chip->gpio_chip), i2c_client_slice(client)); + if (pdata && pdata->setup) { ret = pdata->setup(client, chip->gpio_chip.base, chip->gpio_chip.ngpio, pdata->context); @@ -472,7 +492,10 @@ static int pca953x_probe(struct device_d *dev) #define OF_957X(__nrgpio, __int) (void *)(__nrgpio | PCA957X_TYPE | __int) static const struct of_device_id pca953x_dt_ids[] = { + { .compatible = "nxp,pca6408", .data = OF_953X(8, PCA_INT), }, + { .compatible = "nxp,pca6416", .data = OF_953X(16, PCA_INT), }, { .compatible = "nxp,pca9505", .data = OF_953X(40, PCA_INT), }, + { .compatible = "nxp,pca9506", .data = OF_953X(40, PCA_INT), }, { .compatible = "nxp,pca9534", .data = OF_953X( 8, PCA_INT), }, { .compatible = "nxp,pca9535", .data = OF_953X(16, PCA_INT), }, { .compatible = "nxp,pca9536", .data = OF_953X( 4, 0), }, @@ -487,21 +510,36 @@ static const struct of_device_id pca953x_dt_ids[] = { { .compatible = "nxp,pca9575", .data = OF_957X(16, PCA_INT), }, { .compatible = "nxp,pca9698", .data = OF_953X(40, 0), }, + { .compatible = "nxp,pcal6408", .data = OF_953X(8, PCA_LATCH_INT), }, + { .compatible = "nxp,pcal6416", .data = OF_953X(16, PCA_LATCH_INT), }, + { .compatible = "nxp,pcal6524", .data = OF_953X(24, PCA_LATCH_INT), }, + { .compatible = "nxp,pcal9535", .data = OF_953X(16, PCA_LATCH_INT), }, + { .compatible = "nxp,pcal9554b", .data = OF_953X( 8, PCA_LATCH_INT), }, + { .compatible = "nxp,pcal9555a", .data = OF_953X(16, PCA_LATCH_INT), }, + { .compatible = "maxim,max7310", .data = OF_953X( 8, 0), }, { .compatible = "maxim,max7312", .data = OF_953X(16, PCA_INT), }, { .compatible = "maxim,max7313", .data = OF_953X(16, PCA_INT), }, { .compatible = "maxim,max7315", .data = OF_953X( 8, PCA_INT), }, + { .compatible = "maxim,max7318", .data = OF_953X(16, PCA_INT), }, { .compatible = "ti,pca6107", .data = OF_953X( 8, PCA_INT), }, + { .compatible = "ti,pca9536", .data = OF_953X( 4, 0), }, { .compatible = "ti,tca6408", .data = OF_953X( 8, PCA_INT), }, { .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), }, { .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), }, + { .compatible = "ti,tca9539", .data = OF_953X(16, PCA_INT), }, + + { .compatible = "onnn,cat9554", .data = OF_953X( 8, PCA_INT), }, + { .compatible = "onnn,pca9654", .data = OF_953X( 8, PCA_INT), }, + { .compatible = "onnn,pca9655", .data = OF_953X(16, PCA_INT), }, { .compatible = "exar,xra1202", .data = OF_953X( 8, 0), }, { } }; +MODULE_DEVICE_TABLE(of, pca953x_dt_ids); -static struct driver_d pca953x_driver = { +static struct driver pca953x_driver = { .name = "pca953x", .probe = pca953x_probe, .of_compatible = DRV_OF_COMPAT(pca953x_dt_ids), diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 6c1c0ac352..52c3a6d00a 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2007 David Brownell + /* * Driver for pcf857x, pca857x, and pca967x I2C GPIO expanders * * This code was ported from linux-5.1 kernel by Michael Grzeschik. - * - * Copyright (C) 2007 David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <common.h> @@ -149,10 +145,10 @@ static struct gpio_ops pcf857x_gpio_ops = { .set = pcf857x_set, }; -static int pcf857x_probe(struct device_d *dev) +static int pcf857x_probe(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; struct pcf857x *gpio; unsigned long driver_data; unsigned int n_latch = 0; @@ -223,11 +219,7 @@ static int pcf857x_probe(struct device_d *dev) */ gpio->out = ~n_latch; - ret = gpiochip_add(&gpio->chip); - if (ret) - return ret; - - return ret; + return gpiochip_add(&gpio->chip); } static const struct of_device_id pcf857x_dt_ids[] = { @@ -246,8 +238,9 @@ static const struct of_device_id pcf857x_dt_ids[] = { { .compatible = "maxim,max7329", .data = (void *)8 }, { } }; +MODULE_DEVICE_TABLE(of, pcf857x_dt_ids); -static struct driver_d pcf857x_driver = { +static struct driver pcf857x_driver = { .name = "pcf857x", .probe = pcf857x_probe, .of_compatible = DRV_OF_COMPAT(pcf857x_dt_ids), diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index f34aba9da9..8dd9ca3bd5 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -1,10 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2008,2009 Provigent Ltd. + /* - * Copyright (C) 2008, 2009 Provigent Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061) * * Data sheet: ARM DDI 0190B, September 2000 @@ -87,7 +84,18 @@ static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value) writeb(!!value << offset, chip->base + (1 << (offset + 2))); } +static int pl061_get_direction(struct gpio_chip *gc, unsigned offset) +{ + struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); + + if (readb(chip->base + GPIODIR) & (1 << offset)) + return GPIOF_DIR_OUT; + + return GPIOF_DIR_IN; +} + static struct gpio_ops pl061_gpio_ops = { + .get_direction = pl061_get_direction, .direction_input = pl061_direction_input, .direction_output = pl061_direction_output, .get = pl061_get_value, diff --git a/drivers/gpio/gpio-raspberrypi-exp.c b/drivers/gpio/gpio-raspberrypi-exp.c new file mode 100644 index 0000000000..a021253489 --- /dev/null +++ b/drivers/gpio/gpio-raspberrypi-exp.c @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Raspberry Pi 3 expander GPIO driver + * + * Uses the firmware mailbox service to communicate with the + * GPIO expander on the VPU. + * + * Copyright (C) 2017 Raspberry Pi Trading Ltd. + */ + +#include <common.h> +#include <gpio.h> +#include <init.h> +#include <mach/bcm283x/mbox.h> + +#define NUM_GPIO 8 + +#define RPI_EXP_GPIO_BASE 128 + +#define RPI_EXP_GPIO_DIR_IN 0 +#define RPI_EXP_GPIO_DIR_OUT 1 + +struct rpi_exp_gpio { + struct gpio_chip gc; + struct rpi_firmware *fw; +}; + +/* VC4 firmware mailbox interface data structures */ +struct gpio_set_config { + struct bcm2835_mbox_hdr hdr; /* buf_size, code */ + struct bcm2835_mbox_tag_hdr tag_hdr; /* tag, val_buf_size, val_len */ + union { + struct { + u32 gpio; + u32 direction; + u32 polarity; + u32 term_en; + u32 term_pull_up; + u32 state; + } req; + } body; + u32 end_tag; +}; + +struct gpio_get_config { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + u32 gpio; + u32 direction; + u32 polarity; + u32 term_en; + u32 term_pull_up; + } req; + } body; + u32 end_tag; +}; + +struct gpio_get_set_state { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_hdr tag_hdr; + union { + struct { + u32 gpio; + u32 state; + } req; + } body; + u32 end_tag; +}; + +static inline struct rpi_exp_gpio *to_rpi_gpio(struct gpio_chip *gc) +{ + return container_of(gc, struct rpi_exp_gpio, gc); +} + +static int rpi_exp_gpio_get_polarity(struct gpio_chip *gc, unsigned int off) +{ + struct rpi_exp_gpio *gpio; + int ret; + BCM2835_MBOX_STACK_ALIGN(struct gpio_get_config, get); + BCM2835_MBOX_INIT_HDR(get); + BCM2835_MBOX_INIT_TAG(get, GET_GPIO_CONFIG); + + gpio = to_rpi_gpio(gc); + + get->body.req.gpio = off + RPI_EXP_GPIO_BASE; /* GPIO to update */ + + ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &get->hdr); + if (ret || get->body.req.gpio != 0) { + dev_err(gc->dev, "Failed to get GPIO %u config (%d %x)\n", + off, ret, get->body.req.gpio); + return ret ? ret : -EIO; + } + return get->body.req.polarity; +} + +static int rpi_exp_gpio_dir_in(struct gpio_chip *gc, unsigned int off) +{ + struct rpi_exp_gpio *gpio; + int ret; + BCM2835_MBOX_STACK_ALIGN(struct gpio_set_config, set_in); + BCM2835_MBOX_INIT_HDR(set_in); + BCM2835_MBOX_INIT_TAG(set_in, SET_GPIO_CONFIG); + + gpio = to_rpi_gpio(gc); + + set_in->body.req.gpio = off + RPI_EXP_GPIO_BASE; /* GPIO to update */ + set_in->body.req.direction = RPI_EXP_GPIO_DIR_IN; + set_in->body.req.term_en = 0; /* termination disabled */ + set_in->body.req.term_pull_up = 0; /* n/a as termination disabled */ + set_in->body.req.state = 0; /* n/a as configured as an input */ + + ret = rpi_exp_gpio_get_polarity(gc, off); + if (ret < 0) + return ret; + + set_in->body.req.polarity = ret; /* Retain existing setting */ + + ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &set_in->hdr); + if (ret || set_in->body.req.gpio != 0) { + dev_err(gc->dev, "Failed to set GPIO %u to input (%d %x)\n", + off, ret, set_in->body.req.gpio); + return ret ? ret : -EIO; + } + + return 0; +} + +static int rpi_exp_gpio_dir_out(struct gpio_chip *gc, unsigned int off, int val) +{ + struct rpi_exp_gpio *gpio; + int ret; + BCM2835_MBOX_STACK_ALIGN(struct gpio_set_config, set_out); + BCM2835_MBOX_INIT_HDR(set_out); + BCM2835_MBOX_INIT_TAG(set_out, SET_GPIO_CONFIG); + + gpio = to_rpi_gpio(gc); + + set_out->body.req.gpio = off + RPI_EXP_GPIO_BASE; /* GPIO to update */ + set_out->body.req.direction = RPI_EXP_GPIO_DIR_OUT; + set_out->body.req.term_en = 0; /* n/a as an output */ + set_out->body.req.term_pull_up = 0; /* n/a as termination disabled */ + set_out->body.req.state = val; /* Output state */ + + ret = rpi_exp_gpio_get_polarity(gc, off); + if (ret < 0) + return ret; + set_out->body.req.polarity = ret; /* Retain existing setting */ + + ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &set_out->hdr); + if (ret || set_out->body.req.gpio != 0) { + dev_err(gc->dev, "Failed to set GPIO %u to output (%d %x)\n", + off, ret, set_out->body.req.gpio); + return ret ? ret : -EIO; + } + return 0; +} + +static int rpi_exp_gpio_get_direction(struct gpio_chip *gc, unsigned int off) +{ + struct rpi_exp_gpio *gpio; + int ret; + BCM2835_MBOX_STACK_ALIGN(struct gpio_get_config, get); + BCM2835_MBOX_INIT_HDR(get); + BCM2835_MBOX_INIT_TAG(get, GET_GPIO_CONFIG); + + gpio = to_rpi_gpio(gc); + + get->body.req.gpio = off + RPI_EXP_GPIO_BASE; /* GPIO to update */ + + ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &get->hdr); + if (ret || get->body.req.gpio != 0) { + dev_err(gc->dev, + "Failed to get GPIO %u config (%d %x)\n", off, ret, + get->body.req.gpio); + return ret ? ret : -EIO; + } + if (get->body.req.direction) + return GPIOF_DIR_OUT; + + return GPIOF_DIR_IN; +} + +static int rpi_exp_gpio_get(struct gpio_chip *gc, unsigned int off) +{ + struct rpi_exp_gpio *gpio; + int ret; + BCM2835_MBOX_STACK_ALIGN(struct gpio_get_set_state, get); + BCM2835_MBOX_INIT_HDR(get); + BCM2835_MBOX_INIT_TAG(get, GET_GPIO_STATE); + + gpio = to_rpi_gpio(gc); + + get->body.req.gpio = off + RPI_EXP_GPIO_BASE; /* GPIO to update */ + get->body.req.state = 0; /* storage for returned value */ + + ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &get->hdr); + if (ret || get->body.req.gpio != 0) { + dev_err(gc->dev, + "Failed to get GPIO %u state (%d %x)\n", off, ret, + get->body.req.gpio); + return ret ? ret : -EIO; + } + return !!get->body.req.state; +} + +static void rpi_exp_gpio_set(struct gpio_chip *gc, unsigned int off, int val) +{ + struct rpi_exp_gpio *gpio; + int ret; + BCM2835_MBOX_STACK_ALIGN(struct gpio_get_set_state, set); + BCM2835_MBOX_INIT_HDR(set); + BCM2835_MBOX_INIT_TAG(set, SET_GPIO_STATE); + + gpio = to_rpi_gpio(gc); + + set->body.req.gpio = off + RPI_EXP_GPIO_BASE; /* GPIO to update */ + set->body.req.state = val; /* Output state */ + + ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &set->hdr); + if (ret || set->body.req.gpio != 0) + dev_err(gc->dev, + "Failed to set GPIO %u state (%d %x)\n", off, ret, + set->body.req.gpio); +} + +static struct gpio_ops rpi_exp_gpio_ops = { + .direction_input = rpi_exp_gpio_dir_in, + .direction_output = rpi_exp_gpio_dir_out, + .get_direction = rpi_exp_gpio_get_direction, + .get = rpi_exp_gpio_get, + .set = rpi_exp_gpio_set, +}; + +static int rpi_exp_gpio_probe(struct device *dev) +{ + struct rpi_exp_gpio *rpi_gpio; + int ret; + + rpi_gpio = xzalloc(sizeof(*rpi_gpio)); + + rpi_gpio->gc.dev = dev; + rpi_gpio->gc.base = -1; + rpi_gpio->gc.ngpio = NUM_GPIO; + rpi_gpio->gc.ops = &rpi_exp_gpio_ops; + + ret = gpiochip_add(&rpi_gpio->gc); + if (ret) + return ret; + + dev_dbg(dev, "probed gpiochip with %d gpios, base %d\n", + rpi_gpio->gc.ngpio, rpi_gpio->gc.base); + + return 0; +} + +static __maybe_unused struct of_device_id rpi_exp_gpio_ids[] = { + { + .compatible = "raspberrypi,firmware-gpio", + }, { + /* sentinel */ + }, +}; +MODULE_DEVICE_TABLE(of, rpi_exp_gpio_ids); + +static struct driver rpi_exp_gpio_driver = { + .name = "rpi-exp-gpio", + .probe = rpi_exp_gpio_probe, + .of_compatible = DRV_OF_COMPAT(rpi_exp_gpio_ids), +}; + +device_platform_driver(rpi_exp_gpio_driver); diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c new file mode 100644 index 0000000000..2c13e97b97 --- /dev/null +++ b/drivers/gpio/gpio-rockchip.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <common.h> +#include <errno.h> +#include <io.h> +#include <of.h> +#include <gpio.h> +#include <init.h> +#include <linux/clk.h> +#include <linux/basic_mmio_gpio.h> +#include <mach/rockchip/rockchip.h> + +#define GPIO_TYPE_V1 (0) /* GPIO Version ID reserved */ +#define GPIO_TYPE_V2 (0x01000C2B) /* GPIO Version ID 0x01000C2B */ +#define GPIO_TYPE_V2_1 (0x0101157C) /* GPIO Version ID 0x0101157C */ + +struct rockchip_gpiochip { + struct device *dev; + void __iomem *reg_base; + struct clk *clk; + struct bgpio_chip bgpio_chip; +}; + +/* GPIO registers */ +enum { + RK_GPIO_SWPORT_DR = 0x00, + RK_GPIO_SWPORT_DDR = 0x04, + RK_GPIO_EXT_PORT = 0x50, +}; + +/* GPIO registers */ +enum { + RK_GPIOV2_DR_L = 0x00, + RK_GPIOV2_DR_H = 0x04, + RK_GPIOV2_DDR_L = 0x08, + RK_GPIOV2_DDR_H = 0x0c, + RK_GPIOV2_EXT_PORT = 0x70, +}; + +static struct rockchip_gpiochip *gc_to_rockchip_pinctrl(struct gpio_chip *gc) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + + return container_of(bgc, struct rockchip_gpiochip, bgpio_chip); +} + +static int rockchip_gpiov2_direction_input(struct gpio_chip *gc, unsigned int gpio) +{ + struct rockchip_gpiochip *rgc = gc_to_rockchip_pinctrl(gc); + u32 mask; + + mask = 1 << (16 + (gpio % 16)); + + if (gpio < 16) + writel(mask, rgc->reg_base + RK_GPIOV2_DDR_L); + else + writel(mask, rgc->reg_base + RK_GPIOV2_DDR_H); + + return 0; +} + +static int rockchip_gpiov2_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + struct rockchip_gpiochip *rgc = gc_to_rockchip_pinctrl(gc); + u32 r; + + if (gpio < 16) + r = readl(rgc->reg_base + RK_GPIOV2_DDR_L); + else + r = readl(rgc->reg_base + RK_GPIOV2_DDR_H); + + return r & BIT(gpio % 16) ? GPIOF_DIR_OUT : GPIOF_DIR_IN; +} + +static void rockchip_gpiov2_set_value(struct gpio_chip *gc, unsigned int gpio, + int val) +{ + struct rockchip_gpiochip *rgc = gc_to_rockchip_pinctrl(gc); + u32 mask, vval = 0; + + mask = 1 << (16 + (gpio % 16)); + if (val) + vval = 1 << (gpio % 16); + + if (gpio < 16) + writel(mask | vval, rgc->reg_base + RK_GPIOV2_DR_L); + else + writel(mask | vval, rgc->reg_base + RK_GPIOV2_DR_H); +} + +static int rockchip_gpiov2_direction_output(struct gpio_chip *gc, + unsigned int gpio, int val) +{ + struct rockchip_gpiochip *rgc = gc_to_rockchip_pinctrl(gc); + u32 mask, out, vval = 0; + + mask = 1 << (16 + (gpio % 16)); + out = 1 << (gpio % 16); + if (val) + vval = 1 << (gpio % 16); + + if (gpio < 16) { + writel(mask | vval, rgc->reg_base + RK_GPIOV2_DR_L); + writel(mask | out, rgc->reg_base + RK_GPIOV2_DDR_L); + } else { + writel(mask | vval, rgc->reg_base + RK_GPIOV2_DR_H); + writel(mask | out, rgc->reg_base + RK_GPIOV2_DDR_H); + } + + return 0; +} + +static int rockchip_gpiov2_get_value(struct gpio_chip *gc, unsigned int gpio) +{ + struct rockchip_gpiochip *rgc = gc_to_rockchip_pinctrl(gc); + u32 mask, r; + + mask = 1 << (gpio % 32); + r = readl(rgc->reg_base + RK_GPIOV2_EXT_PORT); + + return r & mask ? 1 : 0; +} + +static struct gpio_ops rockchip_gpio_ops = { + .direction_input = rockchip_gpiov2_direction_input, + .direction_output = rockchip_gpiov2_direction_output, + .get = rockchip_gpiov2_get_value, + .set = rockchip_gpiov2_set_value, + .get_direction = rockchip_gpiov2_get_direction, +}; + +static int rockchip_gpio_probe(struct device *dev) +{ + struct rockchip_gpiochip *rgc; + struct gpio_chip *gpio; + struct resource *res; + void __iomem *reg_base; + u32 id, gpio_type; + int ret; + + rgc = xzalloc(sizeof(*rgc)); + gpio = &rgc->bgpio_chip.gc; + + res = dev_request_mem_resource(dev, 0); + if (IS_ERR(res)) + return PTR_ERR(res); + + rgc->reg_base = IOMEM(res->start); + + rgc->clk = clk_get(dev, NULL); + + if (IS_ERR(rgc->clk)) + return PTR_ERR(rgc->clk); + + ret = clk_enable(rgc->clk); + if (ret) + return ret; + + reg_base = rgc->reg_base; + + id = readl(reg_base + 0x78); + if (id == GPIO_TYPE_V2 || id == GPIO_TYPE_V2_1) + gpio_type = GPIO_TYPE_V2; + else + gpio_type = GPIO_TYPE_V1; + + if (gpio_type >= GPIO_TYPE_V2) { + gpio->ngpio = 32; + gpio->dev = dev; + gpio->ops = &rockchip_gpio_ops; + gpio->base = of_alias_get_id(dev->of_node, "gpio"); + if (gpio->base < 0) + return -EINVAL; + gpio->base *= 32; + } else { + ret = bgpio_init(&rgc->bgpio_chip, dev, 4, + reg_base + RK_GPIO_EXT_PORT, + reg_base + RK_GPIO_SWPORT_DR, NULL, + reg_base + RK_GPIO_SWPORT_DDR, NULL, 0); + if (ret) + return ret; + } + + ret = gpiochip_add(&rgc->bgpio_chip.gc); + if (ret) { + dev_err(dev, "failed to register gpio_chip:: %d\n", ret); + return ret; + } + + dev_dbg(dev, "registered GPIOv%d-compatible bank\n", + gpio_type == GPIO_TYPE_V1 ? 1 : 2); + + return 0; +} + +static struct of_device_id rockchip_gpio_dt_match[] = { + { + .compatible = "rockchip,gpio-bank", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, rockchip_gpio_dt_match); + +static struct driver rockchip_gpio_driver = { + .name = "rockchip-gpio", + .probe = rockchip_gpio_probe, + .of_compatible = DRV_OF_COMPAT(rockchip_gpio_dt_match), +}; + +core_platform_driver(rockchip_gpio_driver); diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c new file mode 100644 index 0000000000..58934fdfa7 --- /dev/null +++ b/drivers/gpio/gpio-sifive.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2019 SiFive + */ + +#include <linux/basic_mmio_gpio.h> +#include <linux/printk.h> +#include <driver.h> +#include <errno.h> + +#define SIFIVE_GPIO_INPUT_VAL 0x00 +#define SIFIVE_GPIO_INPUT_EN 0x04 +#define SIFIVE_GPIO_OUTPUT_EN 0x08 +#define SIFIVE_GPIO_OUTPUT_VAL 0x0C +#define SIFIVE_GPIO_RISE_IE 0x18 +#define SIFIVE_GPIO_FALL_IE 0x20 +#define SIFIVE_GPIO_HIGH_IE 0x28 +#define SIFIVE_GPIO_LOW_IE 0x30 + +#define SIFIVE_GPIO_MAX 32 + +static int __of_irq_count(struct device_node *np) +{ + unsigned npins = 0; + + of_get_property(np, "interrupts", &npins); + + return npins / sizeof(__be32); +} + +static int sifive_gpio_probe(struct device *dev) +{ + struct bgpio_chip *bgc; + struct resource *res; + void __iomem *base; + int ret, ngpio; + + bgc = xzalloc(sizeof(*bgc)); + + res = dev_request_mem_resource(dev, 0); + if (IS_ERR(res)) { + dev_err(dev, "failed to request device memory\n"); + return PTR_ERR(res); + } + base = IOMEM(res->start); + + ngpio = __of_irq_count(dev->of_node); + if (ngpio > SIFIVE_GPIO_MAX) { + dev_err(dev, "Too many GPIO interrupts (max=%d)\n", + SIFIVE_GPIO_MAX); + return -ENXIO; + } + + ret = bgpio_init(bgc, dev, 4, + base + SIFIVE_GPIO_INPUT_VAL, + base + SIFIVE_GPIO_OUTPUT_VAL, + NULL, + base + SIFIVE_GPIO_OUTPUT_EN, + base + SIFIVE_GPIO_INPUT_EN, + 0); + if (ret) { + dev_err(dev, "unable to init generic GPIO\n"); + return ret; + } + + /* Disable all GPIO interrupts */ + writel(0, base + SIFIVE_GPIO_RISE_IE); + writel(0, base + SIFIVE_GPIO_FALL_IE); + writel(0, base + SIFIVE_GPIO_HIGH_IE); + writel(0, base + SIFIVE_GPIO_LOW_IE); + + bgc->gc.ngpio = ngpio; + return gpiochip_add(&bgc->gc); +} + +static const struct of_device_id sifive_gpio_match[] = { + { .compatible = "sifive,gpio0" }, + { .compatible = "sifive,fu540-c000-gpio" }, + { }, +}; +MODULE_DEVICE_TABLE(of, sifive_gpio_match); + +static struct driver sifive_gpio_driver = { + .name = "sifive_gpio", + .of_compatible = sifive_gpio_match, + .probe = sifive_gpio_probe, +}; +postcore_platform_driver(sifive_gpio_driver); diff --git a/drivers/gpio/gpio-starfive-vic.c b/drivers/gpio/gpio-starfive-vic.c new file mode 100644 index 0000000000..399219a3a0 --- /dev/null +++ b/drivers/gpio/gpio-starfive-vic.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * COPYRIGHT 2020 Shanghai StarFive Technology Co., Ltd. + */ + +#include <linux/basic_mmio_gpio.h> +#include <linux/reset.h> +#include <linux/clk.h> +#include <linux/printk.h> +#include <driver.h> +#include <errno.h> +#include <pinctrl.h> + +#define GPIO_EN 0x0 +#define GPIO_IS_LOW 0x10 +#define GPIO_IS_HIGH 0x14 +#define GPIO_IBE_LOW 0x18 +#define GPIO_IBE_HIGH 0x1c +#define GPIO_IEV_LOW 0x20 +#define GPIO_IEV_HIGH 0x24 +#define GPIO_IE_LOW 0x28 +#define GPIO_IE_HIGH 0x2c +#define GPIO_IC_LOW 0x30 +#define GPIO_IC_HIGH 0x34 +//read only +#define GPIO_RIS_LOW 0x38 +#define GPIO_RIS_HIGH 0x3c +#define GPIO_MIS_LOW 0x40 +#define GPIO_MIS_HIGH 0x44 +#define GPIO_DIN_LOW 0x48 +#define GPIO_DIN_HIGH 0x4c + +#define GPIO_DOUT_X_REG 0x50 +#define GPIO_DOEN_X_REG 0x54 + +#define MAX_GPIO 64 + +struct starfive_gpio { + void __iomem *base; + struct gpio_chip gc; +}; + +#define to_starfive_gpio(gc) container_of(gc, struct starfive_gpio, gc) + +static int starfive_direction_input(struct gpio_chip *gc, unsigned offset) +{ + struct starfive_gpio *chip = to_starfive_gpio(gc); + + if (offset >= gc->ngpio) + return -EINVAL; + + writel(0x1, chip->base + GPIO_DOEN_X_REG + offset * 8); + + return 0; +} + +static int starfive_direction_output(struct gpio_chip *gc, unsigned offset, int value) +{ + struct starfive_gpio *chip = to_starfive_gpio(gc); + + if (offset >= gc->ngpio) + return -EINVAL; + writel(0x0, chip->base + GPIO_DOEN_X_REG + offset * 8); + writel(value, chip->base + GPIO_DOUT_X_REG + offset * 8); + + return 0; +} + +static int starfive_get_direction(struct gpio_chip *gc, unsigned offset) +{ + struct starfive_gpio *chip = to_starfive_gpio(gc); + + if (offset >= gc->ngpio) + return -EINVAL; + + return readl(chip->base + GPIO_DOEN_X_REG + offset * 8) & 0x1; +} + +static int starfive_get_value(struct gpio_chip *gc, unsigned offset) +{ + struct starfive_gpio *chip = to_starfive_gpio(gc); + int value; + + if (offset >= gc->ngpio) + return -EINVAL; + + if(offset < 32){ + value = readl(chip->base + GPIO_DIN_LOW); + return (value >> offset) & 0x1; + } else { + value = readl(chip->base + GPIO_DIN_HIGH); + return (value >> (offset - 32)) & 0x1; + } +} + +static void starfive_set_value(struct gpio_chip *gc, unsigned offset, int value) +{ + struct starfive_gpio *chip = to_starfive_gpio(gc); + + if (offset >= gc->ngpio) + return; + + writel(value, chip->base + GPIO_DOUT_X_REG + offset * 8); +} + +static struct gpio_ops starfive_gpio_ops = { + .direction_input = starfive_direction_input, + .direction_output = starfive_direction_output, + .get_direction = starfive_get_direction, + .get = starfive_get_value, + .set = starfive_set_value, +}; + +static int starfive_gpio_probe(struct device *dev) +{ + struct starfive_gpio *chip; + struct resource *res; + struct clk *clk; + int ret; + + clk = clk_get(dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + clk_enable(clk); + + ret = device_reset(dev); + if (ret) + return ret; + + ret = pinctrl_single_probe(dev); + if (ret) + return ret; + + res = dev_get_resource(dev, IORESOURCE_MEM, 0); + if (IS_ERR(res)) + return PTR_ERR(res); + + chip = xzalloc(sizeof(*chip)); + chip->base = IOMEM(res->start); + + chip->gc.base = -1; + chip->gc.ngpio = MAX_GPIO; + chip->gc.dev = dev; + chip->gc.ops = &starfive_gpio_ops; + + /* Disable all GPIO interrupts */ + iowrite32(0, chip->base + GPIO_IE_HIGH); + iowrite32(0, chip->base + GPIO_IE_LOW); + + ret = gpiochip_add(&chip->gc); + if (ret) { + dev_err(dev, "could not add gpiochip\n"); + gpiochip_remove(&chip->gc); + return ret; + } + + writel(1, chip->base + GPIO_EN); + + return 0; +} + +static const struct of_device_id starfive_gpio_match[] = { + { .compatible = "starfive,gpio0", }, + { }, +}; +MODULE_DEVICE_TABLE(of, starfive_gpio_match); + +static struct driver starfive_gpio_driver = { + .probe = starfive_gpio_probe, + .name = "starfive_gpio", + .of_compatible = starfive_gpio_match, +}; +postcore_platform_driver(starfive_gpio_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Huan Feng <huan.feng@starfivetech.com>"); +MODULE_DESCRIPTION("Starfive VIC GPIO generator driver"); diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index d7e64045b4..b736f66c7e 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -1,18 +1,5 @@ -/* - * Copyright (C) 2012 Pengutronix - * Steffen Trumtrar <s.trumtrar@pengutronix.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix #include <common.h> #include <errno.h> @@ -118,7 +105,7 @@ static struct gpio_ops stmpe_gpio_ops = { .set = stmpe_gpio_set_value, }; -static int stmpe_gpio_probe(struct device_d *dev) +static int stmpe_gpio_probe(struct device *dev) { struct stmpe_gpio_chip *stmpegpio; struct stmpe_client_info *ci; @@ -149,13 +136,9 @@ static int stmpe_gpio_probe(struct device_d *dev) return 0; } -static struct driver_d stmpe_gpio_driver = { +static struct driver stmpe_gpio_driver = { .name = "stmpe-gpio", .probe = stmpe_gpio_probe, }; -static int stmpe_gpio_add(void) -{ - return platform_driver_register(&stmpe_gpio_driver); -} -coredevice_initcall(stmpe_gpio_add); +coredevice_platform_driver(stmpe_gpio_driver); diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c index 76535187b0..ff7e86d644 100644 --- a/drivers/gpio/gpio-sx150x.c +++ b/drivers/gpio/gpio-sx150x.c @@ -1,19 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2016 BayLibre, SAS (Neil Armstrong <narmstrong@baylibre.com>) +// SPDX-FileCopyrightText: 2010 Code Aurora Forum + /* * Driver for SX150x I2C GPIO expanders * * This code was ported from linux-4.9 kernel driver by * Andrey Smirnov <andrew.smirnov@gmail.com>. * - * Orginal code with it's copyright info can be found in - * drivers/pinctrl/pinctrl-sx150x.c - * * Note: That although linux driver was converted from being a GPIO * subsystem to Pinctrl subsytem driver, due to Barebox's lack of * similar provisions this driver is still a GPIO driver. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. */ #include <common.h> @@ -22,7 +19,7 @@ #include <xfuncs.h> #include <errno.h> #include <i2c/i2c.h> -#include <regmap.h> +#include <linux/regmap.h> #include <gpio.h> #include <of_device.h> @@ -45,7 +42,6 @@ struct sx150x_device_data { }; struct sx150x_gpio { - struct device *dev; struct i2c_client *client; struct gpio_chip gpio; struct regmap *regmap; @@ -231,7 +227,7 @@ static struct gpio_ops sx150x_gpio_ops = { .set = sx150x_gpio_set, }; -static int sx150x_probe(struct device_d *dev) +static int sx150x_probe(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct sx150x_gpio *sx150x; @@ -259,8 +255,9 @@ static __maybe_unused struct of_device_id sx150x_dt_ids[] = { { .compatible = "semtech,sx1503q", .data = &sx1503q_device_data, }, { } }; +MODULE_DEVICE_TABLE(of, sx150x_dt_ids); -static struct driver_d sx150x_driver = { +static struct driver sx150x_driver = { .name = "sx150x", .probe = sx150x_probe, .of_compatible = sx150x_dt_ids, diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 56808b57e4..693432a8c9 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -1,19 +1,6 @@ -/* * - * Copyright (C) 2010 Erik Gilling <konkers@google.com>, Google, Inc - * Copyright (C) 2013 Lucas Stach <l.stach@pengutronix.de> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2010 Erik Gilling <konkers@google.com>, Google, Inc +// SPDX-FileCopyrightText: 2013 Lucas Stach <l.stach@pengutronix.de> #include <common.h> #include <gpio.h> @@ -138,7 +125,7 @@ static struct gpio_chip tegra_gpio_chip = { .base = 0, }; -static int tegra_gpio_probe(struct device_d *dev) +static int tegra_gpio_probe(struct device *dev) { struct resource *iores; int i, j, ret; @@ -194,15 +181,12 @@ static __maybe_unused struct of_device_id tegra_gpio_dt_ids[] = { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, tegra_gpio_dt_ids); -static struct driver_d tegra_gpio_driver = { +static struct driver tegra_gpio_driver = { .name = "tegra-gpio", .of_compatible = DRV_OF_COMPAT(tegra_gpio_dt_ids), .probe = tegra_gpio_probe, }; -static int __init tegra_gpio_init(void) -{ - return platform_driver_register(&tegra_gpio_driver); -} -coredevice_initcall(tegra_gpio_init); +coredevice_platform_driver(tegra_gpio_driver); diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index ab35310fbe..7c535c2e5e 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -1,18 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2014 Toradex AG (Stefan Agner <stefan@agner.ch>) + /* * vf610 GPIO support through PORT and GPIO module - * - * Copyright (c) 2014 Toradex AG. - * - * Author: Stefan Agner <stefan@agner.ch>. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <common.h> @@ -31,6 +21,8 @@ struct vf610_gpio_port { struct gpio_chip chip; void __iomem *gpio_base; unsigned int pinctrl_base; + bool have_paddr; + bool need_pinctrl; }; #define GPIO_PDOR 0x00 @@ -38,19 +30,46 @@ struct vf610_gpio_port { #define GPIO_PCOR 0x08 #define GPIO_PTOR 0x0c #define GPIO_PDIR 0x10 +#define GPIO_PDDR 0x14 + +struct fsl_gpio_soc_data { + /* SoCs has a Port Data Direction Register (PDDR) */ + bool have_paddr; + bool need_pinctrl; +}; + +static const struct fsl_gpio_soc_data vf610_data = { + .need_pinctrl = true, +}; + +static const struct fsl_gpio_soc_data imx_data = { + .have_paddr = true, +}; static const struct of_device_id vf610_gpio_dt_ids[] = { - { .compatible = "fsl,vf610-gpio" }, + { .compatible = "fsl,vf610-gpio", .data = &vf610_data, }, + { .compatible = "fsl,imx7ulp-gpio", .data = &imx_data, }, + { .compatible = "fsl,imx8ulp-gpio", .data = &imx_data, }, + { .compatible = "fsl,imx93-gpio", .data = &imx_data, }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, vf610_gpio_dt_ids); static int vf610_gpio_get_value(struct gpio_chip *chip, unsigned int gpio) { struct vf610_gpio_port *port = container_of(chip, struct vf610_gpio_port, chip); + unsigned long mask = BIT(gpio); + unsigned long offset = GPIO_PDIR; - return !!(readl(port->gpio_base + GPIO_PDIR) & BIT(gpio)); + if (port->have_paddr) { + mask &= readl(port->gpio_base + GPIO_PDDR); + if (mask) + offset = GPIO_PDOR; + } + + return !!(readl(port->gpio_base + offset) & BIT(gpio)); } static void vf610_gpio_set_value(struct gpio_chip *chip, @@ -67,8 +86,19 @@ static int vf610_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) { struct vf610_gpio_port *port = container_of(chip, struct vf610_gpio_port, chip); + unsigned long mask = BIT(gpio); + u32 val; - return pinctrl_gpio_direction_input(port->pinctrl_base + gpio); + if (port->have_paddr) { + val = readl(port->gpio_base + GPIO_PDDR); + val &= ~mask; + writel(val, port->gpio_base + GPIO_PDDR); + } + + if (port->need_pinctrl) + return pinctrl_gpio_direction_input(port->pinctrl_base + gpio); + + return 0; } static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, @@ -76,18 +106,41 @@ static int vf610_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, { struct vf610_gpio_port *port = container_of(chip, struct vf610_gpio_port, chip); + unsigned long mask = BIT(gpio); + u32 val; vf610_gpio_set_value(chip, gpio, value); - return pinctrl_gpio_direction_output(port->pinctrl_base + gpio); + if (port->have_paddr) { + val = readl(port->gpio_base + GPIO_PDDR); + val |= mask; + writel(val, port->gpio_base + GPIO_PDDR); + } + + if (port->need_pinctrl) + return pinctrl_gpio_direction_output(port->pinctrl_base + gpio); + + return 0; } static int vf610_gpio_get_direction(struct gpio_chip *chip, unsigned gpio) { struct vf610_gpio_port *port = container_of(chip, struct vf610_gpio_port, chip); + u32 val; + + if (port->have_paddr) { + val = readl(port->gpio_base + GPIO_PDDR); + if (val & BIT(gpio)) + return GPIOF_DIR_OUT; + else + return GPIOF_DIR_IN; + } - return pinctrl_gpio_get_direction(port->pinctrl_base + gpio); + if (port->need_pinctrl) + return pinctrl_gpio_get_direction(port->pinctrl_base + gpio); + + return 0; } static struct gpio_ops vf610_gpio_ops = { @@ -98,38 +151,56 @@ static struct gpio_ops vf610_gpio_ops = { .get_direction = vf610_gpio_get_direction, }; -static int vf610_gpio_probe(struct device_d *dev) +static int vf610_gpio_probe(struct device *dev) { int ret, size; struct resource *iores; struct vf610_gpio_port *port; const __be32 *gpio_ranges; + struct fsl_gpio_soc_data *devtype; + + ret = dev_get_drvdata(dev, (const void **)&devtype); + if (ret) + return ret; port = xzalloc(sizeof(*port)); - gpio_ranges = of_get_property(dev->device_node, "gpio-ranges", &size); + gpio_ranges = of_get_property(dev->of_node, "gpio-ranges", &size); if (!gpio_ranges) { - dev_err(dev, "Couldn't read 'gpio-ranges' propery of %s\n", - dev->device_node->full_name); + dev_err(dev, "Couldn't read 'gpio-ranges' propery of %pOF\n", + dev->of_node); ret = -EINVAL; goto free_port; } + port->have_paddr = devtype->have_paddr; + port->need_pinctrl = devtype->need_pinctrl; + port->pinctrl_base = be32_to_cpu(gpio_ranges[PINCTRL_BASE]); - port->chip.ngpio = be32_to_cpu(gpio_ranges[COUNT]); + port->chip.ngpio = 32; + /* + * Some old bindings have two register ranges. When we have two ranges + * the GPIO base is in the second range. With only one range the GPIO + * base is at offset 0x40. + */ iores = dev_request_mem_resource(dev, 1); if (IS_ERR(iores)) { - ret = PTR_ERR(iores); - dev_dbg(dev, "Failed to request memory resource\n"); - goto free_port; + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) { + ret = PTR_ERR(iores); + dev_dbg(dev, "Failed to request memory resource\n"); + goto free_port; + } else { + port->gpio_base = IOMEM(iores->start) + 0x40; + } + } else { + port->gpio_base = IOMEM(iores->start); } - port->gpio_base = IOMEM(iores->start); - port->chip.ops = &vf610_gpio_ops; if (dev->id < 0) { - port->chip.base = of_alias_get_id(dev->device_node, "gpio"); + port->chip.base = of_alias_get_id(dev->of_node, "gpio"); if (port->chip.base < 0) { ret = port->chip.base; dev_dbg(dev, "Failed to get GPIO alias\n"); @@ -150,14 +221,10 @@ free_port: return ret; } -static struct driver_d vf610_gpio_driver = { +static struct driver vf610_gpio_driver = { .name = "gpio-vf610", .probe = vf610_gpio_probe, .of_compatible = DRV_OF_COMPAT(vf610_gpio_dt_ids), }; -static int __init gpio_vf610_init(void) -{ - return platform_driver_register(&vf610_gpio_driver); -} -postcore_initcall(gpio_vf610_init); +postcore_platform_driver(vf610_gpio_driver); diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c new file mode 100644 index 0000000000..1358182547 --- /dev/null +++ b/drivers/gpio/gpio-zynq.c @@ -0,0 +1,437 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Xilinx Zynq GPIO device driver + * + * Copyright (C) 2009 - 2014 Xilinx, Inc. + * + * Based on the Linux kernel driver (drivers/gpio/gpio-zynq.c). + */ + +#include <common.h> +#include <errno.h> +#include <gpio.h> +#include <init.h> +#include <io.h> +#include <of.h> + +/* Maximum banks */ +#define ZYNQ_GPIO_MAX_BANK 4 +#define ZYNQMP_GPIO_MAX_BANK 6 + +#define ZYNQ_GPIO_BANK0_NGPIO 32 +#define ZYNQ_GPIO_BANK1_NGPIO 22 +#define ZYNQ_GPIO_BANK2_NGPIO 32 +#define ZYNQ_GPIO_BANK3_NGPIO 32 + +#define ZYNQMP_GPIO_BANK0_NGPIO 26 +#define ZYNQMP_GPIO_BANK1_NGPIO 26 +#define ZYNQMP_GPIO_BANK2_NGPIO 26 +#define ZYNQMP_GPIO_BANK3_NGPIO 32 +#define ZYNQMP_GPIO_BANK4_NGPIO 32 +#define ZYNQMP_GPIO_BANK5_NGPIO 32 + +#define ZYNQ_GPIO_NR_GPIOS 118 +#define ZYNQMP_GPIO_NR_GPIOS 174 + +#define ZYNQ_GPIO_BANK0_PIN_MIN(str) 0 +#define ZYNQ_GPIO_BANK0_PIN_MAX(str) \ + (ZYNQ_GPIO_BANK0_PIN_MIN(str) + ZYNQ##str##_GPIO_BANK0_NGPIO - 1) +#define ZYNQ_GPIO_BANK1_PIN_MIN(str) (ZYNQ_GPIO_BANK0_PIN_MAX(str) + 1) +#define ZYNQ_GPIO_BANK1_PIN_MAX(str) \ + (ZYNQ_GPIO_BANK1_PIN_MIN(str) + ZYNQ##str##_GPIO_BANK1_NGPIO - 1) +#define ZYNQ_GPIO_BANK2_PIN_MIN(str) (ZYNQ_GPIO_BANK1_PIN_MAX(str) + 1) +#define ZYNQ_GPIO_BANK2_PIN_MAX(str) \ + (ZYNQ_GPIO_BANK2_PIN_MIN(str) + ZYNQ##str##_GPIO_BANK2_NGPIO - 1) +#define ZYNQ_GPIO_BANK3_PIN_MIN(str) (ZYNQ_GPIO_BANK2_PIN_MAX(str) + 1) +#define ZYNQ_GPIO_BANK3_PIN_MAX(str) \ + (ZYNQ_GPIO_BANK3_PIN_MIN(str) + ZYNQ##str##_GPIO_BANK3_NGPIO - 1) +#define ZYNQ_GPIO_BANK4_PIN_MIN(str) (ZYNQ_GPIO_BANK3_PIN_MAX(str) + 1) +#define ZYNQ_GPIO_BANK4_PIN_MAX(str) \ + (ZYNQ_GPIO_BANK4_PIN_MIN(str) + ZYNQ##str##_GPIO_BANK4_NGPIO - 1) +#define ZYNQ_GPIO_BANK5_PIN_MIN(str) (ZYNQ_GPIO_BANK4_PIN_MAX(str) + 1) +#define ZYNQ_GPIO_BANK5_PIN_MAX(str) \ + (ZYNQ_GPIO_BANK5_PIN_MIN(str) + ZYNQ##str##_GPIO_BANK5_NGPIO - 1) + +/* Register offsets for the GPIO device */ +/* LSW Mask & Data -WO */ +#define ZYNQ_GPIO_DATA_LSW_OFFSET(BANK) (0x000 + (8 * BANK)) +/* MSW Mask & Data -WO */ +#define ZYNQ_GPIO_DATA_MSW_OFFSET(BANK) (0x004 + (8 * BANK)) +/* Data Register-RW */ +#define ZYNQ_GPIO_DATA_OFFSET(BANK) (0x040 + (4 * BANK)) +#define ZYNQ_GPIO_DATA_RO_OFFSET(BANK) (0x060 + (4 * BANK)) +/* Direction mode reg-RW */ +#define ZYNQ_GPIO_DIRM_OFFSET(BANK) (0x204 + (0x40 * BANK)) +/* Output enable reg-RW */ +#define ZYNQ_GPIO_OUTEN_OFFSET(BANK) (0x208 + (0x40 * BANK)) +/* Interrupt mask reg-RO */ +#define ZYNQ_GPIO_INTMASK_OFFSET(BANK) (0x20C + (0x40 * BANK)) +/* Interrupt enable reg-WO */ +#define ZYNQ_GPIO_INTEN_OFFSET(BANK) (0x210 + (0x40 * BANK)) +/* Interrupt disable reg-WO */ +#define ZYNQ_GPIO_INTDIS_OFFSET(BANK) (0x214 + (0x40 * BANK)) +/* Interrupt status reg-RO */ +#define ZYNQ_GPIO_INTSTS_OFFSET(BANK) (0x218 + (0x40 * BANK)) +/* Interrupt type reg-RW */ +#define ZYNQ_GPIO_INTTYPE_OFFSET(BANK) (0x21C + (0x40 * BANK)) +/* Interrupt polarity reg-RW */ +#define ZYNQ_GPIO_INTPOL_OFFSET(BANK) (0x220 + (0x40 * BANK)) +/* Interrupt on any, reg-RW */ +#define ZYNQ_GPIO_INTANY_OFFSET(BANK) (0x224 + (0x40 * BANK)) + +/* Disable all interrupts mask */ +#define ZYNQ_GPIO_IXR_DISABLE_ALL 0xFFFFFFFF + +/* Mid pin number of a bank */ +#define ZYNQ_GPIO_MID_PIN_NUM 16 + +/* GPIO upper 16 bit mask */ +#define ZYNQ_GPIO_UPPER_MASK 0xFFFF0000 + +/* set to differentiate zynq from zynqmp, 0=zynqmp, 1=zynq */ +#define ZYNQ_GPIO_QUIRK_IS_ZYNQ BIT(0) +#define GPIO_QUIRK_DATA_RO_BUG BIT(1) + +/** + * struct zynq_gpio - GPIO device private data structure + * @chip: instance of the gpio_chip + * @base_addr: base address of the GPIO device + * @p_data: pointer to platform data + */ +struct zynq_gpio { + struct gpio_chip chip; + void __iomem *base_addr; + const struct zynq_platform_data *p_data; +}; + +/** + * struct zynq_platform_data - Zynq GPIO platform data structure + * @quirks: Flags is used to identify the platform + * @ngpio: max number of gpio pins + * @max_bank: maximum number of gpio banks + * @bank_min: this array represents bank's min pin + * @bank_max: this array represents bank's max pin + */ +struct zynq_platform_data { + u32 quirks; + u16 ngpio; + int max_bank; + int bank_min[ZYNQMP_GPIO_MAX_BANK]; + int bank_max[ZYNQMP_GPIO_MAX_BANK]; +}; + +/** + * zynq_gpio_is_zynq - Test if HW is Zynq or ZynqMP + * @gpio: Pointer to driver data struct + * + * Return: 0 if ZynqMP, 1 if Zynq. + */ +static int zynq_gpio_is_zynq(struct zynq_gpio *gpio) +{ + return !!(gpio->p_data->quirks & ZYNQ_GPIO_QUIRK_IS_ZYNQ); +} + +/** + * gpio_data_ro_bug - Test if HW bug exists or not + * @gpio: Pointer to driver data struct + * + * Return: 0 if bug does not exist, 1 if bug exists. + */ +static int gpio_data_ro_bug(struct zynq_gpio *gpio) +{ + return !!(gpio->p_data->quirks & GPIO_QUIRK_DATA_RO_BUG); +} + +/** + * zynq_gpio_get_bank_pin - Get the bank number and pin number within that bank + * for a given pin in the GPIO device + * @pin_num: gpio pin number within the device + * @bank_num: an output parameter used to return the bank number of the gpio + * pin + * @bank_pin_num: an output parameter used to return pin number within a bank + * for the given gpio pin + * @gpio: gpio device data structure + * + * Returns the bank number and pin offset within the bank. + */ +static int zynq_gpio_get_bank_pin(unsigned int pin_num, unsigned int *bank_num, + unsigned int *bank_pin_num, + struct zynq_gpio *gpio) +{ + int bank; + + for (bank = 0; bank < gpio->p_data->max_bank; bank++) { + if ((pin_num >= gpio->p_data->bank_min[bank]) && + (pin_num <= gpio->p_data->bank_max[bank])) { + *bank_num = bank; + *bank_pin_num = pin_num - gpio->p_data->bank_min[bank]; + return 0; + } + } + + *bank_num = 0; + *bank_pin_num = 0; + return -ENODEV; +} + +/** + * zynq_gpio_get_value - Get the state of the specified pin of GPIO device + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * + * This function reads the state of the specified pin of the GPIO device. + * + * Return: 0 if the pin is low, 1 if pin is high. + */ +static int zynq_gpio_get_value(struct gpio_chip *chip, unsigned int pin) +{ + u32 data; + unsigned int bank_num, bank_pin_num; + struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip); + + if (zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio) < 0) + return -EINVAL; + + if (gpio_data_ro_bug(gpio)) { + if (zynq_gpio_is_zynq(gpio)) { + if (bank_num <= 1) { + data = readl_relaxed( + gpio->base_addr + + ZYNQ_GPIO_DATA_RO_OFFSET(bank_num)); + } else { + data = readl_relaxed( + gpio->base_addr + + ZYNQ_GPIO_DATA_OFFSET(bank_num)); + } + } else { + if (bank_num <= 2) { + data = readl_relaxed( + gpio->base_addr + + ZYNQ_GPIO_DATA_RO_OFFSET(bank_num)); + } else { + data = readl_relaxed( + gpio->base_addr + + ZYNQ_GPIO_DATA_OFFSET(bank_num)); + } + } + } else { + data = readl_relaxed(gpio->base_addr + + ZYNQ_GPIO_DATA_RO_OFFSET(bank_num)); + } + return (data >> bank_pin_num) & 1; +} + +/** + * zynq_gpio_set_value - Modify the state of the pin with specified value + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * @state: value used to modify the state of the specified pin + * + * This function calculates the register offset (i.e to lower 16 bits or + * upper 16 bits) based on the given pin number and sets the state of a + * gpio pin to the specified value. The state is either 0 or non-zero. + */ +static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin, + int state) +{ + unsigned int reg_offset, bank_num, bank_pin_num; + struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip); + + if (zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio) < 0) + return; + + if (bank_pin_num >= ZYNQ_GPIO_MID_PIN_NUM) { + bank_pin_num -= ZYNQ_GPIO_MID_PIN_NUM; + reg_offset = ZYNQ_GPIO_DATA_MSW_OFFSET(bank_num); + } else { + reg_offset = ZYNQ_GPIO_DATA_LSW_OFFSET(bank_num); + } + + /* + * get the 32 bit value to be written to the mask/data register where + * the upper 16 bits is the mask and lower 16 bits is the data + */ + state = !!state; + state = ~(1 << (bank_pin_num + ZYNQ_GPIO_MID_PIN_NUM)) & + ((state << bank_pin_num) | ZYNQ_GPIO_UPPER_MASK); + + writel_relaxed(state, gpio->base_addr + reg_offset); +} + +/** + * zynq_gpio_dir_in - Set the direction of the specified GPIO pin as input + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * + * This function uses the read-modify-write sequence to set the direction of + * the gpio pin as input. + * + * Return: 0 always + */ +static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin) +{ + u32 reg; + unsigned int bank_num, bank_pin_num; + struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip); + + if (zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio) < 0) + return -EINVAL; + /* + * On zynq bank 0 pins 7 and 8 are special and cannot be used + * as inputs. + */ + if (zynq_gpio_is_zynq(gpio) && bank_num == 0 && + (bank_pin_num == 7 || bank_pin_num == 8)) + return -EINVAL; + + /* clear the bit in direction mode reg to set the pin as input */ + reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); + reg &= ~BIT(bank_pin_num); + writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); + + return 0; +} + +/** + * zynq_gpio_dir_out - Set the direction of the specified GPIO pin as output + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * @state: value to be written to specified pin + * + * This function sets the direction of specified GPIO pin as output, configures + * the Output Enable register for the pin and uses zynq_gpio_set to set + * the state of the pin to the value specified. + * + * Return: 0 always + */ +static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, + int state) +{ + u32 reg; + unsigned int bank_num, bank_pin_num; + struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip); + + if (zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio) < 0) + return -EINVAL; + + /* set the GPIO pin as output */ + reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); + reg |= BIT(bank_pin_num); + writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); + + /* configure the output enable reg for the pin */ + reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_OUTEN_OFFSET(bank_num)); + reg |= BIT(bank_pin_num); + writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_OUTEN_OFFSET(bank_num)); + + /* set the state of the pin */ + zynq_gpio_set_value(chip, pin, state); + return 0; +} + +/** + * zynq_gpio_get_direction - Read the direction of the specified GPIO pin + * @chip: gpio_chip instance to be worked on + * @pin: gpio pin number within the device + * + * This function returns the direction of the specified GPIO. + * + * Return: 0 for output, 1 for input + */ +static int zynq_gpio_get_direction(struct gpio_chip *chip, unsigned int pin) +{ + u32 reg; + unsigned int bank_num, bank_pin_num; + struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip); + + if (zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio) < 0) + return -EINVAL; + + reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); + + return !(reg & BIT(bank_pin_num)); +} + +static struct gpio_ops zynq_gpio_ops = { + .direction_input = zynq_gpio_dir_in, + .direction_output = zynq_gpio_dir_out, + .get = zynq_gpio_get_value, + .set = zynq_gpio_set_value, + .get_direction = zynq_gpio_get_direction, +}; + +static int zynqmp_gpio_probe(struct device *dev) +{ + struct resource *iores; + struct zynq_gpio *gpio; + const struct zynq_platform_data *p_data; + int ret; + + gpio = xzalloc(sizeof(*gpio)); + p_data = device_get_match_data(dev); + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) { + ret = PTR_ERR(iores); + goto free_gpio; + } + + gpio->base_addr = IOMEM(iores->start); + gpio->chip.base = of_alias_get_id(dev->of_node, "gpio"); + gpio->chip.ops = &zynq_gpio_ops; + gpio->chip.ngpio = p_data->ngpio; + gpio->chip.dev = dev; + gpio->p_data = p_data; + + return gpiochip_add(&gpio->chip); + +free_gpio: + kfree(gpio); + return ret; +} + +static const struct zynq_platform_data zynqmp_gpio_def = { + .quirks = GPIO_QUIRK_DATA_RO_BUG, + .ngpio = ZYNQMP_GPIO_NR_GPIOS, + .max_bank = ZYNQMP_GPIO_MAX_BANK, + .bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(MP), + .bank_max[0] = ZYNQ_GPIO_BANK0_PIN_MAX(MP), + .bank_min[1] = ZYNQ_GPIO_BANK1_PIN_MIN(MP), + .bank_max[1] = ZYNQ_GPIO_BANK1_PIN_MAX(MP), + .bank_min[2] = ZYNQ_GPIO_BANK2_PIN_MIN(MP), + .bank_max[2] = ZYNQ_GPIO_BANK2_PIN_MAX(MP), + .bank_min[3] = ZYNQ_GPIO_BANK3_PIN_MIN(MP), + .bank_max[3] = ZYNQ_GPIO_BANK3_PIN_MAX(MP), + .bank_min[4] = ZYNQ_GPIO_BANK4_PIN_MIN(MP), + .bank_max[4] = ZYNQ_GPIO_BANK4_PIN_MAX(MP), + .bank_min[5] = ZYNQ_GPIO_BANK5_PIN_MIN(MP), + .bank_max[5] = ZYNQ_GPIO_BANK5_PIN_MAX(MP), +}; + +static const struct zynq_platform_data zynq_gpio_def = { + .quirks = ZYNQ_GPIO_QUIRK_IS_ZYNQ | GPIO_QUIRK_DATA_RO_BUG, + .ngpio = ZYNQ_GPIO_NR_GPIOS, + .max_bank = ZYNQ_GPIO_MAX_BANK, + .bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(), + .bank_max[0] = ZYNQ_GPIO_BANK0_PIN_MAX(), + .bank_min[1] = ZYNQ_GPIO_BANK1_PIN_MIN(), + .bank_max[1] = ZYNQ_GPIO_BANK1_PIN_MAX(), + .bank_min[2] = ZYNQ_GPIO_BANK2_PIN_MIN(), + .bank_max[2] = ZYNQ_GPIO_BANK2_PIN_MAX(), + .bank_min[3] = ZYNQ_GPIO_BANK3_PIN_MIN(), + .bank_max[3] = ZYNQ_GPIO_BANK3_PIN_MAX(), +}; + +static const struct of_device_id zynq_gpio_of_match[] = { + { .compatible = "xlnx,zynq-gpio-1.0", .data = &zynq_gpio_def }, + { .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def }, + { /* end of table */ } +}; +MODULE_DEVICE_TABLE(of, zynq_gpio_of_match); + +static struct driver zynqmp_gpio_driver = { + .name = "zynqmp-gpio", + .of_compatible = zynq_gpio_of_match, + .probe = zynqmp_gpio_probe, +}; + +postcore_platform_driver(zynqmp_gpio_driver); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index f96009896a..a70e13eafc 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #define pr_fmt(fmt) "gpiolib: " fmt #include <init.h> @@ -6,37 +7,70 @@ #include <complete.h> #include <gpio.h> #include <of_gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/overflow.h> #include <errno.h> #include <malloc.h> static LIST_HEAD(chip_list); -struct gpio_info { +struct gpio_desc { struct gpio_chip *chip; bool requested; bool active_low; char *label; + const char *name; }; -static struct gpio_info *gpio_desc; +/* + * This descriptor validation needs to be inserted verbatim into each + * function taking a descriptor, so we need to use a preprocessor + * macro to avoid endless duplication. If the desc is NULL it is an + * optional GPIO and calls should just bail out. + */ +static int validate_desc(const struct gpio_desc *desc, const char *func) +{ + if (!desc) + return 0; + if (IS_ERR(desc)) { + pr_warn("%s: invalid GPIO (errorpointer)\n", func); + return PTR_ERR(desc); + } + + return 1; +} + +#define VALIDATE_DESC(desc) do { \ + int __valid = validate_desc(desc, __func__); \ + if (__valid <= 0) \ + return __valid; \ + } while (0) + +#define VALIDATE_DESC_VOID(desc) do { \ + int __valid = validate_desc(desc, __func__); \ + if (__valid <= 0) \ + return; \ + } while (0) + +static struct gpio_desc *gpio_desc; static int gpio_desc_alloc(void) { - gpio_desc = xzalloc(sizeof(struct gpio_info) * ARCH_NR_GPIOS); + gpio_desc = xzalloc(sizeof(struct gpio_desc) * ARCH_NR_GPIOS); return 0; } pure_initcall(gpio_desc_alloc); -static int gpio_ensure_requested(struct gpio_info *gi, int gpio) +static int gpio_ensure_requested(struct gpio_desc *desc, int gpio) { - if (gi->requested) + if (desc->requested) return 0; return gpio_request(gpio, "gpio"); } -static struct gpio_info *gpio_to_desc(unsigned gpio) +static struct gpio_desc *gpio_to_desc(unsigned gpio) { if (gpio_is_valid(gpio)) if (gpio_desc[gpio].chip) @@ -47,46 +81,46 @@ static struct gpio_info *gpio_to_desc(unsigned gpio) return NULL; } -static int gpio_adjust_value(struct gpio_info *gi, +static unsigned gpiodesc_chip_offset(const struct gpio_desc *desc) +{ + return (desc - gpio_desc) - desc->chip->base; +} + +static int gpio_adjust_value(const struct gpio_desc *desc, int value) { if (value < 0) return value; - return !!value ^ gi->active_low; + return !!value ^ desc->active_low; } -int gpio_request(unsigned gpio, const char *label) +static int gpiodesc_request(struct gpio_desc *desc, const char *label) { - struct gpio_info *gi = gpio_to_desc(gpio); int ret; - if (!gi) { - ret = -ENODEV; - goto done; - } - - if (gi->requested) { + if (desc->requested) { ret = -EBUSY; goto done; } ret = 0; - if (gi->chip->ops->request) { - ret = gi->chip->ops->request(gi->chip, gpio - gi->chip->base); + if (desc->chip->ops->request) { + ret = desc->chip->ops->request(desc->chip, + gpiodesc_chip_offset(desc)); if (ret) goto done; } - gi->requested = true; - gi->active_low = false; - gi->label = xstrdup(label); + desc->requested = true; + desc->active_low = false; + desc->label = xstrdup(label); done: if (ret) - pr_err("_gpio_request: gpio-%d (%s) status %d\n", - gpio, label ? : "?", ret); + pr_err("_gpio_request: gpio-%td (%s) status %d\n", + desc - gpio_desc, label ? : "?", ret); return ret; } @@ -96,7 +130,7 @@ int gpio_find_by_label(const char *label) int i; for (i = 0; i < ARCH_NR_GPIOS; i++) { - struct gpio_info *info = &gpio_desc[i]; + struct gpio_desc *info = &gpio_desc[i]; if (!info->requested || !info->chip || !info->label) continue; @@ -108,225 +142,460 @@ int gpio_find_by_label(const char *label) return -ENOENT; } -void gpio_free(unsigned gpio) +int gpio_find_by_name(const char *name) { - struct gpio_info *gi = gpio_to_desc(gpio); + int i; - if (!gi) - return; + for (i = 0; i < ARCH_NR_GPIOS; i++) { + struct gpio_desc *info = &gpio_desc[i]; - if (!gi->requested) - return; + if (!info->chip || !info->name) + continue; - if (gi->chip->ops->free) - gi->chip->ops->free(gi->chip, gpio - gi->chip->base); + if (!strcmp(info->name, name)) + return i; + } - gi->requested = false; - gi->active_low = false; - free(gi->label); - gi->label = NULL; + return -ENOENT; } -/** - * gpio_request_one - request a single GPIO with initial configuration - * @gpio: the GPIO number - * @flags: GPIO configuration as specified by GPIOF_* - * @label: a literal description string of this GPIO - */ -int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) +int gpio_request(unsigned gpio, const char *label) { - int err; - struct gpio_info *gi = gpio_to_desc(gpio); + struct gpio_desc *desc = gpio_to_desc(gpio); - /* - * Not all of the flags below are mulit-bit, but, for the sake - * of consistency, the code is written as if all of them were. - */ - const bool active_low = (flags & GPIOF_ACTIVE_LOW) == GPIOF_ACTIVE_LOW; - const bool dir_in = (flags & GPIOF_DIR_IN) == GPIOF_DIR_IN; - const bool logical = (flags & GPIOF_LOGICAL) == GPIOF_LOGICAL; - const bool init_active = (flags & GPIOF_INIT_ACTIVE) == GPIOF_INIT_ACTIVE; - const bool init_high = (flags & GPIOF_INIT_HIGH) == GPIOF_INIT_HIGH; + if (!desc) { + pr_err("_gpio_request: gpio-%d (%s) status %d\n", + gpio, label ? : "?", -ENODEV); + return -ENODEV; + } - err = gpio_request(gpio, label); - if (err) - return err; + return gpiodesc_request(desc, label); +} - gi->active_low = active_low; +bool gpiod_slice_acquired(struct gpio_desc *desc) +{ + if (!desc) + return false; - if (dir_in) - err = gpio_direction_input(gpio); - else if (logical) - err = gpio_direction_active(gpio, init_active); - else - err = gpio_direction_output(gpio, init_high); + return slice_acquired(&desc->chip->slice); +} - if (err) - gpio_free(gpio); +bool gpio_slice_acquired(unsigned gpio) +{ + struct gpio_desc *desc = gpio_to_desc(gpio); - return err; + return gpiod_slice_acquired(desc); +} + +static void gpiodesc_free(struct gpio_desc *desc) +{ + if (!desc->requested) + return; + + if (desc->chip->ops->free) + desc->chip->ops->free(desc->chip, gpiodesc_chip_offset(desc)); + + desc->requested = false; + desc->active_low = false; + free(desc->label); + desc->label = NULL; +} + +void gpio_free(unsigned gpio) +{ + struct gpio_desc *desc = gpio_to_desc(gpio); + + gpiodesc_free(desc); } -EXPORT_SYMBOL_GPL(gpio_request_one); /** - * gpio_request_array - request multiple GPIOs in a single call - * @array: array of the 'struct gpio' - * @num: how many GPIOs in the array + * gpiod_put - dispose of a GPIO descriptor + * @desc: GPIO descriptor to dispose of + * + * No descriptor can be used after gpiod_put() has been called on it. */ -int gpio_request_array(const struct gpio *array, size_t num) +void gpiod_put(struct gpio_desc *desc) { - int i, err; + if (!desc) + return; - for (i = 0; i < num; i++, array++) { - err = gpio_request_one(array->gpio, array->flags, array->label); - if (err) - goto err_free; - } - return 0; + gpiodesc_free(desc); +} +EXPORT_SYMBOL(gpiod_put); -err_free: - while (i--) - gpio_free((--array)->gpio); - return err; +/** + * gpiod_put_array - dispose of multiple GPIO descriptors + * @descs: struct gpio_descs containing an array of descriptors + */ +void gpiod_put_array(struct gpio_descs *descs) +{ + unsigned int i; + + for (i = 0; i < descs->ndescs; i++) + gpiod_put(descs->desc[i]); + + kfree(descs); } -EXPORT_SYMBOL_GPL(gpio_request_array); +EXPORT_SYMBOL_GPL(gpiod_put_array); /** - * gpio_free_array - release multiple GPIOs in a single call - * @array: array of the 'struct gpio' - * @num: how many GPIOs in the array + * gpiod_set_raw_value() - assign a gpio's raw value + * @desc: gpio whose value will be assigned + * @value: value to assign + * + * Set the raw value of the GPIO, i.e. the value of its physical line without + * regard for its ACTIVE_LOW status. */ -void gpio_free_array(const struct gpio *array, size_t num) +void gpiod_set_raw_value(struct gpio_desc *desc, int value) { - while (num--) - gpio_free((array++)->gpio); + VALIDATE_DESC_VOID(desc); + + if (desc->chip->ops->set) + desc->chip->ops->set(desc->chip, gpiodesc_chip_offset(desc), value); } -EXPORT_SYMBOL_GPL(gpio_free_array); +EXPORT_SYMBOL(gpiod_set_raw_value); void gpio_set_value(unsigned gpio, int value) { - struct gpio_info *gi = gpio_to_desc(gpio); + struct gpio_desc *desc = gpio_to_desc(gpio); - if (!gi) + if (!desc) return; - if (gpio_ensure_requested(gi, gpio)) + if (gpio_ensure_requested(desc, gpio)) return; - if (gi->chip->ops->set) - gi->chip->ops->set(gi->chip, gpio - gi->chip->base, value); + gpiod_set_raw_value(desc, value); } EXPORT_SYMBOL(gpio_set_value); +/** + * gpiod_set_value() - assign a gpio's value + * @desc: gpio whose value will be assigned + * @value: value to assign + * + * Set the logical value of the GPIO, i.e. taking its ACTIVE_LOW, + * OPEN_DRAIN and OPEN_SOURCE flags into account. + */ +void gpiod_set_value(struct gpio_desc *desc, int value) +{ + VALIDATE_DESC_VOID(desc); + gpiod_set_raw_value(desc, gpio_adjust_value(desc, value)); +} +EXPORT_SYMBOL_GPL(gpiod_set_value); + void gpio_set_active(unsigned gpio, bool value) { - struct gpio_info *gi = gpio_to_desc(gpio); + struct gpio_desc *desc = gpio_to_desc(gpio); - if (!gi) + if (!desc) return; - gpio_set_value(gpio, gpio_adjust_value(gi, value)); + gpiod_set_value(desc, value); } EXPORT_SYMBOL(gpio_set_active); +/** + * gpiod_get_raw_value() - return a gpio's raw value + * @desc: gpio whose value will be returned + * + * Return the GPIO's raw value, i.e. the value of the physical line disregarding + * its ACTIVE_LOW status, or negative errno on failure. + */ +int gpiod_get_raw_value(const struct gpio_desc *desc) +{ + VALIDATE_DESC(desc); + + if (!desc->chip->ops->get) + return -ENOSYS; + + return desc->chip->ops->get(desc->chip, gpiodesc_chip_offset(desc)); +} +EXPORT_SYMBOL_GPL(gpiod_get_raw_value); + int gpio_get_value(unsigned gpio) { - struct gpio_info *gi = gpio_to_desc(gpio); + struct gpio_desc *desc = gpio_to_desc(gpio); int ret; - if (!gi) + if (!desc) return -ENODEV; - ret = gpio_ensure_requested(gi, gpio); + ret = gpio_ensure_requested(desc, gpio); if (ret) return ret; - if (!gi->chip->ops->get) - return -ENOSYS; - return gi->chip->ops->get(gi->chip, gpio - gi->chip->base); + return gpiod_get_raw_value(desc); } EXPORT_SYMBOL(gpio_get_value); +/** + * gpiod_get_value() - return a gpio's value + * @desc: gpio whose value will be returned + * + * Return the GPIO's logical value, i.e. taking the ACTIVE_LOW status into + * account, or negative errno on failure. + */ +int gpiod_get_value(const struct gpio_desc *desc) +{ + VALIDATE_DESC(desc); + + return gpio_adjust_value(desc, gpiod_get_raw_value(desc)); +} +EXPORT_SYMBOL_GPL(gpiod_get_value); + int gpio_is_active(unsigned gpio) { - struct gpio_info *gi = gpio_to_desc(gpio); + struct gpio_desc *desc = gpio_to_desc(gpio); - if (!gi) + if (!desc) return -ENODEV; - return gpio_adjust_value(gi, gpio_get_value(gpio)); + return gpiod_get_value(desc); } EXPORT_SYMBOL(gpio_is_active); +/** + * gpiod_direction_output_raw - set the GPIO direction to output + * @desc: GPIO to set to output + * @value: initial output value of the GPIO + * + * Set the direction of the passed GPIO to output, such as gpiod_set_value() can + * be called safely on it. The initial value of the output must be specified + * as raw value on the physical line without regard for the ACTIVE_LOW status. + * + * Return 0 in case of success, else an error code. + */ +int gpiod_direction_output_raw(struct gpio_desc *desc, int value) +{ + VALIDATE_DESC(desc); + + if (!desc->chip->ops->direction_output) + return -ENOSYS; + + return desc->chip->ops->direction_output(desc->chip, + gpiodesc_chip_offset(desc), value); +} +EXPORT_SYMBOL(gpiod_direction_output_raw); + int gpio_direction_output(unsigned gpio, int value) { - struct gpio_info *gi = gpio_to_desc(gpio); + struct gpio_desc *desc = gpio_to_desc(gpio); int ret; - if (!gi) + if (!desc) return -ENODEV; - ret = gpio_ensure_requested(gi, gpio); + ret = gpio_ensure_requested(desc, gpio); if (ret) return ret; - if (!gi->chip->ops->direction_output) - return -ENOSYS; - return gi->chip->ops->direction_output(gi->chip, gpio - gi->chip->base, - value); + return gpiod_direction_output_raw(desc, value); } EXPORT_SYMBOL(gpio_direction_output); +/** + * gpiod_direction_output - set the GPIO direction to output + * @desc: GPIO to set to output + * @value: initial output value of the GPIO + * + * Set the direction of the passed GPIO to output, such as gpiod_set_value() can + * be called safely on it. The initial value of the output must be specified + * as the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into + * account. + * + * Return 0 in case of success, else an error code. + */ +int gpiod_direction_output(struct gpio_desc *desc, int value) +{ + VALIDATE_DESC(desc); + + return gpiod_direction_output_raw(desc, gpio_adjust_value(desc, value)); +} + int gpio_direction_active(unsigned gpio, bool value) { - struct gpio_info *gi = gpio_to_desc(gpio); + struct gpio_desc *desc = gpio_to_desc(gpio); - if (!gi) + if (!desc) return -ENODEV; - return gpio_direction_output(gpio, gpio_adjust_value(gi, value)); + return gpiod_direction_output(desc, value); } EXPORT_SYMBOL(gpio_direction_active); +/** + * gpiod_direction_input - set the GPIO direction to input + * @desc: GPIO to set to input + * + * Set the direction of the passed GPIO to input, such as gpiod_get_value() can + * be called safely on it. + * + * Return 0 in case of success, else an error code. + */ +int gpiod_direction_input(struct gpio_desc *desc) +{ + VALIDATE_DESC(desc); + + if (!desc->chip->ops->direction_input) + return -ENOSYS; + + return desc->chip->ops->direction_input(desc->chip, + gpiodesc_chip_offset(desc)); +} +EXPORT_SYMBOL(gpiod_direction_input); + int gpio_direction_input(unsigned gpio) { - struct gpio_info *gi = gpio_to_desc(gpio); + struct gpio_desc *desc = gpio_to_desc(gpio); int ret; - if (!gi) + if (!desc) return -ENODEV; - ret = gpio_ensure_requested(gi, gpio); + ret = gpio_ensure_requested(desc, gpio); if (ret) return ret; - if (!gi->chip->ops->direction_input) - return -ENOSYS; - return gi->chip->ops->direction_input(gi->chip, gpio - gi->chip->base); + return gpiod_direction_input(desc); } EXPORT_SYMBOL(gpio_direction_input); -static int gpiochip_find_base(int start, int ngpio) +static int gpiodesc_request_one(struct gpio_desc *desc, unsigned long flags, + const char *label) +{ + int err; + + /* + * Not all of the flags below are mulit-bit, but, for the sake + * of consistency, the code is written as if all of them were. + */ + const bool active_low = (flags & GPIOF_ACTIVE_LOW) == GPIOF_ACTIVE_LOW; + const bool dir_in = (flags & GPIOF_DIR_IN) == GPIOF_DIR_IN; + const bool logical = (flags & GPIOF_LOGICAL) == GPIOF_LOGICAL; + const bool init_active = (flags & GPIOF_INIT_ACTIVE) == GPIOF_INIT_ACTIVE; + const bool init_high = (flags & GPIOF_INIT_HIGH) == GPIOF_INIT_HIGH; + + err = gpiodesc_request(desc, label); + if (err) + return err; + + desc->active_low = active_low; + + if (dir_in) + err = gpiod_direction_input(desc); + else if (logical) + err = gpiod_direction_output(desc, init_active); + else + err = gpiod_direction_output_raw(desc, init_high); + + if (err) + gpiodesc_free(desc); + + return err; +} + +/** + * gpio_request_one - request a single GPIO with initial configuration + * @gpio: the GPIO number + * @flags: GPIO configuration as specified by GPIOF_* + * @label: a literal description string of this GPIO + */ +int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) +{ + struct gpio_desc *desc = gpio_to_desc(gpio); + + if (!desc) + return -ENODEV; + + return gpiodesc_request_one(desc, flags, label); +} +EXPORT_SYMBOL_GPL(gpio_request_one); + +/** + * gpio_request_array - request multiple GPIOs in a single call + * @array: array of the 'struct gpio' + * @num: how many GPIOs in the array + */ +int gpio_request_array(const struct gpio *array, size_t num) +{ + int i, err; + + for (i = 0; i < num; i++, array++) { + err = gpio_request_one(array->gpio, array->flags, array->label); + if (err) + goto err_free; + } + return 0; + +err_free: + while (i--) + gpio_free((--array)->gpio); + return err; +} +EXPORT_SYMBOL_GPL(gpio_request_array); + +/** + * gpio_free_array - release multiple GPIOs in a single call + * @array: array of the 'struct gpio' + * @num: how many GPIOs in the array + */ +void gpio_free_array(const struct gpio *array, size_t num) +{ + while (num--) + gpio_free((array++)->gpio); +} +EXPORT_SYMBOL_GPL(gpio_free_array); + +int gpio_array_to_id(const struct gpio *array, size_t num, u32 *val) +{ + u32 id = 0; + int ret, i; + + if (num > 32) + return -EOVERFLOW; + + ret = gpio_request_array(array, num); + if (ret) + return ret; + + /* Wait until logic level will be stable */ + udelay(5); + for (i = 0; i < num; i++) { + ret = gpio_is_active(array[i].gpio); + if (ret < 0) + goto free_array; + if (ret) + id |= 1UL << i; + } + + *val = id; + ret = 0; + +free_array: + gpio_free_array(array, num); + return ret; +} +EXPORT_SYMBOL(gpio_array_to_id); + +static int gpiochip_find_base(int ngpio) { int i; int spare = 0; int base = -ENOSPC; - if (start < 0) - start = 0; - - for (i = start; i < ARCH_NR_GPIOS; i++) { + for (i = ARCH_NR_GPIOS - 1; i >= 0; i--) { struct gpio_chip *chip = gpio_desc[i].chip; if (!chip) { spare++; if (spare == ngpio) { - base = i + 1 - ngpio; + base = i; break; } } else { spare = 0; - i += chip->ngpio - 1; + i -= chip->ngpio - 1; } } @@ -335,10 +604,12 @@ static int gpiochip_find_base(int start, int ngpio) return base; } +#ifdef CONFIG_OF_GPIO + static int of_hog_gpio(struct device_node *np, struct gpio_chip *chip, unsigned int idx) { - struct device_node *chip_np = chip->dev->device_node; + struct device_node *chip_np = chip->dev->of_node; unsigned long flags = 0; u32 gpio_cells, gpio_num, gpio_flags; int ret, gpio; @@ -412,10 +683,7 @@ static int of_gpiochip_scan_hogs(struct gpio_chip *chip) struct device_node *np; int ret, i; - if (!IS_ENABLED(CONFIG_OFDEVICE) || !chip->dev->device_node) - return 0; - - for_each_available_child_of_node(chip->dev->device_node, np) { + for_each_available_child_of_node(chip->dev->of_node, np) { if (!of_property_read_bool(np, "gpio-hog")) continue; @@ -434,25 +702,340 @@ static int of_gpiochip_scan_hogs(struct gpio_chip *chip) return 0; } -int gpiochip_add(struct gpio_chip *chip) +/* + * of_gpiochip_set_names - Set GPIO line names using OF properties + * @chip: GPIO chip whose lines should be named, if possible + * + * Looks for device property "gpio-line-names" and if it exists assigns + * GPIO line names for the chip. The memory allocated for the assigned + * names belong to the underlying firmware node and should not be released + * by the caller. + */ +static int of_gpiochip_set_names(struct gpio_chip *chip) +{ + struct device_node *np = dev_of_node(chip->dev); + const char **names; + int ret, i, count; + + count = of_property_count_strings(np, "gpio-line-names"); + if (count < 0) + return 0; + + names = kcalloc(count, sizeof(*names), GFP_KERNEL); + if (!names) + return -ENOMEM; + + ret = of_property_read_string_array(np, "gpio-line-names", + names, count); + if (ret < 0) { + kfree(names); + return ret; + } + + /* + * Since property 'gpio-line-names' cannot contains gaps, we + * have to be sure we only assign those pins that really exists + * since chip->ngpio can be less. + */ + if (count > chip->ngpio) + count = chip->ngpio; + + for (i = 0; i < count; i++) { + /* + * Allow overriding "fixed" names provided by the GPIO + * provider. The "fixed" names are more often than not + * generic and less informative than the names given in + * device properties. + */ + if (names[i] && names[i][0]) + gpio_desc[chip->base + i].name = names[i]; + } + + free(names); + + return 0; +} + +/** + * of_gpio_simple_xlate - translate gpiospec to the GPIO number and flags + * @gc: pointer to the gpio_chip structure + * @gpiospec: GPIO specifier as found in the device tree + * @flags: a flags pointer to fill in + * + * This is simple translation function, suitable for the most 1:1 mapped + * GPIO chips. This function performs only one sanity check: whether GPIO + * is less than ngpios (that is specified in the gpio_chip). + */ +static int of_gpio_simple_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, + u32 *flags) +{ + /* + * We're discouraging gpio_cells < 2, since that way you'll have to + * write your own xlate function (that will have to retrieve the GPIO + * number and the flags from a single gpio cell -- this is possible, + * but not recommended). + */ + if (WARN_ON(gc->of_gpio_n_cells < 2)) + return -EINVAL; + + if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells)) + return -EINVAL; + + if (gpiospec->args[0] >= gc->ngpio) + return -EINVAL; + + if (flags) + *flags = gpiospec->args[1]; + + return gc->base + gpiospec->args[0]; +} + +static int of_gpiochip_add(struct gpio_chip *chip) +{ + struct device_node *np; + int ret; + + np = dev_of_node(chip->dev); + if (!np) + return 0; + + if (!chip->ops->of_xlate) + chip->ops->of_xlate = of_gpio_simple_xlate; + + /* + * Separate check since the 'struct gpio_ops' is always the same for + * every 'struct gpio_chip' of the same instance (e.g. 'struct + * imx_gpio_chip'). + */ + if (chip->ops->of_xlate == of_gpio_simple_xlate) + chip->of_gpio_n_cells = 2; + + if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS) + return -EINVAL; + + ret = of_gpiochip_set_names(chip); + if (ret) + return ret; + + return of_gpiochip_scan_hogs(chip); +} +#else +static int of_gpiochip_add(struct gpio_chip *chip) +{ + return 0; +} +#endif + +#ifdef CONFIG_OFDEVICE +static const char *gpio_suffixes[] = { + "gpios", + "gpio", +}; + +static struct property *of_find_gpio_property(struct device_node *np, + const char *_con_id) +{ + struct property *pp = NULL; + char *con_id; + int i; + + for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { + if (_con_id) + con_id = basprintf("%s-%s", _con_id, gpio_suffixes[i]); + else + con_id = basprintf("%s", gpio_suffixes[i]); + + if (!con_id) + return ERR_PTR(-ENOMEM); + + pp = of_find_property(np, con_id, NULL); + free(con_id); + + if (pp) + return pp; + } + + return NULL; +} + +/* Linux compatibility helper: Get a GPIO descriptor from device tree */ +struct gpio_desc *dev_gpiod_get_index(struct device *dev, + struct device_node *np, + const char *con_id, int index, + enum gpiod_flags flags, + const char *label) +{ + struct gpio_desc *desc = NULL; + enum of_gpio_flags of_flags; + struct property *pp; + char *buf = NULL; + int gpio; + int ret; + + if (!np) + return ERR_PTR(-ENODEV); + + pp = of_find_gpio_property(np, con_id); + if (!pp) + return ERR_PTR(-ENOENT); + + gpio = of_get_named_gpio_flags(dev->device_node, pp->name, + index, &of_flags); + if (!gpio_is_valid(gpio)) + return ERR_PTR(gpio < 0 ? gpio : -EINVAL); + + desc = gpio_to_desc(gpio); + + if (of_flags & OF_GPIO_ACTIVE_LOW) + flags |= GPIOF_ACTIVE_LOW; + + buf = NULL; + + if (!label) { + if (con_id) + label = buf = basprintf("%s-%s", dev_name(dev), con_id); + else + label = dev_name(dev); + } + + ret = gpiodesc_request_one(desc, flags, label); + free(buf); + + return ret ? ERR_PTR(ret): desc; +} + +/** + * gpiod_count - return the number of GPIOs associated with a device / function + * or -ENOENT if no GPIO has been assigned to the requested function + * @dev: GPIO consumer, can be NULL for system-global GPIOs + * @_con_id: function within the GPIO consumer + */ +int gpiod_count(struct device *dev, const char *con_id) +{ + struct device_node *np = dev_of_node(dev); + struct property *pp; + + if (!np) + return -ENODEV; + + pp = of_find_gpio_property(np, con_id); + if (!pp) + return -ENOENT; + + return of_gpio_named_count(np, pp->name); +} +EXPORT_SYMBOL_GPL(gpiod_count); + +/** + * gpiod_get_array - obtain multiple GPIOs from a multi-index GPIO function + * @dev: GPIO consumer, can be NULL for system-global GPIOs + * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags + * + * This function acquires all the GPIOs defined under a given function. + * + * Return a struct gpio_descs containing an array of descriptors, -ENOENT if + * no GPIO has been assigned to the requested function, or another IS_ERR() + * code if an error occurred while trying to acquire the GPIOs. + */ +struct gpio_descs *__must_check gpiod_get_array(struct device *dev, + const char *con_id, + enum gpiod_flags flags) +{ + struct gpio_desc *desc; + struct gpio_descs *descs; + int count; + + count = gpiod_count(dev, con_id); + if (count < 0) + return ERR_PTR(count); + + descs = kzalloc(struct_size(descs, desc, count), GFP_KERNEL); + if (!descs) + return ERR_PTR(-ENOMEM); + + for (descs->ndescs = 0; descs->ndescs < count; descs->ndescs++) { + desc = dev_gpiod_get_index(dev, dev_of_node(dev), con_id, + descs->ndescs, flags, NULL); + if (IS_ERR(desc)) { + gpiod_put_array(descs); + return ERR_CAST(desc); + } + + descs->desc[descs->ndescs] = desc; + } + + return descs; +} +EXPORT_SYMBOL_GPL(gpiod_get_array); + +#endif + +static int gpiod_set_array_value_complex(bool raw, + unsigned int array_size, + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap) +{ + int i; + + BUG_ON(array_info != NULL); + + for (i = 0; i < array_size; i++) + gpiod_set_value(desc_array[i], test_bit(i, value_bitmap)); + + return 0; +} + +/** + * gpiod_set_array_value() - assign values to an array of GPIOs + * @array_size: number of elements in the descriptor array / value bitmap + * @desc_array: array of GPIO descriptors whose values will be assigned + * @array_info: information on applicability of fast bitmap processing path + * @value_bitmap: bitmap of values to assign + * + * Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status + * into account. NOTE: This function has no special handling for GPIOs + * in the same bank that could've been set atomically: GPIO sequencing + * is not guaranteed to always remain in the same order. + */ +int gpiod_set_array_value(unsigned int array_size, + struct gpio_desc **desc_array, + struct gpio_array *array_info, + unsigned long *value_bitmap) { - int base, i; + if (!desc_array) + return -EINVAL; + return gpiod_set_array_value_complex(false, array_size, + desc_array, array_info, + value_bitmap); +} +EXPORT_SYMBOL_GPL(gpiod_set_array_value); - base = gpiochip_find_base(chip->base, chip->ngpio); - if (base < 0) - return base; +int gpiochip_add(struct gpio_chip *chip) +{ + int i; - if (chip->base >= 0 && chip->base != base) - return -EBUSY; + if (chip->base >= 0) { + for (i = 0; i < chip->ngpio; i++) { + if (gpio_desc[chip->base + i].chip) + return -EBUSY; + } + } else { + chip->base = gpiochip_find_base(chip->ngpio); + if (chip->base < 0) + return -ENOSPC; + } - chip->base = base; + slice_init(&chip->slice, dev_name(chip->dev)); list_add_tail(&chip->list, &chip_list); for (i = chip->base; i < chip->base + chip->ngpio; i++) gpio_desc[i].chip = chip; - return of_gpiochip_scan_hogs(chip); + return of_gpiochip_add(chip); } void gpiochip_remove(struct gpio_chip *chip) @@ -460,61 +1043,93 @@ void gpiochip_remove(struct gpio_chip *chip) list_del(&chip->list); } -int gpio_get_num(struct device_d *dev, int gpio) +struct gpio_chip *gpio_get_chip_by_dev(struct device *dev) { struct gpio_chip *chip; - if (!dev) - return -ENODEV; - list_for_each_entry(chip, &chip_list, list) { if (chip->dev == dev) - return chip->base + gpio; + return chip; } - return -EPROBE_DEFER; + return NULL; +} + +int gpio_get_num(struct device *dev, int gpio) +{ + struct gpio_chip *chip; + + if (!dev) + return -ENODEV; + + chip = gpio_get_chip_by_dev(dev); + if (!chip) + return -EPROBE_DEFER; + + return chip->base + gpio; } struct gpio_chip *gpio_get_chip(int gpio) { - struct gpio_info *gi = gpio_to_desc(gpio); + struct gpio_desc *desc = gpio_to_desc(gpio); - return gi ? gi->chip : NULL; + return desc ? desc->chip : NULL; } #ifdef CONFIG_CMD_GPIO static int do_gpiolib(int argc, char *argv[]) { + struct gpio_chip *chip = NULL; int i; + if (argc > 2) + return COMMAND_ERROR_USAGE; + + if (argc > 1) { + struct device *dev; + + dev = find_device(argv[1]); + if (!dev) + return -ENODEV; + + chip = gpio_get_chip_by_dev(dev); + if (!chip) + return -EINVAL; + } + for (i = 0; i < ARCH_NR_GPIOS; i++) { - struct gpio_info *gi = &gpio_desc[i]; + struct gpio_desc *desc = &gpio_desc[i]; int val = -1, dir = -1; + int idx; + + if (!desc->chip) + continue; - if (!gi->chip) + if (chip && chip != desc->chip) continue; /* print chip information and header on first gpio */ - if (gi->chip->base == i) { + if (desc->chip->base == i) { printf("\nGPIOs %u-%u, chip %s:\n", - gi->chip->base, - gi->chip->base + gi->chip->ngpio - 1, - gi->chip->dev->name); - printf("%*cdir val requested label\n", 13, ' '); + desc->chip->base, + desc->chip->base + desc->chip->ngpio - 1, + dev_name(desc->chip->dev)); + printf(" %-3s %-3s %-9s %-20s %-20s\n", "dir", "val", "requested", "name", "label"); } - if (gi->chip->ops->get_direction) - dir = gi->chip->ops->get_direction(gi->chip, - i - gi->chip->base); - if (gi->chip->ops->get) - val = gi->chip->ops->get(gi->chip, - i - gi->chip->base); - - printf(" GPIO %*d: %*s %*s %*s %s\n", 4, i, - 3, (dir < 0) ? "unk" : ((dir == GPIOF_DIR_IN) ? "in" : "out"), - 3, (val < 0) ? "unk" : ((val == 0) ? "lo" : "hi"), - 12, gi->requested ? (gi->active_low ? "active low" : "true") : "false", - (gi->requested && gi->label) ? gi->label : ""); + idx = i - desc->chip->base; + + if (desc->chip->ops->get_direction) + dir = desc->chip->ops->get_direction(desc->chip, idx); + if (desc->chip->ops->get) + val = desc->chip->ops->get(desc->chip, idx); + + printf(" GPIO %4d: %-3s %-3s %-9s %-20s %-20s\n", chip ? idx : i, + (dir < 0) ? "unk" : ((dir == GPIOF_DIR_IN) ? "in" : "out"), + (val < 0) ? "unk" : ((val == 0) ? "lo" : "hi"), + desc->requested ? (desc->active_low ? "active low" : "true") : "false", + desc->name ? desc->name : "", + desc->label ? desc->label : ""); } return 0; @@ -523,6 +1138,7 @@ static int do_gpiolib(int argc, char *argv[]) BAREBOX_CMD_START(gpioinfo) .cmd = do_gpiolib, BAREBOX_CMD_DESC("list registered GPIOs") + BAREBOX_CMD_OPTS("[CONTROLLER]") BAREBOX_CMD_GROUP(CMD_GRP_INFO) BAREBOX_CMD_COMPLETE(empty_complete) BAREBOX_CMD_END |