1 // SPDX-License-Identifier: GPL-2.0-or-later
5 #include <linux/init.h>
6 #include <linux/kernel.h>
7 #include <linux/module.h>
8 #include <linux/pmbus.h>
11 /* LTC4286 register */
12 #define LTC4286_MFR_CONFIG1 0xF2
14 /* LTC4286 configuration */
15 #define VRANGE_SELECT_BIT BIT(1)
17 #define LTC4286_MFR_ID_SIZE 3
20 * Initialize the MBR as default settings which is referred to LTC4286 datasheet
21 * (March 22, 2022 version) table 3 page 16
23 static struct pmbus_driver_info ltc4286_info
= {
25 .format
[PSC_VOLTAGE_IN
] = direct
,
26 .format
[PSC_VOLTAGE_OUT
] = direct
,
27 .format
[PSC_CURRENT_OUT
] = direct
,
28 .format
[PSC_POWER
] = direct
,
29 .format
[PSC_TEMPERATURE
] = direct
,
30 .m
[PSC_VOLTAGE_IN
] = 32,
31 .b
[PSC_VOLTAGE_IN
] = 0,
32 .R
[PSC_VOLTAGE_IN
] = 1,
33 .m
[PSC_VOLTAGE_OUT
] = 32,
34 .b
[PSC_VOLTAGE_OUT
] = 0,
35 .R
[PSC_VOLTAGE_OUT
] = 1,
36 .m
[PSC_CURRENT_OUT
] = 1024,
37 .b
[PSC_CURRENT_OUT
] = 0,
39 * The rsense value used in MBR formula in LTC4286 datasheet should be ohm unit.
40 * However, the rsense value that user input is micro ohm.
41 * Thus, the MBR setting which involves rsense should be shifted by 6 digits.
43 .R
[PSC_CURRENT_OUT
] = 3 - 6,
47 * The rsense value used in MBR formula in LTC4286 datasheet should be ohm unit.
48 * However, the rsense value that user input is micro ohm.
49 * Thus, the MBR setting which involves rsense should be shifted by 6 digits.
51 .R
[PSC_POWER
] = 4 - 6,
52 .m
[PSC_TEMPERATURE
] = 1,
53 .b
[PSC_TEMPERATURE
] = 273,
54 .R
[PSC_TEMPERATURE
] = 0,
55 .func
[0] = PMBUS_HAVE_VIN
| PMBUS_HAVE_VOUT
| PMBUS_HAVE_IOUT
|
56 PMBUS_HAVE_PIN
| PMBUS_HAVE_TEMP
| PMBUS_HAVE_STATUS_VOUT
|
57 PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_STATUS_TEMP
,
60 static const struct i2c_device_id ltc4286_id
[] = {
65 MODULE_DEVICE_TABLE(i2c
, ltc4286_id
);
67 static int ltc4286_probe(struct i2c_client
*client
)
70 const struct i2c_device_id
*mid
;
71 u8 block_buffer
[I2C_SMBUS_BLOCK_MAX
+ 1];
72 struct pmbus_driver_info
*info
;
74 int vrange_nval
, vrange_oval
;
76 ret
= i2c_smbus_read_block_data(client
, PMBUS_MFR_ID
, block_buffer
);
78 return dev_err_probe(&client
->dev
, ret
,
79 "Failed to read manufacturer id\n");
83 * Refer to ltc4286 datasheet page 20
84 * the manufacturer id is LTC
86 if (ret
!= LTC4286_MFR_ID_SIZE
||
87 strncmp(block_buffer
, "LTC", LTC4286_MFR_ID_SIZE
)) {
88 return dev_err_probe(&client
->dev
, -ENODEV
,
89 "Manufacturer id mismatch\n");
92 ret
= i2c_smbus_read_block_data(client
, PMBUS_MFR_MODEL
, block_buffer
);
94 return dev_err_probe(&client
->dev
, ret
,
95 "Failed to read manufacturer model\n");
98 for (mid
= ltc4286_id
; mid
->name
[0]; mid
++) {
99 if (!strncasecmp(mid
->name
, block_buffer
, strlen(mid
->name
)))
103 return dev_err_probe(&client
->dev
, -ENODEV
,
104 "Unsupported device\n");
106 if (of_property_read_u32(client
->dev
.of_node
,
107 "shunt-resistor-micro-ohms", &rsense
))
108 rsense
= 300; /* 0.3 mOhm if not set via DT */
113 /* Check for the latter MBR value won't overflow */
114 if (rsense
> (INT_MAX
/ 1024))
117 info
= devm_kmemdup(&client
->dev
, <c4286_info
, sizeof(*info
),
122 /* Check MFR1 CONFIG register bit 1 VRANGE_SELECT before driver loading */
123 vrange_oval
= i2c_smbus_read_word_data(client
, LTC4286_MFR_CONFIG1
);
125 return dev_err_probe(&client
->dev
, vrange_oval
,
126 "Failed to read manufacturer configuration one\n");
127 vrange_nval
= vrange_oval
;
129 if (device_property_read_bool(&client
->dev
, "adi,vrange-low-enable")) {
131 ~VRANGE_SELECT_BIT
; /* VRANGE_SELECT = 0, 25.6 volts */
133 info
->m
[PSC_VOLTAGE_IN
] = 128;
134 info
->m
[PSC_VOLTAGE_OUT
] = 128;
135 info
->m
[PSC_POWER
] = 4 * rsense
;
138 VRANGE_SELECT_BIT
; /* VRANGE_SELECT = 1, 102.4 volts */
140 info
->m
[PSC_POWER
] = rsense
;
142 if (vrange_nval
!= vrange_oval
) {
143 /* Set MFR1 CONFIG register bit 1 VRANGE_SELECT */
144 ret
= i2c_smbus_write_word_data(client
, LTC4286_MFR_CONFIG1
,
147 return dev_err_probe(&client
->dev
, ret
,
148 "Failed to set vrange\n");
151 info
->m
[PSC_CURRENT_OUT
] = 1024 * rsense
;
153 return pmbus_do_probe(client
, info
);
156 static const struct of_device_id ltc4286_of_match
[] = {
157 { .compatible
= "lltc,ltc4286" },
158 { .compatible
= "lltc,ltc4287" },
162 static struct i2c_driver ltc4286_driver
= {
165 .of_match_table
= ltc4286_of_match
,
167 .probe
= ltc4286_probe
,
168 .id_table
= ltc4286_id
,
171 module_i2c_driver(ltc4286_driver
);
173 MODULE_AUTHOR("Delphine CC Chiu <Delphine_CC_Chiu@wiwynn.com>");
174 MODULE_DESCRIPTION("PMBUS driver for LTC4286 and compatibles");
175 MODULE_LICENSE("GPL");