1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * atxp1.c - kernel module for setting CPU VID and general purpose |
4 | * I/Os using the Attansic ATXP1 chip. |
5 | * |
6 | * The ATXP1 can reside on I2C addresses 0x37 or 0x4e. The chip is |
7 | * not auto-detected by the driver and must be instantiated explicitly. |
8 | * See Documentation/i2c/instantiating-devices.rst for more information. |
9 | */ |
10 | |
11 | #include <linux/kernel.h> |
12 | #include <linux/init.h> |
13 | #include <linux/module.h> |
14 | #include <linux/jiffies.h> |
15 | #include <linux/i2c.h> |
16 | #include <linux/hwmon.h> |
17 | #include <linux/hwmon-vid.h> |
18 | #include <linux/err.h> |
19 | #include <linux/kstrtox.h> |
20 | #include <linux/mutex.h> |
21 | #include <linux/sysfs.h> |
22 | #include <linux/slab.h> |
23 | |
24 | MODULE_LICENSE("GPL" ); |
25 | MODULE_DESCRIPTION("System voltages control via Attansic ATXP1" ); |
26 | MODULE_VERSION("0.6.3" ); |
27 | MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>" ); |
28 | |
29 | #define ATXP1_VID 0x00 |
30 | #define ATXP1_CVID 0x01 |
31 | #define ATXP1_GPIO1 0x06 |
32 | #define ATXP1_GPIO2 0x0a |
33 | #define ATXP1_VIDENA 0x20 |
34 | #define ATXP1_VIDMASK 0x1f |
35 | #define ATXP1_GPIO1MASK 0x0f |
36 | |
37 | struct atxp1_data { |
38 | struct i2c_client *client; |
39 | struct mutex update_lock; |
40 | unsigned long last_updated; |
41 | bool valid; |
42 | struct { |
43 | u8 vid; /* VID output register */ |
44 | u8 cpu_vid; /* VID input from CPU */ |
45 | u8 gpio1; /* General purpose I/O register 1 */ |
46 | u8 gpio2; /* General purpose I/O register 2 */ |
47 | } reg; |
48 | u8 vrm; /* Detected CPU VRM */ |
49 | }; |
50 | |
51 | static struct atxp1_data *atxp1_update_device(struct device *dev) |
52 | { |
53 | struct atxp1_data *data = dev_get_drvdata(dev); |
54 | struct i2c_client *client = data->client; |
55 | |
56 | mutex_lock(&data->update_lock); |
57 | |
58 | if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { |
59 | |
60 | /* Update local register data */ |
61 | data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID); |
62 | data->reg.cpu_vid = i2c_smbus_read_byte_data(client, |
63 | ATXP1_CVID); |
64 | data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1); |
65 | data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2); |
66 | |
67 | data->valid = true; |
68 | } |
69 | |
70 | mutex_unlock(lock: &data->update_lock); |
71 | |
72 | return data; |
73 | } |
74 | |
75 | /* sys file functions for cpu0_vid */ |
76 | static ssize_t cpu0_vid_show(struct device *dev, |
77 | struct device_attribute *attr, char *buf) |
78 | { |
79 | int size; |
80 | struct atxp1_data *data; |
81 | |
82 | data = atxp1_update_device(dev); |
83 | |
84 | size = sprintf(buf, fmt: "%d\n" , vid_from_reg(val: data->reg.vid & ATXP1_VIDMASK, |
85 | vrm: data->vrm)); |
86 | |
87 | return size; |
88 | } |
89 | |
90 | static ssize_t cpu0_vid_store(struct device *dev, |
91 | struct device_attribute *attr, const char *buf, |
92 | size_t count) |
93 | { |
94 | struct atxp1_data *data = atxp1_update_device(dev); |
95 | struct i2c_client *client = data->client; |
96 | int vid, cvid; |
97 | unsigned long vcore; |
98 | int err; |
99 | |
100 | err = kstrtoul(s: buf, base: 10, res: &vcore); |
101 | if (err) |
102 | return err; |
103 | |
104 | vcore /= 25; |
105 | vcore *= 25; |
106 | |
107 | /* Calculate VID */ |
108 | vid = vid_to_reg(val: vcore, vrm: data->vrm); |
109 | if (vid < 0) { |
110 | dev_err(dev, "VID calculation failed.\n" ); |
111 | return vid; |
112 | } |
113 | |
114 | /* |
115 | * If output enabled, use control register value. |
116 | * Otherwise original CPU VID |
117 | */ |
118 | if (data->reg.vid & ATXP1_VIDENA) |
119 | cvid = data->reg.vid & ATXP1_VIDMASK; |
120 | else |
121 | cvid = data->reg.cpu_vid; |
122 | |
123 | /* Nothing changed, aborting */ |
124 | if (vid == cvid) |
125 | return count; |
126 | |
127 | dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n" , (int)vcore, vid); |
128 | |
129 | /* Write every 25 mV step to increase stability */ |
130 | if (cvid > vid) { |
131 | for (; cvid >= vid; cvid--) |
132 | i2c_smbus_write_byte_data(client, |
133 | ATXP1_VID, value: cvid | ATXP1_VIDENA); |
134 | } else { |
135 | for (; cvid <= vid; cvid++) |
136 | i2c_smbus_write_byte_data(client, |
137 | ATXP1_VID, value: cvid | ATXP1_VIDENA); |
138 | } |
139 | |
140 | data->valid = false; |
141 | |
142 | return count; |
143 | } |
144 | |
145 | /* |
146 | * CPU core reference voltage |
147 | * unit: millivolt |
148 | */ |
149 | static DEVICE_ATTR_RW(cpu0_vid); |
150 | |
151 | /* sys file functions for GPIO1 */ |
152 | static ssize_t gpio1_show(struct device *dev, struct device_attribute *attr, |
153 | char *buf) |
154 | { |
155 | int size; |
156 | struct atxp1_data *data; |
157 | |
158 | data = atxp1_update_device(dev); |
159 | |
160 | size = sprintf(buf, fmt: "0x%02x\n" , data->reg.gpio1 & ATXP1_GPIO1MASK); |
161 | |
162 | return size; |
163 | } |
164 | |
165 | static ssize_t gpio1_store(struct device *dev, struct device_attribute *attr, |
166 | const char *buf, size_t count) |
167 | { |
168 | struct atxp1_data *data = atxp1_update_device(dev); |
169 | struct i2c_client *client = data->client; |
170 | unsigned long value; |
171 | int err; |
172 | |
173 | err = kstrtoul(s: buf, base: 16, res: &value); |
174 | if (err) |
175 | return err; |
176 | |
177 | value &= ATXP1_GPIO1MASK; |
178 | |
179 | if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) { |
180 | dev_info(dev, "Writing 0x%x to GPIO1.\n" , (unsigned int)value); |
181 | |
182 | i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value); |
183 | |
184 | data->valid = false; |
185 | } |
186 | |
187 | return count; |
188 | } |
189 | |
190 | /* |
191 | * GPIO1 data register |
192 | * unit: Four bit as hex (e.g. 0x0f) |
193 | */ |
194 | static DEVICE_ATTR_RW(gpio1); |
195 | |
196 | /* sys file functions for GPIO2 */ |
197 | static ssize_t gpio2_show(struct device *dev, struct device_attribute *attr, |
198 | char *buf) |
199 | { |
200 | int size; |
201 | struct atxp1_data *data; |
202 | |
203 | data = atxp1_update_device(dev); |
204 | |
205 | size = sprintf(buf, fmt: "0x%02x\n" , data->reg.gpio2); |
206 | |
207 | return size; |
208 | } |
209 | |
210 | static ssize_t gpio2_store(struct device *dev, struct device_attribute *attr, |
211 | const char *buf, size_t count) |
212 | { |
213 | struct atxp1_data *data = atxp1_update_device(dev); |
214 | struct i2c_client *client = data->client; |
215 | unsigned long value; |
216 | int err; |
217 | |
218 | err = kstrtoul(s: buf, base: 16, res: &value); |
219 | if (err) |
220 | return err; |
221 | value &= 0xff; |
222 | |
223 | if (value != data->reg.gpio2) { |
224 | dev_info(dev, "Writing 0x%x to GPIO1.\n" , (unsigned int)value); |
225 | |
226 | i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value); |
227 | |
228 | data->valid = false; |
229 | } |
230 | |
231 | return count; |
232 | } |
233 | |
234 | /* |
235 | * GPIO2 data register |
236 | * unit: Eight bit as hex (e.g. 0xff) |
237 | */ |
238 | static DEVICE_ATTR_RW(gpio2); |
239 | |
240 | static struct attribute *atxp1_attrs[] = { |
241 | &dev_attr_gpio1.attr, |
242 | &dev_attr_gpio2.attr, |
243 | &dev_attr_cpu0_vid.attr, |
244 | NULL |
245 | }; |
246 | ATTRIBUTE_GROUPS(atxp1); |
247 | |
248 | static int atxp1_probe(struct i2c_client *client) |
249 | { |
250 | struct device *dev = &client->dev; |
251 | struct atxp1_data *data; |
252 | struct device *hwmon_dev; |
253 | |
254 | data = devm_kzalloc(dev, size: sizeof(struct atxp1_data), GFP_KERNEL); |
255 | if (!data) |
256 | return -ENOMEM; |
257 | |
258 | /* Get VRM */ |
259 | data->vrm = vid_which_vrm(); |
260 | if (data->vrm != 90 && data->vrm != 91) { |
261 | dev_err(dev, "atxp1: Not supporting VRM %d.%d\n" , |
262 | data->vrm / 10, data->vrm % 10); |
263 | return -ENODEV; |
264 | } |
265 | |
266 | data->client = client; |
267 | mutex_init(&data->update_lock); |
268 | |
269 | hwmon_dev = devm_hwmon_device_register_with_groups(dev, name: client->name, |
270 | drvdata: data, |
271 | groups: atxp1_groups); |
272 | if (IS_ERR(ptr: hwmon_dev)) |
273 | return PTR_ERR(ptr: hwmon_dev); |
274 | |
275 | dev_info(dev, "Using VRM: %d.%d\n" , data->vrm / 10, data->vrm % 10); |
276 | |
277 | return 0; |
278 | }; |
279 | |
280 | static const struct i2c_device_id atxp1_id[] = { |
281 | { "atxp1" , 0 }, |
282 | { } |
283 | }; |
284 | MODULE_DEVICE_TABLE(i2c, atxp1_id); |
285 | |
286 | static struct i2c_driver atxp1_driver = { |
287 | .class = I2C_CLASS_HWMON, |
288 | .driver = { |
289 | .name = "atxp1" , |
290 | }, |
291 | .probe = atxp1_probe, |
292 | .id_table = atxp1_id, |
293 | }; |
294 | |
295 | module_i2c_driver(atxp1_driver); |
296 | |