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 }; 115 116 static ssize_t show_name(struct device *dev, struct device_attribute *devattr, 117 char *buf) 118 { 119 return sprintf(buf, "%s\n", DRVNAME); 120 } 121 122 123 static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); 124 125 static struct platform_device *amb_pdev; 126 127 static u8 amb_read_byte(struct i5k_amb_data *data, unsigned long offset) 128 { 129 return ioread8(data->amb_mmio + offset); 130 } 131 132 static void amb_write_byte(struct i5k_amb_data *data, unsigned long offset, 133 u8 val) 134 { 135 iowrite8(val, data->amb_mmio + offset); 136 } 137 138 static ssize_t show_amb_alarm(struct device *dev, 139 struct device_attribute *devattr, 140 char *buf) 141 { 142 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 143 struct i5k_amb_data *data = dev_get_drvdata(dev); 144 145 if (!(amb_read_byte(data, amb_reg_temp_status(attr->index)) & 0x20) && 146 (amb_read_byte(data, amb_reg_temp_status(attr->index)) & 0x8)) 147 return sprintf(buf, "1\n"); 148 else 149 return sprintf(buf, "0\n"); 150 } 151 152 static ssize_t store_amb_min(struct device *dev, 153 struct device_attribute *devattr, 154 const char *buf, 155 size_t count) 156 { 157 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 158 struct i5k_amb_data *data = dev_get_drvdata(dev); 159 unsigned long temp = simple_strtoul(buf, NULL, 10) / 500; 160 161 if (temp > 255) 162 temp = 255; 163 164 amb_write_byte(data, amb_reg_temp_min(attr->index), temp); 165 return count; 166 } 167 168 static ssize_t store_amb_mid(struct device *dev, 169 struct device_attribute *devattr, 170 const char *buf, 171 size_t count) 172 { 173 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 174 struct i5k_amb_data *data = dev_get_drvdata(dev); 175 unsigned long temp = simple_strtoul(buf, NULL, 10) / 500; 176 177 if (temp > 255) 178 temp = 255; 179 180 amb_write_byte(data, amb_reg_temp_mid(attr->index), temp); 181 return count; 182 } 183 184 static ssize_t store_amb_max(struct device *dev, 185 struct device_attribute *devattr, 186 const char *buf, 187 size_t count) 188 { 189 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 190 struct i5k_amb_data *data = dev_get_drvdata(dev); 191 unsigned long temp = simple_strtoul(buf, NULL, 10) / 500; 192 193 if (temp > 255) 194 temp = 255; 195 196 amb_write_byte(data, amb_reg_temp_max(attr->index), temp); 197 return count; 198 } 199 200 static ssize_t show_amb_min(struct device *dev, 201 struct device_attribute *devattr, 202 char *buf) 203 { 204 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 205 struct i5k_amb_data *data = dev_get_drvdata(dev); 206 return sprintf(buf, "%d\n", 207 500 * amb_read_byte(data, amb_reg_temp_min(attr->index))); 208 } 209 210 static ssize_t show_amb_mid(struct device *dev, 211 struct device_attribute *devattr, 212 char *buf) 213 { 214 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 215 struct i5k_amb_data *data = dev_get_drvdata(dev); 216 return sprintf(buf, "%d\n", 217 500 * amb_read_byte(data, amb_reg_temp_mid(attr->index))); 218 } 219 220 static ssize_t show_amb_max(struct device *dev, 221 struct device_attribute *devattr, 222 char *buf) 223 { 224 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 225 struct i5k_amb_data *data = dev_get_drvdata(dev); 226 return sprintf(buf, "%d\n", 227 500 * amb_read_byte(data, amb_reg_temp_max(attr->index))); 228 } 229 230 static ssize_t show_amb_temp(struct device *dev, 231 struct device_attribute *devattr, 232 char *buf) 233 { 234 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 235 struct i5k_amb_data *data = dev_get_drvdata(dev); 236 return sprintf(buf, "%d\n", 237 500 * amb_read_byte(data, amb_reg_temp(attr->index))); 238 } 239 240 static int __devinit i5k_amb_hwmon_init(struct platform_device *pdev) 241 { 242 int i, j, k, d = 0; 243 u16 c; 244 int res = 0; 245 int num_ambs = 0; 246 struct i5k_amb_data *data = platform_get_drvdata(pdev); 247 248 /* Count the number of AMBs found */ 249 /* ignore the high-order bit, see "Ugly hack" comment above */ 250 for (i = 0; i < MAX_MEM_CHANNELS; i++) 251 num_ambs += hweight16(data->amb_present[i] & 0x7fff); 252 253 /* Set up sysfs stuff */ 254 data->attrs = kzalloc(sizeof(*data->attrs) * num_ambs * KNOBS_PER_AMB, 255 GFP_KERNEL); 256 if (!data->attrs) 257 return -ENOMEM; 258 data->num_attrs = 0; 259 260 for (i = 0; i < MAX_MEM_CHANNELS; i++) { 261 c = data->amb_present[i]; 262 for (j = 0; j < REAL_MAX_AMBS_PER_CHANNEL; j++, c >>= 1) { 263 struct i5k_device_attribute *iattr; 264 265 k = amb_num_from_reg(i, j); 266 if (!(c & 0x1)) 267 continue; 268 d++; 269 270 /* Temperature sysfs knob */ 271 iattr = data->attrs + data->num_attrs; 272 snprintf(iattr->name, AMB_SYSFS_NAME_LEN, 273 "temp%d_input", d); 274 iattr->s_attr.dev_attr.attr.name = iattr->name; 275 iattr->s_attr.dev_attr.attr.mode = S_IRUGO; 276 iattr->s_attr.dev_attr.show = show_amb_temp; 277 iattr->s_attr.index = k; 278 res = device_create_file(&pdev->dev, 279 &iattr->s_attr.dev_attr); 280 if (res) 281 goto exit_remove; 282 data->num_attrs++; 283 284 /* Temperature min sysfs knob */ 285 iattr = data->attrs + data->num_attrs; 286 snprintf(iattr->name, AMB_SYSFS_NAME_LEN, 287 "temp%d_min", d); 288 iattr->s_attr.dev_attr.attr.name = iattr->name; 289 iattr->s_attr.dev_attr.attr.mode = S_IWUSR | S_IRUGO; 290 iattr->s_attr.dev_attr.show = show_amb_min; 291 iattr->s_attr.dev_attr.store = store_amb_min; 292 iattr->s_attr.index = k; 293 res = device_create_file(&pdev->dev, 294 &iattr->s_attr.dev_attr); 295 if (res) 296 goto exit_remove; 297 data->num_attrs++; 298 299 /* Temperature mid sysfs knob */ 300 iattr = data->attrs + data->num_attrs; 301 snprintf(iattr->name, AMB_SYSFS_NAME_LEN, 302 "temp%d_mid", d); 303 iattr->s_attr.dev_attr.attr.name = iattr->name; 304 iattr->s_attr.dev_attr.attr.mode = S_IWUSR | S_IRUGO; 305 iattr->s_attr.dev_attr.show = show_amb_mid; 306 iattr->s_attr.dev_attr.store = store_amb_mid; 307 iattr->s_attr.index = k; 308 res = device_create_file(&pdev->dev, 309 &iattr->s_attr.dev_attr); 310 if (res) 311 goto exit_remove; 312 data->num_attrs++; 313 314 /* Temperature max sysfs knob */ 315 iattr = data->attrs + data->num_attrs; 316 snprintf(iattr->name, AMB_SYSFS_NAME_LEN, 317 "temp%d_max", d); 318 iattr->s_attr.dev_attr.attr.name = iattr->name; 319 iattr->s_attr.dev_attr.attr.mode = S_IWUSR | S_IRUGO; 320 iattr->s_attr.dev_attr.show = show_amb_max; 321 iattr->s_attr.dev_attr.store = store_amb_max; 322 iattr->s_attr.index = k; 323 res = device_create_file(&pdev->dev, 324 &iattr->s_attr.dev_attr); 325 if (res) 326 goto exit_remove; 327 data->num_attrs++; 328 329 /* Temperature alarm sysfs knob */ 330 iattr = data->attrs + data->num_attrs; 331 snprintf(iattr->name, AMB_SYSFS_NAME_LEN, 332 "temp%d_alarm", d); 333 iattr->s_attr.dev_attr.attr.name = iattr->name; 334 iattr->s_attr.dev_attr.attr.mode = S_IRUGO; 335 iattr->s_attr.dev_attr.show = show_amb_alarm; 336 iattr->s_attr.index = k; 337 res = device_create_file(&pdev->dev, 338 &iattr->s_attr.dev_attr); 339 if (res) 340 goto exit_remove; 341 data->num_attrs++; 342 } 343 } 344 345 res = device_create_file(&pdev->dev, &dev_attr_name); 346 if (res) 347 goto exit_remove; 348 349 data->hwmon_dev = hwmon_device_register(&pdev->dev); 350 if (IS_ERR(data->hwmon_dev)) { 351 res = PTR_ERR(data->hwmon_dev); 352 goto exit_remove; 353 } 354 355 return res; 356 357 exit_remove: 358 device_remove_file(&pdev->dev, &dev_attr_name); 359 for (i = 0; i < data->num_attrs; i++) 360 device_remove_file(&pdev->dev, &data->attrs[i].s_attr.dev_attr); 361 kfree(data->attrs); 362 363 return res; 364 } 365 366 static int __devinit i5k_amb_add(void) 367 { 368 int res = -ENODEV; 369 370 /* only ever going to be one of these */ 371 amb_pdev = platform_device_alloc(DRVNAME, 0); 372 if (!amb_pdev) 373 return -ENOMEM; 374 375 res = platform_device_add(amb_pdev); 376 if (res) 377 goto err; 378 return 0; 379 380 err: 381 platform_device_put(amb_pdev); 382 return res; 383 } 384 385 static int __devinit i5k_find_amb_registers(struct i5k_amb_data *data) 386 { 387 struct pci_dev *pcidev; 388 u32 val32; 389 int res = -ENODEV; 390 391 /* Find AMB register memory space */ 392 pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, 393 PCI_DEVICE_ID_INTEL_5000_ERR, 394 NULL); 395 if (!pcidev) 396 return -ENODEV; 397 398 if (pci_read_config_dword(pcidev, I5K_REG_AMB_BASE_ADDR, &val32)) 399 goto out; 400 data->amb_base = val32; 401 402 if (pci_read_config_dword(pcidev, I5K_REG_AMB_LEN_ADDR, &val32)) 403 goto out; 404 data->amb_len = val32; 405 406 /* Is it big enough? */ 407 if (data->amb_len < AMB_CONFIG_SIZE * MAX_AMBS) { 408 dev_err(&pcidev->dev, "AMB region too small!\n"); 409 goto out; 410 } 411 412 res = 0; 413 out: 414 pci_dev_put(pcidev); 415 return res; 416 } 417 418 static int __devinit i5k_channel_probe(u16 *amb_present, unsigned long dev_id) 419 { 420 struct pci_dev *pcidev; 421 u16 val16; 422 int res = -ENODEV; 423 424 /* Copy the DIMM presence map for these two channels */ 425 pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, dev_id, NULL); 426 if (!pcidev) 427 return -ENODEV; 428 429 if (pci_read_config_word(pcidev, I5K_REG_CHAN0_PRESENCE_ADDR, &val16)) 430 goto out; 431 amb_present[0] = val16; 432 433 if (pci_read_config_word(pcidev, I5K_REG_CHAN1_PRESENCE_ADDR, &val16)) 434 goto out; 435 amb_present[1] = val16; 436 437 res = 0; 438 439 out: 440 pci_dev_put(pcidev); 441 return res; 442 } 443 444 static int __devinit i5k_amb_probe(struct platform_device *pdev) 445 { 446 struct i5k_amb_data *data; 447 struct resource *reso; 448 int res = -ENODEV; 449 450 data = kzalloc(sizeof(*data), GFP_KERNEL); 451 if (!data) 452 return -ENOMEM; 453 454 /* Figure out where the AMB registers live */ 455 res = i5k_find_amb_registers(data); 456 if (res) 457 goto err; 458 459 /* Copy the DIMM presence map for the first two channels */ 460 res = i5k_channel_probe(&data->amb_present[0], 461 PCI_DEVICE_ID_INTEL_5000_FBD0); 462 if (res) 463 goto err; 464 465 /* Copy the DIMM presence map for the optional second two channels */ 466 i5k_channel_probe(&data->amb_present[2], 467 PCI_DEVICE_ID_INTEL_5000_FBD1); 468 469 /* Set up resource regions */ 470 reso = request_mem_region(data->amb_base, data->amb_len, DRVNAME); 471 if (!reso) { 472 res = -EBUSY; 473 goto err; 474 } 475 476 data->amb_mmio = ioremap_nocache(data->amb_base, data->amb_len); 477 if (!data->amb_mmio) { 478 res = -EBUSY; 479 goto err_map_failed; 480 } 481 482 platform_set_drvdata(pdev, data); 483 484 res = i5k_amb_hwmon_init(pdev); 485 if (res) 486 goto err_init_failed; 487 488 return res; 489 490 err_init_failed: 491 iounmap(data->amb_mmio); 492 platform_set_drvdata(pdev, NULL); 493 err_map_failed: 494 release_mem_region(data->amb_base, data->amb_len); 495 err: 496 kfree(data); 497 return res; 498 } 499 500 static int __devexit i5k_amb_remove(struct platform_device *pdev) 501 { 502 int i; 503 struct i5k_amb_data *data = platform_get_drvdata(pdev); 504 505 hwmon_device_unregister(data->hwmon_dev); 506 device_remove_file(&pdev->dev, &dev_attr_name); 507 for (i = 0; i < data->num_attrs; i++) 508 device_remove_file(&pdev->dev, &data->attrs[i].s_attr.dev_attr); 509 kfree(data->attrs); 510 iounmap(data->amb_mmio); 511 release_mem_region(data->amb_base, data->amb_len); 512 platform_set_drvdata(pdev, NULL); 513 kfree(data); 514 return 0; 515 } 516 517 static struct platform_driver i5k_amb_driver = { 518 .driver = { 519 .owner = THIS_MODULE, 520 .name = DRVNAME, 521 }, 522 .probe = i5k_amb_probe, 523 .remove = __devexit_p(i5k_amb_remove), 524 }; 525 526 static int __init i5k_amb_init(void) 527 { 528 int res; 529 530 res = platform_driver_register(&i5k_amb_driver); 531 if (res) 532 return res; 533 534 res = i5k_amb_add(); 535 if (res) 536 platform_driver_unregister(&i5k_amb_driver); 537 538 return res; 539 } 540 541 static void __exit i5k_amb_exit(void) 542 { 543 platform_device_unregister(amb_pdev); 544 platform_driver_unregister(&i5k_amb_driver); 545 } 546 547 MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); 548 MODULE_DESCRIPTION("Intel 5000 chipset FB-DIMM AMB temperature sensor"); 549 MODULE_LICENSE("GPL"); 550 551 module_init(i5k_amb_init); 552 module_exit(i5k_amb_exit); 553