1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Driver for MPS MP5990 Hot-Swap Controller
7 #include <linux/module.h>
8 #include <linux/of_device.h>
11 #define MP5990_EFUSE_CFG (0xC4)
12 #define MP5990_VOUT_FORMAT BIT(9)
15 struct pmbus_driver_info info
;
17 u8 vout_linear_exponent
;
20 #define to_mp5990_data(x) container_of(x, struct mp5990_data, info)
22 static int mp5990_read_byte_data(struct i2c_client
*client
, int page
, int reg
)
24 const struct pmbus_driver_info
*info
= pmbus_get_driver_info(client
);
25 struct mp5990_data
*data
= to_mp5990_data(info
);
29 if (data
->vout_mode
== linear
) {
31 * The VOUT format used by the chip is linear11,
32 * not linear16. Report that VOUT is in linear mode
33 * and return exponent value extracted while probing
36 return data
->vout_linear_exponent
;
40 * The datasheet does not support the VOUT command,
41 * but the device responds with a default value of 0x17.
42 * In the standard, 0x17 represents linear mode.
43 * Therefore, we should report that VOUT is in direct
44 * format when the chip is configured for it.
46 return PB_VOUT_MODE_DIRECT
;
53 static int mp5990_read_word_data(struct i2c_client
*client
, int page
,
56 const struct pmbus_driver_info
*info
= pmbus_get_driver_info(client
);
57 struct mp5990_data
*data
= to_mp5990_data(info
);
63 ret
= pmbus_read_word_data(client
, page
, phase
, reg
);
67 * Because the VOUT format used by the chip is linear11 and not
68 * linear16, we disregard bits[15:11]. The exponent is reported
69 * as part of the VOUT_MODE command.
71 if (data
->vout_mode
== linear
) {
72 mantissa
= ((s16
)((ret
& 0x7ff) << 5)) >> 5;
83 static struct pmbus_driver_info mp5990_info
= {
85 .format
[PSC_VOLTAGE_IN
] = direct
,
86 .format
[PSC_VOLTAGE_OUT
] = direct
,
87 .format
[PSC_CURRENT_OUT
] = direct
,
88 .format
[PSC_POWER
] = direct
,
89 .format
[PSC_TEMPERATURE
] = direct
,
90 .m
[PSC_VOLTAGE_IN
] = 32,
91 .b
[PSC_VOLTAGE_IN
] = 0,
92 .R
[PSC_VOLTAGE_IN
] = 0,
93 .m
[PSC_VOLTAGE_OUT
] = 32,
94 .b
[PSC_VOLTAGE_OUT
] = 0,
95 .R
[PSC_VOLTAGE_OUT
] = 0,
96 .m
[PSC_CURRENT_OUT
] = 16,
97 .b
[PSC_CURRENT_OUT
] = 0,
98 .R
[PSC_CURRENT_OUT
] = 0,
102 .m
[PSC_TEMPERATURE
] = 1,
103 .b
[PSC_TEMPERATURE
] = 0,
104 .R
[PSC_TEMPERATURE
] = 0,
106 PMBUS_HAVE_VIN
| PMBUS_HAVE_VOUT
| PMBUS_HAVE_PIN
|
107 PMBUS_HAVE_TEMP
| PMBUS_HAVE_IOUT
|
108 PMBUS_HAVE_STATUS_INPUT
| PMBUS_HAVE_STATUS_TEMP
,
109 .read_byte_data
= mp5990_read_byte_data
,
110 .read_word_data
= mp5990_read_word_data
,
113 static int mp5990_probe(struct i2c_client
*client
)
115 struct pmbus_driver_info
*info
;
116 struct mp5990_data
*data
;
119 data
= devm_kzalloc(&client
->dev
, sizeof(struct mp5990_data
),
124 memcpy(&data
->info
, &mp5990_info
, sizeof(*info
));
127 /* Read Vout Config */
128 ret
= i2c_smbus_read_word_data(client
, MP5990_EFUSE_CFG
);
130 dev_err(&client
->dev
, "Can't get vout mode.");
135 * EFUSE_CFG (0xC4) bit9=1 is linear mode, bit=0 is direct mode.
137 if (ret
& MP5990_VOUT_FORMAT
) {
138 data
->vout_mode
= linear
;
139 data
->info
.format
[PSC_VOLTAGE_IN
] = linear
;
140 data
->info
.format
[PSC_VOLTAGE_OUT
] = linear
;
141 data
->info
.format
[PSC_CURRENT_OUT
] = linear
;
142 data
->info
.format
[PSC_POWER
] = linear
;
143 ret
= i2c_smbus_read_word_data(client
, PMBUS_READ_VOUT
);
145 dev_err(&client
->dev
, "Can't get vout exponent.");
148 data
->vout_linear_exponent
= (u8
)((ret
>> 11) & 0x1f);
150 data
->vout_mode
= direct
;
152 return pmbus_do_probe(client
, info
);
155 static const struct of_device_id mp5990_of_match
[] = {
156 { .compatible
= "mps,mp5990" },
160 static const struct i2c_device_id mp5990_id
[] = {
164 MODULE_DEVICE_TABLE(i2c
, mp5990_id
);
166 static struct i2c_driver mp5990_driver
= {
169 .of_match_table
= mp5990_of_match
,
171 .probe
= mp5990_probe
,
172 .id_table
= mp5990_id
,
174 module_i2c_driver(mp5990_driver
);
176 MODULE_AUTHOR("Peter Yin <peter.yin@quantatw.com>");
177 MODULE_DESCRIPTION("PMBus driver for MP5990 HSC");
178 MODULE_LICENSE("GPL");
179 MODULE_IMPORT_NS("PMBUS");