Commit | Line | Data |
---|---|---|
e7bb1a2a KY |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * sbtsi_temp.c - hwmon driver for a SBI Temperature Sensor Interface (SB-TSI) | |
4 | * compliant AMD SoC temperature device. | |
5 | * | |
6 | * Copyright (c) 2020, Google Inc. | |
7 | * Copyright (c) 2020, Kun Yi <kunyi@google.com> | |
8 | */ | |
9 | ||
10 | #include <linux/err.h> | |
11 | #include <linux/i2c.h> | |
12 | #include <linux/init.h> | |
13 | #include <linux/hwmon.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/mutex.h> | |
16 | #include <linux/of_device.h> | |
17 | #include <linux/of.h> | |
18 | ||
19 | /* | |
20 | * SB-TSI registers only support SMBus byte data access. "_INT" registers are | |
21 | * the integer part of a temperature value or limit, and "_DEC" registers are | |
22 | * corresponding decimal parts. | |
23 | */ | |
24 | #define SBTSI_REG_TEMP_INT 0x01 /* RO */ | |
25 | #define SBTSI_REG_STATUS 0x02 /* RO */ | |
26 | #define SBTSI_REG_CONFIG 0x03 /* RO */ | |
27 | #define SBTSI_REG_TEMP_HIGH_INT 0x07 /* RW */ | |
28 | #define SBTSI_REG_TEMP_LOW_INT 0x08 /* RW */ | |
29 | #define SBTSI_REG_TEMP_DEC 0x10 /* RW */ | |
30 | #define SBTSI_REG_TEMP_HIGH_DEC 0x13 /* RW */ | |
31 | #define SBTSI_REG_TEMP_LOW_DEC 0x14 /* RW */ | |
32 | ||
33 | #define SBTSI_CONFIG_READ_ORDER_SHIFT 5 | |
34 | ||
35 | #define SBTSI_TEMP_MIN 0 | |
36 | #define SBTSI_TEMP_MAX 255875 | |
37 | ||
38 | /* Each client has this additional data */ | |
39 | struct sbtsi_data { | |
40 | struct i2c_client *client; | |
41 | struct mutex lock; | |
42 | }; | |
43 | ||
44 | /* | |
45 | * From SB-TSI spec: CPU temperature readings and limit registers encode the | |
46 | * temperature in increments of 0.125 from 0 to 255.875. The "high byte" | |
47 | * register encodes the base-2 of the integer portion, and the upper 3 bits of | |
48 | * the "low byte" encode in base-2 the decimal portion. | |
49 | * | |
50 | * e.g. INT=0x19, DEC=0x20 represents 25.125 degrees Celsius | |
51 | * | |
52 | * Therefore temperature in millidegree Celsius = | |
53 | * (INT + DEC / 256) * 1000 = (INT * 8 + DEC / 32) * 125 | |
54 | */ | |
55 | static inline int sbtsi_reg_to_mc(s32 integer, s32 decimal) | |
56 | { | |
57 | return ((integer << 3) + (decimal >> 5)) * 125; | |
58 | } | |
59 | ||
60 | /* | |
61 | * Inversely, given temperature in millidegree Celsius | |
62 | * INT = (TEMP / 125) / 8 | |
63 | * DEC = ((TEMP / 125) % 8) * 32 | |
64 | * Caller have to make sure temp doesn't exceed 255875, the max valid value. | |
65 | */ | |
66 | static inline void sbtsi_mc_to_reg(s32 temp, u8 *integer, u8 *decimal) | |
67 | { | |
68 | temp /= 125; | |
69 | *integer = temp >> 3; | |
70 | *decimal = (temp & 0x7) << 5; | |
71 | } | |
72 | ||
73 | static int sbtsi_read(struct device *dev, enum hwmon_sensor_types type, | |
74 | u32 attr, int channel, long *val) | |
75 | { | |
76 | struct sbtsi_data *data = dev_get_drvdata(dev); | |
77 | s32 temp_int, temp_dec; | |
78 | int err; | |
79 | ||
80 | switch (attr) { | |
81 | case hwmon_temp_input: | |
82 | /* | |
83 | * ReadOrder bit specifies the reading order of integer and | |
84 | * decimal part of CPU temp for atomic reads. If bit == 0, | |
85 | * reading integer part triggers latching of the decimal part, | |
86 | * so integer part should be read first. If bit == 1, read | |
87 | * order should be reversed. | |
88 | */ | |
89 | err = i2c_smbus_read_byte_data(data->client, SBTSI_REG_CONFIG); | |
90 | if (err < 0) | |
91 | return err; | |
92 | ||
93 | mutex_lock(&data->lock); | |
94 | if (err & BIT(SBTSI_CONFIG_READ_ORDER_SHIFT)) { | |
95 | temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC); | |
96 | temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT); | |
97 | } else { | |
98 | temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT); | |
99 | temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC); | |
100 | } | |
101 | mutex_unlock(&data->lock); | |
102 | break; | |
103 | case hwmon_temp_max: | |
104 | mutex_lock(&data->lock); | |
105 | temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_INT); | |
106 | temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_DEC); | |
107 | mutex_unlock(&data->lock); | |
108 | break; | |
109 | case hwmon_temp_min: | |
110 | mutex_lock(&data->lock); | |
111 | temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_INT); | |
112 | temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_DEC); | |
113 | mutex_unlock(&data->lock); | |
114 | break; | |
115 | default: | |
116 | return -EINVAL; | |
117 | } | |
118 | ||
119 | ||
120 | if (temp_int < 0) | |
121 | return temp_int; | |
122 | if (temp_dec < 0) | |
123 | return temp_dec; | |
124 | ||
125 | *val = sbtsi_reg_to_mc(temp_int, temp_dec); | |
126 | ||
127 | return 0; | |
128 | } | |
129 | ||
130 | static int sbtsi_write(struct device *dev, enum hwmon_sensor_types type, | |
131 | u32 attr, int channel, long val) | |
132 | { | |
133 | struct sbtsi_data *data = dev_get_drvdata(dev); | |
134 | int reg_int, reg_dec, err; | |
135 | u8 temp_int, temp_dec; | |
136 | ||
137 | switch (attr) { | |
138 | case hwmon_temp_max: | |
139 | reg_int = SBTSI_REG_TEMP_HIGH_INT; | |
140 | reg_dec = SBTSI_REG_TEMP_HIGH_DEC; | |
141 | break; | |
142 | case hwmon_temp_min: | |
143 | reg_int = SBTSI_REG_TEMP_LOW_INT; | |
144 | reg_dec = SBTSI_REG_TEMP_LOW_DEC; | |
145 | break; | |
146 | default: | |
147 | return -EINVAL; | |
148 | } | |
149 | ||
150 | val = clamp_val(val, SBTSI_TEMP_MIN, SBTSI_TEMP_MAX); | |
151 | sbtsi_mc_to_reg(val, &temp_int, &temp_dec); | |
152 | ||
153 | mutex_lock(&data->lock); | |
154 | err = i2c_smbus_write_byte_data(data->client, reg_int, temp_int); | |
155 | if (err) | |
156 | goto exit; | |
157 | ||
158 | err = i2c_smbus_write_byte_data(data->client, reg_dec, temp_dec); | |
159 | exit: | |
160 | mutex_unlock(&data->lock); | |
161 | return err; | |
162 | } | |
163 | ||
164 | static umode_t sbtsi_is_visible(const void *data, | |
165 | enum hwmon_sensor_types type, | |
166 | u32 attr, int channel) | |
167 | { | |
168 | switch (type) { | |
169 | case hwmon_temp: | |
170 | switch (attr) { | |
171 | case hwmon_temp_input: | |
172 | return 0444; | |
173 | case hwmon_temp_min: | |
174 | return 0644; | |
175 | case hwmon_temp_max: | |
176 | return 0644; | |
177 | } | |
178 | break; | |
179 | default: | |
180 | break; | |
181 | } | |
182 | return 0; | |
183 | } | |
184 | ||
185 | static const struct hwmon_channel_info *sbtsi_info[] = { | |
186 | HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), | |
187 | HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX), | |
188 | NULL | |
189 | }; | |
190 | ||
191 | static const struct hwmon_ops sbtsi_hwmon_ops = { | |
192 | .is_visible = sbtsi_is_visible, | |
193 | .read = sbtsi_read, | |
194 | .write = sbtsi_write, | |
195 | }; | |
196 | ||
197 | static const struct hwmon_chip_info sbtsi_chip_info = { | |
198 | .ops = &sbtsi_hwmon_ops, | |
199 | .info = sbtsi_info, | |
200 | }; | |
201 | ||
202 | static int sbtsi_probe(struct i2c_client *client, | |
203 | const struct i2c_device_id *id) | |
204 | { | |
205 | struct device *dev = &client->dev; | |
206 | struct device *hwmon_dev; | |
207 | struct sbtsi_data *data; | |
208 | ||
209 | data = devm_kzalloc(dev, sizeof(struct sbtsi_data), GFP_KERNEL); | |
210 | if (!data) | |
211 | return -ENOMEM; | |
212 | ||
213 | data->client = client; | |
214 | mutex_init(&data->lock); | |
215 | ||
216 | hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, &sbtsi_chip_info, | |
217 | NULL); | |
218 | ||
219 | return PTR_ERR_OR_ZERO(hwmon_dev); | |
220 | } | |
221 | ||
222 | static const struct i2c_device_id sbtsi_id[] = { | |
223 | {"sbtsi", 0}, | |
224 | {} | |
225 | }; | |
226 | MODULE_DEVICE_TABLE(i2c, sbtsi_id); | |
227 | ||
228 | static const struct of_device_id __maybe_unused sbtsi_of_match[] = { | |
229 | { | |
230 | .compatible = "amd,sbtsi", | |
231 | }, | |
232 | { }, | |
233 | }; | |
234 | MODULE_DEVICE_TABLE(of, sbtsi_of_match); | |
235 | ||
236 | static struct i2c_driver sbtsi_driver = { | |
237 | .class = I2C_CLASS_HWMON, | |
238 | .driver = { | |
239 | .name = "sbtsi", | |
240 | .of_match_table = of_match_ptr(sbtsi_of_match), | |
241 | }, | |
242 | .probe = sbtsi_probe, | |
243 | .id_table = sbtsi_id, | |
244 | }; | |
245 | ||
246 | module_i2c_driver(sbtsi_driver); | |
247 | ||
248 | MODULE_AUTHOR("Kun Yi <kunyi@google.com>"); | |
249 | MODULE_DESCRIPTION("Hwmon driver for AMD SB-TSI emulated sensor"); | |
250 | MODULE_LICENSE("GPL"); |