Merge tag 'mm-stable-2023-09-04-14-00' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-block.git] / drivers / ufs / core / ufs-hwmon.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * UFS hardware monitoring support
4  * Copyright (c) 2021, Western Digital Corporation
5  */
6
7 #include <linux/hwmon.h>
8 #include <linux/units.h>
9
10 #include <ufs/ufshcd.h>
11 #include "ufshcd-priv.h"
12
13 struct ufs_hwmon_data {
14         struct ufs_hba *hba;
15         u8 mask;
16 };
17
18 static int ufs_read_temp_enable(struct ufs_hba *hba, u8 mask, long *val)
19 {
20         u32 ee_mask;
21         int err;
22
23         err = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, QUERY_ATTR_IDN_EE_CONTROL, 0, 0,
24                                 &ee_mask);
25         if (err)
26                 return err;
27
28         *val = (mask & ee_mask & MASK_EE_TOO_HIGH_TEMP) || (mask & ee_mask & MASK_EE_TOO_LOW_TEMP);
29
30         return 0;
31 }
32
33 static int ufs_get_temp(struct ufs_hba *hba, enum attr_idn idn, long *val)
34 {
35         u32 value;
36         int err;
37
38         err = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, idn, 0, 0, &value);
39         if (err)
40                 return err;
41
42         if (value == 0)
43                 return -ENODATA;
44
45         *val = ((long)value - 80) * MILLIDEGREE_PER_DEGREE;
46
47         return 0;
48 }
49
50 static int ufs_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
51                           long *val)
52 {
53         struct ufs_hwmon_data *data = dev_get_drvdata(dev);
54         struct ufs_hba *hba = data->hba;
55         int err;
56
57         down(&hba->host_sem);
58
59         if (!ufshcd_is_user_access_allowed(hba)) {
60                 up(&hba->host_sem);
61                 return -EBUSY;
62         }
63
64         ufshcd_rpm_get_sync(hba);
65
66         switch (attr) {
67         case hwmon_temp_enable:
68                 err = ufs_read_temp_enable(hba, data->mask, val);
69
70                 break;
71         case hwmon_temp_crit:
72                 err = ufs_get_temp(hba, QUERY_ATTR_IDN_HIGH_TEMP_BOUND, val);
73
74                 break;
75         case hwmon_temp_lcrit:
76                 err = ufs_get_temp(hba, QUERY_ATTR_IDN_LOW_TEMP_BOUND, val);
77
78                 break;
79         case hwmon_temp_input:
80                 err = ufs_get_temp(hba, QUERY_ATTR_IDN_CASE_ROUGH_TEMP, val);
81
82                 break;
83         default:
84                 err = -EOPNOTSUPP;
85
86                 break;
87         }
88
89         ufshcd_rpm_put_sync(hba);
90
91         up(&hba->host_sem);
92
93         return err;
94 }
95
96 static int ufs_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
97                            long val)
98 {
99         struct ufs_hwmon_data *data = dev_get_drvdata(dev);
100         struct ufs_hba *hba = data->hba;
101         int err;
102
103         if (attr != hwmon_temp_enable)
104                 return -EINVAL;
105
106         if (val != 0 && val != 1)
107                 return -EINVAL;
108
109         down(&hba->host_sem);
110
111         if (!ufshcd_is_user_access_allowed(hba)) {
112                 up(&hba->host_sem);
113                 return -EBUSY;
114         }
115
116         ufshcd_rpm_get_sync(hba);
117
118         if (val == 1)
119                 err = ufshcd_update_ee_usr_mask(hba, MASK_EE_URGENT_TEMP, 0);
120         else
121                 err = ufshcd_update_ee_usr_mask(hba, 0, MASK_EE_URGENT_TEMP);
122
123         ufshcd_rpm_put_sync(hba);
124
125         up(&hba->host_sem);
126
127         return err;
128 }
129
130 static umode_t ufs_hwmon_is_visible(const void *data,
131                                     enum hwmon_sensor_types type, u32 attr,
132                                     int channel)
133 {
134         if (type != hwmon_temp)
135                 return 0;
136
137         switch (attr) {
138         case hwmon_temp_enable:
139                 return 0644;
140         case hwmon_temp_crit:
141         case hwmon_temp_lcrit:
142         case hwmon_temp_input:
143                 return 0444;
144         default:
145                 break;
146         }
147         return 0;
148 }
149
150 static const struct hwmon_channel_info *const ufs_hwmon_info[] = {
151         HWMON_CHANNEL_INFO(temp, HWMON_T_ENABLE | HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LCRIT),
152         NULL
153 };
154
155 static const struct hwmon_ops ufs_hwmon_ops = {
156         .is_visible     = ufs_hwmon_is_visible,
157         .read           = ufs_hwmon_read,
158         .write          = ufs_hwmon_write,
159 };
160
161 static const struct hwmon_chip_info ufs_hwmon_hba_info = {
162         .ops    = &ufs_hwmon_ops,
163         .info   = ufs_hwmon_info,
164 };
165
166 void ufs_hwmon_probe(struct ufs_hba *hba, u8 mask)
167 {
168         struct device *dev = hba->dev;
169         struct ufs_hwmon_data *data;
170         struct device *hwmon;
171
172         data = kzalloc(sizeof(*data), GFP_KERNEL);
173         if (!data)
174                 return;
175
176         data->hba = hba;
177         data->mask = mask;
178
179         hwmon = hwmon_device_register_with_info(dev, "ufs", data, &ufs_hwmon_hba_info, NULL);
180         if (IS_ERR(hwmon)) {
181                 dev_warn(dev, "Failed to instantiate hwmon device\n");
182                 kfree(data);
183                 return;
184         }
185
186         hba->hwmon_device = hwmon;
187 }
188
189 void ufs_hwmon_remove(struct ufs_hba *hba)
190 {
191         struct ufs_hwmon_data *data;
192
193         if (!hba->hwmon_device)
194                 return;
195
196         data = dev_get_drvdata(hba->hwmon_device);
197         hwmon_device_unregister(hba->hwmon_device);
198         hba->hwmon_device = NULL;
199         kfree(data);
200 }
201
202 void ufs_hwmon_notify_event(struct ufs_hba *hba, u8 ee_mask)
203 {
204         if (!hba->hwmon_device)
205                 return;
206
207         if (ee_mask & MASK_EE_TOO_HIGH_TEMP)
208                 hwmon_notify_event(hba->hwmon_device, hwmon_temp, hwmon_temp_max_alarm, 0);
209
210         if (ee_mask & MASK_EE_TOO_LOW_TEMP)
211                 hwmon_notify_event(hba->hwmon_device, hwmon_temp, hwmon_temp_min_alarm, 0);
212 }