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