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