[PATCH net-next v2 12/14] gpio: tc956x: add TC956x/QPS615 support
Bartosz Golaszewski
brgl at kernel.org
Mon Jun 8 04:52:45 PDT 2026
On Fri, 5 Jun 2026 03:00:19 +0200, Alex Elder <elder at riscstar.com> said:
> Toshiba TC956x is an Ethernet-AVB/TSN bridge and is essentially
> a small and highly-specialized SoC. TC956x includes a GPIO block that
> can be accessed, alongside several other peripherals, via two PCIe
> endpoint functions. The PCIe function driver creates an auxiliary
> device for the GPIO block, and that device gets bound to this auxiliary
> device driver.
>
> This driver is implemented using the generic regmap-based GPIO driver.
>
> Co-developed-by: Daniel Thompson <daniel at riscstar.com>
> Signed-off-by: Daniel Thompson <daniel at riscstar.com>
> Signed-off-by: Alex Elder <elder at riscstar.com>
> ---
> MAINTAINERS | 1 +
> drivers/gpio/Kconfig | 12 ++++
> drivers/gpio/Makefile | 1 +
> drivers/gpio/gpio-tc956x.c | 130 +++++++++++++++++++++++++++++++++++++
> 4 files changed, 144 insertions(+)
> create mode 100644 drivers/gpio/gpio-tc956x.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 0924f7ec43cb0..0439607d1155f 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -27057,6 +27057,7 @@ M: Alex Elder <elder at kernel.org>
> M: Daniel Thompson <danielt at kernel.org>
> S: Maintained
> F: Documentation/devicetree/bindings/net/toshiba,tc956x-dwmac.yaml
> +F: drivers/gpio/gpio-tc956x.c
> F: drivers/misc/tc956x_pci.c
>
> TOSHIBA WMI HOTKEYS DRIVER
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 020e51e30317a..36631ca722fa3 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -743,6 +743,18 @@ config GPIO_TB10X
> select GPIO_GENERIC
> select GENERIC_IRQ_CHIP
>
> +config GPIO_TC956X
> + tristate "Toshiba TC956X GPIO support"
> + depends on TOSHIBA_TC956X_PCI
> + select GPIO_REGMAP
> + default m
> + help
> + This enables support for the GPIO controller embedded in the Toshiba
> + TC956X (and Qualcomm QPS615). This device connects to the host
> + via PCIe port, which is the upstream port on an internal PCIe
> + switch. On some platforms, a few of the GPIO lines are used to
> + manage external resets.
> +
> config GPIO_TEGRA
> tristate "NVIDIA Tegra GPIO support"
> default ARCH_TEGRA
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index b267598b517de..c3584e7cba9b4 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -178,6 +178,7 @@ obj-$(CONFIG_GPIO_SYSCON) += gpio-syscon.o
> obj-$(CONFIG_GPIO_TANGIER) += gpio-tangier.o
> obj-$(CONFIG_GPIO_TB10X) += gpio-tb10x.o
> obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
> +obj-$(CONFIG_GPIO_TC956X) += gpio-tc956x.o
> obj-$(CONFIG_GPIO_TEGRA186) += gpio-tegra186.o
> obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o
> obj-$(CONFIG_GPIO_THUNDERX) += gpio-thunderx.o
> diff --git a/drivers/gpio/gpio-tc956x.c b/drivers/gpio/gpio-tc956x.c
> new file mode 100644
> index 0000000000000..0dc6b1028d970
> --- /dev/null
> +++ b/drivers/gpio/gpio-tc956x.c
> @@ -0,0 +1,130 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/*
> + * Copyright (C) 2026 by RISCstar Solutions Corporation. All rights reserved.
> + */
> +
> +/*
> + * The Toshiba TC956X implements a PCIe Gen 3 switch that connects an
> + * upstream x4 port to two downstream PCIe x2 ports. It incorporates
> + * an internal endpoint on a internal PCIe port that implements two
> + * Synopsys XGMAC Ethernet interfaces.
> + *
> + * 35 GPIOs are also implemented by an embedded GPIO controller. Three
> + * registers control the first 32 GPIOs (other than 20 and 21, which are
> + * reserved). Three other registers control GPIOs 32 through 36. GPIOs
> + * 22-24, 27-28, 31, and 34 are treated as "input only".
> + *
> + * There is a TC956X PCI power controller driver that accesses the
> + * direction and output value registers for GPIOs 2 and 3. These
> + * GPIOs control the reset signal for the two downstream PCIe ports.
> + * Their values will never change during operation of this driver, and
> + * this driver reserves these two GPIOS.
> + */
> +
> +#include <linux/auxiliary_bus.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/gpio/regmap.h>
> +
> +#define DRIVER_NAME "tc956x-gpio"
> +
> +#define TC956X_GPIO_COUNT 37 /* Number of GPIOs (20-21 reserved) */
> +
> +/* The GPIO offsets are relative to 0x1200 in TC956X SFR space. */
> +#define GPIO_IN0_OFFSET 0x00 /* Input value (0-31) */
> +#define GPIO_EN0_OFFSET 0x08 /* 0: out; 1: in (0-31) */
> +#define GPIO_OUT0_OFFSET 0x10 /* Output value (0-31) */
> +
> +/*
> + * There are two sets of registers, each representing (up to) 32 GPIOs with a
> + * stride of 4 bytes (IN1 is 4 bytes past IN0, EN1 is 4 bytes past EN0, etc.).
> + */
> +#define GPIO_PER_REG 32
> +#define GPIO_REG_STRIDE 4
> +
> +static int tc956x_gpio_init_valid_mask(struct gpio_chip *gc,
> + unsigned long *valid_mask,
> + unsigned int ngpios)
> +{
> + /*
> + * GPIOs 2 and 3 are used by the PCI power control driver, and
> + * we don't allow them to be used. GPIOs 20 and 21 are reserved
> + * (and not usable).
> + */
> + bitmap_fill(valid_mask, ngpios);
> + bitmap_clear(valid_mask, 2, 2);
> + bitmap_clear(valid_mask, 20, 2);
> +
> + return 0;
> +}
> +
> +static int tc956x_gpio_probe(struct auxiliary_device *adev,
> + const struct auxiliary_device_id *id)
> +{
> + DECLARE_BITMAP(zeroes, TC956X_GPIO_COUNT);
> + DECLARE_BITMAP(fixed, TC956X_GPIO_COUNT);
> + struct gpio_regmap_config config = { };
> + struct gpio_regmap *gpio_regmap;
> + struct device *dev = &adev->dev;
> +
> + /* We need the regmap pointer, stored in our platform data */
> + if (!dev->platform_data)
> + return -EINVAL;
Please use the standardized accessor: dev_get_platdata().
> +
> + /*
> + * Only some of our GPIOs are fixed direction:
> + * 22, 23, 24, 27, 28, 31, and 34 (all input-only)
> + * Set up the fixed bitmap to indicate which are fixed.
> + */
> + bitmap_zero(fixed, TC956X_GPIO_COUNT);
> + bitmap_set(fixed, 22, 3);
> + bitmap_set(fixed, 27, 2);
> + set_bit(31, fixed);
> + set_bit(34, fixed);
> +
> + /* All fixed GPIOs are input; the zeroes bitmap indicates that. */
> + bitmap_zero(zeroes, TC956X_GPIO_COUNT);
> +
> + config.parent = dev;
> + config.regmap = dev->platform_data;
> + config.label = DRIVER_NAME;
> + config.ngpio = TC956X_GPIO_COUNT;
> + config.reg_dat_base = GPIO_REGMAP_ADDR(GPIO_IN0_OFFSET);
> + config.reg_set_base = GPIO_REGMAP_ADDR(GPIO_OUT0_OFFSET);
> + config.reg_dir_in_base = GPIO_REGMAP_ADDR(GPIO_EN0_OFFSET);
> + config.reg_stride = GPIO_REG_STRIDE;
> + config.ngpio_per_reg = GPIO_PER_REG;
> + config.init_valid_mask = tc956x_gpio_init_valid_mask;
> + config.fixed_direction_mask = fixed;
> + config.fixed_direction_output = zeroes;
May I suggest using a compound literal here like:
config = (struct gpio_regmap_config){
...
};
?
> +
> + gpio_regmap = devm_gpio_regmap_register(dev, &config);
> + if (IS_ERR(gpio_regmap))
> + return PTR_ERR(gpio_regmap);
> +
> + return 0;
You can do:
return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &config));
and save a few lines.
> +}
> +
> +static const struct auxiliary_device_id tc956x_gpio_ids[] = {
> + { .name = "tc956x_pci.tc9564-gpio", },
> + { }
> +};
> +MODULE_DEVICE_TABLE(auxiliary, tc956x_gpio_ids);
> +
> +static struct auxiliary_driver tc956x_gpio_driver = {
> + .name = DRIVER_NAME,
> + .probe = tc956x_gpio_probe,
> + .id_table = tc956x_gpio_ids,
> + .driver = {
> + .name = DRIVER_NAME,
> + .owner = THIS_MODULE,
> + .probe_type = PROBE_PREFER_ASYNCHRONOUS,
> + },
> +};
> +module_auxiliary_driver(tc956x_gpio_driver);
> +
> +MODULE_DESCRIPTION("Toshiba TC956X PCIe GPIO Driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("auxiliary:" DRIVER_NAME);
> --
> 2.51.0
>
>
Bart
More information about the linux-arm-kernel
mailing list