Commit ea7f2dfd authored by Bartosz Golaszewski's avatar Bartosz Golaszewski
Browse files

Merge tag 'ib-mfd-gpio-i2c-watchdog-v6.13' of...

Merge tag 'ib-mfd-gpio-i2c-watchdog-v6.13' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/lee/mfd into gpio/for-next

Immutable branch between MFD, GPIO, I2C and Watchdog due for the v6.13 merge window
parents b7adfb60 590bcce8
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -5760,6 +5760,15 @@ F: fs/configfs/
F:	include/linux/configfs.h
F:	samples/configfs/
CONGATEC BOARD CONTROLLER MFD DRIVER
M:	Thomas Richard <thomas.richard@bootlin.com>
S:	Maintained
F:	drivers/gpio/gpio-cgbc.c
F:	drivers/i2c/busses/i2c-cgbc.c
F:	drivers/mfd/cgbc-core.c
F:	drivers/watchdog/cgbc_wdt.c
F:	include/linux/mfd/cgbc.h
CONSOLE SUBSYSTEM
M:	Greg Kroah-Hartman <gregkh@linuxfoundation.org>
S:	Supported
+10 −0
Original line number Diff line number Diff line
@@ -1286,6 +1286,16 @@ config GPIO_BD9571MWV
	  This driver can also be built as a module. If so, the module
	  will be called gpio-bd9571mwv.

config GPIO_CGBC
	tristate "Congatec Board Controller GPIO support"
	depends on MFD_CGBC
	help
	  Select this option to enable GPIO support for the Congatec Board
	  Controller.

	  This driver can also be built as a module. If so, the module will be
	  called gpio-cgbc.

config GPIO_CROS_EC
	tristate "ChromeOS EC GPIO support"
	depends on CROS_EC
+1 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
obj-$(CONFIG_GPIO_BRCMSTB)		+= gpio-brcmstb.o
obj-$(CONFIG_GPIO_BT8XX)		+= gpio-bt8xx.o
obj-$(CONFIG_GPIO_CADENCE)		+= gpio-cadence.o
obj-$(CONFIG_GPIO_CGBC)			+= gpio-cgbc.o
obj-$(CONFIG_GPIO_CLPS711X)		+= gpio-clps711x.o
obj-$(CONFIG_GPIO_SNPS_CREG)		+= gpio-creg-snps.o
obj-$(CONFIG_GPIO_CROS_EC)		+= gpio-cros-ec.o
+196 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Congatec Board Controller GPIO driver
 *
 * Copyright (C) 2024 Bootlin
 * Author: Thomas Richard <thomas.richard@bootlin.com>
 */

#include <linux/gpio/driver.h>
#include <linux/mfd/cgbc.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>

#define CGBC_GPIO_NGPIO	14

#define CGBC_GPIO_CMD_GET	0x64
#define CGBC_GPIO_CMD_SET	0x65
#define CGBC_GPIO_CMD_DIR_GET	0x66
#define CGBC_GPIO_CMD_DIR_SET	0x67

struct cgbc_gpio_data {
	struct gpio_chip	chip;
	struct cgbc_device_data	*cgbc;
	struct mutex lock;
};

static int cgbc_gpio_cmd(struct cgbc_device_data *cgbc,
			 u8 cmd0, u8 cmd1, u8 cmd2, u8 *value)
{
	u8 cmd[3] = {cmd0, cmd1, cmd2};

	return cgbc_command(cgbc, cmd, sizeof(cmd), value, 1, NULL);
}

static int cgbc_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
	struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);
	struct cgbc_device_data *cgbc = gpio->cgbc;
	int ret;
	u8 val;

	scoped_guard(mutex, &gpio->lock)
		ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_GET, (offset > 7) ? 1 : 0, 0, &val);

	offset %= 8;

	if (ret)
		return ret;
	else
		return (int)(val & (u8)BIT(offset));
}

static void __cgbc_gpio_set(struct gpio_chip *chip,
			    unsigned int offset, int value)
{
	struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);
	struct cgbc_device_data *cgbc = gpio->cgbc;
	u8 val;
	int ret;

	ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_GET, (offset > 7) ? 1 : 0, 0, &val);
	if (ret)
		return;

	if (value)
		val |= BIT(offset % 8);
	else
		val &= ~(BIT(offset % 8));

	cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_SET, (offset > 7) ? 1 : 0, val, &val);
}

static void cgbc_gpio_set(struct gpio_chip *chip,
			  unsigned int offset, int value)
{
	struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);

	scoped_guard(mutex, &gpio->lock)
		__cgbc_gpio_set(chip, offset, value);
}

static int cgbc_gpio_direction_set(struct gpio_chip *chip,
				   unsigned int offset, int direction)
{
	struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);
	struct cgbc_device_data *cgbc = gpio->cgbc;
	int ret;
	u8 val;

	ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_GET, (offset > 7) ? 1 : 0, 0, &val);
	if (ret)
		goto end;

	if (direction == GPIO_LINE_DIRECTION_IN)
		val &= ~(BIT(offset % 8));
	else
		val |= BIT(offset % 8);

	ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_SET, (offset > 7) ? 1 : 0, val, &val);

end:
	return ret;
}

static int cgbc_gpio_direction_input(struct gpio_chip *chip,
				     unsigned int offset)
{
	struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);

	guard(mutex)(&gpio->lock);
	return cgbc_gpio_direction_set(chip, offset, GPIO_LINE_DIRECTION_IN);
}

static int cgbc_gpio_direction_output(struct gpio_chip *chip,
				      unsigned int offset, int value)
{
	struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);

	guard(mutex)(&gpio->lock);

	__cgbc_gpio_set(chip, offset, value);
	return cgbc_gpio_direction_set(chip, offset, GPIO_LINE_DIRECTION_OUT);
}

static int cgbc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
{
	struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);
	struct cgbc_device_data *cgbc = gpio->cgbc;
	int ret;
	u8 val;

	scoped_guard(mutex, &gpio->lock)
		ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_GET, (offset > 7) ? 1 : 0, 0, &val);

	if (ret)
		return ret;

	if (val & BIT(offset % 8))
		return GPIO_LINE_DIRECTION_OUT;
	else
		return GPIO_LINE_DIRECTION_IN;
}

static int cgbc_gpio_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct cgbc_device_data *cgbc = dev_get_drvdata(dev->parent);
	struct cgbc_gpio_data *gpio;
	struct gpio_chip *chip;
	int ret;

	gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL);
	if (!gpio)
		return -ENOMEM;

	gpio->cgbc = cgbc;

	platform_set_drvdata(pdev, gpio);

	chip = &gpio->chip;
	chip->label = dev_name(&pdev->dev);
	chip->owner = THIS_MODULE;
	chip->parent = dev;
	chip->base = -1;
	chip->direction_input = cgbc_gpio_direction_input;
	chip->direction_output = cgbc_gpio_direction_output;
	chip->get_direction = cgbc_gpio_get_direction;
	chip->get = cgbc_gpio_get;
	chip->set = cgbc_gpio_set;
	chip->ngpio = CGBC_GPIO_NGPIO;

	ret = devm_mutex_init(dev, &gpio->lock);
	if (ret)
		return ret;

	ret = devm_gpiochip_add_data(dev, chip, gpio);
	if (ret)
		return dev_err_probe(dev, ret, "Could not register GPIO chip\n");

	return 0;
}

static struct platform_driver cgbc_gpio_driver = {
	.driver = {
		.name = "cgbc-gpio",
	},
	.probe	= cgbc_gpio_probe,
};

module_platform_driver(cgbc_gpio_driver);

MODULE_DESCRIPTION("Congatec Board Controller GPIO Driver");
MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:cgbc-gpio");
+10 −0
Original line number Diff line number Diff line
@@ -535,6 +535,16 @@ config I2C_CBUS_GPIO
	  This driver can also be built as a module.  If so, the module
	  will be called i2c-cbus-gpio.

config I2C_CGBC
	tristate "Congatec I2C Controller"
	depends on MFD_CGBC
	help
	  This driver supports the 2 I2C interfaces on the Congatec Board
	  Controller.

	  This driver can also be built as a module. If so, the module will
	  be called i2c-cgbc.ko.

config I2C_CPM
	tristate "Freescale CPM1 or CPM2 (MPC8xx/826x)"
	depends on CPM1 || CPM2
Loading