summaryrefslogtreecommitdiff
path: root/drivers/hwmon/i5500_temp.c
blob: 7b00b38c7f7bf227f928c0b90bfc65a6a78930e9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * i5500_temp - Driver for Intel 5500/5520/X58 chipset thermal sensor
 *
 * Copyright (C) 2012, 2014 Jean Delvare <jdelvare@suse.de>
 */

#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>

/* Register definitions from datasheet */
#define REG_TSTHRCATA	0xE2
#define REG_TSCTRL	0xE8
#define REG_TSTHRRPEX	0xEB
#define REG_TSTHRLO	0xEC
#define REG_TSTHRHI	0xEE
#define REG_CTHINT	0xF0
#define REG_TSFSC	0xF3
#define REG_CTSTS	0xF4
#define REG_TSTHRRQPI	0xF5
#define REG_CTCTRL	0xF7
#define REG_TSTIMER	0xF8

static umode_t i5500_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr,
				int channel)
{
	return 0444;
}

static int i5500_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
		      long *val)
{
	struct pci_dev *pdev = to_pci_dev(dev->parent);
	u16 tsthr;
	s8 tsfsc;
	u8 ctsts;

	switch (type) {
	case hwmon_temp:
		switch (attr) {
		/* Sensor resolution : 0.5 degree C */
		case hwmon_temp_input:
			pci_read_config_word(pdev, REG_TSTHRHI, &tsthr);
			pci_read_config_byte(pdev, REG_TSFSC, &tsfsc);
			*val = (tsthr - tsfsc) * 500;
			return 0;
		case hwmon_temp_max:
			pci_read_config_word(pdev, REG_TSTHRHI, &tsthr);
			*val = tsthr * 500;
			return 0;
		case hwmon_temp_max_hyst:
			pci_read_config_word(pdev, REG_TSTHRLO, &tsthr);
			*val = tsthr * 500;
			return 0;
		case hwmon_temp_crit:
			pci_read_config_word(pdev, REG_TSTHRCATA, &tsthr);
			*val = tsthr * 500;
			return 0;
		case hwmon_temp_max_alarm:
			pci_read_config_byte(pdev, REG_CTSTS, &ctsts);
			*val = !!(ctsts & BIT(1));
			return 0;
		case hwmon_temp_crit_alarm:
			pci_read_config_byte(pdev, REG_CTSTS, &ctsts);
			*val = !!(ctsts & BIT(0));
			return 0;
		default:
			break;
		}
		break;
	default:
		break;
	}

	return -EOPNOTSUPP;
}

static const struct hwmon_ops i5500_ops = {
	.is_visible = i5500_is_visible,
	.read = i5500_read,
};

static const struct hwmon_channel_info * const i5500_info[] = {
	HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
	HWMON_CHANNEL_INFO(temp,
			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_CRIT |
			   HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM
			   ),
	NULL
};

static const struct hwmon_chip_info i5500_chip_info = {
	.ops = &i5500_ops,
	.info = i5500_info,
};

static const struct pci_device_id i5500_temp_ids[] = {
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x3438) },
	{ 0 },
};

MODULE_DEVICE_TABLE(pci, i5500_temp_ids);

static int i5500_temp_probe(struct pci_dev *pdev,
			    const struct pci_device_id *id)
{
	int err;
	struct device *hwmon_dev;
	u32 tstimer;
	s8 tsfsc;

	err = pcim_enable_device(pdev);
	if (err) {
		dev_err(&pdev->dev, "Failed to enable device\n");
		return err;
	}

	pci_read_config_byte(pdev, REG_TSFSC, &tsfsc);
	pci_read_config_dword(pdev, REG_TSTIMER, &tstimer);
	if (tsfsc == 0x7F && tstimer == 0x07D30D40) {
		dev_notice(&pdev->dev, "Sensor seems to be disabled\n");
		return -ENODEV;
	}

	hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, "intel5500", NULL,
							 &i5500_chip_info, NULL);
	return PTR_ERR_OR_ZERO(hwmon_dev);
}

static struct pci_driver i5500_temp_driver = {
	.name = "i5500_temp",
	.id_table = i5500_temp_ids,
	.probe = i5500_temp_probe,
};

module_pci_driver(i5500_temp_driver);

MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
MODULE_DESCRIPTION("Intel 5500/5520/X58 chipset thermal sensor driver");
MODULE_LICENSE("GPL");