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