AMB FB DIMM driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

I have another attempt.  :)  Patch is attached, and comments to the
original quick review are below.  Mostly ACKs.

On Wed, May 23, 2007 at 11:40:47PM +0200, Rudolf Marek wrote:
> Hi Jeffrey,
>> To find your sensors, one can scan the 128k area of memory for what
>> "looks" right.  The ticket suggested looking for a vendor id of 0x8086
>> (Intel) starting the particular 2k slot.  This works on the DSBF-D, but
>> I have a Tyan board which is less agreeable.  But on both boards, the
>> non-DIMM areas in that space have vendor id:device id equal to
>> 0xffff:0xffff.  In other words, we can just look for anything not all
>> ones.  I haven't found anything on paper that justifies this, but it
>> works on the boards I have.  ;)
>
> Yes it should work. Anything other than 0xffff should be considered valid 
> ID.

Good to know

>> /*
>>  * ambtemp.c - Linux kernel module for hardware monitoring
>>  *
>>  * Copyright (C) 2007 Jeff Garrett <jgarrett at teamhpc.com>
>>  *
>>  * Inspired (stolen?) from the k8temp driver.
>
> Well inspired sounds better.

Point taken.  Not funny.  :)

>>  *
>>  * This program is free software; you can redistribute it and/or modify
>>  * it under the terms of the GNU General Public License as published by
>>  * the Free Software Foundation; either version 2 of the License, or
>>  * (at your option) any later version.
>>  *
>>  * This program is distributed in the hope that it will be useful,
>>  * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>  * GNU General Public License for more details.
>>  *
>>  * You should have received a copy of the GNU General Public License
>>  * along with this program; if not, write to the Free Software
>>  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>>  * 02110-1301 USA.
>>  */
>> #include <linux/module.h>
>> #include <linux/delay.h>
>> #include <linux/init.h>
>> #include <linux/slab.h>
>> #include <linux/jiffies.h>
>> #include <linux/pci.h>
>> #include <linux/hwmon.h>
>> #include <linux/hwmon-sysfs.h>
>> #include <linux/err.h>
>> #include <linux/mutex.h>
>> #include <asm/bitops.h>
>> #define TEMP_FROM_REG(val)  ((((u16)(val)) * 500))
>> #define AMBASE_HIGH         0x48
>> #define AMB_EXTENT          0x20000
>> #define TEMP_OFFSET         ((3*256)+0x85)
>
> Careful about tabs versus spaces!

Noted

>> struct ambtemp_data {
>> 	struct class_device *class_dev;
>> 	struct mutex update_lock;
>> 	const char *name;
>> 	char valid;		/* zero until following fields are valid */
>> 	unsigned long last_updated;	/* in jiffies */
>> 	/* register values */
>> 	void __iomem *base;
>> 	u64 valid_sensors;	/* bitmap for which sensors are present */
>> 	u8  temp[64];		/* all 64 possible sensors */
>> };
>> static struct ambtemp_data *ambtemp_update_device(struct device *dev)
>> {
>> 	struct ambtemp_data *data = dev_get_drvdata(dev);
>> 	int i;
>> 	mutex_lock(&data->update_lock);
>> 	if (!data->valid
>> 		|| time_after(jiffies, data->last_updated + HZ)) {
>> 		/* Read the sensors */
>> 		for (i = 0; i < 64; i++)
>> 			if (test_bit(i, &data->valid_sensors))
>> 				data->temp[i] = readb(data->base + 2048*i + TEMP_OFFSET);
>> 		data->last_updated = jiffies;
>> 		data->valid = 1;
>> 	}
>> 	mutex_unlock(&data->update_lock);
>> 	return data;
>> }
>> /*
>>  * Sysfs stuff
>>  */
>> static ssize_t show_name(struct device *dev, struct device_attribute
>> 			 *devattr, char *buf)
>> {
>> 	struct ambtemp_data *data = dev_get_drvdata(dev);
>> 	return sprintf(buf, "%s\n", data->name);
>> }
>> static ssize_t show_temp(struct device *dev,
>> 			 struct device_attribute *devattr, char *buf)
>> {
>> 	struct sensor_device_attribute *attr =
>> 		to_sensor_dev_attr(devattr);
>> 	int sensor = attr->index;
>> 	struct ambtemp_data *data = ambtemp_update_device(dev);
>> 	return sprintf(buf, "%d\n",
>> 			   TEMP_FROM_REG(data->temp[sensor]));
>> }
>> /* There are a total of 4 channels, with 16 possible AMBs per channel */
>> static struct sensor_device_attribute temp_input[] = {
>> 	SENSOR_ATTR(temp00_input, S_IRUGO, show_temp, NULL, 0),
> Without leading zero and decimal please. Maybe you can even do this in 
> dynamic fashion. I think there was at least one driver doing it this way, 
> but I dont recall right now.

Noted.  Should there be any communication with the user about names
then?  For example, in the DSBF-D manual it calls the DIMMs DIMM_00,
DIMM_01, etc. based on channel & number. And this would correlate to
temp00_input, etc.  In other words, locating a hot DIMM is relatively
easy with this information.  If they're called, say,
temp{1,2,17,18,33,34,49,50}_input, then a user can still figure out
which DIMM is which, but has to realize how to correlate 17 to DIMM_10.
It's a little bit more opaque.  And if they're called
temp{1,2,3,4,5,6,7,8}_input, it's even more so.

So, should there be a separate sysfs attribute, something like
temp1_name which may read DIMM_00, for example?

Also, which of the two options above is preferable [no gaps, or gaps but
numbers that are stable...  temp17 always points to the same slot]?

I made it kinda sorta dynamic...   i.e. the structs are in data, so they
are allocated dynamically, not statically, and initialized when needed.
But it still allocates space for 64...

>> 	SENSOR_ATTR(temp01_input, S_IRUGO, show_temp, NULL, 1),
>> 	SENSOR_ATTR(temp02_input, S_IRUGO, show_temp, NULL, 2),
>> 	SENSOR_ATTR(temp03_input, S_IRUGO, show_temp, NULL, 3),
>> 	SENSOR_ATTR(temp04_input, S_IRUGO, show_temp, NULL, 4),
>> 	SENSOR_ATTR(temp05_input, S_IRUGO, show_temp, NULL, 5),
>> 	SENSOR_ATTR(temp06_input, S_IRUGO, show_temp, NULL, 6),
>> 	SENSOR_ATTR(temp07_input, S_IRUGO, show_temp, NULL, 7),
>> 	SENSOR_ATTR(temp08_input, S_IRUGO, show_temp, NULL, 8),
>> 	SENSOR_ATTR(temp09_input, S_IRUGO, show_temp, NULL, 9),
>> 	SENSOR_ATTR(temp0a_input, S_IRUGO, show_temp, NULL, 10),
>> 	SENSOR_ATTR(temp0b_input, S_IRUGO, show_temp, NULL, 11),
>> 	SENSOR_ATTR(temp0c_input, S_IRUGO, show_temp, NULL, 12),
>> 	SENSOR_ATTR(temp0d_input, S_IRUGO, show_temp, NULL, 13),
>> 	SENSOR_ATTR(temp0e_input, S_IRUGO, show_temp, NULL, 14),
>> 	SENSOR_ATTR(temp0f_input, S_IRUGO, show_temp, NULL, 15),
>> 	SENSOR_ATTR(temp10_input, S_IRUGO, show_temp, NULL, 16),
>> 	SENSOR_ATTR(temp11_input, S_IRUGO, show_temp, NULL, 17),
>> 	SENSOR_ATTR(temp12_input, S_IRUGO, show_temp, NULL, 18),
>> 	SENSOR_ATTR(temp13_input, S_IRUGO, show_temp, NULL, 19),
>> 	SENSOR_ATTR(temp14_input, S_IRUGO, show_temp, NULL, 20),
>> 	SENSOR_ATTR(temp15_input, S_IRUGO, show_temp, NULL, 21),
>> 	SENSOR_ATTR(temp16_input, S_IRUGO, show_temp, NULL, 22),
>> 	SENSOR_ATTR(temp17_input, S_IRUGO, show_temp, NULL, 23),
>> 	SENSOR_ATTR(temp18_input, S_IRUGO, show_temp, NULL, 24),
>> 	SENSOR_ATTR(temp19_input, S_IRUGO, show_temp, NULL, 25),
>> 	SENSOR_ATTR(temp1a_input, S_IRUGO, show_temp, NULL, 26),
>> 	SENSOR_ATTR(temp1b_input, S_IRUGO, show_temp, NULL, 27),
>> 	SENSOR_ATTR(temp1c_input, S_IRUGO, show_temp, NULL, 28),
>> 	SENSOR_ATTR(temp1d_input, S_IRUGO, show_temp, NULL, 29),
>> 	SENSOR_ATTR(temp1e_input, S_IRUGO, show_temp, NULL, 30),
>> 	SENSOR_ATTR(temp1f_input, S_IRUGO, show_temp, NULL, 31),
>> 	SENSOR_ATTR(temp20_input, S_IRUGO, show_temp, NULL, 32),
>> 	SENSOR_ATTR(temp21_input, S_IRUGO, show_temp, NULL, 33),
>> 	SENSOR_ATTR(temp22_input, S_IRUGO, show_temp, NULL, 34),
>> 	SENSOR_ATTR(temp23_input, S_IRUGO, show_temp, NULL, 35),
>> 	SENSOR_ATTR(temp24_input, S_IRUGO, show_temp, NULL, 36),
>> 	SENSOR_ATTR(temp25_input, S_IRUGO, show_temp, NULL, 37),
>> 	SENSOR_ATTR(temp26_input, S_IRUGO, show_temp, NULL, 38),
>> 	SENSOR_ATTR(temp27_input, S_IRUGO, show_temp, NULL, 39),
>> 	SENSOR_ATTR(temp28_input, S_IRUGO, show_temp, NULL, 40),
>> 	SENSOR_ATTR(temp29_input, S_IRUGO, show_temp, NULL, 41),
>> 	SENSOR_ATTR(temp2a_input, S_IRUGO, show_temp, NULL, 42),
>> 	SENSOR_ATTR(temp2b_input, S_IRUGO, show_temp, NULL, 43),
>> 	SENSOR_ATTR(temp2c_input, S_IRUGO, show_temp, NULL, 44),
>> 	SENSOR_ATTR(temp2d_input, S_IRUGO, show_temp, NULL, 45),
>> 	SENSOR_ATTR(temp2e_input, S_IRUGO, show_temp, NULL, 46),
>> 	SENSOR_ATTR(temp2f_input, S_IRUGO, show_temp, NULL, 47),
>> 	SENSOR_ATTR(temp30_input, S_IRUGO, show_temp, NULL, 48),
>> 	SENSOR_ATTR(temp31_input, S_IRUGO, show_temp, NULL, 49),
>> 	SENSOR_ATTR(temp32_input, S_IRUGO, show_temp, NULL, 50),
>> 	SENSOR_ATTR(temp33_input, S_IRUGO, show_temp, NULL, 51),
>> 	SENSOR_ATTR(temp34_input, S_IRUGO, show_temp, NULL, 52),
>> 	SENSOR_ATTR(temp35_input, S_IRUGO, show_temp, NULL, 53),
>> 	SENSOR_ATTR(temp36_input, S_IRUGO, show_temp, NULL, 54),
>> 	SENSOR_ATTR(temp37_input, S_IRUGO, show_temp, NULL, 55),
>> 	SENSOR_ATTR(temp38_input, S_IRUGO, show_temp, NULL, 56),
>> 	SENSOR_ATTR(temp39_input, S_IRUGO, show_temp, NULL, 57),
>> 	SENSOR_ATTR(temp3a_input, S_IRUGO, show_temp, NULL, 58),
>> 	SENSOR_ATTR(temp3b_input, S_IRUGO, show_temp, NULL, 59),
>> 	SENSOR_ATTR(temp3c_input, S_IRUGO, show_temp, NULL, 60),
>> 	SENSOR_ATTR(temp3d_input, S_IRUGO, show_temp, NULL, 61),
>> 	SENSOR_ATTR(temp3e_input, S_IRUGO, show_temp, NULL, 62),
>> 	SENSOR_ATTR(temp3f_input, S_IRUGO, show_temp, NULL, 63),
>> };
>> static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
>> static struct pci_device_id ambtemp_ids[] = {
>> 	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x25f0) },
>
> the 0x25f0 should go to the pciids.h Moreover I do not recall if this is 
> used also for something else? I mean has this PCI device some other 
> function?

It's called the error reporting registers by lspci.  As far as I can
tell, it's just a bunch of registers associated to the MCH, and nothing
else in the kernel claims it.

>> 	{ 0 },
>> };
>> MODULE_DEVICE_TABLE(pci, ambtemp_ids);
>> static int __devinit ambtemp_probe(struct pci_dev *pdev,
>> 				  const struct pci_device_id *id)
>> {
>> 	int err;
>> 	int i;
>> 	void *addr;
>> 	unsigned long c;
>> 	
>> 	struct ambtemp_data *data;
>> 	/* The Error Reporting Registers we piggy back on show up at
>> 	 * 10.0, 10.1, and 10.2.  We only want the first function.  The
>> 	 * AMBASE slot on the others is wrong.
>> 	 */
>> 	if (!(PCI_SLOT(pdev->devfn) == 0x10 && PCI_FUNC(pdev->devfn) == 0x00))
>> 	{
>> 		/* Go away */
>> 		err = 0;
>> 		goto exit;
>> 	}
>
> Maybe just function check is enough?
>  if (PCI_FUNC(pdev->devfn) !=...

Noted.

>> 	if (!(data = kzalloc(sizeof(struct ambtemp_data), GFP_KERNEL))) {
>> 		err = -ENOMEM;
>> 		goto exit;
>> 	}
>> 	printk(KERN_INFO "ambtemp: Device found at %04x:%02x.%01x\n", 
>> 		pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
> perhaps just dev_info will work better

Indeed. :)

>> 	printk(KERN_INFO "ambtemp: Scanning for sensors...\n");
> this should be dev_dbg

Ok

>> 	/* Reserve the sensor range starting at AMBASE */
>> 	pci_read_config_dword(pdev, AMBASE_HIGH, &addr);
>
> Perhaps mask out lowest bits? They are reserved 16:0
> Also it may be even set 64bit so question is if we should support this. 
> Maybe some check for 32bit valid address and check for 0x0 ?

Noted.

>> 	data->base = ioremap(addr, AMB_EXTENT);
>
>
>> 	if (!data->base)
>> 	{
>> 		err = -EBUSY;
>> 		goto exit;
>> 	}
>> 	for (i = 0; i < 64; i++)
>> 	{
>> 		c = readl(data->base + 2048*i);
>> 		if (c != 0xffffffffl)
>> 		{
>> 			set_bit(i, &data->valid_sensors);
>> 			printk(KERN_INFO "ambtemp: Sensor at offset 0x%02x\n", i);
> 	dev_dbg

Ok.

>> 		}
>> 	}
>> 	data->name = "ambtemp";
>> 	mutex_init(&data->update_lock);
>> 	dev_set_drvdata(&pdev->dev, data);
>> 	/* Register sysfs hooks */
>> 	for (i = 0; i < ARRAY_SIZE(temp_input); i++)
>> 		if (test_bit(i, &data->valid_sensors))
>> 		{
>> 			err = device_create_file(&pdev->dev, &temp_input[i].dev_attr);
>> 			if (err)
>> 				goto exit_remove;
>> 		}
>> 	err = device_create_file(&pdev->dev, &dev_attr_name);
>> 	if (err)
>> 		goto exit_remove;
>> 	data->class_dev = hwmon_device_register(&pdev->dev);
>> 	if (IS_ERR(data->class_dev)) {
>> 		err = PTR_ERR(data->class_dev);
>> 		goto exit_remove;
>> 	}
>> 	return 0;
>> exit_remove:
>> 	for (i = 0; i < ARRAY_SIZE(temp_input); i++)
>> 		device_remove_file(&pdev->dev, &temp_input[i].dev_attr);
>> 	device_remove_file(&pdev->dev, &dev_attr_name);
>> 	dev_set_drvdata(&pdev->dev, NULL);
>> 	kfree(data);
>> exit:
>> 	return err;
>> }
>> static void __devexit ambtemp_remove(struct pci_dev *pdev)
>> {
>> 	int i;
>> 	struct ambtemp_data *data = dev_get_drvdata(&pdev->dev);
>> 	hwmon_device_unregister(data->class_dev);
>> 	for (i = 0; i < ARRAY_SIZE(temp_input); i++)
>> 		device_remove_file(&pdev->dev, &temp_input[i].dev_attr);
>> 	device_remove_file(&pdev->dev, &dev_attr_name);
>> 	dev_set_drvdata(&pdev->dev, NULL);
>> 	iounmap(data->base);
>> 	kfree(data);
>> }
>> static struct pci_driver ambtemp_driver = {
>> 	.name = "ambtemp",
>> 	.id_table = ambtemp_ids,
>> 	.probe = ambtemp_probe,
>> 	.remove = __devexit_p(ambtemp_remove),
>> };
>> static int __init ambtemp_init(void)
>> {
>> 	return pci_register_driver(&ambtemp_driver);
>> }
>> static void __exit ambtemp_exit(void)
>> {
>> 	pci_unregister_driver(&ambtemp_driver);
>> }
>> MODULE_AUTHOR("Jeff Garrett <jgarrett at teamhpc.com>");
>> MODULE_DESCRIPTION("Intel AMB temperature monitor");
>> MODULE_LICENSE("GPL");
>> module_init(ambtemp_init)
>> module_exit(ambtemp_exit)
>
> Ok so this was just very fast review. For a future reference here are the 
> datasheets:
> http://www.intel.com/design/chipsets/datashts/313071.htm
> http://www.intel.com/design/chipsets/datashts/313072.htm

Yea, I failed to mention it, but I had found those.  I hadn't spent very
long with them though.

> I hope it helps,
>
> Rudolf

Helps immensely.  I'm a bit new to this.  Thanks very much,

Jeff
-------------- next part --------------
A non-text attachment was scrubbed...
Name: ambtemp.patch
Type: text/x-diff
Size: 7999 bytes
Desc: not available
Url : http://lists.lm-sensors.org/pipermail/lm-sensors/attachments/20070613/367351f1/attachment.bin 


[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux