cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

i5k_amb.c (15441B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * A hwmon driver for the Intel 5000 series chipset FB-DIMM AMB
      4 * temperature sensors
      5 * Copyright (C) 2007 IBM
      6 *
      7 * Author: Darrick J. Wong <darrick.wong@oracle.com>
      8 */
      9
     10#include <linux/module.h>
     11#include <linux/hwmon.h>
     12#include <linux/hwmon-sysfs.h>
     13#include <linux/err.h>
     14#include <linux/mutex.h>
     15#include <linux/log2.h>
     16#include <linux/pci.h>
     17#include <linux/platform_device.h>
     18#include <linux/slab.h>
     19
     20#define DRVNAME "i5k_amb"
     21
     22#define I5K_REG_AMB_BASE_ADDR		0x48
     23#define I5K_REG_AMB_LEN_ADDR		0x50
     24#define I5K_REG_CHAN0_PRESENCE_ADDR	0x64
     25#define I5K_REG_CHAN1_PRESENCE_ADDR	0x66
     26
     27#define AMB_REG_TEMP_MIN_ADDR		0x80
     28#define AMB_REG_TEMP_MID_ADDR		0x81
     29#define AMB_REG_TEMP_MAX_ADDR		0x82
     30#define AMB_REG_TEMP_STATUS_ADDR	0x84
     31#define AMB_REG_TEMP_ADDR		0x85
     32
     33#define AMB_CONFIG_SIZE			2048
     34#define AMB_FUNC_3_OFFSET		768
     35
     36static unsigned long amb_reg_temp_status(unsigned int amb)
     37{
     38	return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_STATUS_ADDR +
     39	       AMB_CONFIG_SIZE * amb;
     40}
     41
     42static unsigned long amb_reg_temp_min(unsigned int amb)
     43{
     44	return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_MIN_ADDR +
     45	       AMB_CONFIG_SIZE * amb;
     46}
     47
     48static unsigned long amb_reg_temp_mid(unsigned int amb)
     49{
     50	return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_MID_ADDR +
     51	       AMB_CONFIG_SIZE * amb;
     52}
     53
     54static unsigned long amb_reg_temp_max(unsigned int amb)
     55{
     56	return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_MAX_ADDR +
     57	       AMB_CONFIG_SIZE * amb;
     58}
     59
     60static unsigned long amb_reg_temp(unsigned int amb)
     61{
     62	return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_ADDR +
     63	       AMB_CONFIG_SIZE * amb;
     64}
     65
     66#define MAX_MEM_CHANNELS		4
     67#define MAX_AMBS_PER_CHANNEL		16
     68#define MAX_AMBS			(MAX_MEM_CHANNELS * \
     69					 MAX_AMBS_PER_CHANNEL)
     70#define CHANNEL_SHIFT			4
     71#define DIMM_MASK			0xF
     72/*
     73 * Ugly hack: For some reason the highest bit is set if there
     74 * are _any_ DIMMs in the channel.  Attempting to read from
     75 * this "high-order" AMB results in a memory bus error, so
     76 * for now we'll just ignore that top bit, even though that
     77 * might prevent us from seeing the 16th DIMM in the channel.
     78 */
     79#define REAL_MAX_AMBS_PER_CHANNEL	15
     80#define KNOBS_PER_AMB			6
     81
     82static unsigned long amb_num_from_reg(unsigned int byte_num, unsigned int bit)
     83{
     84	return byte_num * MAX_AMBS_PER_CHANNEL + bit;
     85}
     86
     87#define AMB_SYSFS_NAME_LEN		16
     88struct i5k_device_attribute {
     89	struct sensor_device_attribute s_attr;
     90	char name[AMB_SYSFS_NAME_LEN];
     91};
     92
     93struct i5k_amb_data {
     94	struct device *hwmon_dev;
     95
     96	unsigned long amb_base;
     97	unsigned long amb_len;
     98	u16 amb_present[MAX_MEM_CHANNELS];
     99	void __iomem *amb_mmio;
    100	struct i5k_device_attribute *attrs;
    101	unsigned int num_attrs;
    102};
    103
    104static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
    105			 char *buf)
    106{
    107	return sprintf(buf, "%s\n", DRVNAME);
    108}
    109
    110
    111static DEVICE_ATTR_RO(name);
    112
    113static struct platform_device *amb_pdev;
    114
    115static u8 amb_read_byte(struct i5k_amb_data *data, unsigned long offset)
    116{
    117	return ioread8(data->amb_mmio + offset);
    118}
    119
    120static void amb_write_byte(struct i5k_amb_data *data, unsigned long offset,
    121			   u8 val)
    122{
    123	iowrite8(val, data->amb_mmio + offset);
    124}
    125
    126static ssize_t show_amb_alarm(struct device *dev,
    127			     struct device_attribute *devattr,
    128			     char *buf)
    129{
    130	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
    131	struct i5k_amb_data *data = dev_get_drvdata(dev);
    132
    133	if (!(amb_read_byte(data, amb_reg_temp_status(attr->index)) & 0x20) &&
    134	     (amb_read_byte(data, amb_reg_temp_status(attr->index)) & 0x8))
    135		return sprintf(buf, "1\n");
    136	else
    137		return sprintf(buf, "0\n");
    138}
    139
    140static ssize_t store_amb_min(struct device *dev,
    141			     struct device_attribute *devattr,
    142			     const char *buf,
    143			     size_t count)
    144{
    145	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
    146	struct i5k_amb_data *data = dev_get_drvdata(dev);
    147	unsigned long temp;
    148	int ret = kstrtoul(buf, 10, &temp);
    149	if (ret < 0)
    150		return ret;
    151
    152	temp = temp / 500;
    153	if (temp > 255)
    154		temp = 255;
    155
    156	amb_write_byte(data, amb_reg_temp_min(attr->index), temp);
    157	return count;
    158}
    159
    160static ssize_t store_amb_mid(struct device *dev,
    161			     struct device_attribute *devattr,
    162			     const char *buf,
    163			     size_t count)
    164{
    165	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
    166	struct i5k_amb_data *data = dev_get_drvdata(dev);
    167	unsigned long temp;
    168	int ret = kstrtoul(buf, 10, &temp);
    169	if (ret < 0)
    170		return ret;
    171
    172	temp = temp / 500;
    173	if (temp > 255)
    174		temp = 255;
    175
    176	amb_write_byte(data, amb_reg_temp_mid(attr->index), temp);
    177	return count;
    178}
    179
    180static ssize_t store_amb_max(struct device *dev,
    181			     struct device_attribute *devattr,
    182			     const char *buf,
    183			     size_t count)
    184{
    185	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
    186	struct i5k_amb_data *data = dev_get_drvdata(dev);
    187	unsigned long temp;
    188	int ret = kstrtoul(buf, 10, &temp);
    189	if (ret < 0)
    190		return ret;
    191
    192	temp = temp / 500;
    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
    200static 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
    210static 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
    220static 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
    230static 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
    240static ssize_t show_label(struct device *dev,
    241			  struct device_attribute *devattr,
    242			  char *buf)
    243{
    244	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
    245
    246	return sprintf(buf, "Ch. %d DIMM %d\n", attr->index >> CHANNEL_SHIFT,
    247		       attr->index & DIMM_MASK);
    248}
    249
    250static int i5k_amb_hwmon_init(struct platform_device *pdev)
    251{
    252	int i, j, k, d = 0;
    253	u16 c;
    254	int res = 0;
    255	int num_ambs = 0;
    256	struct i5k_amb_data *data = platform_get_drvdata(pdev);
    257
    258	/* Count the number of AMBs found */
    259	/* ignore the high-order bit, see "Ugly hack" comment above */
    260	for (i = 0; i < MAX_MEM_CHANNELS; i++)
    261		num_ambs += hweight16(data->amb_present[i] & 0x7fff);
    262
    263	/* Set up sysfs stuff */
    264	data->attrs = kzalloc(array3_size(num_ambs, KNOBS_PER_AMB,
    265					  sizeof(*data->attrs)),
    266			      GFP_KERNEL);
    267	if (!data->attrs)
    268		return -ENOMEM;
    269	data->num_attrs = 0;
    270
    271	for (i = 0; i < MAX_MEM_CHANNELS; i++) {
    272		c = data->amb_present[i];
    273		for (j = 0; j < REAL_MAX_AMBS_PER_CHANNEL; j++, c >>= 1) {
    274			struct i5k_device_attribute *iattr;
    275
    276			k = amb_num_from_reg(i, j);
    277			if (!(c & 0x1))
    278				continue;
    279			d++;
    280
    281			/* sysfs label */
    282			iattr = data->attrs + data->num_attrs;
    283			snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
    284				 "temp%d_label", d);
    285			iattr->s_attr.dev_attr.attr.name = iattr->name;
    286			iattr->s_attr.dev_attr.attr.mode = 0444;
    287			iattr->s_attr.dev_attr.show = show_label;
    288			iattr->s_attr.index = k;
    289			sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
    290			res = device_create_file(&pdev->dev,
    291						 &iattr->s_attr.dev_attr);
    292			if (res)
    293				goto exit_remove;
    294			data->num_attrs++;
    295
    296			/* Temperature sysfs knob */
    297			iattr = data->attrs + data->num_attrs;
    298			snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
    299				 "temp%d_input", d);
    300			iattr->s_attr.dev_attr.attr.name = iattr->name;
    301			iattr->s_attr.dev_attr.attr.mode = 0444;
    302			iattr->s_attr.dev_attr.show = show_amb_temp;
    303			iattr->s_attr.index = k;
    304			sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
    305			res = device_create_file(&pdev->dev,
    306						 &iattr->s_attr.dev_attr);
    307			if (res)
    308				goto exit_remove;
    309			data->num_attrs++;
    310
    311			/* Temperature min sysfs knob */
    312			iattr = data->attrs + data->num_attrs;
    313			snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
    314				 "temp%d_min", d);
    315			iattr->s_attr.dev_attr.attr.name = iattr->name;
    316			iattr->s_attr.dev_attr.attr.mode = 0644;
    317			iattr->s_attr.dev_attr.show = show_amb_min;
    318			iattr->s_attr.dev_attr.store = store_amb_min;
    319			iattr->s_attr.index = k;
    320			sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
    321			res = device_create_file(&pdev->dev,
    322						 &iattr->s_attr.dev_attr);
    323			if (res)
    324				goto exit_remove;
    325			data->num_attrs++;
    326
    327			/* Temperature mid sysfs knob */
    328			iattr = data->attrs + data->num_attrs;
    329			snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
    330				 "temp%d_mid", d);
    331			iattr->s_attr.dev_attr.attr.name = iattr->name;
    332			iattr->s_attr.dev_attr.attr.mode = 0644;
    333			iattr->s_attr.dev_attr.show = show_amb_mid;
    334			iattr->s_attr.dev_attr.store = store_amb_mid;
    335			iattr->s_attr.index = k;
    336			sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
    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			/* Temperature max sysfs knob */
    344			iattr = data->attrs + data->num_attrs;
    345			snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
    346				 "temp%d_max", d);
    347			iattr->s_attr.dev_attr.attr.name = iattr->name;
    348			iattr->s_attr.dev_attr.attr.mode = 0644;
    349			iattr->s_attr.dev_attr.show = show_amb_max;
    350			iattr->s_attr.dev_attr.store = store_amb_max;
    351			iattr->s_attr.index = k;
    352			sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
    353			res = device_create_file(&pdev->dev,
    354						 &iattr->s_attr.dev_attr);
    355			if (res)
    356				goto exit_remove;
    357			data->num_attrs++;
    358
    359			/* Temperature alarm sysfs knob */
    360			iattr = data->attrs + data->num_attrs;
    361			snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
    362				 "temp%d_alarm", d);
    363			iattr->s_attr.dev_attr.attr.name = iattr->name;
    364			iattr->s_attr.dev_attr.attr.mode = 0444;
    365			iattr->s_attr.dev_attr.show = show_amb_alarm;
    366			iattr->s_attr.index = k;
    367			sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
    368			res = device_create_file(&pdev->dev,
    369						 &iattr->s_attr.dev_attr);
    370			if (res)
    371				goto exit_remove;
    372			data->num_attrs++;
    373		}
    374	}
    375
    376	res = device_create_file(&pdev->dev, &dev_attr_name);
    377	if (res)
    378		goto exit_remove;
    379
    380	data->hwmon_dev = hwmon_device_register(&pdev->dev);
    381	if (IS_ERR(data->hwmon_dev)) {
    382		res = PTR_ERR(data->hwmon_dev);
    383		goto exit_remove;
    384	}
    385
    386	return res;
    387
    388exit_remove:
    389	device_remove_file(&pdev->dev, &dev_attr_name);
    390	for (i = 0; i < data->num_attrs; i++)
    391		device_remove_file(&pdev->dev, &data->attrs[i].s_attr.dev_attr);
    392	kfree(data->attrs);
    393
    394	return res;
    395}
    396
    397static int i5k_amb_add(void)
    398{
    399	int res;
    400
    401	/* only ever going to be one of these */
    402	amb_pdev = platform_device_alloc(DRVNAME, 0);
    403	if (!amb_pdev)
    404		return -ENOMEM;
    405
    406	res = platform_device_add(amb_pdev);
    407	if (res)
    408		goto err;
    409	return 0;
    410
    411err:
    412	platform_device_put(amb_pdev);
    413	return res;
    414}
    415
    416static int i5k_find_amb_registers(struct i5k_amb_data *data,
    417					    unsigned long devid)
    418{
    419	struct pci_dev *pcidev;
    420	u32 val32;
    421	int res = -ENODEV;
    422
    423	/* Find AMB register memory space */
    424	pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
    425				devid,
    426				NULL);
    427	if (!pcidev)
    428		return -ENODEV;
    429
    430	pci_read_config_dword(pcidev, I5K_REG_AMB_BASE_ADDR, &val32);
    431	if (val32 == (u32)~0)
    432		goto out;
    433	data->amb_base = val32;
    434
    435	pci_read_config_dword(pcidev, I5K_REG_AMB_LEN_ADDR, &val32);
    436	if (val32 == (u32)~0)
    437		goto out;
    438	data->amb_len = val32;
    439
    440	/* Is it big enough? */
    441	if (data->amb_len < AMB_CONFIG_SIZE * MAX_AMBS) {
    442		dev_err(&pcidev->dev, "AMB region too small!\n");
    443		goto out;
    444	}
    445
    446	res = 0;
    447out:
    448	pci_dev_put(pcidev);
    449	return res;
    450}
    451
    452static int i5k_channel_probe(u16 *amb_present, unsigned long dev_id)
    453{
    454	struct pci_dev *pcidev;
    455	u16 val16;
    456	int res = -ENODEV;
    457
    458	/* Copy the DIMM presence map for these two channels */
    459	pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, dev_id, NULL);
    460	if (!pcidev)
    461		return -ENODEV;
    462
    463	pci_read_config_word(pcidev, I5K_REG_CHAN0_PRESENCE_ADDR, &val16);
    464	if (val16 == (u16)~0)
    465		goto out;
    466	amb_present[0] = val16;
    467
    468	pci_read_config_word(pcidev, I5K_REG_CHAN1_PRESENCE_ADDR, &val16);
    469	if (val16 == (u16)~0)
    470		goto out;
    471	amb_present[1] = val16;
    472
    473	res = 0;
    474
    475out:
    476	pci_dev_put(pcidev);
    477	return res;
    478}
    479
    480static struct {
    481	unsigned long err;
    482	unsigned long fbd0;
    483} chipset_ids[]  = {
    484	{ PCI_DEVICE_ID_INTEL_5000_ERR, PCI_DEVICE_ID_INTEL_5000_FBD0 },
    485	{ PCI_DEVICE_ID_INTEL_5400_ERR, PCI_DEVICE_ID_INTEL_5400_FBD0 },
    486	{ 0, 0 }
    487};
    488
    489#ifdef MODULE
    490static const struct pci_device_id i5k_amb_ids[] = {
    491	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5000_ERR) },
    492	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_ERR) },
    493	{ 0, }
    494};
    495MODULE_DEVICE_TABLE(pci, i5k_amb_ids);
    496#endif
    497
    498static int i5k_amb_probe(struct platform_device *pdev)
    499{
    500	struct i5k_amb_data *data;
    501	struct resource *reso;
    502	int i, res;
    503
    504	data = kzalloc(sizeof(*data), GFP_KERNEL);
    505	if (!data)
    506		return -ENOMEM;
    507
    508	/* Figure out where the AMB registers live */
    509	i = 0;
    510	do {
    511		res = i5k_find_amb_registers(data, chipset_ids[i].err);
    512		if (res == 0)
    513			break;
    514		i++;
    515	} while (chipset_ids[i].err);
    516
    517	if (res)
    518		goto err;
    519
    520	/* Copy the DIMM presence map for the first two channels */
    521	res = i5k_channel_probe(&data->amb_present[0], chipset_ids[i].fbd0);
    522	if (res)
    523		goto err;
    524
    525	/* Copy the DIMM presence map for the optional second two channels */
    526	i5k_channel_probe(&data->amb_present[2], chipset_ids[i].fbd0 + 1);
    527
    528	/* Set up resource regions */
    529	reso = request_mem_region(data->amb_base, data->amb_len, DRVNAME);
    530	if (!reso) {
    531		res = -EBUSY;
    532		goto err;
    533	}
    534
    535	data->amb_mmio = ioremap(data->amb_base, data->amb_len);
    536	if (!data->amb_mmio) {
    537		res = -EBUSY;
    538		goto err_map_failed;
    539	}
    540
    541	platform_set_drvdata(pdev, data);
    542
    543	res = i5k_amb_hwmon_init(pdev);
    544	if (res)
    545		goto err_init_failed;
    546
    547	return res;
    548
    549err_init_failed:
    550	iounmap(data->amb_mmio);
    551err_map_failed:
    552	release_mem_region(data->amb_base, data->amb_len);
    553err:
    554	kfree(data);
    555	return res;
    556}
    557
    558static int i5k_amb_remove(struct platform_device *pdev)
    559{
    560	int i;
    561	struct i5k_amb_data *data = platform_get_drvdata(pdev);
    562
    563	hwmon_device_unregister(data->hwmon_dev);
    564	device_remove_file(&pdev->dev, &dev_attr_name);
    565	for (i = 0; i < data->num_attrs; i++)
    566		device_remove_file(&pdev->dev, &data->attrs[i].s_attr.dev_attr);
    567	kfree(data->attrs);
    568	iounmap(data->amb_mmio);
    569	release_mem_region(data->amb_base, data->amb_len);
    570	kfree(data);
    571	return 0;
    572}
    573
    574static struct platform_driver i5k_amb_driver = {
    575	.driver = {
    576		.name = DRVNAME,
    577	},
    578	.probe = i5k_amb_probe,
    579	.remove = i5k_amb_remove,
    580};
    581
    582static int __init i5k_amb_init(void)
    583{
    584	int res;
    585
    586	res = platform_driver_register(&i5k_amb_driver);
    587	if (res)
    588		return res;
    589
    590	res = i5k_amb_add();
    591	if (res)
    592		platform_driver_unregister(&i5k_amb_driver);
    593
    594	return res;
    595}
    596
    597static void __exit i5k_amb_exit(void)
    598{
    599	platform_device_unregister(amb_pdev);
    600	platform_driver_unregister(&i5k_amb_driver);
    601}
    602
    603MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
    604MODULE_DESCRIPTION("Intel 5000 chipset FB-DIMM AMB temperature sensor");
    605MODULE_LICENSE("GPL");
    606
    607module_init(i5k_amb_init);
    608module_exit(i5k_amb_exit);