From patchwork Thu Oct 29 09:49:09 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Alexandru Tachici X-Patchwork-Id: 310762 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3C2D2C2D0A3 for ; Thu, 29 Oct 2020 09:46:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D99A22076E for ; Thu, 29 Oct 2020 09:46:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726791AbgJ2JqF (ORCPT ); Thu, 29 Oct 2020 05:46:05 -0400 Received: from mx0a-00128a01.pphosted.com ([148.163.135.77]:61738 "EHLO mx0a-00128a01.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726564AbgJ2Jp7 (ORCPT ); Thu, 29 Oct 2020 05:45:59 -0400 Received: from pps.filterd (m0167088.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id 09T9jjoS005077; Thu, 29 Oct 2020 05:45:45 -0400 Received: from nwd2mta3.analog.com ([137.71.173.56]) by mx0a-00128a01.pphosted.com with ESMTP id 34ce462q07-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 29 Oct 2020 05:45:45 -0400 Received: from SCSQMBX11.ad.analog.com (SCSQMBX11.ad.analog.com [10.77.17.10]) by nwd2mta3.analog.com (8.14.7/8.14.7) with ESMTP id 09T9jhFo042396 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=FAIL); Thu, 29 Oct 2020 05:45:43 -0400 Received: from SCSQCASHYB6.ad.analog.com (10.77.17.132) by SCSQMBX11.ad.analog.com (10.77.17.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1779.2; Thu, 29 Oct 2020 02:45:42 -0700 Received: from SCSQMBX10.ad.analog.com (10.77.17.5) by SCSQCASHYB6.ad.analog.com (10.77.17.132) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1779.2; Thu, 29 Oct 2020 02:45:09 -0700 Received: from zeus.spd.analog.com (10.66.68.11) by SCSQMBX10.ad.analog.com (10.77.17.5) with Microsoft SMTP Server id 15.1.1779.2 via Frontend Transport; Thu, 29 Oct 2020 02:45:41 -0700 Received: from localhost.localdomain ([10.48.65.12]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 09T9jcAK008861; Thu, 29 Oct 2020 05:45:39 -0400 From: To: , , CC: , , Alexandru Tachici Subject: [PATCH 1/3] hwmon: ltc2992: Add support Date: Thu, 29 Oct 2020 11:49:09 +0200 Message-ID: <20201029094911.79173-2-alexandru.tachici@analog.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20201029094911.79173-1-alexandru.tachici@analog.com> References: <20201029094911.79173-1-alexandru.tachici@analog.com> MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.312, 18.0.737 definitions=2020-10-29_03:2020-10-29,2020-10-29 signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 suspectscore=0 phishscore=0 spamscore=0 malwarescore=0 impostorscore=0 adultscore=0 mlxlogscore=999 bulkscore=0 priorityscore=1501 lowpriorityscore=0 clxscore=1015 mlxscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2010290070 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org From: Alexandru Tachici LTC2992 is a rail-to-rail system monitor that measures current, voltage, and power of two supplies. Two ADCs simultaneously measure each supply’s current. A third ADC monitors the input voltages and four auxiliary external voltages. Signed-off-by: Alexandru Tachici --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/ltc2992.rst | 51 +++ drivers/hwmon/Kconfig | 11 + drivers/hwmon/Makefile | 1 + drivers/hwmon/ltc2992.c | 735 ++++++++++++++++++++++++++++++++ 5 files changed, 799 insertions(+) create mode 100644 Documentation/hwmon/ltc2992.rst create mode 100644 drivers/hwmon/ltc2992.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index e6b91ab12978..f759d70ae9fd 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -104,6 +104,7 @@ Hardware Monitoring Kernel Drivers ltc2947 ltc2978 ltc2990 + ltc2992 ltc3815 ltc4151 ltc4215 diff --git a/Documentation/hwmon/ltc2992.rst b/Documentation/hwmon/ltc2992.rst new file mode 100644 index 000000000000..1dd48ef9f655 --- /dev/null +++ b/Documentation/hwmon/ltc2992.rst @@ -0,0 +1,51 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver ltc2992 +===================== + +Supported chips: + * Linear Technology LTC2992 + Prefix: 'ltc2992' + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ltc2992.pdf + +Author: Alexandru Tachici + + +Description +----------- + +This driver supports hardware monitoring for Linear Technology LTC2992 power monitor. + +LTC2992 is is a rail-to-rail system monitor that measures current, +voltage, and power of two supplies. + +Two ADCs simultaneously measure each supply’s current. A third ADC monitors +the input voltages and four auxiliary external voltages. + + +Sysfs entries +------------- + +The following attributes are supported. Limits are read-write, +all other attributes are read-only. + +inX_input Measured voltage. +inX_min Minimum voltage. +inX_max Maximum voltage. +inX_min_alarm Voltage low alarm. +inX_max_alarm Voltage high alarm. +inX_alarm An overvoltage or undervoltage occured. Cleared on read. + +currX_input Measured current. +currX_min Minimum current. +currX_max Maximum current. +currX_min_alarm Current low alarm. +currX_max_alarm Current high alarm. +currX_alarm An overvoltage or undervoltage occured. Cleared on read. + +powerX_input Measured power. +powerX_min Minimum power. +powerX_max Maximum power. +powerX_min_alarm Power low alarm. +powerX_max_alarm Power high alarm. +powerX_alarm An overpower or underpower occured. Cleared on read. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index a850e4f0e0bd..bf9e387270d6 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -858,6 +858,17 @@ config SENSORS_LTC2990 This driver can also be built as a module. If so, the module will be called ltc2990. +config SENSORS_LTC2992 + tristate "Linear Technology LTC2992" + depends on I2C + help + If you say yes here you get support for Linear Technology LTC2992 + I2C System Monitor. The LTC2992 measures current, voltage, and + power of two supplies. + + This driver can also be built as a module. If so, the module will + be called ltc2992. + config SENSORS_LTC4151 tristate "Linear Technology LTC4151" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 9db2903b61e5..d6172c4807c4 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -118,6 +118,7 @@ obj-$(CONFIG_SENSORS_LTC2947) += ltc2947-core.o obj-$(CONFIG_SENSORS_LTC2947_I2C) += ltc2947-i2c.o obj-$(CONFIG_SENSORS_LTC2947_SPI) += ltc2947-spi.o obj-$(CONFIG_SENSORS_LTC2990) += ltc2990.o +obj-$(CONFIG_SENSORS_LTC2992) += ltc2992.o obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4222) += ltc4222.o diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c new file mode 100644 index 000000000000..940d92b4a1d0 --- /dev/null +++ b/drivers/hwmon/ltc2992.c @@ -0,0 +1,735 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* + * LTC2992 - Dual Wide Range Power Monitor + * + * Copyright 2020 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LTC2992_CTRLB 0x01 +#define LTC2992_FAULT1 0x03 +#define LTC2992_POWER1 0x05 +#define LTC2992_POWER1_MAX 0x08 +#define LTC2992_POWER1_MIN 0x0B +#define LTC2992_POWER1_MAX_THRESH 0x0E +#define LTC2992_POWER1_MIN_THRESH 0x11 +#define LTC2992_DSENSE1 0x14 +#define LTC2992_DSENSE1_MAX 0x16 +#define LTC2992_DSENSE1_MIN 0x18 +#define LTC2992_DSENSE1_MAX_THRESH 0x1A +#define LTC2992_DSENSE1_MIN_THRESH 0x1C +#define LTC2992_SENSE1 0x1E +#define LTC2992_SENSE1_MAX 0x20 +#define LTC2992_SENSE1_MIN 0x22 +#define LTC2992_SENSE1_MAX_THRESH 0x24 +#define LTC2992_SENSE1_MIN_THRESH 0x26 +#define LTC2992_G1 0x28 +#define LTC2992_G1_MAX 0x2A +#define LTC2992_G1_MIN 0x2C +#define LTC2992_G1_MAX_THRESH 0x2E +#define LTC2992_G1_MIN_THRESH 0x30 +#define LTC2992_FAULT2 0x35 +#define LTC2992_G2 0x5A +#define LTC2992_G2_MAX 0x5C +#define LTC2992_G2_MIN 0x5E +#define LTC2992_G2_MAX_THRESH 0x60 +#define LTC2992_G2_MIN_THRESH 0x62 +#define LTC2992_G3 0x64 +#define LTC2992_G3_MAX 0x66 +#define LTC2992_G3_MIN 0x68 +#define LTC2992_G3_MAX_THRESH 0x6A +#define LTC2992_G3_MIN_THRESH 0x6C +#define LTC2992_G4 0x6E +#define LTC2992_G4_MAX 0x70 +#define LTC2992_G4_MIN 0x72 +#define LTC2992_G4_MAX_THRESH 0x74 +#define LTC2992_G4_MIN_THRESH 0x76 +#define LTC2992_FAULT3 0x92 + +#define LTC2992_POWER(x) (LTC2992_POWER1 + ((x) * 0x32)) +#define LTC2992_POWER_MAX(x) (LTC2992_POWER1_MAX + ((x) * 0x32)) +#define LTC2992_POWER_MIN(x) (LTC2992_POWER1_MIN + ((x) * 0x32)) +#define LTC2992_POWER_MAX_THRESH(x) (LTC2992_POWER1_MAX_THRESH + ((x) * 0x32)) +#define LTC2992_POWER_MIN_THRESH(x) (LTC2992_POWER1_MIN_THRESH + ((x) * 0x32)) +#define LTC2992_DSENSE(x) (LTC2992_DSENSE1 + ((x) * 0x32)) +#define LTC2992_DSENSE_MAX(x) (LTC2992_DSENSE1_MAX + ((x) * 0x32)) +#define LTC2992_DSENSE_MIN(x) (LTC2992_DSENSE1_MIN + ((x) * 0x32)) +#define LTC2992_DSENSE_MAX_THRESH(x) (LTC2992_DSENSE1_MAX_THRESH + ((x) * 0x32)) +#define LTC2992_DSENSE_MIN_THRESH(x) (LTC2992_DSENSE1_MIN_THRESH + ((x) * 0x32)) +#define LTC2992_SENSE(x) (LTC2992_SENSE1 + ((x) * 0x32)) +#define LTC2992_SENSE_MAX(x) (LTC2992_SENSE1_MAX + ((x) * 0x32)) +#define LTC2992_SENSE_MIN(x) (LTC2992_SENSE1_MIN + ((x) * 0x32)) +#define LTC2992_SENSE_MAX_THRESH(x) (LTC2992_SENSE1_MAX_THRESH + ((x) * 0x32)) +#define LTC2992_SENSE_MIN_THRESH(x) (LTC2992_SENSE1_MIN_THRESH + ((x) * 0x32)) +#define LTC2992_POWER_FAULT(x) (LTC2992_FAULT1 + ((x) * 0x32)) +#define LTC2992_SENSE_FAULT(x) (LTC2992_FAULT1 + ((x) * 0x32)) +#define LTC2992_DSENSE_FAULT(x) (LTC2992_FAULT1 + ((x) * 0x32)) + +/* FAULT1 FAULT2 registers common bitfields */ +#define LTC2992_POWER_FAULT_MSK GENMASK(7, 6) +#define LTC2992_DSENSE_FAULT_MSK GENMASK(5, 4) +#define LTC2992_SENSE_FAULT_MSK GENMASK(3, 2) + +/* FAULT1 bitfields */ +#define LTC2992_GPIO1_FAULT_MSK GENMASK(1, 0) + +/* FAULT2 bitfields */ +#define LTC2992_GPIO2_FAULT_MSK GENMASK(1, 0) + +/* FAULT3 bitfields */ +#define LTC2992_GPIO3_FAULT_MSK GENMASK(7, 6) +#define LTC2992_GPIO4_FAULT_MSK GENMASK(5, 4) + +#define LTC2992_IADC_NANOV_LSB 12500 +#define LTC2992_VADC_UV_LSB 25000 +#define LTC2992_VADC_GPIO_UV_LSB 500 + +struct ltc2992_state { + struct i2c_client *client; + struct regmap *regmap; + u32 r_sense_uohm[2]; +}; + +struct ltc2992_gpio_regs { + u8 data; + u8 max; + u8 min; + u8 max_thresh; + u8 min_thresh; + u8 alarm; + u8 alarm_msk; +}; + +static const struct ltc2992_gpio_regs ltc2992_gpio_addr_map[] = { + { + .data = LTC2992_G1, + .max = LTC2992_G1_MAX, + .min = LTC2992_G1_MIN, + .max_thresh = LTC2992_G1_MAX_THRESH, + .min_thresh = LTC2992_G1_MIN_THRESH, + .alarm = LTC2992_FAULT1, + .alarm_msk = LTC2992_GPIO1_FAULT_MSK, + }, + { + .data = LTC2992_G2, + .max = LTC2992_G2_MAX, + .min = LTC2992_G2_MIN, + .max_thresh = LTC2992_G2_MAX_THRESH, + .min_thresh = LTC2992_G2_MIN_THRESH, + .alarm = LTC2992_FAULT2, + .alarm_msk = LTC2992_GPIO2_FAULT_MSK, + }, + { + .data = LTC2992_G3, + .max = LTC2992_G3_MAX, + .min = LTC2992_G3_MIN, + .max_thresh = LTC2992_G3_MAX_THRESH, + .min_thresh = LTC2992_G3_MIN_THRESH, + .alarm = LTC2992_FAULT3, + .alarm_msk = LTC2992_GPIO3_FAULT_MSK, + }, + { + .data = LTC2992_G4, + .max = LTC2992_G4_MAX, + .min = LTC2992_G4_MIN, + .max_thresh = LTC2992_G4_MAX_THRESH, + .min_thresh = LTC2992_G4_MIN_THRESH, + .alarm = LTC2992_FAULT3, + .alarm_msk = LTC2992_GPIO4_FAULT_MSK, + }, +}; + +static int ltc2992_read_reg(struct ltc2992_state *st, u8 addr, const u8 reg_len, u32 *val) +{ + u8 regvals[4]; + int ret; + int i; + + ret = regmap_bulk_read(st->regmap, addr, regvals, reg_len); + if (ret < 0) + return ret; + + *val = 0; + for (i = 0; i < reg_len; i++) + *val |= regvals[reg_len - i - 1] << (i * 8); + + return 0; +} + +static int ltc2992_write_reg(struct ltc2992_state *st, u8 addr, const u8 reg_len, u32 val) +{ + u8 regvals[4]; + int i; + + for (i = 0; i < reg_len; i++) + regvals[reg_len - i - 1] = (val >> (i * 8)) & 0xFF; + + return regmap_bulk_write(st->regmap, addr, regvals, reg_len); +} + +static umode_t ltc2992_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, + int channel) +{ + const struct ltc2992_state *st = data; + + switch (type) { + case hwmon_in: + switch (attr) { + case hwmon_in_input: + case hwmon_in_min: + case hwmon_in_max: + case hwmon_in_alarm: + return 0444; + case hwmon_in_min_alarm: + case hwmon_in_max_alarm: + return 0644; + } + break; + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + case hwmon_curr_min: + case hwmon_curr_max: + case hwmon_curr_alarm: + if (st->r_sense_uohm[channel]) + return 0444; + break; + case hwmon_curr_min_alarm: + case hwmon_curr_max_alarm: + if (st->r_sense_uohm[channel]) + return 0644; + break; + } + break; + case hwmon_power: + switch (attr) { + case hwmon_power_input: + case hwmon_power_min: + case hwmon_power_max: + case hwmon_power_alarm: + if (st->r_sense_uohm[channel]) + return 0444; + break; + case hwmon_power_min_alarm: + case hwmon_power_max_alarm: + if (st->r_sense_uohm[channel]) + return 0644; + break; + } + break; + default: + break; + } + + return 0; +} + +static int ltc2992_get_voltage(struct ltc2992_state *st, u32 reg, u32 scale, long *val) +{ + u32 reg_val; + int ret; + + ret = ltc2992_read_reg(st, reg, 2, ®_val); + if (ret < 0) + return ret; + + reg_val = reg_val >> 4; + *val = DIV_ROUND_CLOSEST(reg_val * scale, 1000); + + return 0; +} + +static int ltc2992_set_voltage(struct ltc2992_state *st, u32 reg, u32 scale, long val) +{ + val = DIV_ROUND_CLOSEST(val * 1000, scale); + val = val << 4; + + return ltc2992_write_reg(st, reg, 2, val); +} + +static int ltc2992_read_gpio_alarm(struct ltc2992_state *st, int nr_gpio, long *val) +{ + u32 reg_val; + int ret; + + ret = ltc2992_read_reg(st, ltc2992_gpio_addr_map[nr_gpio].alarm, 1, ®_val); + if (ret < 0) + return ret; + + *val = !!(reg_val & ltc2992_gpio_addr_map[nr_gpio].alarm_msk); + + reg_val &= ~ltc2992_gpio_addr_map[nr_gpio].alarm_msk; + return ltc2992_write_reg(st, ltc2992_gpio_addr_map[nr_gpio].alarm, 1, reg_val); +} + +static int ltc2992_read_gpios_in(struct device *dev, u32 attr, int nr_gpio, long *val) +{ + struct ltc2992_state *st = dev_get_drvdata(dev); + u32 reg; + + switch (attr) { + case hwmon_in_input: + reg = ltc2992_gpio_addr_map[nr_gpio].data; + break; + case hwmon_in_min: + reg = ltc2992_gpio_addr_map[nr_gpio].min; + break; + case hwmon_in_max: + reg = ltc2992_gpio_addr_map[nr_gpio].max; + break; + case hwmon_in_min_alarm: + reg = ltc2992_gpio_addr_map[nr_gpio].min_thresh; + break; + case hwmon_in_max_alarm: + reg = ltc2992_gpio_addr_map[nr_gpio].max_thresh; + break; + case hwmon_in_alarm: + return ltc2992_read_gpio_alarm(st, nr_gpio, val); + default: + return -EOPNOTSUPP; + } + + return ltc2992_get_voltage(st, reg, LTC2992_VADC_GPIO_UV_LSB, val); +} + +static int ltc2992_read_in(struct device *dev, u32 attr, int channel, long *val) +{ + struct ltc2992_state *st = dev_get_drvdata(dev); + u32 reg_val; + u32 reg; + int ret; + + if (channel > 1) + return ltc2992_read_gpios_in(dev, attr, channel - 2, val); + + switch (attr) { + case hwmon_in_input: + reg = LTC2992_SENSE(channel); + break; + case hwmon_in_min: + reg = LTC2992_SENSE_MIN(channel); + break; + case hwmon_in_max: + reg = LTC2992_SENSE_MAX(channel); + break; + case hwmon_in_min_alarm: + reg = LTC2992_SENSE_MIN_THRESH(channel); + break; + case hwmon_in_max_alarm: + reg = LTC2992_SENSE_MAX_THRESH(channel); + break; + case hwmon_in_alarm: + ret = ltc2992_read_reg(st, LTC2992_SENSE_FAULT(channel), 1, ®_val); + if (ret < 0) + return ret; + + *val = !!(reg_val & LTC2992_SENSE_FAULT_MSK); + + reg_val &= ~LTC2992_SENSE_FAULT_MSK; + return ltc2992_write_reg(st, LTC2992_SENSE_FAULT(channel), 1, reg_val); + default: + return -EOPNOTSUPP; + } + + return ltc2992_get_voltage(st, reg, LTC2992_VADC_UV_LSB, val); +} + +static int ltc2992_get_current(struct ltc2992_state *st, u32 reg, u32 channel, long *val) +{ + u32 reg_val; + int ret; + + ret = ltc2992_read_reg(st, reg, 2, ®_val); + if (ret < 0) + return ret; + + reg_val = reg_val >> 4; + *val = DIV_ROUND_CLOSEST(reg_val * LTC2992_IADC_NANOV_LSB, st->r_sense_uohm[channel]); + + return 0; +} + +static int ltc2992_set_current(struct ltc2992_state *st, u32 reg, u32 channel, long val) +{ + u32 reg_val; + + reg_val = DIV_ROUND_CLOSEST(val * st->r_sense_uohm[channel], LTC2992_IADC_NANOV_LSB); + reg_val = reg_val << 4; + + return ltc2992_write_reg(st, reg, 2, reg_val); +} + +static int ltc2992_read_curr(struct device *dev, u32 attr, int channel, long *val) +{ + struct ltc2992_state *st = dev_get_drvdata(dev); + u32 reg_val; + u32 reg; + int ret; + + switch (attr) { + case hwmon_curr_input: + reg = LTC2992_DSENSE(channel); + break; + case hwmon_curr_min: + reg = LTC2992_DSENSE_MIN(channel); + break; + case hwmon_curr_max: + reg = LTC2992_DSENSE_MAX(channel); + break; + case hwmon_curr_min_alarm: + reg = LTC2992_DSENSE_MIN_THRESH(channel); + break; + case hwmon_curr_max_alarm: + reg = LTC2992_DSENSE_MAX_THRESH(channel); + break; + case hwmon_curr_alarm: + ret = ltc2992_read_reg(st, LTC2992_DSENSE_FAULT(channel), 1, ®_val); + if (ret < 0) + return ret; + + *val = !!(reg_val & LTC2992_DSENSE_FAULT_MSK); + + reg_val &= ~LTC2992_DSENSE_FAULT_MSK; + return ltc2992_write_reg(st, LTC2992_DSENSE_FAULT(channel), 1, reg_val); + default: + return -EOPNOTSUPP; + } + + return ltc2992_get_current(st, reg, channel, val); +} + +static int ltc2992_get_power(struct ltc2992_state *st, u32 reg, u32 channel, long *val) +{ + u32 reg_val; + int ret; + + ret = ltc2992_read_reg(st, reg, 3, ®_val); + if (ret < 0) + return ret; + + *val = mul_u64_u32_div(reg_val, LTC2992_VADC_UV_LSB * LTC2992_IADC_NANOV_LSB, + st->r_sense_uohm[channel] * 1000); + + return 0; +} + +static int ltc2992_set_power(struct ltc2992_state *st, u32 reg, u32 channel, long val) +{ + u32 reg_val; + + reg_val = mul_u64_u32_div(val, st->r_sense_uohm[channel] * 1000, + LTC2992_VADC_UV_LSB * LTC2992_IADC_NANOV_LSB); + + return ltc2992_write_reg(st, reg, 3, reg_val); +} + +static int ltc2992_read_power(struct device *dev, u32 attr, int channel, long *val) +{ + struct ltc2992_state *st = dev_get_drvdata(dev); + u32 reg_val; + u32 reg; + int ret; + + switch (attr) { + case hwmon_power_input: + reg = LTC2992_POWER(channel); + break; + case hwmon_power_min: + reg = LTC2992_POWER_MIN(channel); + break; + case hwmon_power_max: + reg = LTC2992_POWER_MAX(channel); + break; + case hwmon_power_min_alarm: + reg = LTC2992_POWER_MIN_THRESH(channel); + break; + case hwmon_power_max_alarm: + reg = LTC2992_POWER_MAX_THRESH(channel); + break; + case hwmon_power_alarm: + ret = ltc2992_read_reg(st, LTC2992_POWER_FAULT(channel), 1, ®_val); + if (ret < 0) + return ret; + + *val = !!(reg_val & LTC2992_POWER_FAULT_MSK); + + reg_val &= ~LTC2992_POWER_FAULT_MSK; + return ltc2992_write_reg(st, LTC2992_POWER_FAULT(channel), 1, reg_val); + default: + return -EOPNOTSUPP; + } + + return ltc2992_get_power(st, reg, channel, val); +} + +static int ltc2992_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, + long *val) +{ + switch (type) { + case hwmon_in: + return ltc2992_read_in(dev, attr, channel, val); + case hwmon_curr: + return ltc2992_read_curr(dev, attr, channel, val); + case hwmon_power: + return ltc2992_read_power(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int ltc2992_write_curr(struct device *dev, u32 attr, int channel, long val) +{ + struct ltc2992_state *st = dev_get_drvdata(dev); + u32 reg = 0; + + switch (attr) { + case hwmon_curr_min_alarm: + reg = LTC2992_DSENSE_MIN_THRESH(channel); + break; + case hwmon_curr_max_alarm: + reg = LTC2992_DSENSE_MAX_THRESH(channel); + break; + default: + return -EOPNOTSUPP; + } + + return ltc2992_set_current(st, reg, channel, val); +} + +static int ltc2992_write_gpios_in(struct device *dev, u32 attr, int nr_gpio, long val) +{ + struct ltc2992_state *st = dev_get_drvdata(dev); + u32 reg; + + switch (attr) { + case hwmon_in_min_alarm: + reg = ltc2992_gpio_addr_map[nr_gpio].min_thresh; + break; + case hwmon_in_max_alarm: + reg = ltc2992_gpio_addr_map[nr_gpio].max_thresh; + break; + default: + return -EOPNOTSUPP; + } + + return ltc2992_set_voltage(st, reg, LTC2992_VADC_GPIO_UV_LSB, val); +} + +static int ltc2992_write_in(struct device *dev, u32 attr, int channel, long val) +{ + struct ltc2992_state *st = dev_get_drvdata(dev); + u32 reg = 0; + + if (channel > 1) + return ltc2992_write_gpios_in(dev, attr, channel - 2, val); + + switch (attr) { + case hwmon_curr_min_alarm: + reg = LTC2992_SENSE_MIN_THRESH(channel); + break; + case hwmon_curr_max_alarm: + reg = LTC2992_SENSE_MAX_THRESH(channel); + break; + default: + return -EOPNOTSUPP; + } + + return ltc2992_set_voltage(st, reg, LTC2992_VADC_UV_LSB, val); +} + +static int ltc2992_write_power(struct device *dev, u32 attr, int channel, long val) +{ + struct ltc2992_state *st = dev_get_drvdata(dev); + u32 reg = 0; + + switch (attr) { + case hwmon_power_min_alarm: + reg = LTC2992_POWER_MIN_THRESH(channel); + break; + case hwmon_power_max_alarm: + reg = LTC2992_POWER_MAX_THRESH(channel); + break; + default: + return -EOPNOTSUPP; + } + + return ltc2992_set_power(st, reg, channel, val); +} + +static int ltc2992_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, + long val) +{ + switch (type) { + case hwmon_in: + return ltc2992_write_in(dev, attr, channel, val); + case hwmon_curr: + return ltc2992_write_curr(dev, attr, channel, val); + case hwmon_power: + return ltc2992_write_power(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_ops ltc2992_hwmon_ops = { + .is_visible = ltc2992_is_visible, + .read = ltc2992_read, + .write = ltc2992_write, +}; + +static const u32 ltc2992_in_config[] = { + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_MIN_ALARM | + HWMON_I_MAX_ALARM | HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_MIN_ALARM | + HWMON_I_MAX_ALARM | HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_MIN_ALARM | + HWMON_I_MAX_ALARM | HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_MIN_ALARM | + HWMON_I_MAX_ALARM | HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_MIN_ALARM | + HWMON_I_MAX_ALARM | HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_MIN_ALARM | + HWMON_I_MAX_ALARM | HWMON_I_ALARM, + 0 +}; + +static const struct hwmon_channel_info ltc2992_in = { + .type = hwmon_in, + .config = ltc2992_in_config, +}; + +static const u32 ltc2992_curr_config[] = { + HWMON_C_INPUT | HWMON_C_MIN | HWMON_C_MAX | HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM | + HWMON_C_ALARM, + HWMON_C_INPUT | HWMON_C_MIN | HWMON_C_MAX | HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM | + HWMON_C_ALARM, + 0 +}; + +static const struct hwmon_channel_info ltc2992_curr = { + .type = hwmon_curr, + .config = ltc2992_curr_config, +}; + +static const u32 ltc2992_power_config[] = { + HWMON_P_INPUT | HWMON_P_MIN | HWMON_P_MAX | HWMON_P_MIN_ALARM | HWMON_P_MAX_ALARM | + HWMON_P_ALARM, + HWMON_P_INPUT | HWMON_P_MIN | HWMON_P_MAX | HWMON_P_MIN_ALARM | HWMON_P_MAX_ALARM | + HWMON_P_ALARM, + 0 +}; + +static const struct hwmon_channel_info ltc2992_power = { + .type = hwmon_power, + .config = ltc2992_power_config, +}; + +static const struct hwmon_channel_info *ltc2992_info[] = { + <c2992_in, + <c2992_curr, + <c2992_power, + NULL +}; + +static const struct hwmon_chip_info ltc2992_chip_info = { + .ops = <c2992_hwmon_ops, + .info = ltc2992_info, +}; + +static const struct regmap_config ltc2992_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xE8, +}; + +static int ltc2992_parse_dt(struct ltc2992_state *st) +{ + struct fwnode_handle *fwnode; + struct fwnode_handle *child; + u32 addr; + u32 val; + int ret; + + fwnode = dev_fwnode(&st->client->dev); + + fwnode_for_each_available_child_node(fwnode, child) { + ret = fwnode_property_read_u32(child, "reg", &addr); + if (ret < 0) + return ret; + + if (addr > 1) + return -EINVAL; + + ret = fwnode_property_read_u32(child, "shunt-resistor-micro-ohms", &val); + if (!ret) + st->r_sense_uohm[addr] = val; + } + + return 0; +} + +static int ltc2992_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct device *hwmon_dev; + struct ltc2992_state *st; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + st = devm_kzalloc(&client->dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->client = client; + st->regmap = devm_regmap_init_i2c(client, <c2992_regmap_config); + if (IS_ERR(st->regmap)) + return PTR_ERR(st->regmap); + + ret = ltc2992_parse_dt(st); + if (ret < 0) + return ret; + + hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, client->name, st, + <c2992_chip_info, NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct of_device_id ltc2992_of_match[] = { + { .compatible = "adi,ltc2992" }, + { } +}; +MODULE_DEVICE_TABLE(of, ltc2992_of_match); + +static const struct i2c_device_id ltc2992_i2c_id[] = { + {"ltc2992", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ltc2992_i2c_id); + +static struct i2c_driver ltc2992_i2c_driver = { + .driver = { + .name = "ltc2992", + .of_match_table = ltc2992_of_match, + }, + .probe = ltc2992_i2c_probe, + .id_table = ltc2992_i2c_id, +}; + +module_i2c_driver(ltc2992_i2c_driver); + +MODULE_AUTHOR("Alexandru Tachici "); +MODULE_DESCRIPTION("Hwmon driver for Linear Technology 2992"); +MODULE_LICENSE("Dual BSD/GPL"); From patchwork Thu Oct 29 09:49:10 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandru Tachici X-Patchwork-Id: 310763 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4ACC9C2D0A3 for ; Thu, 29 Oct 2020 09:46:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0A41820790 for ; Thu, 29 Oct 2020 09:45:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726572AbgJ2Jp6 (ORCPT ); Thu, 29 Oct 2020 05:45:58 -0400 Received: from mx0a-00128a01.pphosted.com ([148.163.135.77]:60926 "EHLO mx0a-00128a01.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725929AbgJ2Jp6 (ORCPT ); Thu, 29 Oct 2020 05:45:58 -0400 Received: from pps.filterd (m0167088.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id 09T9jSfb005042; Thu, 29 Oct 2020 05:45:45 -0400 Received: from nwd2mta3.analog.com ([137.71.173.56]) by mx0a-00128a01.pphosted.com with ESMTP id 34ce462q08-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 29 Oct 2020 05:45:45 -0400 Received: from SCSQMBX11.ad.analog.com (SCSQMBX11.ad.analog.com [10.77.17.10]) by nwd2mta3.analog.com (8.14.7/8.14.7) with ESMTP id 09T9jhFp042396 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=FAIL); Thu, 29 Oct 2020 05:45:44 -0400 Received: from SCSQCASHYB7.ad.analog.com (10.77.17.133) by SCSQMBX11.ad.analog.com (10.77.17.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1779.2; Thu, 29 Oct 2020 02:45:43 -0700 Received: from SCSQMBX10.ad.analog.com (10.77.17.5) by SCSQCASHYB7.ad.analog.com (10.77.17.133) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1779.2; Thu, 29 Oct 2020 02:45:42 -0700 Received: from zeus.spd.analog.com (10.66.68.11) by SCSQMBX10.ad.analog.com (10.77.17.5) with Microsoft SMTP Server id 15.1.1779.2 via Frontend Transport; Thu, 29 Oct 2020 02:45:42 -0700 Received: from localhost.localdomain ([10.48.65.12]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 09T9jcAL008861; Thu, 29 Oct 2020 05:45:41 -0400 From: To: , , CC: , , Alexandru Tachici Subject: [PATCH 2/3] hwmon: ltc2992: Add support for GPIOs. Date: Thu, 29 Oct 2020 11:49:10 +0200 Message-ID: <20201029094911.79173-3-alexandru.tachici@analog.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20201029094911.79173-1-alexandru.tachici@analog.com> References: <20201029094911.79173-1-alexandru.tachici@analog.com> MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.312, 18.0.737 definitions=2020-10-29_03:2020-10-29,2020-10-29 signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 suspectscore=0 phishscore=0 spamscore=0 malwarescore=0 impostorscore=0 adultscore=0 mlxlogscore=999 bulkscore=0 priorityscore=1501 lowpriorityscore=0 clxscore=1015 mlxscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2010290070 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org From: Alexandru Tachici LTC2992 has 4 open-drain GPIOS. This patch exports to user space the 4 GPIOs using the GPIO driver Linux API. Signed-off-by: Alexandru Tachici Reported-by: kernel test robot Reported-by: kernel test robot --- drivers/hwmon/Kconfig | 1 + drivers/hwmon/ltc2992.c | 160 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index bf9e387270d6..8a8eb42fb1ec 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -861,6 +861,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 diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c index 940d92b4a1d0..3fe6d34cdade 100644 --- a/drivers/hwmon/ltc2992.c +++ b/drivers/hwmon/ltc2992.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,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)) @@ -95,8 +99,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]; }; @@ -109,6 +123,8 @@ struct ltc2992_gpio_regs { u8 min_thresh; u8 alarm; u8 alarm_msk; + u8 ctrl; + u8 ctrl_bit; }; static const struct ltc2992_gpio_regs ltc2992_gpio_addr_map[] = { @@ -120,6 +136,8 @@ static const struct ltc2992_gpio_regs ltc2992_gpio_addr_map[] = { .min_thresh = LTC2992_G1_MIN_THRESH, .alarm = LTC2992_FAULT1, .alarm_msk = LTC2992_GPIO1_FAULT_MSK, + .ctrl = LTC2992_GPIO_IO_CTRL, + .ctrl_bit = LTC2992_GPIO1_BIT, }, { .data = LTC2992_G2, @@ -129,6 +147,8 @@ static const struct ltc2992_gpio_regs ltc2992_gpio_addr_map[] = { .min_thresh = LTC2992_G2_MIN_THRESH, .alarm = LTC2992_FAULT2, .alarm_msk = LTC2992_GPIO2_FAULT_MSK, + .ctrl = LTC2992_GPIO_IO_CTRL, + .ctrl_bit = LTC2992_GPIO2_BIT, }, { .data = LTC2992_G3, @@ -138,6 +158,8 @@ static const struct ltc2992_gpio_regs ltc2992_gpio_addr_map[] = { .min_thresh = LTC2992_G3_MIN_THRESH, .alarm = LTC2992_FAULT3, .alarm_msk = LTC2992_GPIO3_FAULT_MSK, + .ctrl = LTC2992_GPIO_IO_CTRL, + .ctrl_bit = LTC2992_GPIO3_BIT, }, { .data = LTC2992_G4, @@ -147,9 +169,15 @@ static const struct ltc2992_gpio_regs ltc2992_gpio_addr_map[] = { .min_thresh = LTC2992_G4_MIN_THRESH, .alarm = LTC2992_FAULT3, .alarm_msk = LTC2992_GPIO4_FAULT_MSK, + .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, u32 *val) { u8 regvals[4]; @@ -178,6 +206,134 @@ 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; + u32 reg; + int ret; + + mutex_lock(&st->gpio_mutex); + ret = ltc2992_read_reg(st, LTC2992_GPIO_STATUS, 1, ®); + mutex_unlock(&st->gpio_mutex); + + if (ret < 0) + return ret; + + 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; + u32 reg; + int ret; + + mutex_lock(&st->gpio_mutex); + ret = ltc2992_read_reg(st, LTC2992_GPIO_STATUS, 1, ®); + mutex_unlock(&st->gpio_mutex); + + if (ret < 0) + return ret; + + 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; + u32 reg_val; + int ret; + + mutex_lock(&st->gpio_mutex); + ret = ltc2992_read_reg(st, ltc2992_gpio_addr_map[offset].ctrl, 1, ®_val); + if (ret < 0) { + mutex_unlock(&st->gpio_mutex); + return; + } + + gpio_ctrl = reg_val; + 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); +} + +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) { @@ -701,6 +857,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, <c2992_chip_info, NULL); From patchwork Thu Oct 29 09:49:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandru Tachici X-Patchwork-Id: 314784 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B3EB0C2D0A3 for ; Thu, 29 Oct 2020 09:46:02 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6BB5F2076E for ; Thu, 29 Oct 2020 09:46:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726731AbgJ2JqA (ORCPT ); Thu, 29 Oct 2020 05:46:00 -0400 Received: from mx0a-00128a01.pphosted.com ([148.163.135.77]:62026 "EHLO mx0a-00128a01.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726607AbgJ2Jp7 (ORCPT ); Thu, 29 Oct 2020 05:45:59 -0400 Received: from pps.filterd (m0167088.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id 09T9jXpK005056; Thu, 29 Oct 2020 05:45:46 -0400 Received: from nwd2mta4.analog.com ([137.71.173.58]) by mx0a-00128a01.pphosted.com with ESMTP id 34ce462q0f-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 29 Oct 2020 05:45:45 -0400 Received: from ASHBMBX9.ad.analog.com (ASHBMBX9.ad.analog.com [10.64.17.10]) by nwd2mta4.analog.com (8.14.7/8.14.7) with ESMTP id 09T9ji32023819 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=FAIL); Thu, 29 Oct 2020 05:45:44 -0400 Received: from ASHBMBX9.ad.analog.com (10.64.17.10) by ASHBMBX9.ad.analog.com (10.64.17.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1779.2; Thu, 29 Oct 2020 05:45:43 -0400 Received: from zeus.spd.analog.com (10.66.68.11) by ASHBMBX9.ad.analog.com (10.64.17.10) with Microsoft SMTP Server id 15.1.1779.2 via Frontend Transport; Thu, 29 Oct 2020 05:45:43 -0400 Received: from localhost.localdomain ([10.48.65.12]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 09T9jcAM008861; Thu, 29 Oct 2020 05:45:42 -0400 From: To: , , CC: , , Alexandru Tachici Subject: [PATCH 3/3] dt-binding: hwmon: Add documentation for ltc2992 Date: Thu, 29 Oct 2020 11:49:11 +0200 Message-ID: <20201029094911.79173-4-alexandru.tachici@analog.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20201029094911.79173-1-alexandru.tachici@analog.com> References: <20201029094911.79173-1-alexandru.tachici@analog.com> MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.312, 18.0.737 definitions=2020-10-29_03:2020-10-29,2020-10-29 signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 suspectscore=0 phishscore=0 spamscore=0 malwarescore=0 impostorscore=0 adultscore=0 mlxlogscore=999 bulkscore=0 priorityscore=1501 lowpriorityscore=0 clxscore=1015 mlxscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2010290070 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org From: Alexandru Tachici Add documentation for ltc2992. Signed-off-by: Alexandru Tachici --- .../bindings/hwmon/adi,ltc2992.yaml | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml diff --git a/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml b/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml new file mode 100644 index 000000000000..1b603026ed2d --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/adi,ltc2992.yaml @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/adi,ltc2992.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Linear Technology 2992 Power Monitor + +maintainers: + - Alexandru Tachici + +description: | + Linear Technology 2992 Dual Wide Range Power Monitor + https://www.analog.com/media/en/technical-documentation/data-sheets/ltc2992.pdf + +properties: + compatible: + enum: + - adi,ltc2992 + + reg: + maxItems: 1 + + avcc-supply: true + +patternProperties: + "^channel@([0-1])$": + type: object + description: | + Represents the two supplies to be monitored. + + properties: + reg: + description: | + The channel number. LTC2992 can monitor two supplies. + items: + minimum: 0 + maximum: 1 + + shunt-resistor-micro-ohms: + description: + The value of curent sense resistor in microohms. + required: + - reg + + additionalProperties: false + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c1 { + #address-cells = <1>; + #size-cells = <0>; + + ltc2992@6F { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "adi,ltc2992"; + reg = <0x6F>; + + channel@0 { + reg = <0x0>; + shunt-resistor-micro-ohms = <10000>; + }; + + channel@1 { + reg = <0x1>; + shunt-resistor-micro-ohms = <10000>; + }; + }; + }; +...