Blame view

drivers/hwmon/via-cputemp.c 7.1 KB
935912c53   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
70c38772a   Harald Welte   hwmon: Add driver...
2
3
4
5
6
7
8
  /*
   * 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>
70c38772a   Harald Welte   hwmon: Add driver...
9
   */
edb8d53c6   Joe Perches   hwmon: (via-cpute...
10
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
70c38772a   Harald Welte   hwmon: Add driver...
11
  #include <linux/module.h>
70c38772a   Harald Welte   hwmon: Add driver...
12
13
  #include <linux/init.h>
  #include <linux/slab.h>
70c38772a   Harald Welte   hwmon: Add driver...
14
  #include <linux/hwmon.h>
764e043bb   Jean Delvare   hwmon: (via-cpute...
15
  #include <linux/hwmon-vid.h>
70c38772a   Harald Welte   hwmon: Add driver...
16
17
18
19
20
21
22
23
24
  #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>
267fc9788   Andi Kleen   HWMON: Convert vi...
25
  #include <asm/cpu_device_id.h>
70c38772a   Harald Welte   hwmon: Add driver...
26
27
  
  #define DRVNAME	"via_cputemp"
f27994186   H. Peter Anvin   hwmon: (via-cpute...
28
  enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME };
70c38772a   Harald Welte   hwmon: Add driver...
29
30
31
32
33
34
35
36
  
  /*
   * Functions declaration
   */
  
  struct via_cputemp_data {
  	struct device *hwmon_dev;
  	const char *name;
764e043bb   Jean Delvare   hwmon: (via-cpute...
37
  	u8 vrm;
70c38772a   Harald Welte   hwmon: Add driver...
38
  	u32 id;
764e043bb   Jean Delvare   hwmon: (via-cpute...
39
40
  	u32 msr_temp;
  	u32 msr_vid;
70c38772a   Harald Welte   hwmon: Add driver...
41
42
43
44
45
  };
  
  /*
   * Sysfs stuff
   */
f74c24f86   Guenter Roeck   hwmon: (via-cpute...
46
47
  static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
  			 char *buf)
70c38772a   Harald Welte   hwmon: Add driver...
48
49
50
51
52
53
54
55
56
57
58
59
60
  {
  	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;
  }
f74c24f86   Guenter Roeck   hwmon: (via-cpute...
61
62
  static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
  			 char *buf)
70c38772a   Harald Welte   hwmon: Add driver...
63
64
65
66
  {
  	struct via_cputemp_data *data = dev_get_drvdata(dev);
  	u32 eax, edx;
  	int err;
764e043bb   Jean Delvare   hwmon: (via-cpute...
67
  	err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx);
70c38772a   Harald Welte   hwmon: Add driver...
68
69
70
71
72
73
  	if (err)
  		return -EAGAIN;
  
  	return sprintf(buf, "%lu
  ", ((unsigned long)eax & 0xffffff) * 1000);
  }
1664d7fd6   Julia Lawall   hwmon: (via-cpute...
74
75
  static ssize_t cpu0_vid_show(struct device *dev,
  			     struct device_attribute *devattr, char *buf)
764e043bb   Jean Delvare   hwmon: (via-cpute...
76
77
78
79
80
81
82
83
84
85
86
87
  {
  	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));
  }
f74c24f86   Guenter Roeck   hwmon: (via-cpute...
88
89
90
  static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, SHOW_TEMP);
  static SENSOR_DEVICE_ATTR_RO(temp1_label, name, SHOW_LABEL);
  static SENSOR_DEVICE_ATTR_RO(name, name, SHOW_NAME);
70c38772a   Harald Welte   hwmon: Add driver...
91
92
93
94
95
96
97
98
99
100
101
  
  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...
102
  /* Optional attributes */
1664d7fd6   Julia Lawall   hwmon: (via-cpute...
103
  static DEVICE_ATTR_RO(cpu0_vid);
764e043bb   Jean Delvare   hwmon: (via-cpute...
104

6c931ae1c   Bill Pemberton   hwmon: remove use...
105
  static int via_cputemp_probe(struct platform_device *pdev)
70c38772a   Harald Welte   hwmon: Add driver...
106
107
108
109
110
  {
  	struct via_cputemp_data *data;
  	struct cpuinfo_x86 *c = &cpu_data(pdev->id);
  	int err;
  	u32 eax, edx;
505dc0cc7   Guenter Roeck   hwmon: (via-cpute...
111
112
113
114
  	data = devm_kzalloc(&pdev->dev, sizeof(struct via_cputemp_data),
  			    GFP_KERNEL);
  	if (!data)
  		return -ENOMEM;
70c38772a   Harald Welte   hwmon: Add driver...
115
116
117
  
  	data->id = pdev->id;
  	data->name = "via_cputemp";
e3a2d2be5   davidwang   hwmon: (via-cpute...
118
  	if (c->x86 == 7) {
764e043bb   Jean Delvare   hwmon: (via-cpute...
119
  		data->msr_temp = 0x1423;
e3a2d2be5   davidwang   hwmon: (via-cpute...
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
  	} else {
  		switch (c->x86_model) {
  		case 0xA:
  			/* C7 A */
  		case 0xD:
  			/* C7 D */
  			data->msr_temp = 0x1169;
  			data->msr_vid = 0x198;
  			break;
  		case 0xF:
  			/* Nano */
  			data->msr_temp = 0x1423;
  			break;
  		default:
  			return -ENODEV;
  		}
70c38772a   Harald Welte   hwmon: Add driver...
136
137
138
  	}
  
  	/* test if we can access the TEMPERATURE MSR */
764e043bb   Jean Delvare   hwmon: (via-cpute...
139
  	err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx);
70c38772a   Harald Welte   hwmon: Add driver...
140
141
142
143
  	if (err) {
  		dev_err(&pdev->dev,
  			"Unable to access TEMPERATURE MSR, giving up
  ");
505dc0cc7   Guenter Roeck   hwmon: (via-cpute...
144
  		return err;
70c38772a   Harald Welte   hwmon: Add driver...
145
146
147
148
149
150
  	}
  
  	platform_set_drvdata(pdev, data);
  
  	err = sysfs_create_group(&pdev->dev.kobj, &via_cputemp_group);
  	if (err)
505dc0cc7   Guenter Roeck   hwmon: (via-cpute...
151
  		return err;
70c38772a   Harald Welte   hwmon: Add driver...
152

764e043bb   Jean Delvare   hwmon: (via-cpute...
153
154
155
156
157
158
159
160
  	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...
161
162
163
164
165
166
167
168
169
170
171
172
  	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...
173
174
  	if (data->vrm)
  		device_remove_file(&pdev->dev, &dev_attr_cpu0_vid);
70c38772a   Harald Welte   hwmon: Add driver...
175
  	sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
70c38772a   Harald Welte   hwmon: Add driver...
176
177
  	return err;
  }
281dfd0b6   Bill Pemberton   hwmon: remove use...
178
  static int via_cputemp_remove(struct platform_device *pdev)
70c38772a   Harald Welte   hwmon: Add driver...
179
180
181
182
  {
  	struct via_cputemp_data *data = platform_get_drvdata(pdev);
  
  	hwmon_device_unregister(data->hwmon_dev);
764e043bb   Jean Delvare   hwmon: (via-cpute...
183
184
  	if (data->vrm)
  		device_remove_file(&pdev->dev, &dev_attr_cpu0_vid);
70c38772a   Harald Welte   hwmon: Add driver...
185
  	sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
70c38772a   Harald Welte   hwmon: Add driver...
186
187
188
189
190
  	return 0;
  }
  
  static struct platform_driver via_cputemp_driver = {
  	.driver = {
70c38772a   Harald Welte   hwmon: Add driver...
191
192
193
  		.name = DRVNAME,
  	},
  	.probe = via_cputemp_probe,
9e5e9b7a9   Bill Pemberton   hwmon: remove use...
194
  	.remove = via_cputemp_remove,
70c38772a   Harald Welte   hwmon: Add driver...
195
196
197
198
199
200
201
202
203
204
  };
  
  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);
df60d7013   Sebastian Andrzej Siewior   hwmon: (via-cpute...
205
  static int via_cputemp_online(unsigned int cpu)
70c38772a   Harald Welte   hwmon: Add driver...
206
207
208
209
210
211
212
213
  {
  	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...
214
215
  		pr_err("Device allocation failed
  ");
70c38772a   Harald Welte   hwmon: Add driver...
216
217
218
219
220
221
222
223
224
225
226
  		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...
227
228
  		pr_err("Device addition failed (%d)
  ", err);
70c38772a   Harald Welte   hwmon: Add driver...
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  		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;
  }
df60d7013   Sebastian Andrzej Siewior   hwmon: (via-cpute...
247
  static int via_cputemp_down_prep(unsigned int cpu)
70c38772a   Harald Welte   hwmon: Add driver...
248
  {
ae9e0ce73   Jan Beulich   hwmon: (via-cpute...
249
  	struct pdev_entry *p;
70c38772a   Harald Welte   hwmon: Add driver...
250
  	mutex_lock(&pdev_list_mutex);
ae9e0ce73   Jan Beulich   hwmon: (via-cpute...
251
  	list_for_each_entry(p, &pdev_list, list) {
70c38772a   Harald Welte   hwmon: Add driver...
252
253
254
  		if (p->cpu == cpu) {
  			platform_device_unregister(p->pdev);
  			list_del(&p->list);
ae9e0ce73   Jan Beulich   hwmon: (via-cpute...
255
  			mutex_unlock(&pdev_list_mutex);
70c38772a   Harald Welte   hwmon: Add driver...
256
  			kfree(p);
df60d7013   Sebastian Andrzej Siewior   hwmon: (via-cpute...
257
  			return 0;
70c38772a   Harald Welte   hwmon: Add driver...
258
259
260
  		}
  	}
  	mutex_unlock(&pdev_list_mutex);
df60d7013   Sebastian Andrzej Siewior   hwmon: (via-cpute...
261
  	return 0;
70c38772a   Harald Welte   hwmon: Add driver...
262
  }
e273bd98c   Jan Beulich   hwmon: struct x86...
263
  static const struct x86_cpu_id __initconst cputemp_ids[] = {
5cfc7ac7c   Thomas Gleixner   hwmon: Convert to...
264
265
266
267
  	X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 6, X86_CENTAUR_FAM6_C7_A,	NULL),
  	X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 6, X86_CENTAUR_FAM6_C7_D,	NULL),
  	X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 6, X86_CENTAUR_FAM6_NANO,	NULL),
  	X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 7, X86_MODEL_ANY,		NULL),
267fc9788   Andi Kleen   HWMON: Convert vi...
268
269
270
  	{}
  };
  MODULE_DEVICE_TABLE(x86cpu, cputemp_ids);
df60d7013   Sebastian Andrzej Siewior   hwmon: (via-cpute...
271
  static enum cpuhp_state via_temp_online;
70c38772a   Harald Welte   hwmon: Add driver...
272
273
  static int __init via_cputemp_init(void)
  {
df60d7013   Sebastian Andrzej Siewior   hwmon: (via-cpute...
274
  	int err;
70c38772a   Harald Welte   hwmon: Add driver...
275

267fc9788   Andi Kleen   HWMON: Convert vi...
276
277
  	if (!x86_match_cpu(cputemp_ids))
  		return -ENODEV;
70c38772a   Harald Welte   hwmon: Add driver...
278
279
280
281
  
  	err = platform_driver_register(&via_cputemp_driver);
  	if (err)
  		goto exit;
df60d7013   Sebastian Andrzej Siewior   hwmon: (via-cpute...
282
283
284
285
286
  	err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/via:online",
  				via_cputemp_online, via_cputemp_down_prep);
  	if (err < 0)
  		goto exit_driver_unreg;
  	via_temp_online = err;
ae9e0ce73   Jan Beulich   hwmon: (via-cpute...
287
288
  
  #ifndef CONFIG_HOTPLUG_CPU
70c38772a   Harald Welte   hwmon: Add driver...
289
290
  	if (list_empty(&pdev_list)) {
  		err = -ENODEV;
df60d7013   Sebastian Andrzej Siewior   hwmon: (via-cpute...
291
  		goto exit_hp_unreg;
70c38772a   Harald Welte   hwmon: Add driver...
292
  	}
ae9e0ce73   Jan Beulich   hwmon: (via-cpute...
293
  #endif
70c38772a   Harald Welte   hwmon: Add driver...
294
  	return 0;
ae9e0ce73   Jan Beulich   hwmon: (via-cpute...
295
  #ifndef CONFIG_HOTPLUG_CPU
df60d7013   Sebastian Andrzej Siewior   hwmon: (via-cpute...
296
297
298
  exit_hp_unreg:
  	cpuhp_remove_state_nocalls(via_temp_online);
  #endif
70c38772a   Harald Welte   hwmon: Add driver...
299
300
301
302
303
304
305
306
  exit_driver_unreg:
  	platform_driver_unregister(&via_cputemp_driver);
  exit:
  	return err;
  }
  
  static void __exit via_cputemp_exit(void)
  {
df60d7013   Sebastian Andrzej Siewior   hwmon: (via-cpute...
307
  	cpuhp_remove_state(via_temp_online);
70c38772a   Harald Welte   hwmon: Add driver...
308
309
310
311
312
313
314
315
316
  	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)