Subject: [PATCH] hwmon:gm965temp add GM/GME965 IGP temperature report.[Ver0] Intel GM/GME965 has two on-die thermal sensors. Driver support the graphics hot spot temperature report. Chips GM/GME965 is tested, and seems work. While GM45 and more other desktop chips are not tested, it may be not work for them. So user need to enable flag "TEST_MORE" to test them. Thanks to Tobias Hain<tobias.hain@xxxxxx> for helped me to test GM965 chip too, to add desktop chips. Thanks to Mauro Blanco<blancomau@xxxxxxxxx> for helped me to test GME965 chip. MODULE_DEVICE_TABLE calling in this path just for test, I am not quit sure about it. This path waiting for your review, please help me to improve it. Thank you! Signed-off-by: Lu Zhihe <tombowfly@xxxxxxxxx> --- diff -uprN -X linux-2.6.32-rc3.orig/Documentation/dontdiff linux-2.6.32-rc3.orig/drivers/hwmon/Kconfig linux-2.6.32-rc3/drivers/hwmon/Kconfig --- linux-2.6.32-rc3.orig/drivers/hwmon/Kconfig 2009-10-10 23:42:13.000000000 +0000 +++ linux-2.6.32-rc3/drivers/hwmon/Kconfig 2009-10-12 22:33:33.000000000 +0000 @@ -372,6 +372,16 @@ config SENSORS_GL520SM This driver can also be built as a module. If so, the module will be called gl520sm. +config SENSORS_GM965TEMP + tristate "Intel GM965 temperature sensor" + depends on PCI + help + If you say yes here you get support for GM965 IGP temperatue + sensor. + + This driver can also be built as a module. If so, the module + will be called gm965temp. + config SENSORS_CORETEMP tristate "Intel Core/Core2/Atom temperature sensor" depends on X86 && EXPERIMENTAL diff -uprN -X linux-2.6.32-rc3.orig/Documentation/dontdiff linux-2.6.32-rc3.orig/drivers/hwmon/Makefile linux-2.6.32-rc3/drivers/hwmon/Makefile --- linux-2.6.32-rc3.orig/drivers/hwmon/Makefile 2009-10-10 23:42:06.000000000 +0000 +++ linux-2.6.32-rc3/drivers/hwmon/Makefile 2009-10-12 22:33:33.000000000 +0000 @@ -46,6 +46,7 @@ obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o obj-$(CONFIG_SENSORS_G760A) += g760a.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o +obj-$(CONFIG_SENSORS_GM965TEMP) += gm965temp.o obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o diff -uprN -X linux-2.6.32-rc3.orig/Documentation/dontdiff linux-2.6.32-rc3.orig/drivers/hwmon/gm965temp.c linux-2.6.32-rc3/drivers/hwmon/gm965temp.c --- linux-2.6.32-rc3.orig/drivers/hwmon/gm965temp.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.32-rc3/drivers/hwmon/gm965temp.c 2009-10-12 22:56:41.000000000 +0000 @@ -0,0 +1,517 @@ +/* + * A hwmon driver for the Intel GM/GME965 chipset IGP + * temperature sensors + * + * Copyright (C) 2009 Lu Zhihe <tombowfly at gmail.com> + * + * Tested and helped improved by: + * + * Tobias Hain <tobias.hain@xxxxxx> + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/jiffies.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/platform_device.h> + +#define DRVNAME "gm965temp" + +#if 0 +#define TEST_MORE /*For test more desktop chipsets*/ +#endif +#define USE_RTR /*Flag for report method*/ + +enum { SHOW_TEMP, SHOW_TJMAX, SHOW_TTARGET, SHOW_LABEL } SHOW; + +/*Mobile Series Chipsets, read from TR1/RTR1*/ +#define PCI_DEVICE_ID_INTEL_82965GM 0x2a00 +#define PCI_DEVICE_ID_INTEL_82965GME 0x2a10 +#define PCI_DEVICE_ID_INTEL_GM45 0x2a40 + +#ifdef TEST_MORE +/*3 Series Chipsets, read from TSTTP.RELT*/ +#define PCI_DEVICE_ID_INTEL_Q35 0x29b0 +#define PCI_DEVICE_ID_INTEL_G33 0x29c0 +#define PCI_DEVICE_ID_INTEL_Q33 0x29d0 + +/*4 Series Chipsets, read from TSTTP.RELT*/ +#define PCI_DEVICE_ID_INTEL_Q45 0x2e10 +#define PCI_DEVICE_ID_INTEL_G45 0x2e20 +#define PCI_DEVICE_ID_INTEL_G41 0x2e30 +#define PCI_DEVICE_ID_INTEL_B43_BASE 0x2e40 +#define PCI_DEVICE_ID_INTEL_B43_SOFT_SKU 0x2e90 +#endif + +#define IS_GM (data->chipset_id == PCI_DEVICE_ID_INTEL_82965GM \ + || data->chipset_id == PCI_DEVICE_ID_INTEL_82965GME \ + || data->chipset_id == PCI_DEVICE_ID_INTEL_GM45) + +#define GM965_SYSFS_NAME_LEN 16 + +#define MCHBAR_I965 0x48 /* MCH Memory Mapped Register BAR */ +#define MCHBAR_MASK 0xfffffc000ULL /* bits 35:14 */ + +static ssize_t show_label(struct device *dev, + struct device_attribute *devattr, + char *buf); + +static ssize_t show_temp(struct device *dev, + struct device_attribute *devattr, + char *buf); +struct gm965temp_data { + struct device *hwmon_dev; + struct mutex update_lock; + + u64 igp_base; + unsigned long igp_len; + void __iomem *igp_mmio; + + unsigned int temp; + unsigned long chipset_id; +}; + +static ssize_t show_name(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + return sprintf(buf, "%s\n", DRVNAME); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, SHOW_TEMP); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, NULL, SHOW_TJMAX); +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL, + SHOW_TTARGET); +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, SHOW_LABEL); +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static struct attribute *gm965temp_attributes[] = { + &sensor_dev_attr_temp1_label.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + NULL +}; + +static const struct attribute_group gm965temp_group = { + .attrs = gm965temp_attributes, +}; + +static const struct pci_device_id gm965temp_pci_ids[] = { +#define ID(x) \ + { \ + .class = (PCI_CLASS_BRIDGE_HOST << 8), \ + .class_mask = ~0, \ + .vendor = PCI_VENDOR_ID_INTEL, \ + .device = x, \ + .subvendor = PCI_ANY_ID, \ + .subdevice = PCI_ANY_ID, \ + } + ID(PCI_DEVICE_ID_INTEL_82965GM), + ID(PCI_DEVICE_ID_INTEL_82965GME), + ID(PCI_DEVICE_ID_INTEL_GM45), +#ifdef TEST_MORE + ID(PCI_DEVICE_ID_INTEL_Q35), + ID(PCI_DEVICE_ID_INTEL_G33), + ID(PCI_DEVICE_ID_INTEL_Q33), + ID(PCI_DEVICE_ID_INTEL_Q45), + ID(PCI_DEVICE_ID_INTEL_G45), + ID(PCI_DEVICE_ID_INTEL_G41), + ID(PCI_DEVICE_ID_INTEL_B43_BASE), + ID(PCI_DEVICE_ID_INTEL_B43_SOFT_SKU), +#endif + {} +}; + +MODULE_DEVICE_TABLE(pci, gm965temp_pci_ids); + +static struct platform_device *igp_pdev; +static struct pci_dev *pcidev; +static unsigned long devid; + +static u8 igp_read_byte(struct gm965temp_data *data, unsigned long offset) +{ + return ioread8(data->igp_mmio + offset); +} + +static u16 igp_read_short(struct gm965temp_data *data, unsigned long offset) +{ + return ioread16(data->igp_mmio + offset); +} + +static u32 igp_read_int(struct gm965temp_data *data, unsigned long offset) +{ + return ioread32(data->igp_mmio + offset); +} + +static void igp_write_short(struct gm965temp_data *data, unsigned long offset, + u16 val) +{ + iowrite16(val, data->igp_mmio + offset); +} + +static ssize_t show_label(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + return sprintf(buf, "GM965 IGP\n"); +} + + +#define TSC1 0x1001 +#define TSS1 0x1004 +#define TR1 0x1006 +#define TOF1 0x1007 +#define RTR1 0x1008 + +#define G_TSC1 0xCD8 /*8 bits*/ +#define G_TSS 0xCDA /*8 bits*/ +#define TSTTP 0xCDC /*32 bits*/ + +#define G_TSE 0x80 + +#define TSE 0x8000 +#define TMOV (0x01 << 10) +#define G_TMOV (0x01 << 4) + +#define RELT_MASK 0xFF000000 /*bits 31:24*/ +#define HTPS_MASK 0xFF00 /*bits 15:8 */ +#define CTPS_MASK 0xFF /*bits 7:0 */ + +#define MAX_RETRIES 6 + +static struct gm965temp_data *gm965temp_update_device(struct device *dev) +{ + struct gm965temp_data *data = dev_get_drvdata(dev); + unsigned short tsc1_val = 0; +#ifdef USE_RTR + char rtr1_val = 0; + unsigned char tof1_val = 0; +#else + unsigned char tr1_val = 0; +#endif + unsigned short tss1_val = 0, tmov = 0; + unsigned int tsttp_val = 0, htps_val = 0; + char relt_val = 0; + unsigned int tsc1 = 0, tss1 = 0, tse = 0; + int temp_val = 0; + int i = MAX_RETRIES; + + data->temp = 0x0; + if (IS_GM) { + tsc1 = TSC1; + tss1 = TSS1; + tse = TSE; + tmov = TMOV; + } else { + tsc1 = G_TSC1; + tss1 = G_TSS; + tse = G_TSE; + tmov = G_TMOV; + } + + mutex_lock(&data->update_lock); + + if (IS_GM) + tsc1_val = igp_read_short(data, tsc1); + else + tsc1_val = igp_read_byte(data, tsc1); + + if (!(tsc1_val & tse)) { + igp_write_short(data, tsc1, tsc1_val|tse); + if (IS_GM) + tsc1_val = igp_read_short(data, tsc1); + else + tsc1_val = igp_read_byte(data, tsc1); + } else { + if (IS_GM) + tss1_val = igp_read_short(data, tss1); + else + tss1_val = igp_read_byte(data, tss1); + + /* Wait for the thermal sensor read ready */ + while (!(tss1_val & tmov) && (0 < i--)) { + mdelay(1); + udelay(600); + if (IS_GM) + tss1_val = igp_read_short(data, tss1); + else + tss1_val = igp_read_byte(data, tss1); + } + + /* function to map register values to temperatures taken from + * p. 358 http://www.intel.com/design/chipsets/datashts/313053.htm + */ + if (tss1_val & tmov) { + if (IS_GM) { +#ifdef USE_RTR + rtr1_val = igp_read_byte(data, RTR1); + tof1_val = igp_read_byte(data, TOF1); + rtr1_val += tof1_val; + if ((rtr1_val != 0xFF) && (rtr1_val != 0x0)) { + data->temp = (16*rtr1_val*rtr1_val + - 11071*rtr1_val + 1610500)/10; + } +#else + tr1_val = igp_read_byte(data, TR1); + if ((tr1_val != 0xFF) && (tr1_val != 0x0)) { + data->temp = (16*tr1_val*tr1_val + - 11071*tr1_val + 1610500)/10; + } +#endif + } else { + tsttp_val = igp_read_int(data, TSTTP); + htps_val = (tsttp_val & HTPS_MASK) >> 8; + relt_val = (tsttp_val & RELT_MASK) >> 24; + temp_val = htps_val + relt_val; + data->temp = (16*temp_val*relt_val + - 11071*temp_val + 1610500)/10; + } + } + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static ssize_t show_temp(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct gm965temp_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + unsigned int temp = 0; + + if (attr->index == SHOW_TEMP) { + data = gm965temp_update_device(dev); + temp = data->temp; + return sprintf(buf, "%d\n", temp); + } else { + unsigned int TjMax = 110; + return sprintf(buf, "%d\n", TjMax*1000); + } +} + +static int __devinit gm965temp_hwmon_init(struct platform_device *pdev) +{ + int res = 0; + struct gm965temp_data *data = platform_get_drvdata(pdev); + + res = device_create_file(&pdev->dev, &dev_attr_name); + if (res) + goto exit_remove; + + res = sysfs_create_group(&pdev->dev.kobj, &gm965temp_group); + if (res) + goto exit_dev; + + 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: + sysfs_remove_group(&pdev->dev.kobj, &gm965temp_group); +exit_dev: + device_remove_file(&pdev->dev, &dev_attr_name); + + return res; +} + +static int __devinit gm965temp_add(void) +{ + int res = -ENODEV; + + /* only ever going to be one of these */ + igp_pdev = platform_device_alloc(DRVNAME, 0); + if (!igp_pdev) + return -ENOMEM; + + res = platform_device_add(igp_pdev); + if (res) + goto err; + return 0; + +err: + platform_device_put(igp_pdev); + return res; +} + +static int __devinit gm965_find_registers(struct gm965temp_data *data) +{ + u32 val32 = 0x0UL; + u64 val64 = 0x0ULL; + int res = -ENODEV; + + if (pci_read_config_dword(pcidev, MCHBAR_I965, &val32)) + goto out; + + if (!(val32 & 1)) + pci_write_config_dword(pcidev, MCHBAR_I965, val32 | 1); + + val64 = (u64)val32; + if (pci_read_config_dword(pcidev, MCHBAR_I965 + 4, &val32)) + goto out; + + val64 |= (u64)val32 << 32; + data->igp_base = val64 & MCHBAR_MASK; + data->igp_len = 16*1024; + + data->chipset_id = devid; + + res = 0; +out: + pci_dev_put(pcidev); + return res; +} + +static unsigned long chipset_ids[] = { + PCI_DEVICE_ID_INTEL_82965GM, + PCI_DEVICE_ID_INTEL_82965GME, + PCI_DEVICE_ID_INTEL_GM45, +#ifdef TEST_MORE + PCI_DEVICE_ID_INTEL_Q35, + PCI_DEVICE_ID_INTEL_G33, + PCI_DEVICE_ID_INTEL_Q33, + PCI_DEVICE_ID_INTEL_Q45, + PCI_DEVICE_ID_INTEL_G45, + PCI_DEVICE_ID_INTEL_G41, + PCI_DEVICE_ID_INTEL_B43_BASE, + PCI_DEVICE_ID_INTEL_B43_SOFT_SKU, +#endif + 0 +}; + +static int __devinit gm965temp_probe(struct platform_device *pdev) +{ + struct gm965temp_data *data = NULL; + struct resource *reso = NULL; + int res = -ENODEV; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = gm965_find_registers(data); + if (res) + goto err; + /* Set up resource regions */ + reso = request_mem_region(data->igp_base, data->igp_len, DRVNAME); + if (!reso) { + printk(KERN_ERR "request_mem_region failed\n"); + res = -EBUSY; + goto err; + } + + data->igp_mmio = ioremap_nocache(data->igp_base, data->igp_len); + if (!data->igp_mmio) { + printk(KERN_ERR "ioremap_nocache failed\n"); + res = -EBUSY; + goto err_map_failed; + } + + platform_set_drvdata(pdev, data); + + mutex_init(&data->update_lock); + + res = gm965temp_hwmon_init(pdev); + if (res) + goto err_init_failed; + + return res; + +err_init_failed: + iounmap(data->igp_mmio); + platform_set_drvdata(pdev, NULL); +err_map_failed: + release_mem_region(data->igp_base, data->igp_len); +err: + kfree(data); + return res; +} + +static int __devexit gm965temp_remove(struct platform_device *pdev) +{ + struct gm965temp_data *data = platform_get_drvdata(pdev); + hwmon_device_unregister(data->hwmon_dev); + device_remove_file(&pdev->dev, &dev_attr_name); + + iounmap(data->igp_mmio); + release_mem_region(data->igp_base, data->igp_len); + platform_set_drvdata(pdev, NULL); + kfree(data); + return 0; +} + +static struct platform_driver gm965temp_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + }, + .probe = gm965temp_probe, + .remove = __devexit_p(gm965temp_remove), +}; + +static int __init gm965temp_init(void) +{ + int res = 0; + unsigned long *i = NULL; + + for (i = chipset_ids; *i != 0; i++) { + pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, *i, NULL); + if (!pcidev) { + printk(KERN_INFO "Try pci_get_device for devid 0x%lx failed\n", + *i); + pci_dev_put(pcidev); + return -ENODEV; + } else { + devid = *i; + break; + } + } + + res = platform_driver_register(&gm965temp_driver); + if (res) + return res; + + res = gm965temp_add(); + if (res) + platform_driver_unregister(&gm965temp_driver); + + return res; +} + +static void __exit gm965temp_exit(void) +{ + platform_device_unregister(igp_pdev); + platform_driver_unregister(&gm965temp_driver); +} + +MODULE_AUTHOR("Lu Zhihe"); +MODULE_DESCRIPTION("Intel GM965 chipset IGP temperature sensor"); +MODULE_LICENSE("GPL and additional rights"); + +module_init(gm965temp_init); +module_exit(gm965temp_exit); + _______________________________________________ lm-sensors mailing list lm-sensors@xxxxxxxxxxxxxx http://lists.lm-sensors.org/mailman/listinfo/lm-sensors