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