Blame view

drivers/hwmon/via-cputemp.c 8.39 KB
70c38772a   Harald Welte   hwmon: Add driver...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  /*
   * via-cputemp.c - Driver for VIA CPU core temperature monitoring
   * Copyright (C) 2009 VIA Technologies, Inc.
   *
   * based on existing coretemp.c, which is
   *
   * Copyright (C) 2007 Rudolf Marek <r.marek@assembler.cz>
   *
   * 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; version 2 of the License.
   *
   * 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., 51 Franklin Street, Fifth Floor, Boston, MA
   * 02110-1301 USA.
   */
edb8d53c6   Joe Perches   hwmon: (via-cpute...
23
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
70c38772a   Harald Welte   hwmon: Add driver...
24
  #include <linux/module.h>
70c38772a   Harald Welte   hwmon: Add driver...
25
26
  #include <linux/init.h>
  #include <linux/slab.h>
70c38772a   Harald Welte   hwmon: Add driver...
27
  #include <linux/hwmon.h>
764e043bb   Jean Delvare   hwmon: (via-cpute...
28
  #include <linux/hwmon-vid.h>
70c38772a   Harald Welte   hwmon: Add driver...
29
30
31
32
33
34
35
36
37
38
39
  #include <linux/sysfs.h>
  #include <linux/hwmon-sysfs.h>
  #include <linux/err.h>
  #include <linux/mutex.h>
  #include <linux/list.h>
  #include <linux/platform_device.h>
  #include <linux/cpu.h>
  #include <asm/msr.h>
  #include <asm/processor.h>
  
  #define DRVNAME	"via_cputemp"
f27994186   H. Peter Anvin   hwmon: (via-cpute...
40
  enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME };
70c38772a   Harald Welte   hwmon: Add driver...
41
42
43
44
45
46
47
48
  
  /*
   * Functions declaration
   */
  
  struct via_cputemp_data {
  	struct device *hwmon_dev;
  	const char *name;
764e043bb   Jean Delvare   hwmon: (via-cpute...
49
  	u8 vrm;
70c38772a   Harald Welte   hwmon: Add driver...
50
  	u32 id;
764e043bb   Jean Delvare   hwmon: (via-cpute...
51
52
  	u32 msr_temp;
  	u32 msr_vid;
70c38772a   Harald Welte   hwmon: Add driver...
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
  };
  
  /*
   * Sysfs stuff
   */
  
  static ssize_t show_name(struct device *dev, struct device_attribute
  			  *devattr, char *buf)
  {
  	int ret;
  	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
  	struct via_cputemp_data *data = dev_get_drvdata(dev);
  
  	if (attr->index == SHOW_NAME)
  		ret = sprintf(buf, "%s
  ", data->name);
  	else	/* show label */
  		ret = sprintf(buf, "Core %d
  ", data->id);
  	return ret;
  }
  
  static ssize_t show_temp(struct device *dev,
  			 struct device_attribute *devattr, char *buf)
  {
  	struct via_cputemp_data *data = dev_get_drvdata(dev);
  	u32 eax, edx;
  	int err;
764e043bb   Jean Delvare   hwmon: (via-cpute...
81
  	err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx);
70c38772a   Harald Welte   hwmon: Add driver...
82
83
84
85
86
87
  	if (err)
  		return -EAGAIN;
  
  	return sprintf(buf, "%lu
  ", ((unsigned long)eax & 0xffffff) * 1000);
  }
764e043bb   Jean Delvare   hwmon: (via-cpute...
88
89
90
91
92
93
94
95
96
97
98
99
100
101
  static ssize_t show_cpu_vid(struct device *dev,
  			    struct device_attribute *devattr, char *buf)
  {
  	struct via_cputemp_data *data = dev_get_drvdata(dev);
  	u32 eax, edx;
  	int err;
  
  	err = rdmsr_safe_on_cpu(data->id, data->msr_vid, &eax, &edx);
  	if (err)
  		return -EAGAIN;
  
  	return sprintf(buf, "%d
  ", vid_from_reg(~edx & 0x7f, data->vrm));
  }
70c38772a   Harald Welte   hwmon: Add driver...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
  static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL,
  			  SHOW_TEMP);
  static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL);
  static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, SHOW_NAME);
  
  static struct attribute *via_cputemp_attributes[] = {
  	&sensor_dev_attr_name.dev_attr.attr,
  	&sensor_dev_attr_temp1_label.dev_attr.attr,
  	&sensor_dev_attr_temp1_input.dev_attr.attr,
  	NULL
  };
  
  static const struct attribute_group via_cputemp_group = {
  	.attrs = via_cputemp_attributes,
  };
764e043bb   Jean Delvare   hwmon: (via-cpute...
117
118
  /* Optional attributes */
  static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_cpu_vid, NULL);
70c38772a   Harald Welte   hwmon: Add driver...
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
  static int __devinit via_cputemp_probe(struct platform_device *pdev)
  {
  	struct via_cputemp_data *data;
  	struct cpuinfo_x86 *c = &cpu_data(pdev->id);
  	int err;
  	u32 eax, edx;
  
  	data = kzalloc(sizeof(struct via_cputemp_data), GFP_KERNEL);
  	if (!data) {
  		err = -ENOMEM;
  		dev_err(&pdev->dev, "Out of memory
  ");
  		goto exit;
  	}
  
  	data->id = pdev->id;
  	data->name = "via_cputemp";
  
  	switch (c->x86_model) {
  	case 0xA:
  		/* C7 A */
  	case 0xD:
  		/* C7 D */
764e043bb   Jean Delvare   hwmon: (via-cpute...
142
143
  		data->msr_temp = 0x1169;
  		data->msr_vid = 0x198;
70c38772a   Harald Welte   hwmon: Add driver...
144
145
146
  		break;
  	case 0xF:
  		/* Nano */
764e043bb   Jean Delvare   hwmon: (via-cpute...
147
  		data->msr_temp = 0x1423;
70c38772a   Harald Welte   hwmon: Add driver...
148
149
150
151
152
153
154
  		break;
  	default:
  		err = -ENODEV;
  		goto exit_free;
  	}
  
  	/* test if we can access the TEMPERATURE MSR */
764e043bb   Jean Delvare   hwmon: (via-cpute...
155
  	err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx);
70c38772a   Harald Welte   hwmon: Add driver...
156
157
158
159
160
161
162
163
164
165
166
167
  	if (err) {
  		dev_err(&pdev->dev,
  			"Unable to access TEMPERATURE MSR, giving up
  ");
  		goto exit_free;
  	}
  
  	platform_set_drvdata(pdev, data);
  
  	err = sysfs_create_group(&pdev->dev.kobj, &via_cputemp_group);
  	if (err)
  		goto exit_free;
764e043bb   Jean Delvare   hwmon: (via-cpute...
168
169
170
171
172
173
174
175
  	if (data->msr_vid)
  		data->vrm = vid_which_vrm();
  
  	if (data->vrm) {
  		err = device_create_file(&pdev->dev, &dev_attr_cpu0_vid);
  		if (err)
  			goto exit_remove;
  	}
70c38772a   Harald Welte   hwmon: Add driver...
176
177
178
179
180
181
182
183
184
185
186
187
  	data->hwmon_dev = hwmon_device_register(&pdev->dev);
  	if (IS_ERR(data->hwmon_dev)) {
  		err = PTR_ERR(data->hwmon_dev);
  		dev_err(&pdev->dev, "Class registration failed (%d)
  ",
  			err);
  		goto exit_remove;
  	}
  
  	return 0;
  
  exit_remove:
764e043bb   Jean Delvare   hwmon: (via-cpute...
188
189
  	if (data->vrm)
  		device_remove_file(&pdev->dev, &dev_attr_cpu0_vid);
70c38772a   Harald Welte   hwmon: Add driver...
190
191
192
193
194
195
196
197
198
199
200
201
202
  	sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
  exit_free:
  	platform_set_drvdata(pdev, NULL);
  	kfree(data);
  exit:
  	return err;
  }
  
  static int __devexit via_cputemp_remove(struct platform_device *pdev)
  {
  	struct via_cputemp_data *data = platform_get_drvdata(pdev);
  
  	hwmon_device_unregister(data->hwmon_dev);
764e043bb   Jean Delvare   hwmon: (via-cpute...
203
204
  	if (data->vrm)
  		device_remove_file(&pdev->dev, &dev_attr_cpu0_vid);
70c38772a   Harald Welte   hwmon: Add driver...
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
  	sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
  	platform_set_drvdata(pdev, NULL);
  	kfree(data);
  	return 0;
  }
  
  static struct platform_driver via_cputemp_driver = {
  	.driver = {
  		.owner = THIS_MODULE,
  		.name = DRVNAME,
  	},
  	.probe = via_cputemp_probe,
  	.remove = __devexit_p(via_cputemp_remove),
  };
  
  struct pdev_entry {
  	struct list_head list;
  	struct platform_device *pdev;
  	unsigned int cpu;
  };
  
  static LIST_HEAD(pdev_list);
  static DEFINE_MUTEX(pdev_list_mutex);
  
  static int __cpuinit via_cputemp_device_add(unsigned int cpu)
  {
  	int err;
  	struct platform_device *pdev;
  	struct pdev_entry *pdev_entry;
  
  	pdev = platform_device_alloc(DRVNAME, cpu);
  	if (!pdev) {
  		err = -ENOMEM;
edb8d53c6   Joe Perches   hwmon: (via-cpute...
238
239
  		pr_err("Device allocation failed
  ");
70c38772a   Harald Welte   hwmon: Add driver...
240
241
242
243
244
245
246
247
248
249
250
  		goto exit;
  	}
  
  	pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL);
  	if (!pdev_entry) {
  		err = -ENOMEM;
  		goto exit_device_put;
  	}
  
  	err = platform_device_add(pdev);
  	if (err) {
edb8d53c6   Joe Perches   hwmon: (via-cpute...
251
252
  		pr_err("Device addition failed (%d)
  ", err);
70c38772a   Harald Welte   hwmon: Add driver...
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
  		goto exit_device_free;
  	}
  
  	pdev_entry->pdev = pdev;
  	pdev_entry->cpu = cpu;
  	mutex_lock(&pdev_list_mutex);
  	list_add_tail(&pdev_entry->list, &pdev_list);
  	mutex_unlock(&pdev_list_mutex);
  
  	return 0;
  
  exit_device_free:
  	kfree(pdev_entry);
  exit_device_put:
  	platform_device_put(pdev);
  exit:
  	return err;
  }
a5f42a6bc   Jan Beulich   x86/hwmon: {core,...
271
  static void __cpuinit via_cputemp_device_remove(unsigned int cpu)
70c38772a   Harald Welte   hwmon: Add driver...
272
  {
ae9e0ce73   Jan Beulich   hwmon: (via-cpute...
273
  	struct pdev_entry *p;
70c38772a   Harald Welte   hwmon: Add driver...
274
  	mutex_lock(&pdev_list_mutex);
ae9e0ce73   Jan Beulich   hwmon: (via-cpute...
275
  	list_for_each_entry(p, &pdev_list, list) {
70c38772a   Harald Welte   hwmon: Add driver...
276
277
278
  		if (p->cpu == cpu) {
  			platform_device_unregister(p->pdev);
  			list_del(&p->list);
ae9e0ce73   Jan Beulich   hwmon: (via-cpute...
279
  			mutex_unlock(&pdev_list_mutex);
70c38772a   Harald Welte   hwmon: Add driver...
280
  			kfree(p);
ae9e0ce73   Jan Beulich   hwmon: (via-cpute...
281
  			return;
70c38772a   Harald Welte   hwmon: Add driver...
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  		}
  	}
  	mutex_unlock(&pdev_list_mutex);
  }
  
  static int __cpuinit via_cputemp_cpu_callback(struct notifier_block *nfb,
  				 unsigned long action, void *hcpu)
  {
  	unsigned int cpu = (unsigned long) hcpu;
  
  	switch (action) {
  	case CPU_ONLINE:
  	case CPU_DOWN_FAILED:
  		via_cputemp_device_add(cpu);
  		break;
  	case CPU_DOWN_PREPARE:
  		via_cputemp_device_remove(cpu);
  		break;
  	}
  	return NOTIFY_OK;
  }
  
  static struct notifier_block via_cputemp_cpu_notifier __refdata = {
  	.notifier_call = via_cputemp_cpu_callback,
  };
70c38772a   Harald Welte   hwmon: Add driver...
307
308
309
310
  
  static int __init via_cputemp_init(void)
  {
  	int i, err;
70c38772a   Harald Welte   hwmon: Add driver...
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
  
  	if (cpu_data(0).x86_vendor != X86_VENDOR_CENTAUR) {
  		printk(KERN_DEBUG DRVNAME ": Not a VIA CPU
  ");
  		err = -ENODEV;
  		goto exit;
  	}
  
  	err = platform_driver_register(&via_cputemp_driver);
  	if (err)
  		goto exit;
  
  	for_each_online_cpu(i) {
  		struct cpuinfo_x86 *c = &cpu_data(i);
  
  		if (c->x86 != 6)
  			continue;
  
  		if (c->x86_model < 0x0a)
  			continue;
  
  		if (c->x86_model > 0x0f) {
edb8d53c6   Joe Perches   hwmon: (via-cpute...
333
334
  			pr_warn("Unknown CPU model 0x%x
  ", c->x86_model);
70c38772a   Harald Welte   hwmon: Add driver...
335
336
  			continue;
  		}
ae9e0ce73   Jan Beulich   hwmon: (via-cpute...
337
  		via_cputemp_device_add(i);
70c38772a   Harald Welte   hwmon: Add driver...
338
  	}
ae9e0ce73   Jan Beulich   hwmon: (via-cpute...
339
340
  
  #ifndef CONFIG_HOTPLUG_CPU
70c38772a   Harald Welte   hwmon: Add driver...
341
342
343
344
  	if (list_empty(&pdev_list)) {
  		err = -ENODEV;
  		goto exit_driver_unreg;
  	}
ae9e0ce73   Jan Beulich   hwmon: (via-cpute...
345
  #endif
70c38772a   Harald Welte   hwmon: Add driver...
346

70c38772a   Harald Welte   hwmon: Add driver...
347
  	register_hotcpu_notifier(&via_cputemp_cpu_notifier);
70c38772a   Harald Welte   hwmon: Add driver...
348
  	return 0;
ae9e0ce73   Jan Beulich   hwmon: (via-cpute...
349
  #ifndef CONFIG_HOTPLUG_CPU
70c38772a   Harald Welte   hwmon: Add driver...
350
351
  exit_driver_unreg:
  	platform_driver_unregister(&via_cputemp_driver);
ae9e0ce73   Jan Beulich   hwmon: (via-cpute...
352
  #endif
70c38772a   Harald Welte   hwmon: Add driver...
353
354
355
356
357
358
359
  exit:
  	return err;
  }
  
  static void __exit via_cputemp_exit(void)
  {
  	struct pdev_entry *p, *n;
17c10d61c   Chen Gong   hwmon: ({core, pk...
360

70c38772a   Harald Welte   hwmon: Add driver...
361
  	unregister_hotcpu_notifier(&via_cputemp_cpu_notifier);
70c38772a   Harald Welte   hwmon: Add driver...
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
  	mutex_lock(&pdev_list_mutex);
  	list_for_each_entry_safe(p, n, &pdev_list, list) {
  		platform_device_unregister(p->pdev);
  		list_del(&p->list);
  		kfree(p);
  	}
  	mutex_unlock(&pdev_list_mutex);
  	platform_driver_unregister(&via_cputemp_driver);
  }
  
  MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>");
  MODULE_DESCRIPTION("VIA CPU temperature monitor");
  MODULE_LICENSE("GPL");
  
  module_init(via_cputemp_init)
  module_exit(via_cputemp_exit)