1 /* 2 * k8temp.c - Linux kernel module for hardware monitoring 3 * 4 * Copyright (C) 2006 Rudolf Marek <r.marek@assembler.cz> 5 * 6 * Inspired from the w83785 and amd756 drivers. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 21 * 02110-1301 USA. 22 */ 23 24 #include <linux/module.h> 25 #include <linux/delay.h> 26 #include <linux/init.h> 27 #include <linux/slab.h> 28 #include <linux/jiffies.h> 29 #include <linux/pci.h> 30 #include <linux/hwmon.h> 31 #include <linux/hwmon-sysfs.h> 32 #include <linux/err.h> 33 #include <linux/mutex.h> 34 35 #define TEMP_FROM_REG(val) (((((val) >> 16) & 0xff) - 49) * 1000) 36 #define REG_TEMP 0xe4 37 #define SEL_PLACE 0x40 38 #define SEL_CORE 0x04 39 40 struct k8temp_data { 41 struct device *hwmon_dev; 42 struct mutex update_lock; 43 const char *name; 44 char valid; /* zero until following fields are valid */ 45 unsigned long last_updated; /* in jiffies */ 46 47 /* registers values */ 48 u8 sensorsp; /* sensor presence bits - SEL_CORE & SEL_PLACE */ 49 u32 temp[2][2]; /* core, place */ 50 }; 51 52 static struct k8temp_data *k8temp_update_device(struct device *dev) 53 { 54 struct k8temp_data *data = dev_get_drvdata(dev); 55 struct pci_dev *pdev = to_pci_dev(dev); 56 u8 tmp; 57 58 mutex_lock(&data->update_lock); 59 60 if (!data->valid 61 || time_after(jiffies, data->last_updated + HZ)) { 62 pci_read_config_byte(pdev, REG_TEMP, &tmp); 63 tmp &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */ 64 pci_write_config_byte(pdev, REG_TEMP, tmp); 65 pci_read_config_dword(pdev, REG_TEMP, &data->temp[0][0]); 66 67 if (data->sensorsp & SEL_PLACE) { 68 tmp |= SEL_PLACE; /* Select sensor 1, core0 */ 69 pci_write_config_byte(pdev, REG_TEMP, tmp); 70 pci_read_config_dword(pdev, REG_TEMP, 71 &data->temp[0][1]); 72 } 73 74 if (data->sensorsp & SEL_CORE) { 75 tmp &= ~SEL_PLACE; /* Select sensor 0, core1 */ 76 tmp |= SEL_CORE; 77 pci_write_config_byte(pdev, REG_TEMP, tmp); 78 pci_read_config_dword(pdev, REG_TEMP, 79 &data->temp[1][0]); 80 81 if (data->sensorsp & SEL_PLACE) { 82 tmp |= SEL_PLACE; /* Select sensor 1, core1 */ 83 pci_write_config_byte(pdev, REG_TEMP, tmp); 84 pci_read_config_dword(pdev, REG_TEMP, 85 &data->temp[1][1]); 86 } 87 } 88 89 data->last_updated = jiffies; 90 data->valid = 1; 91 } 92 93 mutex_unlock(&data->update_lock); 94 return data; 95 } 96 97 /* 98 * Sysfs stuff 99 */ 100 101 static ssize_t show_name(struct device *dev, struct device_attribute 102 *devattr, char *buf) 103 { 104 struct k8temp_data *data = dev_get_drvdata(dev); 105 106 return sprintf(buf, "%s\n", data->name); 107 } 108 109 110 static ssize_t show_temp(struct device *dev, 111 struct device_attribute *devattr, char *buf) 112 { 113 struct sensor_device_attribute_2 *attr = 114 to_sensor_dev_attr_2(devattr); 115 int core = attr->nr; 116 int place = attr->index; 117 struct k8temp_data *data = k8temp_update_device(dev); 118 119 return sprintf(buf, "%d\n", 120 TEMP_FROM_REG(data->temp[core][place])); 121 } 122 123 /* core, place */ 124 125 static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0); 126 static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1); 127 static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 1, 0); 128 static SENSOR_DEVICE_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 1, 1); 129 static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); 130 131 static struct pci_device_id k8temp_ids[] = { 132 { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MISC) }, 133 { 0 }, 134 }; 135 136 MODULE_DEVICE_TABLE(pci, k8temp_ids); 137 138 static int __devinit k8temp_probe(struct pci_dev *pdev, 139 const struct pci_device_id *id) 140 { 141 int err; 142 u8 scfg; 143 u32 temp; 144 struct k8temp_data *data; 145 u32 cpuid = cpuid_eax(1); 146 147 /* this feature should be available since SH-C0 core */ 148 if ((cpuid == 0xf40) || (cpuid == 0xf50) || (cpuid == 0xf51)) { 149 err = -ENODEV; 150 goto exit; 151 } 152 153 if (!(data = kzalloc(sizeof(struct k8temp_data), GFP_KERNEL))) { 154 err = -ENOMEM; 155 goto exit; 156 } 157 158 pci_read_config_byte(pdev, REG_TEMP, &scfg); 159 scfg &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */ 160 pci_write_config_byte(pdev, REG_TEMP, scfg); 161 pci_read_config_byte(pdev, REG_TEMP, &scfg); 162 163 if (scfg & (SEL_PLACE | SEL_CORE)) { 164 dev_err(&pdev->dev, "Configuration bit(s) stuck at 1!\n"); 165 err = -ENODEV; 166 goto exit_free; 167 } 168 169 scfg |= (SEL_PLACE | SEL_CORE); 170 pci_write_config_byte(pdev, REG_TEMP, scfg); 171 172 /* now we know if we can change core and/or sensor */ 173 pci_read_config_byte(pdev, REG_TEMP, &data->sensorsp); 174 175 if (data->sensorsp & SEL_PLACE) { 176 scfg &= ~SEL_CORE; /* Select sensor 1, core0 */ 177 pci_write_config_byte(pdev, REG_TEMP, scfg); 178 pci_read_config_dword(pdev, REG_TEMP, &temp); 179 scfg |= SEL_CORE; /* prepare for next selection */ 180 if (!((temp >> 16) & 0xff)) /* if temp is 0 -49C is not likely */ 181 data->sensorsp &= ~SEL_PLACE; 182 } 183 184 if (data->sensorsp & SEL_CORE) { 185 scfg &= ~SEL_PLACE; /* Select sensor 0, core1 */ 186 pci_write_config_byte(pdev, REG_TEMP, scfg); 187 pci_read_config_dword(pdev, REG_TEMP, &temp); 188 if (!((temp >> 16) & 0xff)) /* if temp is 0 -49C is not likely */ 189 data->sensorsp &= ~SEL_CORE; 190 } 191 192 data->name = "k8temp"; 193 mutex_init(&data->update_lock); 194 dev_set_drvdata(&pdev->dev, data); 195 196 /* Register sysfs hooks */ 197 err = device_create_file(&pdev->dev, 198 &sensor_dev_attr_temp1_input.dev_attr); 199 if (err) 200 goto exit_remove; 201 202 /* sensor can be changed and reports something */ 203 if (data->sensorsp & SEL_PLACE) { 204 err = device_create_file(&pdev->dev, 205 &sensor_dev_attr_temp2_input.dev_attr); 206 if (err) 207 goto exit_remove; 208 } 209 210 /* core can be changed and reports something */ 211 if (data->sensorsp & SEL_CORE) { 212 err = device_create_file(&pdev->dev, 213 &sensor_dev_attr_temp3_input.dev_attr); 214 if (err) 215 goto exit_remove; 216 if (data->sensorsp & SEL_PLACE) 217 err = device_create_file(&pdev->dev, 218 &sensor_dev_attr_temp4_input. 219 dev_attr); 220 if (err) 221 goto exit_remove; 222 } 223 224 err = device_create_file(&pdev->dev, &dev_attr_name); 225 if (err) 226 goto exit_remove; 227 228 data->hwmon_dev = hwmon_device_register(&pdev->dev); 229 230 if (IS_ERR(data->hwmon_dev)) { 231 err = PTR_ERR(data->hwmon_dev); 232 goto exit_remove; 233 } 234 235 return 0; 236 237 exit_remove: 238 device_remove_file(&pdev->dev, 239 &sensor_dev_attr_temp1_input.dev_attr); 240 device_remove_file(&pdev->dev, 241 &sensor_dev_attr_temp2_input.dev_attr); 242 device_remove_file(&pdev->dev, 243 &sensor_dev_attr_temp3_input.dev_attr); 244 device_remove_file(&pdev->dev, 245 &sensor_dev_attr_temp4_input.dev_attr); 246 device_remove_file(&pdev->dev, &dev_attr_name); 247 exit_free: 248 dev_set_drvdata(&pdev->dev, NULL); 249 kfree(data); 250 exit: 251 return err; 252 } 253 254 static void __devexit k8temp_remove(struct pci_dev *pdev) 255 { 256 struct k8temp_data *data = dev_get_drvdata(&pdev->dev); 257 258 hwmon_device_unregister(data->hwmon_dev); 259 device_remove_file(&pdev->dev, 260 &sensor_dev_attr_temp1_input.dev_attr); 261 device_remove_file(&pdev->dev, 262 &sensor_dev_attr_temp2_input.dev_attr); 263 device_remove_file(&pdev->dev, 264 &sensor_dev_attr_temp3_input.dev_attr); 265 device_remove_file(&pdev->dev, 266 &sensor_dev_attr_temp4_input.dev_attr); 267 device_remove_file(&pdev->dev, &dev_attr_name); 268 dev_set_drvdata(&pdev->dev, NULL); 269 kfree(data); 270 } 271 272 static struct pci_driver k8temp_driver = { 273 .name = "k8temp", 274 .id_table = k8temp_ids, 275 .probe = k8temp_probe, 276 .remove = __devexit_p(k8temp_remove), 277 }; 278 279 static int __init k8temp_init(void) 280 { 281 return pci_register_driver(&k8temp_driver); 282 } 283 284 static void __exit k8temp_exit(void) 285 { 286 pci_unregister_driver(&k8temp_driver); 287 } 288 289 MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>"); 290 MODULE_DESCRIPTION("AMD K8 core temperature monitor"); 291 MODULE_LICENSE("GPL"); 292 293 module_init(k8temp_init) 294 module_exit(k8temp_exit) 295