0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/module.h>
0011 #include <linux/hwmon.h>
0012 #include <linux/hwmon-sysfs.h>
0013 #include <linux/err.h>
0014 #include <linux/mutex.h>
0015 #include <linux/log2.h>
0016 #include <linux/pci.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/slab.h>
0019
0020 #define DRVNAME "i5k_amb"
0021
0022 #define I5K_REG_AMB_BASE_ADDR 0x48
0023 #define I5K_REG_AMB_LEN_ADDR 0x50
0024 #define I5K_REG_CHAN0_PRESENCE_ADDR 0x64
0025 #define I5K_REG_CHAN1_PRESENCE_ADDR 0x66
0026
0027 #define AMB_REG_TEMP_MIN_ADDR 0x80
0028 #define AMB_REG_TEMP_MID_ADDR 0x81
0029 #define AMB_REG_TEMP_MAX_ADDR 0x82
0030 #define AMB_REG_TEMP_STATUS_ADDR 0x84
0031 #define AMB_REG_TEMP_ADDR 0x85
0032
0033 #define AMB_CONFIG_SIZE 2048
0034 #define AMB_FUNC_3_OFFSET 768
0035
0036 static unsigned long amb_reg_temp_status(unsigned int amb)
0037 {
0038 return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_STATUS_ADDR +
0039 AMB_CONFIG_SIZE * amb;
0040 }
0041
0042 static unsigned long amb_reg_temp_min(unsigned int amb)
0043 {
0044 return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_MIN_ADDR +
0045 AMB_CONFIG_SIZE * amb;
0046 }
0047
0048 static unsigned long amb_reg_temp_mid(unsigned int amb)
0049 {
0050 return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_MID_ADDR +
0051 AMB_CONFIG_SIZE * amb;
0052 }
0053
0054 static unsigned long amb_reg_temp_max(unsigned int amb)
0055 {
0056 return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_MAX_ADDR +
0057 AMB_CONFIG_SIZE * amb;
0058 }
0059
0060 static unsigned long amb_reg_temp(unsigned int amb)
0061 {
0062 return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_ADDR +
0063 AMB_CONFIG_SIZE * amb;
0064 }
0065
0066 #define MAX_MEM_CHANNELS 4
0067 #define MAX_AMBS_PER_CHANNEL 16
0068 #define MAX_AMBS (MAX_MEM_CHANNELS * \
0069 MAX_AMBS_PER_CHANNEL)
0070 #define CHANNEL_SHIFT 4
0071 #define DIMM_MASK 0xF
0072
0073
0074
0075
0076
0077
0078
0079 #define REAL_MAX_AMBS_PER_CHANNEL 15
0080 #define KNOBS_PER_AMB 6
0081
0082 static unsigned long amb_num_from_reg(unsigned int byte_num, unsigned int bit)
0083 {
0084 return byte_num * MAX_AMBS_PER_CHANNEL + bit;
0085 }
0086
0087 #define AMB_SYSFS_NAME_LEN 16
0088 struct i5k_device_attribute {
0089 struct sensor_device_attribute s_attr;
0090 char name[AMB_SYSFS_NAME_LEN];
0091 };
0092
0093 struct i5k_amb_data {
0094 struct device *hwmon_dev;
0095
0096 unsigned long amb_base;
0097 unsigned long amb_len;
0098 u16 amb_present[MAX_MEM_CHANNELS];
0099 void __iomem *amb_mmio;
0100 struct i5k_device_attribute *attrs;
0101 unsigned int num_attrs;
0102 };
0103
0104 static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
0105 char *buf)
0106 {
0107 return sprintf(buf, "%s\n", DRVNAME);
0108 }
0109
0110
0111 static DEVICE_ATTR_RO(name);
0112
0113 static struct platform_device *amb_pdev;
0114
0115 static u8 amb_read_byte(struct i5k_amb_data *data, unsigned long offset)
0116 {
0117 return ioread8(data->amb_mmio + offset);
0118 }
0119
0120 static void amb_write_byte(struct i5k_amb_data *data, unsigned long offset,
0121 u8 val)
0122 {
0123 iowrite8(val, data->amb_mmio + offset);
0124 }
0125
0126 static ssize_t show_amb_alarm(struct device *dev,
0127 struct device_attribute *devattr,
0128 char *buf)
0129 {
0130 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
0131 struct i5k_amb_data *data = dev_get_drvdata(dev);
0132
0133 if (!(amb_read_byte(data, amb_reg_temp_status(attr->index)) & 0x20) &&
0134 (amb_read_byte(data, amb_reg_temp_status(attr->index)) & 0x8))
0135 return sprintf(buf, "1\n");
0136 else
0137 return sprintf(buf, "0\n");
0138 }
0139
0140 static ssize_t store_amb_min(struct device *dev,
0141 struct device_attribute *devattr,
0142 const char *buf,
0143 size_t count)
0144 {
0145 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
0146 struct i5k_amb_data *data = dev_get_drvdata(dev);
0147 unsigned long temp;
0148 int ret = kstrtoul(buf, 10, &temp);
0149 if (ret < 0)
0150 return ret;
0151
0152 temp = temp / 500;
0153 if (temp > 255)
0154 temp = 255;
0155
0156 amb_write_byte(data, amb_reg_temp_min(attr->index), temp);
0157 return count;
0158 }
0159
0160 static ssize_t store_amb_mid(struct device *dev,
0161 struct device_attribute *devattr,
0162 const char *buf,
0163 size_t count)
0164 {
0165 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
0166 struct i5k_amb_data *data = dev_get_drvdata(dev);
0167 unsigned long temp;
0168 int ret = kstrtoul(buf, 10, &temp);
0169 if (ret < 0)
0170 return ret;
0171
0172 temp = temp / 500;
0173 if (temp > 255)
0174 temp = 255;
0175
0176 amb_write_byte(data, amb_reg_temp_mid(attr->index), temp);
0177 return count;
0178 }
0179
0180 static ssize_t store_amb_max(struct device *dev,
0181 struct device_attribute *devattr,
0182 const char *buf,
0183 size_t count)
0184 {
0185 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
0186 struct i5k_amb_data *data = dev_get_drvdata(dev);
0187 unsigned long temp;
0188 int ret = kstrtoul(buf, 10, &temp);
0189 if (ret < 0)
0190 return ret;
0191
0192 temp = temp / 500;
0193 if (temp > 255)
0194 temp = 255;
0195
0196 amb_write_byte(data, amb_reg_temp_max(attr->index), temp);
0197 return count;
0198 }
0199
0200 static ssize_t show_amb_min(struct device *dev,
0201 struct device_attribute *devattr,
0202 char *buf)
0203 {
0204 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
0205 struct i5k_amb_data *data = dev_get_drvdata(dev);
0206 return sprintf(buf, "%d\n",
0207 500 * amb_read_byte(data, amb_reg_temp_min(attr->index)));
0208 }
0209
0210 static ssize_t show_amb_mid(struct device *dev,
0211 struct device_attribute *devattr,
0212 char *buf)
0213 {
0214 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
0215 struct i5k_amb_data *data = dev_get_drvdata(dev);
0216 return sprintf(buf, "%d\n",
0217 500 * amb_read_byte(data, amb_reg_temp_mid(attr->index)));
0218 }
0219
0220 static ssize_t show_amb_max(struct device *dev,
0221 struct device_attribute *devattr,
0222 char *buf)
0223 {
0224 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
0225 struct i5k_amb_data *data = dev_get_drvdata(dev);
0226 return sprintf(buf, "%d\n",
0227 500 * amb_read_byte(data, amb_reg_temp_max(attr->index)));
0228 }
0229
0230 static ssize_t show_amb_temp(struct device *dev,
0231 struct device_attribute *devattr,
0232 char *buf)
0233 {
0234 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
0235 struct i5k_amb_data *data = dev_get_drvdata(dev);
0236 return sprintf(buf, "%d\n",
0237 500 * amb_read_byte(data, amb_reg_temp(attr->index)));
0238 }
0239
0240 static ssize_t show_label(struct device *dev,
0241 struct device_attribute *devattr,
0242 char *buf)
0243 {
0244 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
0245
0246 return sprintf(buf, "Ch. %d DIMM %d\n", attr->index >> CHANNEL_SHIFT,
0247 attr->index & DIMM_MASK);
0248 }
0249
0250 static int i5k_amb_hwmon_init(struct platform_device *pdev)
0251 {
0252 int i, j, k, d = 0;
0253 u16 c;
0254 int res = 0;
0255 int num_ambs = 0;
0256 struct i5k_amb_data *data = platform_get_drvdata(pdev);
0257
0258
0259
0260 for (i = 0; i < MAX_MEM_CHANNELS; i++)
0261 num_ambs += hweight16(data->amb_present[i] & 0x7fff);
0262
0263
0264 data->attrs = kzalloc(array3_size(num_ambs, KNOBS_PER_AMB,
0265 sizeof(*data->attrs)),
0266 GFP_KERNEL);
0267 if (!data->attrs)
0268 return -ENOMEM;
0269 data->num_attrs = 0;
0270
0271 for (i = 0; i < MAX_MEM_CHANNELS; i++) {
0272 c = data->amb_present[i];
0273 for (j = 0; j < REAL_MAX_AMBS_PER_CHANNEL; j++, c >>= 1) {
0274 struct i5k_device_attribute *iattr;
0275
0276 k = amb_num_from_reg(i, j);
0277 if (!(c & 0x1))
0278 continue;
0279 d++;
0280
0281
0282 iattr = data->attrs + data->num_attrs;
0283 snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
0284 "temp%d_label", d);
0285 iattr->s_attr.dev_attr.attr.name = iattr->name;
0286 iattr->s_attr.dev_attr.attr.mode = 0444;
0287 iattr->s_attr.dev_attr.show = show_label;
0288 iattr->s_attr.index = k;
0289 sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
0290 res = device_create_file(&pdev->dev,
0291 &iattr->s_attr.dev_attr);
0292 if (res)
0293 goto exit_remove;
0294 data->num_attrs++;
0295
0296
0297 iattr = data->attrs + data->num_attrs;
0298 snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
0299 "temp%d_input", d);
0300 iattr->s_attr.dev_attr.attr.name = iattr->name;
0301 iattr->s_attr.dev_attr.attr.mode = 0444;
0302 iattr->s_attr.dev_attr.show = show_amb_temp;
0303 iattr->s_attr.index = k;
0304 sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
0305 res = device_create_file(&pdev->dev,
0306 &iattr->s_attr.dev_attr);
0307 if (res)
0308 goto exit_remove;
0309 data->num_attrs++;
0310
0311
0312 iattr = data->attrs + data->num_attrs;
0313 snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
0314 "temp%d_min", d);
0315 iattr->s_attr.dev_attr.attr.name = iattr->name;
0316 iattr->s_attr.dev_attr.attr.mode = 0644;
0317 iattr->s_attr.dev_attr.show = show_amb_min;
0318 iattr->s_attr.dev_attr.store = store_amb_min;
0319 iattr->s_attr.index = k;
0320 sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
0321 res = device_create_file(&pdev->dev,
0322 &iattr->s_attr.dev_attr);
0323 if (res)
0324 goto exit_remove;
0325 data->num_attrs++;
0326
0327
0328 iattr = data->attrs + data->num_attrs;
0329 snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
0330 "temp%d_mid", d);
0331 iattr->s_attr.dev_attr.attr.name = iattr->name;
0332 iattr->s_attr.dev_attr.attr.mode = 0644;
0333 iattr->s_attr.dev_attr.show = show_amb_mid;
0334 iattr->s_attr.dev_attr.store = store_amb_mid;
0335 iattr->s_attr.index = k;
0336 sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
0337 res = device_create_file(&pdev->dev,
0338 &iattr->s_attr.dev_attr);
0339 if (res)
0340 goto exit_remove;
0341 data->num_attrs++;
0342
0343
0344 iattr = data->attrs + data->num_attrs;
0345 snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
0346 "temp%d_max", d);
0347 iattr->s_attr.dev_attr.attr.name = iattr->name;
0348 iattr->s_attr.dev_attr.attr.mode = 0644;
0349 iattr->s_attr.dev_attr.show = show_amb_max;
0350 iattr->s_attr.dev_attr.store = store_amb_max;
0351 iattr->s_attr.index = k;
0352 sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
0353 res = device_create_file(&pdev->dev,
0354 &iattr->s_attr.dev_attr);
0355 if (res)
0356 goto exit_remove;
0357 data->num_attrs++;
0358
0359
0360 iattr = data->attrs + data->num_attrs;
0361 snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
0362 "temp%d_alarm", d);
0363 iattr->s_attr.dev_attr.attr.name = iattr->name;
0364 iattr->s_attr.dev_attr.attr.mode = 0444;
0365 iattr->s_attr.dev_attr.show = show_amb_alarm;
0366 iattr->s_attr.index = k;
0367 sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
0368 res = device_create_file(&pdev->dev,
0369 &iattr->s_attr.dev_attr);
0370 if (res)
0371 goto exit_remove;
0372 data->num_attrs++;
0373 }
0374 }
0375
0376 res = device_create_file(&pdev->dev, &dev_attr_name);
0377 if (res)
0378 goto exit_remove;
0379
0380 data->hwmon_dev = hwmon_device_register(&pdev->dev);
0381 if (IS_ERR(data->hwmon_dev)) {
0382 res = PTR_ERR(data->hwmon_dev);
0383 goto exit_remove;
0384 }
0385
0386 return res;
0387
0388 exit_remove:
0389 device_remove_file(&pdev->dev, &dev_attr_name);
0390 for (i = 0; i < data->num_attrs; i++)
0391 device_remove_file(&pdev->dev, &data->attrs[i].s_attr.dev_attr);
0392 kfree(data->attrs);
0393
0394 return res;
0395 }
0396
0397 static int i5k_amb_add(void)
0398 {
0399 int res;
0400
0401
0402 amb_pdev = platform_device_alloc(DRVNAME, 0);
0403 if (!amb_pdev)
0404 return -ENOMEM;
0405
0406 res = platform_device_add(amb_pdev);
0407 if (res)
0408 goto err;
0409 return 0;
0410
0411 err:
0412 platform_device_put(amb_pdev);
0413 return res;
0414 }
0415
0416 static int i5k_find_amb_registers(struct i5k_amb_data *data,
0417 unsigned long devid)
0418 {
0419 struct pci_dev *pcidev;
0420 u32 val32;
0421 int res = -ENODEV;
0422
0423
0424 pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
0425 devid,
0426 NULL);
0427 if (!pcidev)
0428 return -ENODEV;
0429
0430 pci_read_config_dword(pcidev, I5K_REG_AMB_BASE_ADDR, &val32);
0431 if (val32 == (u32)~0)
0432 goto out;
0433 data->amb_base = val32;
0434
0435 pci_read_config_dword(pcidev, I5K_REG_AMB_LEN_ADDR, &val32);
0436 if (val32 == (u32)~0)
0437 goto out;
0438 data->amb_len = val32;
0439
0440
0441 if (data->amb_len < AMB_CONFIG_SIZE * MAX_AMBS) {
0442 dev_err(&pcidev->dev, "AMB region too small!\n");
0443 goto out;
0444 }
0445
0446 res = 0;
0447 out:
0448 pci_dev_put(pcidev);
0449 return res;
0450 }
0451
0452 static int i5k_channel_probe(u16 *amb_present, unsigned long dev_id)
0453 {
0454 struct pci_dev *pcidev;
0455 u16 val16;
0456 int res = -ENODEV;
0457
0458
0459 pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, dev_id, NULL);
0460 if (!pcidev)
0461 return -ENODEV;
0462
0463 pci_read_config_word(pcidev, I5K_REG_CHAN0_PRESENCE_ADDR, &val16);
0464 if (val16 == (u16)~0)
0465 goto out;
0466 amb_present[0] = val16;
0467
0468 pci_read_config_word(pcidev, I5K_REG_CHAN1_PRESENCE_ADDR, &val16);
0469 if (val16 == (u16)~0)
0470 goto out;
0471 amb_present[1] = val16;
0472
0473 res = 0;
0474
0475 out:
0476 pci_dev_put(pcidev);
0477 return res;
0478 }
0479
0480 static struct {
0481 unsigned long err;
0482 unsigned long fbd0;
0483 } chipset_ids[] = {
0484 { PCI_DEVICE_ID_INTEL_5000_ERR, PCI_DEVICE_ID_INTEL_5000_FBD0 },
0485 { PCI_DEVICE_ID_INTEL_5400_ERR, PCI_DEVICE_ID_INTEL_5400_FBD0 },
0486 { 0, 0 }
0487 };
0488
0489 #ifdef MODULE
0490 static const struct pci_device_id i5k_amb_ids[] = {
0491 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5000_ERR) },
0492 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_ERR) },
0493 { 0, }
0494 };
0495 MODULE_DEVICE_TABLE(pci, i5k_amb_ids);
0496 #endif
0497
0498 static int i5k_amb_probe(struct platform_device *pdev)
0499 {
0500 struct i5k_amb_data *data;
0501 struct resource *reso;
0502 int i, res;
0503
0504 data = kzalloc(sizeof(*data), GFP_KERNEL);
0505 if (!data)
0506 return -ENOMEM;
0507
0508
0509 i = 0;
0510 do {
0511 res = i5k_find_amb_registers(data, chipset_ids[i].err);
0512 if (res == 0)
0513 break;
0514 i++;
0515 } while (chipset_ids[i].err);
0516
0517 if (res)
0518 goto err;
0519
0520
0521 res = i5k_channel_probe(&data->amb_present[0], chipset_ids[i].fbd0);
0522 if (res)
0523 goto err;
0524
0525
0526 i5k_channel_probe(&data->amb_present[2], chipset_ids[i].fbd0 + 1);
0527
0528
0529 reso = request_mem_region(data->amb_base, data->amb_len, DRVNAME);
0530 if (!reso) {
0531 res = -EBUSY;
0532 goto err;
0533 }
0534
0535 data->amb_mmio = ioremap(data->amb_base, data->amb_len);
0536 if (!data->amb_mmio) {
0537 res = -EBUSY;
0538 goto err_map_failed;
0539 }
0540
0541 platform_set_drvdata(pdev, data);
0542
0543 res = i5k_amb_hwmon_init(pdev);
0544 if (res)
0545 goto err_init_failed;
0546
0547 return res;
0548
0549 err_init_failed:
0550 iounmap(data->amb_mmio);
0551 err_map_failed:
0552 release_mem_region(data->amb_base, data->amb_len);
0553 err:
0554 kfree(data);
0555 return res;
0556 }
0557
0558 static int i5k_amb_remove(struct platform_device *pdev)
0559 {
0560 int i;
0561 struct i5k_amb_data *data = platform_get_drvdata(pdev);
0562
0563 hwmon_device_unregister(data->hwmon_dev);
0564 device_remove_file(&pdev->dev, &dev_attr_name);
0565 for (i = 0; i < data->num_attrs; i++)
0566 device_remove_file(&pdev->dev, &data->attrs[i].s_attr.dev_attr);
0567 kfree(data->attrs);
0568 iounmap(data->amb_mmio);
0569 release_mem_region(data->amb_base, data->amb_len);
0570 kfree(data);
0571 return 0;
0572 }
0573
0574 static struct platform_driver i5k_amb_driver = {
0575 .driver = {
0576 .name = DRVNAME,
0577 },
0578 .probe = i5k_amb_probe,
0579 .remove = i5k_amb_remove,
0580 };
0581
0582 static int __init i5k_amb_init(void)
0583 {
0584 int res;
0585
0586 res = platform_driver_register(&i5k_amb_driver);
0587 if (res)
0588 return res;
0589
0590 res = i5k_amb_add();
0591 if (res)
0592 platform_driver_unregister(&i5k_amb_driver);
0593
0594 return res;
0595 }
0596
0597 static void __exit i5k_amb_exit(void)
0598 {
0599 platform_device_unregister(amb_pdev);
0600 platform_driver_unregister(&i5k_amb_driver);
0601 }
0602
0603 MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
0604 MODULE_DESCRIPTION("Intel 5000 chipset FB-DIMM AMB temperature sensor");
0605 MODULE_LICENSE("GPL");
0606
0607 module_init(i5k_amb_init);
0608 module_exit(i5k_amb_exit);