Blame view

drivers/hwmon/atxp1.c 9.17 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>
5a0e3ad6a   Tejun Heo   include cleanup: ...
31
  #include <linux/slab.h>
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
32
33
34
  
  MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("System voltages control via Attansic ATXP1");
13b3c3fa2   Jean Delvare   hwmon: (atxp1) Fi...
35
  MODULE_VERSION("0.6.3");
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
36
37
38
39
40
41
42
43
44
  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...
45
  static const unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END };
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
46

71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
47
48
49
  static int atxp1_probe(struct i2c_client *client,
  		       const struct i2c_device_id *id);
  static int atxp1_remove(struct i2c_client *client);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
50
  static struct atxp1_data * atxp1_update_device(struct device *dev);
310ec7921   Jean Delvare   i2c: Drop the kin...
51
  static int atxp1_detect(struct i2c_client *client, struct i2c_board_info *info);
71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
52
53
  
  static const struct i2c_device_id atxp1_id[] = {
1f86df49d   Jean Delvare   i2c: Drop I2C_CLI...
54
  	{ "atxp1", 0 },
71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
55
56
57
  	{ }
  };
  MODULE_DEVICE_TABLE(i2c, atxp1_id);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
58
59
  
  static struct i2c_driver atxp1_driver = {
71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
60
  	.class		= I2C_CLASS_HWMON,
cdaf79349   Laurent Riffard   [PATCH] i2c: Drop...
61
  	.driver = {
cdaf79349   Laurent Riffard   [PATCH] i2c: Drop...
62
63
  		.name	= "atxp1",
  	},
71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
64
65
66
67
  	.probe		= atxp1_probe,
  	.remove		= atxp1_remove,
  	.id_table	= atxp1_id,
  	.detect		= atxp1_detect,
c3813d6af   Jean Delvare   i2c: Get rid of s...
68
  	.address_list	= normal_i2c,
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
69
70
71
  };
  
  struct atxp1_data {
1beeffe43   Tony Jones   hwmon: Convert fr...
72
  	struct device *hwmon_dev;
9a61bf630   Ingo Molnar   [PATCH] hwmon: Se...
73
  	struct mutex update_lock;
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
  	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...
92
  	mutex_lock(&data->update_lock);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
93

0cacdf298   Jean Delvare   [PATCH] I2C: use ...
94
  	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
95
96
97
98
99
100
101
102
103
  
  		/* 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...
104
  	mutex_unlock(&data->update_lock);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
105
106
107
108
109
  
  	return(data);
  }
  
  /* sys file functions for cpu0_vid */
6f637a649   Greg Kroah-Hartman   [PATCH] I2C: fix ...
110
  static ssize_t atxp1_showvcore(struct device *dev, struct device_attribute *attr, char *buf)
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
111
112
113
114
115
116
117
118
119
120
121
  {
  	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 ...
122
  static ssize_t atxp1_storevcore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
123
124
125
  {
  	struct atxp1_data *data;
  	struct i2c_client *client;
c41bdb526   Alexey Dobriyan   atxp1: Signed/uns...
126
  	int vid, cvid;
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
  	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...
154
155
  	dev_dbg(dev, "Setting VCore to %d mV (0x%02x)
  ", vcore, vid);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
  
  	/* 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 ...
180
  static ssize_t atxp1_showgpio1(struct device *dev, struct device_attribute *attr, char *buf)
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
181
182
183
184
185
186
187
188
189
190
191
  {
  	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 ...
192
  static ssize_t atxp1_storegpio1(struct device *dev, struct device_attribute *attr, const char*buf, size_t count)
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
  {
  	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 ...
223
  static ssize_t atxp1_showgpio2(struct device *dev, struct device_attribute *attr, char *buf)
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
224
225
226
227
228
229
230
231
232
233
234
  {
  	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 ...
235
  static ssize_t atxp1_storegpio2(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
  {
  	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...
262
263
264
265
266
267
268
269
270
271
  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 ...
272

71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
273
  /* Return 0 if detection is successful, -ENODEV otherwise */
310ec7921   Jean Delvare   i2c: Drop the kin...
274
  static int atxp1_detect(struct i2c_client *new_client,
71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
275
  			struct i2c_board_info *info)
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
276
  {
71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
277
  	struct i2c_adapter *adapter = new_client->adapter;
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
278

9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
279
280
281
  	u8 temp;
  
  	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
282
  		return -ENODEV;
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
283
284
285
286
287
  
  	/* 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) &&
13b3c3fa2   Jean Delvare   hwmon: (atxp1) Fi...
288
289
  	     (i2c_smbus_read_byte_data(new_client, 0xff) == 0)))
  		return -ENODEV;
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
290

13b3c3fa2   Jean Delvare   hwmon: (atxp1) Fi...
291
292
293
  	/* 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);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
294

13b3c3fa2   Jean Delvare   hwmon: (atxp1) Fi...
295
296
297
  	if (!((i2c_smbus_read_byte_data(new_client, 0x10) == temp) &&
  	      (i2c_smbus_read_byte_data(new_client, 0x11) == temp)))
  		return -ENODEV;
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
298
299
  
  	/* Get VRM */
71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
300
  	temp = vid_which_vrm();
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
301

71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
302
303
304
305
306
  	if ((temp != 90) && (temp != 91)) {
  		dev_err(&adapter->dev, "atxp1: Not supporting VRM %d.%d
  ",
  				temp / 10, temp % 10);
  		return -ENODEV;
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
307
  	}
71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
308
  	strlcpy(info->type, "atxp1", I2C_NAME_SIZE);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
309

71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
310
311
  	return 0;
  }
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
312

71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
313
314
315
316
317
  static int atxp1_probe(struct i2c_client *new_client,
  		       const struct i2c_device_id *id)
  {
  	struct atxp1_data *data;
  	int err;
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
318

71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
319
320
321
322
  	data = kzalloc(sizeof(struct atxp1_data), GFP_KERNEL);
  	if (!data) {
  		err = -ENOMEM;
  		goto exit;
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
323
  	}
71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
324
325
326
327
328
329
330
  	/* Get VRM */
  	data->vrm = vid_which_vrm();
  
  	i2c_set_clientdata(new_client, data);
  	data->valid = 0;
  
  	mutex_init(&data->update_lock);
a5ebe668a   Jean Delvare   hwmon: Fix unchec...
331
332
  	/* Register sysfs hooks */
  	if ((err = sysfs_create_group(&new_client->dev.kobj, &atxp1_group)))
71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
333
  		goto exit_free;
a5ebe668a   Jean Delvare   hwmon: Fix unchec...
334

1beeffe43   Tony Jones   hwmon: Convert fr...
335
336
337
  	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...
338
  		goto exit_remove_files;
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
339
  	}
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
340
341
342
343
344
  	dev_info(&new_client->dev, "Using VRM: %d.%d
  ",
  			 data->vrm / 10, data->vrm % 10);
  
  	return 0;
a5ebe668a   Jean Delvare   hwmon: Fix unchec...
345
346
  exit_remove_files:
  	sysfs_remove_group(&new_client->dev.kobj, &atxp1_group);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
347
348
349
350
351
  exit_free:
  	kfree(data);
  exit:
  	return err;
  };
71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
352
  static int atxp1_remove(struct i2c_client *client)
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
353
  {
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
354
  	struct atxp1_data * data = i2c_get_clientdata(client);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
355

1beeffe43   Tony Jones   hwmon: Convert fr...
356
  	hwmon_device_unregister(data->hwmon_dev);
a5ebe668a   Jean Delvare   hwmon: Fix unchec...
357
  	sysfs_remove_group(&client->dev.kobj, &atxp1_group);
943b0830c   Mark M. Hoffman   [PATCH] I2C hwmon...
358

71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
359
  	kfree(data);
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
360

71163c7c3   Jean Delvare   hwmon: (atxp1) Co...
361
  	return 0;
9cb7d1843   Sebastian Witt   [PATCH] I2C: add ...
362
363
364
365
366
367
368
369
370
371
372
373
374
375
  };
  
  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);