Commit 9ca26df1 authored by Alexandru Tachici's avatar Alexandru Tachici Committed by Guenter Roeck
Browse files

hwmon: (ltc2992) Add support for GPIOs.



LTC2992 has 4 open-drain GPIOS. This patch exports to user
space the 4 GPIOs using the GPIO driver Linux API.

Signed-off-by: default avatarAlexandru Tachici <alexandru.tachici@analog.com>
Signed-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
parent b0bd407e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -874,6 +874,7 @@ config SENSORS_LTC2990
config SENSORS_LTC2992
	tristate "Linear Technology LTC2992"
	depends on I2C
	depends on GPIOLIB
	help
	  If you say yes here you get support for Linear Technology LTC2992
	  I2C System Monitor. The LTC2992 measures current, voltage, and
+159 −1
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
@@ -54,6 +55,9 @@
#define LTC2992_G4_MAX_THRESH		0x74
#define LTC2992_G4_MIN_THRESH		0x76
#define LTC2992_FAULT3			0x92
#define LTC2992_GPIO_STATUS		0x95
#define LTC2992_GPIO_IO_CTRL		0x96
#define LTC2992_GPIO_CTRL		0x97

#define LTC2992_POWER(x)		(LTC2992_POWER1 + ((x) * 0x32))
#define LTC2992_POWER_MAX(x)		(LTC2992_POWER1_MAX + ((x) * 0x32))
@@ -96,8 +100,18 @@
#define LTC2992_VADC_UV_LSB		25000
#define LTC2992_VADC_GPIO_UV_LSB	500

#define LTC2992_GPIO_NR		4
#define LTC2992_GPIO1_BIT	7
#define LTC2992_GPIO2_BIT	6
#define LTC2992_GPIO3_BIT	0
#define LTC2992_GPIO4_BIT	6
#define LTC2992_GPIO_BIT(x)	(LTC2992_GPIO_NR - (x) - 1)

struct ltc2992_state {
	struct i2c_client		*client;
	struct gpio_chip		gc;
	struct mutex			gpio_mutex; /* lock for gpio access */
	const char			*gpio_names[LTC2992_GPIO_NR];
	struct regmap			*regmap;
	u32				r_sense_uohm[2];
};
@@ -111,6 +125,8 @@ struct ltc2992_gpio_regs {
	u8	alarm;
	u8	min_alarm_msk;
	u8	max_alarm_msk;
	u8	ctrl;
	u8	ctrl_bit;
};

static const struct ltc2992_gpio_regs ltc2992_gpio_addr_map[] = {
@@ -123,6 +139,8 @@ static const struct ltc2992_gpio_regs ltc2992_gpio_addr_map[] = {
		.alarm = LTC2992_FAULT1,
		.min_alarm_msk = LTC2992_GPIO1_FAULT_MSK(0),
		.max_alarm_msk = LTC2992_GPIO1_FAULT_MSK(1),
		.ctrl = LTC2992_GPIO_IO_CTRL,
		.ctrl_bit = LTC2992_GPIO1_BIT,
	},
	{
		.data = LTC2992_G2,
@@ -133,6 +151,8 @@ static const struct ltc2992_gpio_regs ltc2992_gpio_addr_map[] = {
		.alarm = LTC2992_FAULT2,
		.min_alarm_msk = LTC2992_GPIO2_FAULT_MSK(0),
		.max_alarm_msk = LTC2992_GPIO2_FAULT_MSK(1),
		.ctrl = LTC2992_GPIO_IO_CTRL,
		.ctrl_bit = LTC2992_GPIO2_BIT,
	},
	{
		.data = LTC2992_G3,
@@ -143,6 +163,8 @@ static const struct ltc2992_gpio_regs ltc2992_gpio_addr_map[] = {
		.alarm = LTC2992_FAULT3,
		.min_alarm_msk = LTC2992_GPIO3_FAULT_MSK(0),
		.max_alarm_msk = LTC2992_GPIO3_FAULT_MSK(1),
		.ctrl = LTC2992_GPIO_IO_CTRL,
		.ctrl_bit = LTC2992_GPIO3_BIT,
	},
	{
		.data = LTC2992_G4,
@@ -153,14 +175,20 @@ static const struct ltc2992_gpio_regs ltc2992_gpio_addr_map[] = {
		.alarm = LTC2992_FAULT3,
		.min_alarm_msk = LTC2992_GPIO4_FAULT_MSK(0),
		.max_alarm_msk = LTC2992_GPIO4_FAULT_MSK(1),
		.ctrl = LTC2992_GPIO_CTRL,
		.ctrl_bit = LTC2992_GPIO4_BIT,
	},
};

static const char *ltc2992_gpio_names[LTC2992_GPIO_NR] = {
	"GPIO1", "GPIO2", "GPIO3", "GPIO4",
};

static int ltc2992_read_reg(struct ltc2992_state *st, u8 addr, const u8 reg_len)
{
	u8 regvals[4];
	int ret;
	int val;
	int ret;
	int i;

	ret = regmap_bulk_read(st->regmap, addr, regvals, reg_len);
@@ -185,6 +213,132 @@ static int ltc2992_write_reg(struct ltc2992_state *st, u8 addr, const u8 reg_len
	return regmap_bulk_write(st->regmap, addr, regvals, reg_len);
}

static int ltc2992_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
	struct ltc2992_state *st = gpiochip_get_data(chip);
	unsigned long gpio_status;
	int reg;

	mutex_lock(&st->gpio_mutex);
	reg = ltc2992_read_reg(st, LTC2992_GPIO_STATUS, 1);
	mutex_unlock(&st->gpio_mutex);

	if (reg < 0)
		return reg;

	gpio_status = reg;

	return !test_bit(LTC2992_GPIO_BIT(offset), &gpio_status);
}

static int ltc2992_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
				     unsigned long *bits)
{
	struct ltc2992_state *st = gpiochip_get_data(chip);
	unsigned long gpio_status;
	unsigned int gpio_nr;
	int reg;

	mutex_lock(&st->gpio_mutex);
	reg = ltc2992_read_reg(st, LTC2992_GPIO_STATUS, 1);
	mutex_unlock(&st->gpio_mutex);

	if (reg < 0)
		return reg;

	gpio_status = reg;

	gpio_nr = 0;
	for_each_set_bit_from(gpio_nr, mask, LTC2992_GPIO_NR) {
		if (test_bit(LTC2992_GPIO_BIT(gpio_nr), &gpio_status))
			set_bit(gpio_nr, bits);
	}

	return 0;
}

static void ltc2992_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
{
	struct ltc2992_state *st = gpiochip_get_data(chip);
	unsigned long gpio_ctrl;
	int reg;

	mutex_lock(&st->gpio_mutex);
	reg = ltc2992_read_reg(st, ltc2992_gpio_addr_map[offset].ctrl, 1);
	if (reg < 0) {
		mutex_unlock(&st->gpio_mutex);
		return;
	}

	gpio_ctrl = reg;
	assign_bit(ltc2992_gpio_addr_map[offset].ctrl_bit, &gpio_ctrl, value);

	ltc2992_write_reg(st, ltc2992_gpio_addr_map[offset].ctrl, 1, gpio_ctrl);
	mutex_unlock(&st->gpio_mutex);
}

static void ltc2992_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask,
				      unsigned long *bits)
{
	struct ltc2992_state *st = gpiochip_get_data(chip);
	unsigned long gpio_ctrl_io = 0;
	unsigned long gpio_ctrl = 0;
	unsigned int gpio_nr;

	for_each_set_bit(gpio_nr, mask, LTC2992_GPIO_NR) {
		if (gpio_nr < 3)
			assign_bit(ltc2992_gpio_addr_map[gpio_nr].ctrl_bit, &gpio_ctrl_io, true);

		if (gpio_nr == 3)
			assign_bit(ltc2992_gpio_addr_map[gpio_nr].ctrl_bit, &gpio_ctrl, true);
	}

	mutex_lock(&st->gpio_mutex);
	ltc2992_write_reg(st, LTC2992_GPIO_IO_CTRL, 1, gpio_ctrl_io);
	ltc2992_write_reg(st, LTC2992_GPIO_CTRL, 1, gpio_ctrl);
	mutex_unlock(&st->gpio_mutex);
}

static int ltc2992_config_gpio(struct ltc2992_state *st)
{
	const char *name = dev_name(&st->client->dev);
	char *gpio_name;
	int ret;
	int i;

	ret = ltc2992_write_reg(st, LTC2992_GPIO_IO_CTRL, 1, 0);
	if (ret < 0)
		return ret;

	mutex_init(&st->gpio_mutex);

	for (i = 0; i < ARRAY_SIZE(st->gpio_names); i++) {
		gpio_name = devm_kasprintf(&st->client->dev, GFP_KERNEL, "ltc2992-%x-%s",
					   st->client->addr, ltc2992_gpio_names[i]);
		if (!gpio_name)
			return -ENOMEM;

		st->gpio_names[i] = gpio_name;
	}

	st->gc.label = name;
	st->gc.parent = &st->client->dev;
	st->gc.owner = THIS_MODULE;
	st->gc.base = -1;
	st->gc.names = st->gpio_names;
	st->gc.ngpio = ARRAY_SIZE(st->gpio_names);
	st->gc.get = ltc2992_gpio_get;
	st->gc.get_multiple = ltc2992_gpio_get_multiple;
	st->gc.set = ltc2992_gpio_set;
	st->gc.set_multiple = ltc2992_gpio_set_multiple;

	ret = devm_gpiochip_add_data(&st->client->dev, &st->gc, st);
	if (ret)
		dev_err(&st->client->dev, "GPIO registering failed (%d)\n", ret);

	return ret;
}

static umode_t ltc2992_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr,
				  int channel)
{
@@ -779,6 +933,10 @@ static int ltc2992_i2c_probe(struct i2c_client *client, const struct i2c_device_
	if (ret < 0)
		return ret;

	ret = ltc2992_config_gpio(st);
	if (ret < 0)
		return ret;

	hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, client->name, st,
							 &ltc2992_chip_info, NULL);