xref: /linux/drivers/hwmon/i5k_amb.c (revision e9f0878c4b2004ac19581274c1ae4c61ae3ca70e)
1 /*
2  * A hwmon driver for the Intel 5000 series chipset FB-DIMM AMB
3  * temperature sensors
4  * Copyright (C) 2007 IBM
5  *
6  * Author: Darrick J. Wong <darrick.wong@oracle.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22 
23 #include <linux/module.h>
24 #include <linux/hwmon.h>
25 #include <linux/hwmon-sysfs.h>
26 #include <linux/err.h>
27 #include <linux/mutex.h>
28 #include <linux/log2.h>
29 #include <linux/pci.h>
30 #include <linux/platform_device.h>
31 #include <linux/slab.h>
32 
33 #define DRVNAME "i5k_amb"
34 
35 #define I5K_REG_AMB_BASE_ADDR		0x48
36 #define I5K_REG_AMB_LEN_ADDR		0x50
37 #define I5K_REG_CHAN0_PRESENCE_ADDR	0x64
38 #define I5K_REG_CHAN1_PRESENCE_ADDR	0x66
39 
40 #define AMB_REG_TEMP_MIN_ADDR		0x80
41 #define AMB_REG_TEMP_MID_ADDR		0x81
42 #define AMB_REG_TEMP_MAX_ADDR		0x82
43 #define AMB_REG_TEMP_STATUS_ADDR	0x84
44 #define AMB_REG_TEMP_ADDR		0x85
45 
46 #define AMB_CONFIG_SIZE			2048
47 #define AMB_FUNC_3_OFFSET		768
48 
49 static unsigned long amb_reg_temp_status(unsigned int amb)
50 {
51 	return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_STATUS_ADDR +
52 	       AMB_CONFIG_SIZE * amb;
53 }
54 
55 static unsigned long amb_reg_temp_min(unsigned int amb)
56 {
57 	return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_MIN_ADDR +
58 	       AMB_CONFIG_SIZE * amb;
59 }
60 
61 static unsigned long amb_reg_temp_mid(unsigned int amb)
62 {
63 	return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_MID_ADDR +
64 	       AMB_CONFIG_SIZE * amb;
65 }
66 
67 static unsigned long amb_reg_temp_max(unsigned int amb)
68 {
69 	return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_MAX_ADDR +
70 	       AMB_CONFIG_SIZE * amb;
71 }
72 
73 static unsigned long amb_reg_temp(unsigned int amb)
74 {
75 	return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_ADDR +
76 	       AMB_CONFIG_SIZE * amb;
77 }
78 
79 #define MAX_MEM_CHANNELS		4
80 #define MAX_AMBS_PER_CHANNEL		16
81 #define MAX_AMBS			(MAX_MEM_CHANNELS * \
82 					 MAX_AMBS_PER_CHANNEL)
83 #define CHANNEL_SHIFT			4
84 #define DIMM_MASK			0xF
85 /*
86  * Ugly hack: For some reason the highest bit is set if there
87  * are _any_ DIMMs in the channel.  Attempting to read from
88  * this "high-order" AMB results in a memory bus error, so
89  * for now we'll just ignore that top bit, even though that
90  * might prevent us from seeing the 16th DIMM in the channel.
91  */
92 #define REAL_MAX_AMBS_PER_CHANNEL	15
93 #define KNOBS_PER_AMB			6
94 
95 static unsigned long amb_num_from_reg(unsigned int byte_num, unsigned int bit)
96 {
97 	return byte_num * MAX_AMBS_PER_CHANNEL + bit;
98 }
99 
100 #define AMB_SYSFS_NAME_LEN		16
101 struct i5k_device_attribute {
102 	struct sensor_device_attribute s_attr;
103 	char name[AMB_SYSFS_NAME_LEN];
104 };
105 
106 struct i5k_amb_data {
107 	struct device *hwmon_dev;
108 
109 	unsigned long amb_base;
110 	unsigned long amb_len;
111 	u16 amb_present[MAX_MEM_CHANNELS];
112 	void __iomem *amb_mmio;
113 	struct i5k_device_attribute *attrs;
114 	unsigned int num_attrs;
115 };
116 
117 static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
118 			 char *buf)
119 {
120 	return sprintf(buf, "%s\n", DRVNAME);
121 }
122 
123 
124 static DEVICE_ATTR_RO(name);
125 
126 static struct platform_device *amb_pdev;
127 
128 static u8 amb_read_byte(struct i5k_amb_data *data, unsigned long offset)
129 {
130 	return ioread8(data->amb_mmio + offset);
131 }
132 
133 static void amb_write_byte(struct i5k_amb_data *data, unsigned long offset,
134 			   u8 val)
135 {
136 	iowrite8(val, data->amb_mmio + offset);
137 }
138 
139 static ssize_t show_amb_alarm(struct device *dev,
140 			     struct device_attribute *devattr,
141 			     char *buf)
142 {
143 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
144 	struct i5k_amb_data *data = dev_get_drvdata(dev);
145 
146 	if (!(amb_read_byte(data, amb_reg_temp_status(attr->index)) & 0x20) &&
147 	     (amb_read_byte(data, amb_reg_temp_status(attr->index)) & 0x8))
148 		return sprintf(buf, "1\n");
149 	else
150 		return sprintf(buf, "0\n");
151 }
152 
153 static ssize_t store_amb_min(struct device *dev,
154 			     struct device_attribute *devattr,
155 			     const char *buf,
156 			     size_t count)
157 {
158 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
159 	struct i5k_amb_data *data = dev_get_drvdata(dev);
160 	unsigned long temp;
161 	int ret = kstrtoul(buf, 10, &temp);
162 	if (ret < 0)
163 		return ret;
164 
165 	temp = temp / 500;
166 	if (temp > 255)
167 		temp = 255;
168 
169 	amb_write_byte(data, amb_reg_temp_min(attr->index), temp);
170 	return count;
171 }
172 
173 static ssize_t store_amb_mid(struct device *dev,
174 			     struct device_attribute *devattr,
175 			     const char *buf,
176 			     size_t count)
177 {
178 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
179 	struct i5k_amb_data *data = dev_get_drvdata(dev);
180 	unsigned long temp;
181 	int ret = kstrtoul(buf, 10, &temp);
182 	if (ret < 0)
183 		return ret;
184 
185 	temp = temp / 500;
186 	if (temp > 255)
187 		temp = 255;
188 
189 	amb_write_byte(data, amb_reg_temp_mid(attr->index), temp);
190 	return count;
191 }
192 
193 static ssize_t store_amb_max(struct device *dev,
194 			     struct device_attribute *devattr,
195 			     const char *buf,
196 			     size_t count)
197 {
198 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
199 	struct i5k_amb_data *data = dev_get_drvdata(dev);
200 	unsigned long temp;
201 	int ret = kstrtoul(buf, 10, &temp);
202 	if (ret < 0)
203 		return ret;
204 
205 	temp = temp / 500;
206 	if (temp > 255)
207 		temp = 255;
208 
209 	amb_write_byte(data, amb_reg_temp_max(attr->index), temp);
210 	return count;
211 }
212 
213 static ssize_t show_amb_min(struct device *dev,
214 			     struct device_attribute *devattr,
215 			     char *buf)
216 {
217 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
218 	struct i5k_amb_data *data = dev_get_drvdata(dev);
219 	return sprintf(buf, "%d\n",
220 		500 * amb_read_byte(data, amb_reg_temp_min(attr->index)));
221 }
222 
223 static ssize_t show_amb_mid(struct device *dev,
224 			     struct device_attribute *devattr,
225 			     char *buf)
226 {
227 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
228 	struct i5k_amb_data *data = dev_get_drvdata(dev);
229 	return sprintf(buf, "%d\n",
230 		500 * amb_read_byte(data, amb_reg_temp_mid(attr->index)));
231 }
232 
233 static ssize_t show_amb_max(struct device *dev,
234 			     struct device_attribute *devattr,
235 			     char *buf)
236 {
237 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
238 	struct i5k_amb_data *data = dev_get_drvdata(dev);
239 	return sprintf(buf, "%d\n",
240 		500 * amb_read_byte(data, amb_reg_temp_max(attr->index)));
241 }
242 
243 static ssize_t show_amb_temp(struct device *dev,
244 			     struct device_attribute *devattr,
245 			     char *buf)
246 {
247 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
248 	struct i5k_amb_data *data = dev_get_drvdata(dev);
249 	return sprintf(buf, "%d\n",
250 		500 * amb_read_byte(data, amb_reg_temp(attr->index)));
251 }
252 
253 static ssize_t show_label(struct device *dev,
254 			  struct device_attribute *devattr,
255 			  char *buf)
256 {
257 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
258 
259 	return sprintf(buf, "Ch. %d DIMM %d\n", attr->index >> CHANNEL_SHIFT,
260 		       attr->index & DIMM_MASK);
261 }
262 
263 static int i5k_amb_hwmon_init(struct platform_device *pdev)
264 {
265 	int i, j, k, d = 0;
266 	u16 c;
267 	int res = 0;
268 	int num_ambs = 0;
269 	struct i5k_amb_data *data = platform_get_drvdata(pdev);
270 
271 	/* Count the number of AMBs found */
272 	/* ignore the high-order bit, see "Ugly hack" comment above */
273 	for (i = 0; i < MAX_MEM_CHANNELS; i++)
274 		num_ambs += hweight16(data->amb_present[i] & 0x7fff);
275 
276 	/* Set up sysfs stuff */
277 	data->attrs = kzalloc(array3_size(num_ambs, KNOBS_PER_AMB,
278 					  sizeof(*data->attrs)),
279 			      GFP_KERNEL);
280 	if (!data->attrs)
281 		return -ENOMEM;
282 	data->num_attrs = 0;
283 
284 	for (i = 0; i < MAX_MEM_CHANNELS; i++) {
285 		c = data->amb_present[i];
286 		for (j = 0; j < REAL_MAX_AMBS_PER_CHANNEL; j++, c >>= 1) {
287 			struct i5k_device_attribute *iattr;
288 
289 			k = amb_num_from_reg(i, j);
290 			if (!(c & 0x1))
291 				continue;
292 			d++;
293 
294 			/* sysfs label */
295 			iattr = data->attrs + data->num_attrs;
296 			snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
297 				 "temp%d_label", d);
298 			iattr->s_attr.dev_attr.attr.name = iattr->name;
299 			iattr->s_attr.dev_attr.attr.mode = S_IRUGO;
300 			iattr->s_attr.dev_attr.show = show_label;
301 			iattr->s_attr.index = k;
302 			sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
303 			res = device_create_file(&pdev->dev,
304 						 &iattr->s_attr.dev_attr);
305 			if (res)
306 				goto exit_remove;
307 			data->num_attrs++;
308 
309 			/* Temperature sysfs knob */
310 			iattr = data->attrs + data->num_attrs;
311 			snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
312 				 "temp%d_input", d);
313 			iattr->s_attr.dev_attr.attr.name = iattr->name;
314 			iattr->s_attr.dev_attr.attr.mode = S_IRUGO;
315 			iattr->s_attr.dev_attr.show = show_amb_temp;
316 			iattr->s_attr.index = k;
317 			sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
318 			res = device_create_file(&pdev->dev,
319 						 &iattr->s_attr.dev_attr);
320 			if (res)
321 				goto exit_remove;
322 			data->num_attrs++;
323 
324 			/* Temperature min sysfs knob */
325 			iattr = data->attrs + data->num_attrs;
326 			snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
327 				 "temp%d_min", d);
328 			iattr->s_attr.dev_attr.attr.name = iattr->name;
329 			iattr->s_attr.dev_attr.attr.mode = S_IWUSR | S_IRUGO;
330 			iattr->s_attr.dev_attr.show = show_amb_min;
331 			iattr->s_attr.dev_attr.store = store_amb_min;
332 			iattr->s_attr.index = k;
333 			sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
334 			res = device_create_file(&pdev->dev,
335 						 &iattr->s_attr.dev_attr);
336 			if (res)
337 				goto exit_remove;
338 			data->num_attrs++;
339 
340 			/* Temperature mid sysfs knob */
341 			iattr = data->attrs + data->num_attrs;
342 			snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
343 				 "temp%d_mid", d);
344 			iattr->s_attr.dev_attr.attr.name = iattr->name;
345 			iattr->s_attr.dev_attr.attr.mode = S_IWUSR | S_IRUGO;
346 			iattr->s_attr.dev_attr.show = show_amb_mid;
347 			iattr->s_attr.dev_attr.store = store_amb_mid;
348 			iattr->s_attr.index = k;
349 			sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
350 			res = device_create_file(&pdev->dev,
351 						 &iattr->s_attr.dev_attr);
352 			if (res)
353 				goto exit_remove;
354 			data->num_attrs++;
355 
356 			/* Temperature max sysfs knob */
357 			iattr = data->attrs + data->num_attrs;
358 			snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
359 				 "temp%d_max", d);
360 			iattr->s_attr.dev_attr.attr.name = iattr->name;
361 			iattr->s_attr.dev_attr.attr.mode = S_IWUSR | S_IRUGO;
362 			iattr->s_attr.dev_attr.show = show_amb_max;
363 			iattr->s_attr.dev_attr.store = store_amb_max;
364 			iattr->s_attr.index = k;
365 			sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
366 			res = device_create_file(&pdev->dev,
367 						 &iattr->s_attr.dev_attr);
368 			if (res)
369 				goto exit_remove;
370 			data->num_attrs++;
371 
372 			/* Temperature alarm sysfs knob */
373 			iattr = data->attrs + data->num_attrs;
374 			snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
375 				 "temp%d_alarm", d);
376 			iattr->s_attr.dev_attr.attr.name = iattr->name;
377 			iattr->s_attr.dev_attr.attr.mode = S_IRUGO;
378 			iattr->s_attr.dev_attr.show = show_amb_alarm;
379 			iattr->s_attr.index = k;
380 			sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
381 			res = device_create_file(&pdev->dev,
382 						 &iattr->s_attr.dev_attr);
383 			if (res)
384 				goto exit_remove;
385 			data->num_attrs++;
386 		}
387 	}
388 
389 	res = device_create_file(&pdev->dev, &dev_attr_name);
390 	if (res)
391 		goto exit_remove;
392 
393 	data->hwmon_dev = hwmon_device_register(&pdev->dev);
394 	if (IS_ERR(data->hwmon_dev)) {
395 		res = PTR_ERR(data->hwmon_dev);
396 		goto exit_remove;
397 	}
398 
399 	return res;
400 
401 exit_remove:
402 	device_remove_file(&pdev->dev, &dev_attr_name);
403 	for (i = 0; i < data->num_attrs; i++)
404 		device_remove_file(&pdev->dev, &data->attrs[i].s_attr.dev_attr);
405 	kfree(data->attrs);
406 
407 	return res;
408 }
409 
410 static int i5k_amb_add(void)
411 {
412 	int res = -ENODEV;
413 
414 	/* only ever going to be one of these */
415 	amb_pdev = platform_device_alloc(DRVNAME, 0);
416 	if (!amb_pdev)
417 		return -ENOMEM;
418 
419 	res = platform_device_add(amb_pdev);
420 	if (res)
421 		goto err;
422 	return 0;
423 
424 err:
425 	platform_device_put(amb_pdev);
426 	return res;
427 }
428 
429 static int i5k_find_amb_registers(struct i5k_amb_data *data,
430 					    unsigned long devid)
431 {
432 	struct pci_dev *pcidev;
433 	u32 val32;
434 	int res = -ENODEV;
435 
436 	/* Find AMB register memory space */
437 	pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
438 				devid,
439 				NULL);
440 	if (!pcidev)
441 		return -ENODEV;
442 
443 	if (pci_read_config_dword(pcidev, I5K_REG_AMB_BASE_ADDR, &val32))
444 		goto out;
445 	data->amb_base = val32;
446 
447 	if (pci_read_config_dword(pcidev, I5K_REG_AMB_LEN_ADDR, &val32))
448 		goto out;
449 	data->amb_len = val32;
450 
451 	/* Is it big enough? */
452 	if (data->amb_len < AMB_CONFIG_SIZE * MAX_AMBS) {
453 		dev_err(&pcidev->dev, "AMB region too small!\n");
454 		goto out;
455 	}
456 
457 	res = 0;
458 out:
459 	pci_dev_put(pcidev);
460 	return res;
461 }
462 
463 static int i5k_channel_probe(u16 *amb_present, unsigned long dev_id)
464 {
465 	struct pci_dev *pcidev;
466 	u16 val16;
467 	int res = -ENODEV;
468 
469 	/* Copy the DIMM presence map for these two channels */
470 	pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, dev_id, NULL);
471 	if (!pcidev)
472 		return -ENODEV;
473 
474 	if (pci_read_config_word(pcidev, I5K_REG_CHAN0_PRESENCE_ADDR, &val16))
475 		goto out;
476 	amb_present[0] = val16;
477 
478 	if (pci_read_config_word(pcidev, I5K_REG_CHAN1_PRESENCE_ADDR, &val16))
479 		goto out;
480 	amb_present[1] = val16;
481 
482 	res = 0;
483 
484 out:
485 	pci_dev_put(pcidev);
486 	return res;
487 }
488 
489 static struct {
490 	unsigned long err;
491 	unsigned long fbd0;
492 } chipset_ids[]  = {
493 	{ PCI_DEVICE_ID_INTEL_5000_ERR, PCI_DEVICE_ID_INTEL_5000_FBD0 },
494 	{ PCI_DEVICE_ID_INTEL_5400_ERR, PCI_DEVICE_ID_INTEL_5400_FBD0 },
495 	{ 0, 0 }
496 };
497 
498 #ifdef MODULE
499 static const struct pci_device_id i5k_amb_ids[] = {
500 	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5000_ERR) },
501 	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_ERR) },
502 	{ 0, }
503 };
504 MODULE_DEVICE_TABLE(pci, i5k_amb_ids);
505 #endif
506 
507 static int i5k_amb_probe(struct platform_device *pdev)
508 {
509 	struct i5k_amb_data *data;
510 	struct resource *reso;
511 	int i, res;
512 
513 	data = kzalloc(sizeof(*data), GFP_KERNEL);
514 	if (!data)
515 		return -ENOMEM;
516 
517 	/* Figure out where the AMB registers live */
518 	i = 0;
519 	do {
520 		res = i5k_find_amb_registers(data, chipset_ids[i].err);
521 		if (res == 0)
522 			break;
523 		i++;
524 	} while (chipset_ids[i].err);
525 
526 	if (res)
527 		goto err;
528 
529 	/* Copy the DIMM presence map for the first two channels */
530 	res = i5k_channel_probe(&data->amb_present[0], chipset_ids[i].fbd0);
531 	if (res)
532 		goto err;
533 
534 	/* Copy the DIMM presence map for the optional second two channels */
535 	i5k_channel_probe(&data->amb_present[2], chipset_ids[i].fbd0 + 1);
536 
537 	/* Set up resource regions */
538 	reso = request_mem_region(data->amb_base, data->amb_len, DRVNAME);
539 	if (!reso) {
540 		res = -EBUSY;
541 		goto err;
542 	}
543 
544 	data->amb_mmio = ioremap_nocache(data->amb_base, data->amb_len);
545 	if (!data->amb_mmio) {
546 		res = -EBUSY;
547 		goto err_map_failed;
548 	}
549 
550 	platform_set_drvdata(pdev, data);
551 
552 	res = i5k_amb_hwmon_init(pdev);
553 	if (res)
554 		goto err_init_failed;
555 
556 	return res;
557 
558 err_init_failed:
559 	iounmap(data->amb_mmio);
560 err_map_failed:
561 	release_mem_region(data->amb_base, data->amb_len);
562 err:
563 	kfree(data);
564 	return res;
565 }
566 
567 static int i5k_amb_remove(struct platform_device *pdev)
568 {
569 	int i;
570 	struct i5k_amb_data *data = platform_get_drvdata(pdev);
571 
572 	hwmon_device_unregister(data->hwmon_dev);
573 	device_remove_file(&pdev->dev, &dev_attr_name);
574 	for (i = 0; i < data->num_attrs; i++)
575 		device_remove_file(&pdev->dev, &data->attrs[i].s_attr.dev_attr);
576 	kfree(data->attrs);
577 	iounmap(data->amb_mmio);
578 	release_mem_region(data->amb_base, data->amb_len);
579 	kfree(data);
580 	return 0;
581 }
582 
583 static struct platform_driver i5k_amb_driver = {
584 	.driver = {
585 		.name = DRVNAME,
586 	},
587 	.probe = i5k_amb_probe,
588 	.remove = i5k_amb_remove,
589 };
590 
591 static int __init i5k_amb_init(void)
592 {
593 	int res;
594 
595 	res = platform_driver_register(&i5k_amb_driver);
596 	if (res)
597 		return res;
598 
599 	res = i5k_amb_add();
600 	if (res)
601 		platform_driver_unregister(&i5k_amb_driver);
602 
603 	return res;
604 }
605 
606 static void __exit i5k_amb_exit(void)
607 {
608 	platform_device_unregister(amb_pdev);
609 	platform_driver_unregister(&i5k_amb_driver);
610 }
611 
612 MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
613 MODULE_DESCRIPTION("Intel 5000 chipset FB-DIMM AMB temperature sensor");
614 MODULE_LICENSE("GPL");
615 
616 module_init(i5k_amb_init);
617 module_exit(i5k_amb_exit);
618