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