File Index
Files and directories
Sign in
FILE

mp5990.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hwmon/pmbus/mp5990.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Driver for MPS MP5990 Hot-Swap Controller
  4 */
  5
  6#include <linux/i2c.h>
  7#include <linux/module.h>
  8#include <linux/of_device.h>
  9#include "pmbus.h"
 10
 11enum chips { mp5990, mp5998 };
 12
 13#define MP5990_EFUSE_CFG	(0xC4)
 14#define MP5990_VOUT_FORMAT	BIT(9)
 15
 16struct mp5990_data {
 17	struct pmbus_driver_info info;
 18	u8 vout_mode;
 19	u8 vout_linear_exponent;
 20};
 21
 22#define to_mp5990_data(x)  container_of(x, struct mp5990_data, info)
 23
 24static int mp5990_read_byte_data(struct i2c_client *client, int page, int reg)
 25{
 26	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
 27	struct mp5990_data *data = to_mp5990_data(info);
 28
 29	switch (reg) {
 30	case PMBUS_VOUT_MODE:
 31		if (data->vout_mode == linear) {
 32			/*
 33			 * The VOUT format used by the chip is linear11,
 34			 * not linear16. Report that VOUT is in linear mode
 35			 * and return exponent value extracted while probing
 36			 * the chip.
 37			 */
 38			return data->vout_linear_exponent;
 39		}
 40
 41		/*
 42		 * The datasheet does not support the VOUT command,
 43		 * but the device responds with a default value of 0x17.
 44		 * In the standard, 0x17 represents linear mode.
 45		 * Therefore, we should report that VOUT is in direct
 46		 * format when the chip is configured for it.
 47		 */
 48		return PB_VOUT_MODE_DIRECT;
 49
 50	default:
 51		return -ENODATA;
 52	}
 53}
 54
 55static int mp5990_read_word_data(struct i2c_client *client, int page,
 56				 int phase, int reg)
 57{
 58	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
 59	struct mp5990_data *data = to_mp5990_data(info);
 60	int ret;
 61	s32 mantissa;
 62
 63	switch (reg) {
 64	case PMBUS_READ_VOUT:
 65		ret = pmbus_read_word_data(client, page, phase, reg);
 66		if (ret < 0)
 67			return ret;
 68		/*
 69		 * Because the VOUT format used by the chip is linear11 and not
 70		 * linear16, we disregard bits[15:11]. The exponent is reported
 71		 * as part of the VOUT_MODE command.
 72		 */
 73		if (data->vout_mode == linear) {
 74			mantissa = ((s16)((ret & 0x7ff) << 5)) >> 5;
 75			ret = mantissa;
 76		}
 77		break;
 78	default:
 79		return -ENODATA;
 80	}
 81
 82	return ret;
 83}
 84
 85static struct pmbus_driver_info mp5990_info = {
 86	.pages = 1,
 87	.format[PSC_VOLTAGE_IN] = direct,
 88	.format[PSC_VOLTAGE_OUT] = direct,
 89	.format[PSC_CURRENT_OUT] = direct,
 90	.format[PSC_POWER] = direct,
 91	.format[PSC_TEMPERATURE] = direct,
 92	.m[PSC_VOLTAGE_IN] = 32,
 93	.b[PSC_VOLTAGE_IN] = 0,
 94	.R[PSC_VOLTAGE_IN] = 0,
 95	.m[PSC_VOLTAGE_OUT] = 32,
 96	.b[PSC_VOLTAGE_OUT] = 0,
 97	.R[PSC_VOLTAGE_OUT] = 0,
 98	.m[PSC_CURRENT_OUT] = 16,
 99	.b[PSC_CURRENT_OUT] = 0,
100	.R[PSC_CURRENT_OUT] = 0,
101	.m[PSC_POWER] = 1,
102	.b[PSC_POWER] = 0,
103	.R[PSC_POWER] = 0,
104	.m[PSC_TEMPERATURE] = 1,
105	.b[PSC_TEMPERATURE] = 0,
106	.R[PSC_TEMPERATURE] = 0,
107	.func[0] =
108		PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_PIN |
109		PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT |
110		PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
111	.read_byte_data = mp5990_read_byte_data,
112	.read_word_data = mp5990_read_word_data,
113};
114
115static struct pmbus_driver_info mp5998_info = {
116	.pages = 1,
117	.format[PSC_VOLTAGE_IN] = direct,
118	.format[PSC_VOLTAGE_OUT] = direct,
119	.format[PSC_CURRENT_IN] = direct,
120	.format[PSC_CURRENT_OUT] = direct,
121	.format[PSC_POWER] = direct,
122	.format[PSC_TEMPERATURE] = direct,
123	.m[PSC_VOLTAGE_IN] = 64,
124	.b[PSC_VOLTAGE_IN] = 0,
125	.R[PSC_VOLTAGE_IN] = 0,
126	.m[PSC_VOLTAGE_OUT] = 64,
127	.b[PSC_VOLTAGE_OUT] = 0,
128	.R[PSC_VOLTAGE_OUT] = 0,
129	.m[PSC_CURRENT_IN] = 16,
130	.b[PSC_CURRENT_IN] = 0,
131	.R[PSC_CURRENT_IN] = 0,
132	.m[PSC_CURRENT_OUT] = 16,
133	.b[PSC_CURRENT_OUT] = 0,
134	.R[PSC_CURRENT_OUT] = 0,
135	.m[PSC_POWER] = 2,
136	.b[PSC_POWER] = 0,
137	.R[PSC_POWER] = 0,
138	.m[PSC_TEMPERATURE] = 1,
139	.b[PSC_TEMPERATURE] = 0,
140	.R[PSC_TEMPERATURE] = 0,
141	.func[0] =
142		PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
143		PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT |
144		PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_IOUT |
145		PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
146	.read_byte_data = mp5990_read_byte_data,
147	.read_word_data = mp5990_read_word_data,
148};
149
150static const struct i2c_device_id mp5990_id[] = {
151	{"mp5990", mp5990},
152	{"mp5998", mp5998},
153	{ }
154};
155MODULE_DEVICE_TABLE(i2c, mp5990_id);
156
157static int mp5990_probe(struct i2c_client *client)
158{
159	struct pmbus_driver_info *info;
160	struct mp5990_data *data;
161	enum chips chip;
162	int ret;
163
164	data = devm_kzalloc(&client->dev, sizeof(struct mp5990_data),
165			    GFP_KERNEL);
166	if (!data)
167		return -ENOMEM;
168
169	if (client->dev.of_node)
170		chip = (uintptr_t)of_device_get_match_data(&client->dev);
171	else
172		chip = i2c_match_id(mp5990_id, client)->driver_data;
173
174	if (chip == mp5990)
175		memcpy(&data->info, &mp5990_info, sizeof(*info));
176	else
177		memcpy(&data->info, &mp5998_info, sizeof(*info));
178	info = &data->info;
179
180	/* Read Vout Config */
181	ret = i2c_smbus_read_word_data(client, MP5990_EFUSE_CFG);
182	if (ret < 0) {
183		dev_err(&client->dev, "Can't get vout mode.");
184		return ret;
185	}
186
187	/*
188	 * EFUSE_CFG (0xC4) bit9=1 is linear mode, bit=0 is direct mode.
189	 */
190	if (ret & MP5990_VOUT_FORMAT) {
191		data->vout_mode = linear;
192		data->info.format[PSC_VOLTAGE_IN] = linear;
193		data->info.format[PSC_VOLTAGE_OUT] = linear;
194		data->info.format[PSC_CURRENT_OUT] = linear;
195		data->info.format[PSC_POWER] = linear;
196		if (chip == mp5998)
197			data->info.format[PSC_CURRENT_IN] = linear;
198
199		ret = i2c_smbus_read_word_data(client, PMBUS_READ_VOUT);
200		if (ret < 0) {
201			dev_err(&client->dev, "Can't get vout exponent.");
202			return ret;
203		}
204		data->vout_linear_exponent = (u8)((ret >> 11) & 0x1f);
205	} else {
206		data->vout_mode = direct;
207	}
208	return pmbus_do_probe(client, info);
209}
210
211static const struct of_device_id mp5990_of_match[] = {
212	{ .compatible = "mps,mp5990", .data = (void *)mp5990 },
213	{ .compatible = "mps,mp5998", .data = (void *)mp5998 },
214	{}
215};
216
217static struct i2c_driver mp5990_driver = {
218	.driver = {
219		   .name = "mp5990",
220		   .of_match_table = mp5990_of_match,
221	},
222	.probe = mp5990_probe,
223	.id_table = mp5990_id,
224};
225module_i2c_driver(mp5990_driver);
226
227MODULE_AUTHOR("Peter Yin <peter.yin@quantatw.com>");
228MODULE_DESCRIPTION("PMBus driver for MP5990 HSC");
229MODULE_LICENSE("GPL");
230MODULE_IMPORT_NS("PMBUS");
~/ Read-only file preview.