Skip to content

Commit b64b6cb

Browse files
Leo Yanggroeck
authored andcommitted
hwmon: Add driver for TI INA233 Current and Power Monitor
Driver for Texas Instruments INA233 Current and Power Monitor With I2C-, SMBus-, and PMBus-Compatible Interface Signed-off-by: Leo Yang <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Guenter Roeck <[email protected]>
1 parent 52ffdbb commit b64b6cb

File tree

6 files changed

+284
-0
lines changed

6 files changed

+284
-0
lines changed

Documentation/hwmon/ina233.rst

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
.. SPDX-License-Identifier: GPL-2.0
2+
3+
Kernel driver ina233
4+
====================
5+
6+
Supported chips:
7+
8+
* TI INA233
9+
10+
Prefix: 'ina233'
11+
12+
* Datasheet
13+
14+
Publicly available at the TI website : https://www.ti.com/lit/ds/symlink/ina233.pdf
15+
16+
Author: Leo Yang <[email protected]>
17+
18+
Usage Notes
19+
-----------
20+
21+
The shunt resistor value can be configured by a device tree property;
22+
see Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml for details.
23+
24+
25+
Description
26+
-----------
27+
28+
This driver supports hardware monitoring for TI INA233.
29+
30+
The driver is a client driver to the core PMBus driver. Please see
31+
Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
32+
33+
The driver provides the following attributes for input voltage:
34+
35+
**in1_input**
36+
37+
**in1_label**
38+
39+
**in1_max**
40+
41+
**in1_max_alarm**
42+
43+
**in1_min**
44+
45+
**in1_min_alarm**
46+
47+
The driver provides the following attributes for shunt voltage:
48+
49+
**in2_input**
50+
51+
**in2_label**
52+
53+
The driver provides the following attributes for output voltage:
54+
55+
**in3_input**
56+
57+
**in3_label**
58+
59+
**in3_alarm**
60+
61+
The driver provides the following attributes for output current:
62+
63+
**curr1_input**
64+
65+
**curr1_label**
66+
67+
**curr1_max**
68+
69+
**curr1_max_alarm**
70+
71+
The driver provides the following attributes for input power:
72+
73+
**power1_input**
74+
75+
**power1_label**

Documentation/hwmon/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ Hardware Monitoring Kernel Drivers
9191
ibmpowernv
9292
ina209
9393
ina2xx
94+
ina233
9495
ina238
9596
ina3221
9697
inspur-ipsps1

MAINTAINERS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11331,6 +11331,13 @@ L: [email protected]
1133111331
S: Orphan
1133211332
F: drivers/video/fbdev/imsttfb.c
1133311333

11334+
INA233 HARDWARE MONITOR DRIVERS
11335+
M: Leo Yang <[email protected]>
11336+
11337+
S: Maintained
11338+
F: Documentation/hwmon/ina233.rst
11339+
F: drivers/hwmon/pmbus/ina233.c
11340+
1133411341
INDEX OF FURTHER KERNEL DOCUMENTATION
1133511342
M: Carlos Bilbao <[email protected]>
1133611343
S: Maintained

drivers/hwmon/pmbus/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,15 @@ config SENSORS_DPS920AB
133133
This driver can also be built as a module. If so, the module will
134134
be called dps920ab.
135135

136+
config SENSORS_INA233
137+
tristate "Texas Instruments INA233 and compatibles"
138+
help
139+
If you say yes here you get hardware monitoring support for Texas
140+
Instruments INA233.
141+
142+
This driver can also be built as a module. If so, the module will
143+
be called ina233.
144+
136145
config SENSORS_INSPUR_IPSPS
137146
tristate "INSPUR Power System Power Supply"
138147
help

drivers/hwmon/pmbus/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ obj-$(CONFIG_SENSORS_DELTA_AHE50DC_FAN) += delta-ahe50dc-fan.o
1515
obj-$(CONFIG_SENSORS_FSP_3Y) += fsp-3y.o
1616
obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o
1717
obj-$(CONFIG_SENSORS_DPS920AB) += dps920ab.o
18+
obj-$(CONFIG_SENSORS_INA233) += ina233.o
1819
obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o
1920
obj-$(CONFIG_SENSORS_IR35221) += ir35221.o
2021
obj-$(CONFIG_SENSORS_IR36021) += ir36021.o

drivers/hwmon/pmbus/ina233.c

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Hardware monitoring driver for ina233
4+
*
5+
* Copyright (c) 2025 Leo Yang
6+
*/
7+
8+
#include <linux/err.h>
9+
#include <linux/i2c.h>
10+
#include <linux/init.h>
11+
#include <linux/kernel.h>
12+
#include <linux/module.h>
13+
#include "pmbus.h"
14+
15+
#define MFR_READ_VSHUNT 0xd1
16+
#define MFR_CALIBRATION 0xd4
17+
18+
#define INA233_MAX_CURRENT_DEFAULT 32768000 /* uA */
19+
#define INA233_RSHUNT_DEFAULT 2000 /* uOhm */
20+
21+
#define MAX_M_VAL 32767
22+
23+
static void calculate_coef(int *m, int *R, u32 current_lsb, int power_coef)
24+
{
25+
u64 scaled_m;
26+
int scale_factor = 0;
27+
int scale_coef = 1;
28+
29+
/*
30+
* 1000000 from Current_LSB A->uA .
31+
* scale_coef is for scaling up to minimize rounding errors,
32+
* If there is no decimal information, no need to scale.
33+
*/
34+
if (1000000 % current_lsb) {
35+
/* Scaling to keep integer precision */
36+
scale_factor = -3;
37+
scale_coef = 1000;
38+
}
39+
40+
/*
41+
* Unit Conversion (Current_LSB A->uA) and use scaling(scale_factor)
42+
* to keep integer precision.
43+
* Formulae referenced from spec.
44+
*/
45+
scaled_m = div64_u64(1000000 * scale_coef, (u64)current_lsb * power_coef);
46+
47+
/* Maximize while keeping it bounded.*/
48+
while (scaled_m > MAX_M_VAL) {
49+
scaled_m = div_u64(scaled_m, 10);
50+
scale_factor++;
51+
}
52+
/* Scale up only if fractional part exists. */
53+
while (scaled_m * 10 < MAX_M_VAL && scale_coef != 1) {
54+
scaled_m *= 10;
55+
scale_factor--;
56+
}
57+
58+
*m = scaled_m;
59+
*R = scale_factor;
60+
}
61+
62+
static int ina233_read_word_data(struct i2c_client *client, int page,
63+
int phase, int reg)
64+
{
65+
int ret;
66+
67+
switch (reg) {
68+
case PMBUS_VIRT_READ_VMON:
69+
ret = pmbus_read_word_data(client, 0, 0xff, MFR_READ_VSHUNT);
70+
71+
/* Adjust returned value to match VIN coefficients */
72+
/* VIN: 1.25 mV VSHUNT: 2.5 uV LSB */
73+
ret = DIV_ROUND_CLOSEST(ret * 25, 12500);
74+
break;
75+
default:
76+
ret = -ENODATA;
77+
break;
78+
}
79+
return ret;
80+
}
81+
82+
static int ina233_probe(struct i2c_client *client)
83+
{
84+
struct device *dev = &client->dev;
85+
int ret, m, R;
86+
u32 rshunt;
87+
u32 max_current;
88+
u32 current_lsb;
89+
u16 calibration;
90+
struct pmbus_driver_info *info;
91+
92+
info = devm_kzalloc(dev, sizeof(struct pmbus_driver_info),
93+
GFP_KERNEL);
94+
if (!info)
95+
return -ENOMEM;
96+
97+
info->pages = 1;
98+
info->format[PSC_VOLTAGE_IN] = direct;
99+
info->format[PSC_VOLTAGE_OUT] = direct;
100+
info->format[PSC_CURRENT_OUT] = direct;
101+
info->format[PSC_POWER] = direct;
102+
info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_INPUT
103+
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
104+
| PMBUS_HAVE_POUT
105+
| PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON;
106+
info->m[PSC_VOLTAGE_IN] = 8;
107+
info->R[PSC_VOLTAGE_IN] = 2;
108+
info->m[PSC_VOLTAGE_OUT] = 8;
109+
info->R[PSC_VOLTAGE_OUT] = 2;
110+
info->read_word_data = ina233_read_word_data;
111+
112+
/* If INA233 skips current/power, shunt-resistor and current-lsb aren't needed. */
113+
/* read rshunt value (uOhm) */
114+
ret = device_property_read_u32(dev, "shunt-resistor", &rshunt);
115+
if (ret) {
116+
if (ret != -EINVAL)
117+
return dev_err_probe(dev, ret, "Shunt resistor property read fail.\n");
118+
rshunt = INA233_RSHUNT_DEFAULT;
119+
}
120+
if (!rshunt)
121+
return dev_err_probe(dev, -EINVAL,
122+
"Shunt resistor cannot be zero.\n");
123+
124+
/* read Maximum expected current value (uA) */
125+
ret = device_property_read_u32(dev, "ti,maximum-expected-current-microamp", &max_current);
126+
if (ret) {
127+
if (ret != -EINVAL)
128+
return dev_err_probe(dev, ret,
129+
"Maximum expected current property read fail.\n");
130+
max_current = INA233_MAX_CURRENT_DEFAULT;
131+
}
132+
if (max_current < 32768)
133+
return dev_err_probe(dev, -EINVAL,
134+
"Maximum expected current cannot less then 32768.\n");
135+
136+
/* Calculate Current_LSB according to the spec formula */
137+
current_lsb = max_current / 32768;
138+
139+
/* calculate current coefficient */
140+
calculate_coef(&m, &R, current_lsb, 1);
141+
info->m[PSC_CURRENT_OUT] = m;
142+
info->R[PSC_CURRENT_OUT] = R;
143+
144+
/* calculate power coefficient */
145+
calculate_coef(&m, &R, current_lsb, 25);
146+
info->m[PSC_POWER] = m;
147+
info->R[PSC_POWER] = R;
148+
149+
/* write MFR_CALIBRATION register, Apply formula from spec with unit scaling. */
150+
calibration = div64_u64(5120000000ULL, (u64)rshunt * current_lsb);
151+
if (calibration > 0x7FFF)
152+
return dev_err_probe(dev, -EINVAL,
153+
"Product of Current_LSB %u and shunt resistor %u too small, MFR_CALIBRATION reg exceeds 0x7FFF.\n",
154+
current_lsb, rshunt);
155+
ret = i2c_smbus_write_word_data(client, MFR_CALIBRATION, calibration);
156+
if (ret < 0)
157+
return dev_err_probe(dev, ret, "Unable to write calibration.\n");
158+
159+
dev_dbg(dev, "power monitor %s (Rshunt = %u uOhm, Current_LSB = %u uA/bit)\n",
160+
client->name, rshunt, current_lsb);
161+
162+
return pmbus_do_probe(client, info);
163+
}
164+
165+
static const struct i2c_device_id ina233_id[] = {
166+
{"ina233", 0},
167+
{}
168+
};
169+
MODULE_DEVICE_TABLE(i2c, ina233_id);
170+
171+
static const struct of_device_id __maybe_unused ina233_of_match[] = {
172+
{ .compatible = "ti,ina233" },
173+
{}
174+
};
175+
MODULE_DEVICE_TABLE(of, ina233_of_match);
176+
177+
static struct i2c_driver ina233_driver = {
178+
.driver = {
179+
.name = "ina233",
180+
.of_match_table = of_match_ptr(ina233_of_match),
181+
},
182+
.probe = ina233_probe,
183+
.id_table = ina233_id,
184+
};
185+
186+
module_i2c_driver(ina233_driver);
187+
188+
MODULE_AUTHOR("Leo Yang <[email protected]>");
189+
MODULE_DESCRIPTION("PMBus driver for INA233 and compatible chips");
190+
MODULE_LICENSE("GPL");
191+
MODULE_IMPORT_NS("PMBUS");

0 commit comments

Comments
 (0)