xref: /openbmc/linux/drivers/hwmon/pmbus/ltc3815.c (revision f20c7d91)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Hardware monitoring driver for LTC3815
4  *
5  * Copyright (c) 2015 Linear Technology
6  * Copyright (c) 2015 Guenter Roeck
7  */
8 
9 #include <linux/err.h>
10 #include <linux/i2c.h>
11 #include <linux/init.h>
12 #include <linux/jiffies.h>
13 #include <linux/kernel.h>
14 #include <linux/module.h>
15 #include "pmbus.h"
16 
17 #define LTC3815_MFR_IOUT_PEAK	0xd7
18 #define LTC3815_MFR_VOUT_PEAK	0xdd
19 #define LTC3815_MFR_VIN_PEAK	0xde
20 #define LTC3815_MFR_TEMP_PEAK	0xdf
21 #define LTC3815_MFR_IIN_PEAK	0xe1
22 #define LTC3815_MFR_SPECIAL_ID	0xe7
23 
24 #define LTC3815_ID		0x8000
25 #define LTC3815_ID_MASK		0xff00
26 
27 static int ltc3815_read_byte_data(struct i2c_client *client, int page, int reg)
28 {
29 	int ret;
30 
31 	switch (reg) {
32 	case PMBUS_VOUT_MODE:
33 		/*
34 		 * The chip returns 0x3e, suggesting VID mode with manufacturer
35 		 * specific VID codes. Since the output voltage is reported
36 		 * with a LSB of 0.5mV, override and report direct mode with
37 		 * appropriate coefficients.
38 		 */
39 		ret = 0x40;
40 		break;
41 	default:
42 		ret = -ENODATA;
43 		break;
44 	}
45 	return ret;
46 }
47 
48 static int ltc3815_write_byte(struct i2c_client *client, int page, u8 reg)
49 {
50 	int ret;
51 
52 	switch (reg) {
53 	case PMBUS_CLEAR_FAULTS:
54 		/*
55 		 * LTC3815 does not support the CLEAR_FAULTS command.
56 		 * Emulate it by clearing the status register.
57 		 */
58 		ret = pmbus_read_word_data(client, 0, 0xff, PMBUS_STATUS_WORD);
59 		if (ret > 0) {
60 			pmbus_write_word_data(client, 0, PMBUS_STATUS_WORD,
61 					      ret);
62 			ret = 0;
63 		}
64 		break;
65 	default:
66 		ret = -ENODATA;
67 		break;
68 	}
69 	return ret;
70 }
71 
72 static int ltc3815_read_word_data(struct i2c_client *client, int page,
73 				  int phase, int reg)
74 {
75 	int ret;
76 
77 	switch (reg) {
78 	case PMBUS_VIRT_READ_VIN_MAX:
79 		ret = pmbus_read_word_data(client, page, phase,
80 					   LTC3815_MFR_VIN_PEAK);
81 		break;
82 	case PMBUS_VIRT_READ_VOUT_MAX:
83 		ret = pmbus_read_word_data(client, page, phase,
84 					   LTC3815_MFR_VOUT_PEAK);
85 		break;
86 	case PMBUS_VIRT_READ_TEMP_MAX:
87 		ret = pmbus_read_word_data(client, page, phase,
88 					   LTC3815_MFR_TEMP_PEAK);
89 		break;
90 	case PMBUS_VIRT_READ_IOUT_MAX:
91 		ret = pmbus_read_word_data(client, page, phase,
92 					   LTC3815_MFR_IOUT_PEAK);
93 		break;
94 	case PMBUS_VIRT_READ_IIN_MAX:
95 		ret = pmbus_read_word_data(client, page, phase,
96 					   LTC3815_MFR_IIN_PEAK);
97 		break;
98 	case PMBUS_VIRT_RESET_VOUT_HISTORY:
99 	case PMBUS_VIRT_RESET_VIN_HISTORY:
100 	case PMBUS_VIRT_RESET_TEMP_HISTORY:
101 	case PMBUS_VIRT_RESET_IOUT_HISTORY:
102 	case PMBUS_VIRT_RESET_IIN_HISTORY:
103 		ret = 0;
104 		break;
105 	default:
106 		ret = -ENODATA;
107 		break;
108 	}
109 	return ret;
110 }
111 
112 static int ltc3815_write_word_data(struct i2c_client *client, int page,
113 				   int reg, u16 word)
114 {
115 	int ret;
116 
117 	switch (reg) {
118 	case PMBUS_VIRT_RESET_IIN_HISTORY:
119 		ret = pmbus_write_word_data(client, page,
120 					    LTC3815_MFR_IIN_PEAK, 0);
121 		break;
122 	case PMBUS_VIRT_RESET_IOUT_HISTORY:
123 		ret = pmbus_write_word_data(client, page,
124 					    LTC3815_MFR_IOUT_PEAK, 0);
125 		break;
126 	case PMBUS_VIRT_RESET_VOUT_HISTORY:
127 		ret = pmbus_write_word_data(client, page,
128 					    LTC3815_MFR_VOUT_PEAK, 0);
129 		break;
130 	case PMBUS_VIRT_RESET_VIN_HISTORY:
131 		ret = pmbus_write_word_data(client, page,
132 					    LTC3815_MFR_VIN_PEAK, 0);
133 		break;
134 	case PMBUS_VIRT_RESET_TEMP_HISTORY:
135 		ret = pmbus_write_word_data(client, page,
136 					    LTC3815_MFR_TEMP_PEAK, 0);
137 		break;
138 	default:
139 		ret = -ENODATA;
140 		break;
141 	}
142 	return ret;
143 }
144 
145 static const struct i2c_device_id ltc3815_id[] = {
146 	{"ltc3815", 0},
147 	{ }
148 };
149 MODULE_DEVICE_TABLE(i2c, ltc3815_id);
150 
151 static struct pmbus_driver_info ltc3815_info = {
152 	.pages = 1,
153 	.format[PSC_VOLTAGE_IN] = direct,
154 	.format[PSC_VOLTAGE_OUT] = direct,
155 	.format[PSC_CURRENT_IN] = direct,
156 	.format[PSC_CURRENT_OUT] = direct,
157 	.format[PSC_TEMPERATURE] = direct,
158 	.m[PSC_VOLTAGE_IN] = 250,
159 	.b[PSC_VOLTAGE_IN] = 0,
160 	.R[PSC_VOLTAGE_IN] = 0,
161 	.m[PSC_VOLTAGE_OUT] = 2,
162 	.b[PSC_VOLTAGE_OUT] = 0,
163 	.R[PSC_VOLTAGE_OUT] = 3,
164 	.m[PSC_CURRENT_IN] = 1,
165 	.b[PSC_CURRENT_IN] = 0,
166 	.R[PSC_CURRENT_IN] = 2,
167 	.m[PSC_CURRENT_OUT] = 1,
168 	.b[PSC_CURRENT_OUT] = 0,
169 	.R[PSC_CURRENT_OUT] = 2,
170 	.m[PSC_TEMPERATURE] = 1,
171 	.b[PSC_TEMPERATURE] = 0,
172 	.R[PSC_TEMPERATURE] = 0,
173 	.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_VOUT |
174 		PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP,
175 	.read_byte_data = ltc3815_read_byte_data,
176 	.read_word_data = ltc3815_read_word_data,
177 	.write_byte = ltc3815_write_byte,
178 	.write_word_data = ltc3815_write_word_data,
179 };
180 
181 static int ltc3815_probe(struct i2c_client *client,
182 			 const struct i2c_device_id *id)
183 {
184 	int chip_id;
185 
186 	if (!i2c_check_functionality(client->adapter,
187 				     I2C_FUNC_SMBUS_READ_WORD_DATA))
188 		return -ENODEV;
189 
190 	chip_id = i2c_smbus_read_word_data(client, LTC3815_MFR_SPECIAL_ID);
191 	if (chip_id < 0)
192 		return chip_id;
193 	if ((chip_id & LTC3815_ID_MASK) != LTC3815_ID)
194 		return -ENODEV;
195 
196 	return pmbus_do_probe(client, id, &ltc3815_info);
197 }
198 
199 static struct i2c_driver ltc3815_driver = {
200 	.driver = {
201 		   .name = "ltc3815",
202 		   },
203 	.probe = ltc3815_probe,
204 	.remove = pmbus_do_remove,
205 	.id_table = ltc3815_id,
206 };
207 
208 module_i2c_driver(ltc3815_driver);
209 
210 MODULE_AUTHOR("Guenter Roeck");
211 MODULE_DESCRIPTION("PMBus driver for LTC3815");
212 MODULE_LICENSE("GPL");
213