Commit | Line | Data |
---|---|---|
4d420a6a AJ |
1 | /* |
2 | * Copyright (C) 2017 IBM Corp. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | */ | |
9 | ||
10 | #include <linux/kernel.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/init.h> | |
13 | #include <linux/err.h> | |
14 | #include <linux/i2c.h> | |
15 | #include "pmbus.h" | |
16 | ||
17 | enum max31785_regs { | |
18 | MFR_REVISION = 0x9b, | |
19 | }; | |
20 | ||
21 | #define MAX31785_NR_PAGES 23 | |
22 | ||
56ad86b4 AJ |
23 | static int max31785_get_pwm(struct i2c_client *client, int page) |
24 | { | |
25 | int rv; | |
26 | ||
27 | rv = pmbus_get_fan_rate_device(client, page, 0, percent); | |
28 | if (rv < 0) | |
29 | return rv; | |
30 | else if (rv >= 0x8000) | |
31 | return 0; | |
32 | else if (rv >= 0x2711) | |
33 | return 0x2710; | |
34 | ||
35 | return rv; | |
36 | } | |
37 | ||
38 | static int max31785_get_pwm_mode(struct i2c_client *client, int page) | |
39 | { | |
40 | int config; | |
41 | int command; | |
42 | ||
43 | config = pmbus_read_byte_data(client, page, PMBUS_FAN_CONFIG_12); | |
44 | if (config < 0) | |
45 | return config; | |
46 | ||
47 | command = pmbus_read_word_data(client, page, PMBUS_FAN_COMMAND_1); | |
48 | if (command < 0) | |
49 | return command; | |
50 | ||
51 | if (config & PB_FAN_1_RPM) | |
52 | return (command >= 0x8000) ? 3 : 2; | |
53 | ||
54 | if (command >= 0x8000) | |
55 | return 3; | |
56 | else if (command >= 0x2711) | |
57 | return 0; | |
58 | ||
59 | return 1; | |
60 | } | |
61 | ||
62 | static int max31785_read_word_data(struct i2c_client *client, int page, | |
63 | int reg) | |
64 | { | |
65 | int rv; | |
66 | ||
67 | switch (reg) { | |
68 | case PMBUS_VIRT_PWM_1: | |
69 | rv = max31785_get_pwm(client, page); | |
70 | break; | |
71 | case PMBUS_VIRT_PWM_ENABLE_1: | |
72 | rv = max31785_get_pwm_mode(client, page); | |
73 | break; | |
74 | default: | |
75 | rv = -ENODATA; | |
76 | break; | |
77 | } | |
78 | ||
79 | return rv; | |
80 | } | |
81 | ||
82 | static inline u32 max31785_scale_pwm(u32 sensor_val) | |
83 | { | |
84 | /* | |
85 | * The datasheet describes the accepted value range for manual PWM as | |
86 | * [0, 0x2710], while the hwmon pwmX sysfs interface accepts values in | |
87 | * [0, 255]. The MAX31785 uses DIRECT mode to scale the FAN_COMMAND | |
88 | * registers and in PWM mode the coefficients are m=1, b=0, R=2. The | |
89 | * important observation here is that 0x2710 == 10000 == 100 * 100. | |
90 | * | |
91 | * R=2 (== 10^2 == 100) accounts for scaling the value provided at the | |
92 | * sysfs interface into the required hardware resolution, but it does | |
93 | * not yet yield a value that we can write to the device (this initial | |
94 | * scaling is handled by pmbus_data2reg()). Multiplying by 100 below | |
95 | * translates the parameter value into the percentage units required by | |
96 | * PMBus, and then we scale back by 255 as required by the hwmon pwmX | |
97 | * interface to yield the percentage value at the appropriate | |
98 | * resolution for hardware. | |
99 | */ | |
100 | return (sensor_val * 100) / 255; | |
101 | } | |
102 | ||
103 | static int max31785_pwm_enable(struct i2c_client *client, int page, | |
104 | u16 word) | |
105 | { | |
106 | int config = 0; | |
107 | int rate; | |
108 | ||
109 | switch (word) { | |
110 | case 0: | |
111 | rate = 0x7fff; | |
112 | break; | |
113 | case 1: | |
114 | rate = pmbus_get_fan_rate_cached(client, page, 0, percent); | |
115 | if (rate < 0) | |
116 | return rate; | |
117 | rate = max31785_scale_pwm(rate); | |
118 | break; | |
119 | case 2: | |
120 | config = PB_FAN_1_RPM; | |
121 | rate = pmbus_get_fan_rate_cached(client, page, 0, rpm); | |
122 | if (rate < 0) | |
123 | return rate; | |
124 | break; | |
125 | case 3: | |
126 | rate = 0xffff; | |
127 | break; | |
128 | default: | |
129 | return -EINVAL; | |
130 | } | |
131 | ||
132 | return pmbus_update_fan(client, page, 0, config, PB_FAN_1_RPM, rate); | |
133 | } | |
134 | ||
135 | static int max31785_write_word_data(struct i2c_client *client, int page, | |
136 | int reg, u16 word) | |
137 | { | |
138 | switch (reg) { | |
139 | case PMBUS_VIRT_PWM_1: | |
140 | return pmbus_update_fan(client, page, 0, 0, PB_FAN_1_RPM, | |
141 | max31785_scale_pwm(word)); | |
142 | case PMBUS_VIRT_PWM_ENABLE_1: | |
143 | return max31785_pwm_enable(client, page, word); | |
144 | default: | |
145 | break; | |
146 | } | |
147 | ||
148 | return -ENODATA; | |
149 | } | |
150 | ||
4d420a6a | 151 | #define MAX31785_FAN_FUNCS \ |
56ad86b4 | 152 | (PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 | PMBUS_HAVE_PWM12) |
4d420a6a AJ |
153 | |
154 | #define MAX31785_TEMP_FUNCS \ | |
155 | (PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP) | |
156 | ||
157 | #define MAX31785_VOUT_FUNCS \ | |
158 | (PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT) | |
159 | ||
160 | static const struct pmbus_driver_info max31785_info = { | |
161 | .pages = MAX31785_NR_PAGES, | |
162 | ||
56ad86b4 AJ |
163 | .write_word_data = max31785_write_word_data, |
164 | .read_word_data = max31785_read_word_data, | |
165 | ||
4d420a6a AJ |
166 | /* RPM */ |
167 | .format[PSC_FAN] = direct, | |
168 | .m[PSC_FAN] = 1, | |
169 | .b[PSC_FAN] = 0, | |
170 | .R[PSC_FAN] = 0, | |
56ad86b4 AJ |
171 | /* PWM */ |
172 | .format[PSC_PWM] = direct, | |
173 | .m[PSC_PWM] = 1, | |
174 | .b[PSC_PWM] = 0, | |
175 | .R[PSC_PWM] = 2, | |
4d420a6a AJ |
176 | .func[0] = MAX31785_FAN_FUNCS, |
177 | .func[1] = MAX31785_FAN_FUNCS, | |
178 | .func[2] = MAX31785_FAN_FUNCS, | |
179 | .func[3] = MAX31785_FAN_FUNCS, | |
180 | .func[4] = MAX31785_FAN_FUNCS, | |
181 | .func[5] = MAX31785_FAN_FUNCS, | |
182 | ||
183 | .format[PSC_TEMPERATURE] = direct, | |
184 | .m[PSC_TEMPERATURE] = 1, | |
185 | .b[PSC_TEMPERATURE] = 0, | |
186 | .R[PSC_TEMPERATURE] = 2, | |
187 | .func[6] = MAX31785_TEMP_FUNCS, | |
188 | .func[7] = MAX31785_TEMP_FUNCS, | |
189 | .func[8] = MAX31785_TEMP_FUNCS, | |
190 | .func[9] = MAX31785_TEMP_FUNCS, | |
191 | .func[10] = MAX31785_TEMP_FUNCS, | |
192 | .func[11] = MAX31785_TEMP_FUNCS, | |
193 | .func[12] = MAX31785_TEMP_FUNCS, | |
194 | .func[13] = MAX31785_TEMP_FUNCS, | |
195 | .func[14] = MAX31785_TEMP_FUNCS, | |
196 | .func[15] = MAX31785_TEMP_FUNCS, | |
197 | .func[16] = MAX31785_TEMP_FUNCS, | |
198 | ||
199 | .format[PSC_VOLTAGE_OUT] = direct, | |
200 | .m[PSC_VOLTAGE_OUT] = 1, | |
201 | .b[PSC_VOLTAGE_OUT] = 0, | |
202 | .R[PSC_VOLTAGE_OUT] = 0, | |
203 | .func[17] = MAX31785_VOUT_FUNCS, | |
204 | .func[18] = MAX31785_VOUT_FUNCS, | |
205 | .func[19] = MAX31785_VOUT_FUNCS, | |
206 | .func[20] = MAX31785_VOUT_FUNCS, | |
207 | .func[21] = MAX31785_VOUT_FUNCS, | |
208 | .func[22] = MAX31785_VOUT_FUNCS, | |
209 | }; | |
210 | ||
211 | static int max31785_probe(struct i2c_client *client, | |
212 | const struct i2c_device_id *id) | |
213 | { | |
214 | struct device *dev = &client->dev; | |
215 | struct pmbus_driver_info *info; | |
216 | s64 ret; | |
217 | ||
218 | info = devm_kzalloc(dev, sizeof(struct pmbus_driver_info), GFP_KERNEL); | |
219 | if (!info) | |
220 | return -ENOMEM; | |
221 | ||
222 | *info = max31785_info; | |
223 | ||
224 | ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 255); | |
225 | if (ret < 0) | |
226 | return ret; | |
227 | ||
228 | return pmbus_do_probe(client, id, info); | |
229 | } | |
230 | ||
231 | static const struct i2c_device_id max31785_id[] = { | |
232 | { "max31785", 0 }, | |
233 | { "max31785a", 0 }, | |
234 | { }, | |
235 | }; | |
236 | ||
237 | MODULE_DEVICE_TABLE(i2c, max31785_id); | |
238 | ||
239 | static struct i2c_driver max31785_driver = { | |
240 | .driver = { | |
241 | .name = "max31785", | |
242 | }, | |
243 | .probe = max31785_probe, | |
244 | .remove = pmbus_do_remove, | |
245 | .id_table = max31785_id, | |
246 | }; | |
247 | ||
248 | module_i2c_driver(max31785_driver); | |
249 | ||
250 | MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>"); | |
251 | MODULE_DESCRIPTION("PMBus driver for the Maxim MAX31785"); | |
252 | MODULE_LICENSE("GPL"); |