xref: /linux/drivers/hwmon/pmbus/adm1275.c (revision 8b313ca7f1b98263ce22519b25a9c2a362eeb898)
19d2ecfb7SGuenter Roeck /*
29d2ecfb7SGuenter Roeck  * Hardware monitoring driver for Analog Devices ADM1275 Hot-Swap Controller
39d2ecfb7SGuenter Roeck  * and Digital Power Monitor
49d2ecfb7SGuenter Roeck  *
59d2ecfb7SGuenter Roeck  * Copyright (c) 2011 Ericsson AB.
69d2ecfb7SGuenter Roeck  *
79d2ecfb7SGuenter Roeck  * This program is free software; you can redistribute it and/or modify
89d2ecfb7SGuenter Roeck  * it under the terms of the GNU General Public License as published by
99d2ecfb7SGuenter Roeck  * the Free Software Foundation; either version 2 of the License, or
109d2ecfb7SGuenter Roeck  * (at your option) any later version.
119d2ecfb7SGuenter Roeck  *
129d2ecfb7SGuenter Roeck  * This program is distributed in the hope that it will be useful,
139d2ecfb7SGuenter Roeck  * but WITHOUT ANY WARRANTY; without even the implied warranty of
149d2ecfb7SGuenter Roeck  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
159d2ecfb7SGuenter Roeck  * GNU General Public License for more details.
169d2ecfb7SGuenter Roeck  */
179d2ecfb7SGuenter Roeck 
189d2ecfb7SGuenter Roeck #include <linux/kernel.h>
199d2ecfb7SGuenter Roeck #include <linux/module.h>
209d2ecfb7SGuenter Roeck #include <linux/init.h>
219d2ecfb7SGuenter Roeck #include <linux/err.h>
229d2ecfb7SGuenter Roeck #include <linux/slab.h>
239d2ecfb7SGuenter Roeck #include <linux/i2c.h>
249d2ecfb7SGuenter Roeck #include "pmbus.h"
259d2ecfb7SGuenter Roeck 
265cf231a3SGuenter Roeck enum chips { adm1275, adm1276 };
275cf231a3SGuenter Roeck 
28c576e30cSGuenter Roeck #define ADM1275_PEAK_IOUT		0xd0
29c576e30cSGuenter Roeck #define ADM1275_PEAK_VIN		0xd1
30c576e30cSGuenter Roeck #define ADM1275_PEAK_VOUT		0xd2
319d2ecfb7SGuenter Roeck #define ADM1275_PMON_CONFIG		0xd4
329d2ecfb7SGuenter Roeck 
339d2ecfb7SGuenter Roeck #define ADM1275_VIN_VOUT_SELECT		(1 << 6)
349d2ecfb7SGuenter Roeck #define ADM1275_VRANGE			(1 << 5)
359d2ecfb7SGuenter Roeck 
36c5e67636SGuenter Roeck #define ADM1275_IOUT_WARN2_LIMIT	0xd7
37c5e67636SGuenter Roeck #define ADM1275_DEVICE_CONFIG		0xd8
38c5e67636SGuenter Roeck 
39c5e67636SGuenter Roeck #define ADM1275_IOUT_WARN2_SELECT	(1 << 4)
40c5e67636SGuenter Roeck 
415cf231a3SGuenter Roeck #define ADM1276_PEAK_PIN		0xda
425cf231a3SGuenter Roeck 
43c5e67636SGuenter Roeck #define ADM1275_MFR_STATUS_IOUT_WARN2	(1 << 0)
44c5e67636SGuenter Roeck 
45c5e67636SGuenter Roeck struct adm1275_data {
465cf231a3SGuenter Roeck 	int id;
47c5e67636SGuenter Roeck 	bool have_oc_fault;
48c5e67636SGuenter Roeck 	struct pmbus_driver_info info;
49c5e67636SGuenter Roeck };
50c5e67636SGuenter Roeck 
51c5e67636SGuenter Roeck #define to_adm1275_data(x)  container_of(x, struct adm1275_data, info)
52c5e67636SGuenter Roeck 
53c576e30cSGuenter Roeck static int adm1275_read_word_data(struct i2c_client *client, int page, int reg)
54c576e30cSGuenter Roeck {
55c5e67636SGuenter Roeck 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
56c5e67636SGuenter Roeck 	const struct adm1275_data *data = to_adm1275_data(info);
575cf231a3SGuenter Roeck 	int ret = 0;
58c576e30cSGuenter Roeck 
59c576e30cSGuenter Roeck 	if (page)
60c5e67636SGuenter Roeck 		return -ENXIO;
61c576e30cSGuenter Roeck 
62c576e30cSGuenter Roeck 	switch (reg) {
63c5e67636SGuenter Roeck 	case PMBUS_IOUT_UC_FAULT_LIMIT:
64c5e67636SGuenter Roeck 		if (data->have_oc_fault) {
65c5e67636SGuenter Roeck 			ret = -ENXIO;
66c5e67636SGuenter Roeck 			break;
67c5e67636SGuenter Roeck 		}
68c5e67636SGuenter Roeck 		ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT);
69c5e67636SGuenter Roeck 		break;
70c5e67636SGuenter Roeck 	case PMBUS_IOUT_OC_FAULT_LIMIT:
71c5e67636SGuenter Roeck 		if (!data->have_oc_fault) {
72c5e67636SGuenter Roeck 			ret = -ENXIO;
73c5e67636SGuenter Roeck 			break;
74c5e67636SGuenter Roeck 		}
75c5e67636SGuenter Roeck 		ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT);
76c5e67636SGuenter Roeck 		break;
77c576e30cSGuenter Roeck 	case PMBUS_VIRT_READ_IOUT_MAX:
78c576e30cSGuenter Roeck 		ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_IOUT);
79c576e30cSGuenter Roeck 		break;
80c576e30cSGuenter Roeck 	case PMBUS_VIRT_READ_VOUT_MAX:
81c576e30cSGuenter Roeck 		ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VOUT);
82c576e30cSGuenter Roeck 		break;
83c576e30cSGuenter Roeck 	case PMBUS_VIRT_READ_VIN_MAX:
84c576e30cSGuenter Roeck 		ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VIN);
85c576e30cSGuenter Roeck 		break;
865cf231a3SGuenter Roeck 	case PMBUS_VIRT_READ_PIN_MAX:
875cf231a3SGuenter Roeck 		if (data->id != adm1276) {
885cf231a3SGuenter Roeck 			ret = -ENXIO;
895cf231a3SGuenter Roeck 			break;
905cf231a3SGuenter Roeck 		}
915cf231a3SGuenter Roeck 		ret = pmbus_read_word_data(client, 0, ADM1276_PEAK_PIN);
925cf231a3SGuenter Roeck 		break;
93c576e30cSGuenter Roeck 	case PMBUS_VIRT_RESET_IOUT_HISTORY:
94c576e30cSGuenter Roeck 	case PMBUS_VIRT_RESET_VOUT_HISTORY:
95c576e30cSGuenter Roeck 	case PMBUS_VIRT_RESET_VIN_HISTORY:
965cf231a3SGuenter Roeck 		break;
975cf231a3SGuenter Roeck 	case PMBUS_VIRT_RESET_PIN_HISTORY:
985cf231a3SGuenter Roeck 		if (data->id != adm1276)
995cf231a3SGuenter Roeck 			ret = -ENXIO;
100c576e30cSGuenter Roeck 		break;
101c576e30cSGuenter Roeck 	default:
102c576e30cSGuenter Roeck 		ret = -ENODATA;
103c576e30cSGuenter Roeck 		break;
104c576e30cSGuenter Roeck 	}
105c576e30cSGuenter Roeck 	return ret;
106c576e30cSGuenter Roeck }
107c576e30cSGuenter Roeck 
108c576e30cSGuenter Roeck static int adm1275_write_word_data(struct i2c_client *client, int page, int reg,
109c576e30cSGuenter Roeck 				   u16 word)
110c576e30cSGuenter Roeck {
111c576e30cSGuenter Roeck 	int ret;
112c576e30cSGuenter Roeck 
113c576e30cSGuenter Roeck 	if (page)
114c5e67636SGuenter Roeck 		return -ENXIO;
115c576e30cSGuenter Roeck 
116c576e30cSGuenter Roeck 	switch (reg) {
117c5e67636SGuenter Roeck 	case PMBUS_IOUT_UC_FAULT_LIMIT:
118c5e67636SGuenter Roeck 	case PMBUS_IOUT_OC_FAULT_LIMIT:
119c5e67636SGuenter Roeck 		ret = pmbus_write_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT,
120c5e67636SGuenter Roeck 					    word);
121c5e67636SGuenter Roeck 		break;
122c576e30cSGuenter Roeck 	case PMBUS_VIRT_RESET_IOUT_HISTORY:
123c576e30cSGuenter Roeck 		ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_IOUT, 0);
124c576e30cSGuenter Roeck 		break;
125c576e30cSGuenter Roeck 	case PMBUS_VIRT_RESET_VOUT_HISTORY:
126c576e30cSGuenter Roeck 		ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VOUT, 0);
127c576e30cSGuenter Roeck 		break;
128c576e30cSGuenter Roeck 	case PMBUS_VIRT_RESET_VIN_HISTORY:
129c576e30cSGuenter Roeck 		ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VIN, 0);
130c576e30cSGuenter Roeck 		break;
1315cf231a3SGuenter Roeck 	case PMBUS_VIRT_RESET_PIN_HISTORY:
1325cf231a3SGuenter Roeck 		ret = pmbus_write_word_data(client, 0, ADM1276_PEAK_PIN, 0);
1335cf231a3SGuenter Roeck 		break;
134c576e30cSGuenter Roeck 	default:
135c576e30cSGuenter Roeck 		ret = -ENODATA;
136c576e30cSGuenter Roeck 		break;
137c576e30cSGuenter Roeck 	}
138c576e30cSGuenter Roeck 	return ret;
139c576e30cSGuenter Roeck }
140c576e30cSGuenter Roeck 
141c5e67636SGuenter Roeck static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg)
142c5e67636SGuenter Roeck {
143c5e67636SGuenter Roeck 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
144c5e67636SGuenter Roeck 	const struct adm1275_data *data = to_adm1275_data(info);
145c5e67636SGuenter Roeck 	int mfr_status, ret;
146c5e67636SGuenter Roeck 
147da8e48abSGuenter Roeck 	if (page > 0)
148c5e67636SGuenter Roeck 		return -ENXIO;
149c5e67636SGuenter Roeck 
150c5e67636SGuenter Roeck 	switch (reg) {
151c5e67636SGuenter Roeck 	case PMBUS_STATUS_IOUT:
152c5e67636SGuenter Roeck 		ret = pmbus_read_byte_data(client, page, PMBUS_STATUS_IOUT);
153c5e67636SGuenter Roeck 		if (ret < 0)
154c5e67636SGuenter Roeck 			break;
155c5e67636SGuenter Roeck 		mfr_status = pmbus_read_byte_data(client, page,
156c5e67636SGuenter Roeck 						  PMBUS_STATUS_MFR_SPECIFIC);
157c5e67636SGuenter Roeck 		if (mfr_status < 0) {
158c5e67636SGuenter Roeck 			ret = mfr_status;
159c5e67636SGuenter Roeck 			break;
160c5e67636SGuenter Roeck 		}
161c5e67636SGuenter Roeck 		if (mfr_status & ADM1275_MFR_STATUS_IOUT_WARN2) {
162c5e67636SGuenter Roeck 			ret |= data->have_oc_fault ?
163c5e67636SGuenter Roeck 			  PB_IOUT_OC_FAULT : PB_IOUT_UC_FAULT;
164c5e67636SGuenter Roeck 		}
165c5e67636SGuenter Roeck 		break;
166c5e67636SGuenter Roeck 	default:
167c5e67636SGuenter Roeck 		ret = -ENODATA;
168c5e67636SGuenter Roeck 		break;
169c5e67636SGuenter Roeck 	}
170c5e67636SGuenter Roeck 	return ret;
171c5e67636SGuenter Roeck }
172c5e67636SGuenter Roeck 
17387102808SGuenter Roeck static const struct i2c_device_id adm1275_id[] = {
17487102808SGuenter Roeck 	{ "adm1275", adm1275 },
17587102808SGuenter Roeck 	{ "adm1276", adm1276 },
17687102808SGuenter Roeck 	{ }
17787102808SGuenter Roeck };
17887102808SGuenter Roeck MODULE_DEVICE_TABLE(i2c, adm1275_id);
17987102808SGuenter Roeck 
1809d2ecfb7SGuenter Roeck static int adm1275_probe(struct i2c_client *client,
1819d2ecfb7SGuenter Roeck 			 const struct i2c_device_id *id)
1829d2ecfb7SGuenter Roeck {
18387102808SGuenter Roeck 	u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
184c5e67636SGuenter Roeck 	int config, device_config;
1859d2ecfb7SGuenter Roeck 	int ret;
1869d2ecfb7SGuenter Roeck 	struct pmbus_driver_info *info;
187c5e67636SGuenter Roeck 	struct adm1275_data *data;
18887102808SGuenter Roeck 	const struct i2c_device_id *mid;
1899d2ecfb7SGuenter Roeck 
1909d2ecfb7SGuenter Roeck 	if (!i2c_check_functionality(client->adapter,
19187102808SGuenter Roeck 				     I2C_FUNC_SMBUS_READ_BYTE_DATA
19287102808SGuenter Roeck 				     | I2C_FUNC_SMBUS_BLOCK_DATA))
1939d2ecfb7SGuenter Roeck 		return -ENODEV;
1949d2ecfb7SGuenter Roeck 
19587102808SGuenter Roeck 	ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, block_buffer);
19687102808SGuenter Roeck 	if (ret < 0) {
19787102808SGuenter Roeck 		dev_err(&client->dev, "Failed to read Manufacturer ID\n");
19887102808SGuenter Roeck 		return ret;
19987102808SGuenter Roeck 	}
20087102808SGuenter Roeck 	if (ret != 3 || strncmp(block_buffer, "ADI", 3)) {
20187102808SGuenter Roeck 		dev_err(&client->dev, "Unsupported Manufacturer ID\n");
20287102808SGuenter Roeck 		return -ENODEV;
20387102808SGuenter Roeck 	}
20487102808SGuenter Roeck 
20587102808SGuenter Roeck 	ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, block_buffer);
20687102808SGuenter Roeck 	if (ret < 0) {
20787102808SGuenter Roeck 		dev_err(&client->dev, "Failed to read Manufacturer Model\n");
20887102808SGuenter Roeck 		return ret;
20987102808SGuenter Roeck 	}
21087102808SGuenter Roeck 	for (mid = adm1275_id; mid->name[0]; mid++) {
21187102808SGuenter Roeck 		if (!strncasecmp(mid->name, block_buffer, strlen(mid->name)))
21287102808SGuenter Roeck 			break;
21387102808SGuenter Roeck 	}
21487102808SGuenter Roeck 	if (!mid->name[0]) {
21587102808SGuenter Roeck 		dev_err(&client->dev, "Unsupported device\n");
21687102808SGuenter Roeck 		return -ENODEV;
21787102808SGuenter Roeck 	}
21887102808SGuenter Roeck 
21987102808SGuenter Roeck 	if (id->driver_data != mid->driver_data)
22087102808SGuenter Roeck 		dev_notice(&client->dev,
22187102808SGuenter Roeck 			   "Device mismatch: Configured %s, detected %s\n",
22287102808SGuenter Roeck 			   id->name, mid->name);
22387102808SGuenter Roeck 
22487102808SGuenter Roeck 	config = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG);
22587102808SGuenter Roeck 	if (config < 0)
22687102808SGuenter Roeck 		return config;
22787102808SGuenter Roeck 
22887102808SGuenter Roeck 	device_config = i2c_smbus_read_byte_data(client, ADM1275_DEVICE_CONFIG);
22987102808SGuenter Roeck 	if (device_config < 0)
23087102808SGuenter Roeck 		return device_config;
23187102808SGuenter Roeck 
232*8b313ca7SGuenter Roeck 	data = devm_kzalloc(&client->dev, sizeof(struct adm1275_data),
233*8b313ca7SGuenter Roeck 			    GFP_KERNEL);
234c5e67636SGuenter Roeck 	if (!data)
2359d2ecfb7SGuenter Roeck 		return -ENOMEM;
2369d2ecfb7SGuenter Roeck 
23787102808SGuenter Roeck 	data->id = mid->driver_data;
2389d2ecfb7SGuenter Roeck 
239c5e67636SGuenter Roeck 	info = &data->info;
240c5e67636SGuenter Roeck 
2419d2ecfb7SGuenter Roeck 	info->pages = 1;
2421061d851SGuenter Roeck 	info->format[PSC_VOLTAGE_IN] = direct;
2431061d851SGuenter Roeck 	info->format[PSC_VOLTAGE_OUT] = direct;
2441061d851SGuenter Roeck 	info->format[PSC_CURRENT_OUT] = direct;
2459d2ecfb7SGuenter Roeck 	info->m[PSC_CURRENT_OUT] = 807;
2469d2ecfb7SGuenter Roeck 	info->b[PSC_CURRENT_OUT] = 20475;
2479d2ecfb7SGuenter Roeck 	info->R[PSC_CURRENT_OUT] = -1;
2489d2ecfb7SGuenter Roeck 	info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
2499d2ecfb7SGuenter Roeck 
250c576e30cSGuenter Roeck 	info->read_word_data = adm1275_read_word_data;
251c5e67636SGuenter Roeck 	info->read_byte_data = adm1275_read_byte_data;
252c576e30cSGuenter Roeck 	info->write_word_data = adm1275_write_word_data;
253c576e30cSGuenter Roeck 
2549d2ecfb7SGuenter Roeck 	if (config & ADM1275_VRANGE) {
2559d2ecfb7SGuenter Roeck 		info->m[PSC_VOLTAGE_IN] = 19199;
2569d2ecfb7SGuenter Roeck 		info->b[PSC_VOLTAGE_IN] = 0;
2579d2ecfb7SGuenter Roeck 		info->R[PSC_VOLTAGE_IN] = -2;
2589d2ecfb7SGuenter Roeck 		info->m[PSC_VOLTAGE_OUT] = 19199;
2599d2ecfb7SGuenter Roeck 		info->b[PSC_VOLTAGE_OUT] = 0;
2609d2ecfb7SGuenter Roeck 		info->R[PSC_VOLTAGE_OUT] = -2;
2619d2ecfb7SGuenter Roeck 	} else {
2629d2ecfb7SGuenter Roeck 		info->m[PSC_VOLTAGE_IN] = 6720;
2639d2ecfb7SGuenter Roeck 		info->b[PSC_VOLTAGE_IN] = 0;
2649d2ecfb7SGuenter Roeck 		info->R[PSC_VOLTAGE_IN] = -1;
2659d2ecfb7SGuenter Roeck 		info->m[PSC_VOLTAGE_OUT] = 6720;
2669d2ecfb7SGuenter Roeck 		info->b[PSC_VOLTAGE_OUT] = 0;
2679d2ecfb7SGuenter Roeck 		info->R[PSC_VOLTAGE_OUT] = -1;
2689d2ecfb7SGuenter Roeck 	}
2699d2ecfb7SGuenter Roeck 
270c5e67636SGuenter Roeck 	if (device_config & ADM1275_IOUT_WARN2_SELECT)
271c5e67636SGuenter Roeck 		data->have_oc_fault = true;
272c5e67636SGuenter Roeck 
27387102808SGuenter Roeck 	switch (data->id) {
2745cf231a3SGuenter Roeck 	case adm1275:
2759d2ecfb7SGuenter Roeck 		if (config & ADM1275_VIN_VOUT_SELECT)
2765cf231a3SGuenter Roeck 			info->func[0] |=
2775cf231a3SGuenter Roeck 			  PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
2789d2ecfb7SGuenter Roeck 		else
2795cf231a3SGuenter Roeck 			info->func[0] |=
2805cf231a3SGuenter Roeck 			  PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT;
2815cf231a3SGuenter Roeck 		break;
2825cf231a3SGuenter Roeck 	case adm1276:
2835cf231a3SGuenter Roeck 		info->format[PSC_POWER] = direct;
2845cf231a3SGuenter Roeck 		info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_PIN
2855cf231a3SGuenter Roeck 		  | PMBUS_HAVE_STATUS_INPUT;
2865cf231a3SGuenter Roeck 		if (config & ADM1275_VIN_VOUT_SELECT)
2875cf231a3SGuenter Roeck 			info->func[0] |=
2885cf231a3SGuenter Roeck 			  PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
2895cf231a3SGuenter Roeck 		if (config & ADM1275_VRANGE) {
2905cf231a3SGuenter Roeck 			info->m[PSC_POWER] = 6043;
2915cf231a3SGuenter Roeck 			info->b[PSC_POWER] = 0;
2925cf231a3SGuenter Roeck 			info->R[PSC_POWER] = -2;
2935cf231a3SGuenter Roeck 		} else {
2945cf231a3SGuenter Roeck 			info->m[PSC_POWER] = 2115;
2955cf231a3SGuenter Roeck 			info->b[PSC_POWER] = 0;
2965cf231a3SGuenter Roeck 			info->R[PSC_POWER] = -1;
2975cf231a3SGuenter Roeck 		}
2985cf231a3SGuenter Roeck 		break;
2995cf231a3SGuenter Roeck 	}
3009d2ecfb7SGuenter Roeck 
301*8b313ca7SGuenter Roeck 	return pmbus_do_probe(client, id, info);
3029d2ecfb7SGuenter Roeck }
3039d2ecfb7SGuenter Roeck 
3049d2ecfb7SGuenter Roeck static int adm1275_remove(struct i2c_client *client)
3059d2ecfb7SGuenter Roeck {
306866cf12aSGuenter Roeck 	pmbus_do_remove(client);
307866cf12aSGuenter Roeck 	return 0;
3089d2ecfb7SGuenter Roeck }
3099d2ecfb7SGuenter Roeck 
3109d2ecfb7SGuenter Roeck static struct i2c_driver adm1275_driver = {
3119d2ecfb7SGuenter Roeck 	.driver = {
3129d2ecfb7SGuenter Roeck 		   .name = "adm1275",
3139d2ecfb7SGuenter Roeck 		   },
3149d2ecfb7SGuenter Roeck 	.probe = adm1275_probe,
3159d2ecfb7SGuenter Roeck 	.remove = adm1275_remove,
3169d2ecfb7SGuenter Roeck 	.id_table = adm1275_id,
3179d2ecfb7SGuenter Roeck };
3189d2ecfb7SGuenter Roeck 
319f0967eeaSAxel Lin module_i2c_driver(adm1275_driver);
3209d2ecfb7SGuenter Roeck 
3219d2ecfb7SGuenter Roeck MODULE_AUTHOR("Guenter Roeck");
3225cf231a3SGuenter Roeck MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275 and compatibles");
3239d2ecfb7SGuenter Roeck MODULE_LICENSE("GPL");
324