1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * k8temp.c - Linux kernel module for hardware monitoring 4 * 5 * Copyright (C) 2006 Rudolf Marek <r.marek@assembler.cz> 6 * 7 * Inspired from the w83785 and amd756 drivers. 8 */ 9 10 #include <linux/module.h> 11 #include <linux/init.h> 12 #include <linux/slab.h> 13 #include <linux/pci.h> 14 #include <linux/hwmon.h> 15 #include <linux/err.h> 16 #include <linux/mutex.h> 17 #include <asm/processor.h> 18 #include <asm/cpuid/api.h> 19 20 #define TEMP_FROM_REG(val) (((((val) >> 16) & 0xff) - 49) * 1000) 21 #define REG_TEMP 0xe4 22 #define SEL_PLACE 0x40 23 #define SEL_CORE 0x04 24 25 struct k8temp_data { 26 struct mutex update_lock; 27 28 /* registers values */ 29 u8 sensorsp; /* sensor presence bits - SEL_CORE, SEL_PLACE */ 30 u8 swap_core_select; /* meaning of SEL_CORE is inverted */ 31 u32 temp_offset; 32 }; 33 34 static const struct pci_device_id k8temp_ids[] = { 35 { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MISC) }, 36 { 0 }, 37 }; 38 MODULE_DEVICE_TABLE(pci, k8temp_ids); 39 40 static int is_rev_g_desktop(u8 model) 41 { 42 u32 brandidx; 43 44 if (model < 0x69) 45 return 0; 46 47 if (model == 0xc1 || model == 0x6c || model == 0x7c) 48 return 0; 49 50 /* 51 * Differentiate between AM2 and ASB1. 52 * See "Constructing the processor Name String" in "Revision 53 * Guide for AMD NPT Family 0Fh Processors" (33610). 54 */ 55 brandidx = cpuid_ebx(0x80000001); 56 brandidx = (brandidx >> 9) & 0x1f; 57 58 /* Single core */ 59 if ((model == 0x6f || model == 0x7f) && 60 (brandidx == 0x7 || brandidx == 0x9 || brandidx == 0xc)) 61 return 0; 62 63 /* Dual core */ 64 if (model == 0x6b && 65 (brandidx == 0xb || brandidx == 0xc)) 66 return 0; 67 68 return 1; 69 } 70 71 static umode_t 72 k8temp_is_visible(const void *drvdata, enum hwmon_sensor_types type, 73 u32 attr, int channel) 74 { 75 const struct k8temp_data *data = drvdata; 76 77 if ((channel & 1) && !(data->sensorsp & SEL_PLACE)) 78 return 0; 79 80 if ((channel & 2) && !(data->sensorsp & SEL_CORE)) 81 return 0; 82 83 return 0444; 84 } 85 86 static int 87 k8temp_read(struct device *dev, enum hwmon_sensor_types type, 88 u32 attr, int channel, long *val) 89 { 90 struct k8temp_data *data = dev_get_drvdata(dev); 91 struct pci_dev *pdev = to_pci_dev(dev->parent); 92 int core, place; 93 u32 temp; 94 u8 tmp; 95 96 core = (channel >> 1) & 1; 97 place = channel & 1; 98 99 core ^= data->swap_core_select; 100 101 mutex_lock(&data->update_lock); 102 pci_read_config_byte(pdev, REG_TEMP, &tmp); 103 tmp &= ~(SEL_PLACE | SEL_CORE); 104 if (core) 105 tmp |= SEL_CORE; 106 if (place) 107 tmp |= SEL_PLACE; 108 pci_write_config_byte(pdev, REG_TEMP, tmp); 109 pci_read_config_dword(pdev, REG_TEMP, &temp); 110 mutex_unlock(&data->update_lock); 111 112 *val = TEMP_FROM_REG(temp) + data->temp_offset; 113 114 return 0; 115 } 116 117 static const struct hwmon_ops k8temp_ops = { 118 .is_visible = k8temp_is_visible, 119 .read = k8temp_read, 120 }; 121 122 static const struct hwmon_channel_info * const k8temp_info[] = { 123 HWMON_CHANNEL_INFO(temp, 124 HWMON_T_INPUT, HWMON_T_INPUT, HWMON_T_INPUT, HWMON_T_INPUT), 125 NULL 126 }; 127 128 static const struct hwmon_chip_info k8temp_chip_info = { 129 .ops = &k8temp_ops, 130 .info = k8temp_info, 131 }; 132 133 static int k8temp_probe(struct pci_dev *pdev, 134 const struct pci_device_id *id) 135 { 136 u8 scfg; 137 u32 temp; 138 u8 model, stepping; 139 struct k8temp_data *data; 140 struct device *hwmon_dev; 141 142 data = devm_kzalloc(&pdev->dev, sizeof(struct k8temp_data), GFP_KERNEL); 143 if (!data) 144 return -ENOMEM; 145 146 model = boot_cpu_data.x86_model; 147 stepping = boot_cpu_data.x86_stepping; 148 149 /* feature available since SH-C0, exclude older revisions */ 150 if ((model == 4 && stepping == 0) || 151 (model == 5 && stepping <= 1)) 152 return -ENODEV; 153 154 /* 155 * AMD NPT family 0fh, i.e. RevF and RevG: 156 * meaning of SEL_CORE bit is inverted 157 */ 158 if (model >= 0x40) { 159 data->swap_core_select = 1; 160 dev_warn(&pdev->dev, 161 "Temperature readouts might be wrong - check erratum #141\n"); 162 } 163 164 /* 165 * RevG desktop CPUs (i.e. no socket S1G1 or ASB1 parts) need 166 * additional offset, otherwise reported temperature is below 167 * ambient temperature 168 */ 169 if (is_rev_g_desktop(model)) 170 data->temp_offset = 21000; 171 172 pci_read_config_byte(pdev, REG_TEMP, &scfg); 173 scfg &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */ 174 pci_write_config_byte(pdev, REG_TEMP, scfg); 175 pci_read_config_byte(pdev, REG_TEMP, &scfg); 176 177 if (scfg & (SEL_PLACE | SEL_CORE)) { 178 dev_err(&pdev->dev, "Configuration bit(s) stuck at 1!\n"); 179 return -ENODEV; 180 } 181 182 scfg |= (SEL_PLACE | SEL_CORE); 183 pci_write_config_byte(pdev, REG_TEMP, scfg); 184 185 /* now we know if we can change core and/or sensor */ 186 pci_read_config_byte(pdev, REG_TEMP, &data->sensorsp); 187 188 if (data->sensorsp & SEL_PLACE) { 189 scfg &= ~SEL_CORE; /* Select sensor 1, core0 */ 190 pci_write_config_byte(pdev, REG_TEMP, scfg); 191 pci_read_config_dword(pdev, REG_TEMP, &temp); 192 scfg |= SEL_CORE; /* prepare for next selection */ 193 if (!((temp >> 16) & 0xff)) /* if temp is 0 -49C is unlikely */ 194 data->sensorsp &= ~SEL_PLACE; 195 } 196 197 if (data->sensorsp & SEL_CORE) { 198 scfg &= ~SEL_PLACE; /* Select sensor 0, core1 */ 199 pci_write_config_byte(pdev, REG_TEMP, scfg); 200 pci_read_config_dword(pdev, REG_TEMP, &temp); 201 if (!((temp >> 16) & 0xff)) /* if temp is 0 -49C is unlikely */ 202 data->sensorsp &= ~SEL_CORE; 203 } 204 205 mutex_init(&data->update_lock); 206 207 hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, 208 "k8temp", 209 data, 210 &k8temp_chip_info, 211 NULL); 212 213 return PTR_ERR_OR_ZERO(hwmon_dev); 214 } 215 216 static struct pci_driver k8temp_driver = { 217 .name = "k8temp", 218 .id_table = k8temp_ids, 219 .probe = k8temp_probe, 220 }; 221 222 module_pci_driver(k8temp_driver); 223 224 MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>"); 225 MODULE_DESCRIPTION("AMD K8 core temperature monitor"); 226 MODULE_LICENSE("GPL"); 227