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