Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * k8temp.c - Linux kernel module for hardware monitoring
0004  *
0005  * Copyright (C) 2006 Rudolf Marek <r.marek@assembler.cz>
0006  *
0007  * Inspired from the w83785 and amd756 drivers.
0008  */
0009 
0010 #include <linux/module.h>
0011 #include <linux/init.h>
0012 #include <linux/slab.h>
0013 #include <linux/pci.h>
0014 #include <linux/hwmon.h>
0015 #include <linux/err.h>
0016 #include <linux/mutex.h>
0017 #include <asm/processor.h>
0018 
0019 #define TEMP_FROM_REG(val)  (((((val) >> 16) & 0xff) - 49) * 1000)
0020 #define REG_TEMP    0xe4
0021 #define SEL_PLACE   0x40
0022 #define SEL_CORE    0x04
0023 
0024 struct k8temp_data {
0025     struct mutex update_lock;
0026 
0027     /* registers values */
0028     u8 sensorsp;        /* sensor presence bits - SEL_CORE, SEL_PLACE */
0029     u8 swap_core_select;    /* meaning of SEL_CORE is inverted */
0030     u32 temp_offset;
0031 };
0032 
0033 static const struct pci_device_id k8temp_ids[] = {
0034     { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MISC) },
0035     { 0 },
0036 };
0037 MODULE_DEVICE_TABLE(pci, k8temp_ids);
0038 
0039 static int is_rev_g_desktop(u8 model)
0040 {
0041     u32 brandidx;
0042 
0043     if (model < 0x69)
0044         return 0;
0045 
0046     if (model == 0xc1 || model == 0x6c || model == 0x7c)
0047         return 0;
0048 
0049     /*
0050      * Differentiate between AM2 and ASB1.
0051      * See "Constructing the processor Name String" in "Revision
0052      * Guide for AMD NPT Family 0Fh Processors" (33610).
0053      */
0054     brandidx = cpuid_ebx(0x80000001);
0055     brandidx = (brandidx >> 9) & 0x1f;
0056 
0057     /* Single core */
0058     if ((model == 0x6f || model == 0x7f) &&
0059         (brandidx == 0x7 || brandidx == 0x9 || brandidx == 0xc))
0060         return 0;
0061 
0062     /* Dual core */
0063     if (model == 0x6b &&
0064         (brandidx == 0xb || brandidx == 0xc))
0065         return 0;
0066 
0067     return 1;
0068 }
0069 
0070 static umode_t
0071 k8temp_is_visible(const void *drvdata, enum hwmon_sensor_types type,
0072           u32 attr, int channel)
0073 {
0074     const struct k8temp_data *data = drvdata;
0075 
0076     if ((channel & 1) && !(data->sensorsp & SEL_PLACE))
0077         return 0;
0078 
0079     if ((channel & 2) && !(data->sensorsp & SEL_CORE))
0080         return 0;
0081 
0082     return 0444;
0083 }
0084 
0085 static int
0086 k8temp_read(struct device *dev, enum hwmon_sensor_types type,
0087         u32 attr, int channel, long *val)
0088 {
0089     struct k8temp_data *data = dev_get_drvdata(dev);
0090     struct pci_dev *pdev = to_pci_dev(dev->parent);
0091     int core, place;
0092     u32 temp;
0093     u8 tmp;
0094 
0095     core = (channel >> 1) & 1;
0096     place = channel & 1;
0097 
0098     core ^= data->swap_core_select;
0099 
0100     mutex_lock(&data->update_lock);
0101     pci_read_config_byte(pdev, REG_TEMP, &tmp);
0102     tmp &= ~(SEL_PLACE | SEL_CORE);
0103     if (core)
0104         tmp |= SEL_CORE;
0105     if (place)
0106         tmp |= SEL_PLACE;
0107     pci_write_config_byte(pdev, REG_TEMP, tmp);
0108     pci_read_config_dword(pdev, REG_TEMP, &temp);
0109     mutex_unlock(&data->update_lock);
0110 
0111     *val = TEMP_FROM_REG(temp) + data->temp_offset;
0112 
0113     return 0;
0114 }
0115 
0116 static const struct hwmon_ops k8temp_ops = {
0117     .is_visible = k8temp_is_visible,
0118     .read = k8temp_read,
0119 };
0120 
0121 static const struct hwmon_channel_info *k8temp_info[] = {
0122     HWMON_CHANNEL_INFO(temp,
0123         HWMON_T_INPUT, HWMON_T_INPUT, HWMON_T_INPUT, HWMON_T_INPUT),
0124     NULL
0125 };
0126 
0127 static const struct hwmon_chip_info k8temp_chip_info = {
0128     .ops = &k8temp_ops,
0129     .info = k8temp_info,
0130 };
0131 
0132 static int k8temp_probe(struct pci_dev *pdev,
0133                   const struct pci_device_id *id)
0134 {
0135     u8 scfg;
0136     u32 temp;
0137     u8 model, stepping;
0138     struct k8temp_data *data;
0139     struct device *hwmon_dev;
0140 
0141     data = devm_kzalloc(&pdev->dev, sizeof(struct k8temp_data), GFP_KERNEL);
0142     if (!data)
0143         return -ENOMEM;
0144 
0145     model = boot_cpu_data.x86_model;
0146     stepping = boot_cpu_data.x86_stepping;
0147 
0148     /* feature available since SH-C0, exclude older revisions */
0149     if ((model == 4 && stepping == 0) ||
0150         (model == 5 && stepping <= 1))
0151         return -ENODEV;
0152 
0153     /*
0154      * AMD NPT family 0fh, i.e. RevF and RevG:
0155      * meaning of SEL_CORE bit is inverted
0156      */
0157     if (model >= 0x40) {
0158         data->swap_core_select = 1;
0159         dev_warn(&pdev->dev,
0160              "Temperature readouts might be wrong - check erratum #141\n");
0161     }
0162 
0163     /*
0164      * RevG desktop CPUs (i.e. no socket S1G1 or ASB1 parts) need
0165      * additional offset, otherwise reported temperature is below
0166      * ambient temperature
0167      */
0168     if (is_rev_g_desktop(model))
0169         data->temp_offset = 21000;
0170 
0171     pci_read_config_byte(pdev, REG_TEMP, &scfg);
0172     scfg &= ~(SEL_PLACE | SEL_CORE);    /* Select sensor 0, core0 */
0173     pci_write_config_byte(pdev, REG_TEMP, scfg);
0174     pci_read_config_byte(pdev, REG_TEMP, &scfg);
0175 
0176     if (scfg & (SEL_PLACE | SEL_CORE)) {
0177         dev_err(&pdev->dev, "Configuration bit(s) stuck at 1!\n");
0178         return -ENODEV;
0179     }
0180 
0181     scfg |= (SEL_PLACE | SEL_CORE);
0182     pci_write_config_byte(pdev, REG_TEMP, scfg);
0183 
0184     /* now we know if we can change core and/or sensor */
0185     pci_read_config_byte(pdev, REG_TEMP, &data->sensorsp);
0186 
0187     if (data->sensorsp & SEL_PLACE) {
0188         scfg &= ~SEL_CORE;  /* Select sensor 1, core0 */
0189         pci_write_config_byte(pdev, REG_TEMP, scfg);
0190         pci_read_config_dword(pdev, REG_TEMP, &temp);
0191         scfg |= SEL_CORE;   /* prepare for next selection */
0192         if (!((temp >> 16) & 0xff)) /* if temp is 0 -49C is unlikely */
0193             data->sensorsp &= ~SEL_PLACE;
0194     }
0195 
0196     if (data->sensorsp & SEL_CORE) {
0197         scfg &= ~SEL_PLACE; /* Select sensor 0, core1 */
0198         pci_write_config_byte(pdev, REG_TEMP, scfg);
0199         pci_read_config_dword(pdev, REG_TEMP, &temp);
0200         if (!((temp >> 16) & 0xff)) /* if temp is 0 -49C is unlikely */
0201             data->sensorsp &= ~SEL_CORE;
0202     }
0203 
0204     mutex_init(&data->update_lock);
0205 
0206     hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
0207                              "k8temp",
0208                              data,
0209                              &k8temp_chip_info,
0210                              NULL);
0211 
0212     return PTR_ERR_OR_ZERO(hwmon_dev);
0213 }
0214 
0215 static struct pci_driver k8temp_driver = {
0216     .name = "k8temp",
0217     .id_table = k8temp_ids,
0218     .probe = k8temp_probe,
0219 };
0220 
0221 module_pci_driver(k8temp_driver);
0222 
0223 MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>");
0224 MODULE_DESCRIPTION("AMD K8 core temperature monitor");
0225 MODULE_LICENSE("GPL");