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
27static 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
48static 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, page: 0, phase: 0xff, reg: PMBUS_STATUS_WORD);
59 if (ret > 0) {
60 pmbus_write_word_data(client, page: 0, reg: PMBUS_STATUS_WORD,
61 word: ret);
62 ret = 0;
63 }
64 break;
65 default:
66 ret = -ENODATA;
67 break;
68 }
69 return ret;
70}
71
72static 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
112static 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, word: 0);
121 break;
122 case PMBUS_VIRT_RESET_IOUT_HISTORY:
123 ret = pmbus_write_word_data(client, page,
124 LTC3815_MFR_IOUT_PEAK, word: 0);
125 break;
126 case PMBUS_VIRT_RESET_VOUT_HISTORY:
127 ret = pmbus_write_word_data(client, page,
128 LTC3815_MFR_VOUT_PEAK, word: 0);
129 break;
130 case PMBUS_VIRT_RESET_VIN_HISTORY:
131 ret = pmbus_write_word_data(client, page,
132 LTC3815_MFR_VIN_PEAK, word: 0);
133 break;
134 case PMBUS_VIRT_RESET_TEMP_HISTORY:
135 ret = pmbus_write_word_data(client, page,
136 LTC3815_MFR_TEMP_PEAK, word: 0);
137 break;
138 default:
139 ret = -ENODATA;
140 break;
141 }
142 return ret;
143}
144
145static const struct i2c_device_id ltc3815_id[] = {
146 {"ltc3815", 0},
147 { }
148};
149MODULE_DEVICE_TABLE(i2c, ltc3815_id);
150
151static 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
181static int ltc3815_probe(struct i2c_client *client)
182{
183 int chip_id;
184
185 if (!i2c_check_functionality(adap: client->adapter,
186 I2C_FUNC_SMBUS_READ_WORD_DATA))
187 return -ENODEV;
188
189 chip_id = i2c_smbus_read_word_data(client, LTC3815_MFR_SPECIAL_ID);
190 if (chip_id < 0)
191 return chip_id;
192 if ((chip_id & LTC3815_ID_MASK) != LTC3815_ID)
193 return -ENODEV;
194
195 return pmbus_do_probe(client, info: &ltc3815_info);
196}
197
198static struct i2c_driver ltc3815_driver = {
199 .driver = {
200 .name = "ltc3815",
201 },
202 .probe = ltc3815_probe,
203 .id_table = ltc3815_id,
204};
205
206module_i2c_driver(ltc3815_driver);
207
208MODULE_AUTHOR("Guenter Roeck");
209MODULE_DESCRIPTION("PMBus driver for LTC3815");
210MODULE_LICENSE("GPL");
211MODULE_IMPORT_NS(PMBUS);
212

source code of linux/drivers/hwmon/pmbus/ltc3815.c