Blame view

drivers/hwmon/atxp1.c 9.38 KB
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  /*
      atxp1.c - kernel module for setting CPU VID and general purpose
                       I/Os using the Attansic ATXP1 chip.
  
      This program is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.
  
      This program is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.
  
      You should have received a copy of the GNU General Public License
      along with this program; if not, write to the Free Software
      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  
  */
  
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/module.h>
0cacdf298   Jean Delvare   [PATCH] I2C: use ...
24
  #include <linux/jiffies.h>
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
25
  #include <linux/i2c.h>
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
26
  #include <linux/hwmon.h>
303760b44   Jean Delvare   [PATCH] hwmon: hw...
27
  #include <linux/hwmon-vid.h>
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
28
  #include <linux/err.h>
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
29
  #include <linux/mutex.h>
a5ebe668a   Jean Delvare   hwmon: Fix unchec...
30
  #include <linux/sysfs.h>
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
31
32
33
34
35
36
37
38
39
40
41
42
43
  
  MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("System voltages control via Attansic ATXP1");
  MODULE_VERSION("0.6.2");
  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
25e9c86d5   Mark M. Hoffman   hwmon: normal_i2c...
44
  static const unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END };
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
45

f4b502612   Jean Delvare   [PATCH] hwmon: hw...
46
  I2C_CLIENT_INSMOD_1(atxp1);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
47
48
49
50
51
52
53
  
  static int atxp1_attach_adapter(struct i2c_adapter * adapter);
  static int atxp1_detach_client(struct i2c_client * client);
  static struct atxp1_data * atxp1_update_device(struct device *dev);
  static int atxp1_detect(struct i2c_adapter *adapter, int address, int kind);
  
  static struct i2c_driver atxp1_driver = {
cdaf79349   Laurent Riffard   [PATCH] i2c: Drop...
54
  	.driver = {
cdaf79349   Laurent Riffard   [PATCH] i2c: Drop...
55
56
  		.name	= "atxp1",
  	},
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
57
58
59
60
61
62
  	.attach_adapter = atxp1_attach_adapter,
  	.detach_client	= atxp1_detach_client,
  };
  
  struct atxp1_data {
  	struct i2c_client client;
1beeffe43   Tony Jones   hwmon: Convert fr...
63
  	struct device *hwmon_dev;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
64
  	struct mutex update_lock;
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
  	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 */
  };
  
  static struct atxp1_data * atxp1_update_device(struct device *dev)
  {
  	struct i2c_client *client;
  	struct atxp1_data *data;
  
  	client = to_i2c_client(dev);
  	data = i2c_get_clientdata(client);
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
83
  	mutex_lock(&data->update_lock);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
84

0cacdf298   Jean Delvare   [PATCH] I2C: use ...
85
  	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
86
87
88
89
90
91
92
93
94
  
  		/* Update local register data */
  		data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID);
  		data->reg.cpu_vid = i2c_smbus_read_byte_data(client, ATXP1_CVID);
  		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   Ingo Molnar   [PATCH] hwmon: Se...
95
  	mutex_unlock(&data->update_lock);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
96
97
98
99
100
  
  	return(data);
  }
  
  /* sys file functions for cpu0_vid */
6f637a649   Greg Kroah-Hartman   [PATCH] I2C: fix ...
101
  static ssize_t atxp1_showvcore(struct device *dev, struct device_attribute *attr, char *buf)
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
102
103
104
105
106
107
108
109
110
111
112
  {
  	int size;
  	struct atxp1_data *data;
  
  	data = atxp1_update_device(dev);
  
  	size = sprintf(buf, "%d
  ", vid_from_reg(data->reg.vid & ATXP1_VIDMASK, data->vrm));
  
  	return size;
  }
6f637a649   Greg Kroah-Hartman   [PATCH] I2C: fix ...
113
  static ssize_t atxp1_storevcore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
114
115
116
  {
  	struct atxp1_data *data;
  	struct i2c_client *client;
c41bdb526   Alexey Dobriyan   atxp1: Signed/uns...
117
  	int vid, cvid;
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
  	unsigned int vcore;
  
  	client = to_i2c_client(dev);
  	data = atxp1_update_device(dev);
  
  	vcore = simple_strtoul(buf, NULL, 10);
  	vcore /= 25;
  	vcore *= 25;
  
  	/* Calculate VID */
  	vid = vid_to_reg(vcore, data->vrm);
  
  	if (vid < 0) {
  		dev_err(dev, "VID calculation failed.
  ");
  		return -1;
  	}
  
  	/* If output enabled, use control register value. Otherwise original CPU VID */
  	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;
164cad9ba   Prakash Punnoor   [PATCH] Don't fil...
145
146
  	dev_dbg(dev, "Setting VCore to %d mV (0x%02x)
  ", vcore, vid);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
  
  	/* Write every 25 mV step to increase stability */
  	if (cvid > vid) {
  		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);
  		}
  	}
  
  	data->valid = 0;
  
  	return count;
  }
  
  /* CPU core reference voltage
      unit: millivolt
  */
  static DEVICE_ATTR(cpu0_vid, S_IRUGO | S_IWUSR, atxp1_showvcore, atxp1_storevcore);
  
  /* sys file functions for GPIO1 */
6f637a649   Greg Kroah-Hartman   [PATCH] I2C: fix ...
171
  static ssize_t atxp1_showgpio1(struct device *dev, struct device_attribute *attr, char *buf)
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
172
173
174
175
176
177
178
179
180
181
182
  {
  	int size;
  	struct atxp1_data *data;
  
  	data = atxp1_update_device(dev);
  
  	size = sprintf(buf, "0x%02x
  ", data->reg.gpio1 & ATXP1_GPIO1MASK);
  
  	return size;
  }
6f637a649   Greg Kroah-Hartman   [PATCH] I2C: fix ...
183
  static ssize_t atxp1_storegpio1(struct device *dev, struct device_attribute *attr, const char*buf, size_t count)
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
  {
  	struct atxp1_data *data;
  	struct i2c_client *client;
  	unsigned int value;
  
  	client = to_i2c_client(dev);
  	data = atxp1_update_device(dev);
  
  	value = simple_strtoul(buf, NULL, 16);
  
  	value &= ATXP1_GPIO1MASK;
  
  	if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) {
  		dev_info(dev, "Writing 0x%x to GPIO1.
  ", value);
  
  		i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value);
  
  		data->valid = 0;
  	}
  
  	return count;
  }
  
  /* GPIO1 data register
      unit: Four bit as hex (e.g. 0x0f)
  */
  static DEVICE_ATTR(gpio1, S_IRUGO | S_IWUSR, atxp1_showgpio1, atxp1_storegpio1);
  
  /* sys file functions for GPIO2 */
6f637a649   Greg Kroah-Hartman   [PATCH] I2C: fix ...
214
  static ssize_t atxp1_showgpio2(struct device *dev, struct device_attribute *attr, char *buf)
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
215
216
217
218
219
220
221
222
223
224
225
  {
  	int size;
  	struct atxp1_data *data;
  
  	data = atxp1_update_device(dev);
  
  	size = sprintf(buf, "0x%02x
  ", data->reg.gpio2);
  
  	return size;
  }
6f637a649   Greg Kroah-Hartman   [PATCH] I2C: fix ...
226
  static ssize_t atxp1_storegpio2(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
  {
  	struct atxp1_data *data;
  	struct i2c_client *client;
  	unsigned int value;
  
  	client = to_i2c_client(dev);
  	data = atxp1_update_device(dev);
  
  	value = simple_strtoul(buf, NULL, 16) & 0xff;
  
  	if (value != data->reg.gpio2) {
  		dev_info(dev, "Writing 0x%x to GPIO1.
  ", value);
  
  		i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value);
  
  		data->valid = 0;
  	}
  
  	return count;
  }
  
  /* GPIO2 data register
      unit: Eight bit as hex (e.g. 0xff)
  */
  static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2);
a5ebe668a   Jean Delvare   hwmon: Fix unchec...
253
254
255
256
257
258
259
260
261
262
  static struct attribute *atxp1_attributes[] = {
  	&dev_attr_gpio1.attr,
  	&dev_attr_gpio2.attr,
  	&dev_attr_cpu0_vid.attr,
  	NULL
  };
  
  static const struct attribute_group atxp1_group = {
  	.attrs = atxp1_attributes,
  };
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
263
264
265
  
  static int atxp1_attach_adapter(struct i2c_adapter *adapter)
  {
ddec748f3   Jean Delvare   [PATCH] hwmon: Mi...
266
267
  	if (!(adapter->class & I2C_CLASS_HWMON))
  		return 0;
2ed2dc3c1   Jean Delvare   [PATCH] hwmon: hw...
268
  	return i2c_probe(adapter, &addr_data, &atxp1_detect);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
269
270
271
272
273
274
275
276
277
278
279
  };
  
  static int atxp1_detect(struct i2c_adapter *adapter, int address, int kind)
  {
  	struct i2c_client * new_client;
  	struct atxp1_data * data;
  	int err = 0;
  	u8 temp;
  
  	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
  		goto exit;
ba9c2e8d1   Deepak Saxena   [PATCH] hwmon: kz...
280
  	if (!(data = kzalloc(sizeof(struct atxp1_data), GFP_KERNEL))) {
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
281
282
283
  		err = -ENOMEM;
  		goto exit;
  	}
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
  	new_client = &data->client;
  	i2c_set_clientdata(new_client, data);
  
  	new_client->addr = address;
  	new_client->adapter = adapter;
  	new_client->driver = &atxp1_driver;
  	new_client->flags = 0;
  
  	/* Detect ATXP1, checking if vendor ID registers are all zero */
  	if (!((i2c_smbus_read_byte_data(new_client, 0x3e) == 0) &&
  	     (i2c_smbus_read_byte_data(new_client, 0x3f) == 0) &&
  	     (i2c_smbus_read_byte_data(new_client, 0xfe) == 0) &&
  	     (i2c_smbus_read_byte_data(new_client, 0xff) == 0) )) {
  
  		/* No vendor ID, now checking if registers 0x10,0x11 (non-existent)
  		 * showing the same as register 0x00 */
  		temp = i2c_smbus_read_byte_data(new_client, 0x00);
  
  		if (!((i2c_smbus_read_byte_data(new_client, 0x10) == temp) &&
  			 (i2c_smbus_read_byte_data(new_client, 0x11) == temp) ))
  			goto exit_free;
  	}
  
  	/* Get VRM */
303760b44   Jean Delvare   [PATCH] hwmon: hw...
308
  	data->vrm = vid_which_vrm();
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
309
310
311
312
313
314
315
316
317
318
319
  
  	if ((data->vrm != 90) && (data->vrm != 91)) {
  		dev_err(&new_client->dev, "Not supporting VRM %d.%d
  ",
  				data->vrm / 10, data->vrm % 10);
  		goto exit_free;
  	}
  
  	strncpy(new_client->name, "atxp1", I2C_NAME_SIZE);
  
  	data->valid = 0;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
320
  	mutex_init(&data->update_lock);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
321
322
323
324
325
326
327
328
329
  
  	err = i2c_attach_client(new_client);
  
  	if (err)
  	{
  		dev_err(&new_client->dev, "Attach client error.
  ");
  		goto exit_free;
  	}
a5ebe668a   Jean Delvare   hwmon: Fix unchec...
330
331
332
  	/* Register sysfs hooks */
  	if ((err = sysfs_create_group(&new_client->dev.kobj, &atxp1_group)))
  		goto exit_detach;
1beeffe43   Tony Jones   hwmon: Convert fr...
333
334
335
  	data->hwmon_dev = hwmon_device_register(&new_client->dev);
  	if (IS_ERR(data->hwmon_dev)) {
  		err = PTR_ERR(data->hwmon_dev);
a5ebe668a   Jean Delvare   hwmon: Fix unchec...
336
  		goto exit_remove_files;
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
337
  	}
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
338
339
340
341
342
  	dev_info(&new_client->dev, "Using VRM: %d.%d
  ",
  			 data->vrm / 10, data->vrm % 10);
  
  	return 0;
a5ebe668a   Jean Delvare   hwmon: Fix unchec...
343
344
  exit_remove_files:
  	sysfs_remove_group(&new_client->dev.kobj, &atxp1_group);
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
345
346
  exit_detach:
  	i2c_detach_client(new_client);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
347
348
349
350
351
352
353
354
  exit_free:
  	kfree(data);
  exit:
  	return err;
  };
  
  static int atxp1_detach_client(struct i2c_client * client)
  {
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
355
  	struct atxp1_data * data = i2c_get_clientdata(client);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
356
  	int err;
1beeffe43   Tony Jones   hwmon: Convert fr...
357
  	hwmon_device_unregister(data->hwmon_dev);
a5ebe668a   Jean Delvare   hwmon: Fix unchec...
358
  	sysfs_remove_group(&client->dev.kobj, &atxp1_group);
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
359

9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
360
361
362
363
364
365
  	err = i2c_detach_client(client);
  
  	if (err)
  		dev_err(&client->dev, "Failed to detach client.
  ");
  	else
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
366
  		kfree(data);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
  
  	return err;
  };
  
  static int __init atxp1_init(void)
  {
  	return i2c_add_driver(&atxp1_driver);
  };
  
  static void __exit atxp1_exit(void)
  {
  	i2c_del_driver(&atxp1_driver);
  };
  
  module_init(atxp1_init);
  module_exit(atxp1_exit);