// SPDX-License-Identifier: GPL-2.0-or-later /* * A hwmon driver for the Intel 5000 series chipset FB-DIMM AMB * temperature sensors * Copyright (C) 2007 IBM * * Author: Darrick J. Wong <darrick.wong@oracle.com>
*/
#define MAX_MEM_CHANNELS 4 #define MAX_AMBS_PER_CHANNEL 16 #define MAX_AMBS (MAX_MEM_CHANNELS * \
MAX_AMBS_PER_CHANNEL) #define CHANNEL_SHIFT 4 #define DIMM_MASK 0xF /* * Ugly hack: For some reason the highest bit is set if there * are _any_ DIMMs in the channel. Attempting to read from * this "high-order" AMB results in a memory bus error, so * for now we'll just ignore that top bit, even though that * might prevent us from seeing the 16th DIMM in the channel.
*/ #define REAL_MAX_AMBS_PER_CHANNEL 15 #define KNOBS_PER_AMB 6
staticint i5k_amb_hwmon_init(struct platform_device *pdev)
{ int i, j, k, d = 0;
u16 c; int res = 0; int num_ambs = 0; struct i5k_amb_data *data = platform_get_drvdata(pdev);
/* Count the number of AMBs found */ /* ignore the high-order bit, see "Ugly hack" comment above */ for (i = 0; i < MAX_MEM_CHANNELS; i++)
num_ambs += hweight16(data->amb_present[i] & 0x7fff);
/* Set up sysfs stuff */
data->attrs = kzalloc(array3_size(num_ambs, KNOBS_PER_AMB, sizeof(*data->attrs)),
GFP_KERNEL); if (!data->attrs) return -ENOMEM;
data->num_attrs = 0;
for (i = 0; i < MAX_MEM_CHANNELS; i++) {
c = data->amb_present[i]; for (j = 0; j < REAL_MAX_AMBS_PER_CHANNEL; j++, c >>= 1) { struct i5k_device_attribute *iattr;
k = amb_num_from_reg(i, j); if (!(c & 0x1)) continue;
d++;
res = device_create_file(&pdev->dev, &dev_attr_name.attr); if (res) goto exit_remove;
data->hwmon_dev = hwmon_device_register(&pdev->dev); if (IS_ERR(data->hwmon_dev)) {
res = PTR_ERR(data->hwmon_dev); goto exit_remove;
}
return res;
exit_remove:
device_remove_file(&pdev->dev, &dev_attr_name.attr); for (i = 0; i < data->num_attrs; i++)
device_remove_file(&pdev->dev, &data->attrs[i].s_attr.dev_attr);
kfree(data->attrs);
return res;
}
staticint i5k_amb_add(void)
{ int res;
/* only ever going to be one of these */
amb_pdev = platform_device_alloc(DRVNAME, 0); if (!amb_pdev) return -ENOMEM;
res = platform_device_add(amb_pdev); if (res) goto err; return 0;
err:
platform_device_put(amb_pdev); return res;
}
staticint i5k_find_amb_registers(struct i5k_amb_data *data, unsignedlong devid)
{ struct pci_dev *pcidev;
u32 val32; int res = -ENODEV;
/* Find AMB register memory space */
pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
devid,
NULL); if (!pcidev) return -ENODEV;
staticint i5k_amb_probe(struct platform_device *pdev)
{ struct i5k_amb_data *data; struct resource *reso; int i, res;
data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM;
/* Figure out where the AMB registers live */
i = 0; do {
res = i5k_find_amb_registers(data, chipset_ids[i].err); if (res == 0) break;
i++;
} while (chipset_ids[i].err);
if (res) goto err;
/* Copy the DIMM presence map for the first two channels */
res = i5k_channel_probe(&data->amb_present[0], chipset_ids[i].fbd0); if (res) goto err;
/* Copy the DIMM presence map for the optional second two channels */
i5k_channel_probe(&data->amb_present[2], chipset_ids[i].fbd0 + 1);
/* Set up resource regions */
reso = request_mem_region(data->amb_base, data->amb_len, DRVNAME); if (!reso) {
res = -EBUSY; goto err;
}
data->amb_mmio = ioremap(data->amb_base, data->amb_len); if (!data->amb_mmio) {
res = -EBUSY; goto err_map_failed;
}
platform_set_drvdata(pdev, data);
res = i5k_amb_hwmon_init(pdev); if (res) goto err_init_failed;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.