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
ufs_read_temp_enable(struct ufs_hba * hba,u8 mask,long * val)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
ufs_get_temp(struct ufs_hba * hba,enum attr_idn idn,long * val)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
ufs_hwmon_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)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
ufs_hwmon_write(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long val)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
ufs_hwmon_is_visible(const void * _data,enum hwmon_sensor_types type,u32 attr,int channel)130 static umode_t ufs_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr,
131 int channel)
132 {
133 if (type != hwmon_temp)
134 return 0;
135
136 switch (attr) {
137 case hwmon_temp_enable:
138 return 0644;
139 case hwmon_temp_crit:
140 case hwmon_temp_lcrit:
141 case hwmon_temp_input:
142 return 0444;
143 default:
144 break;
145 }
146 return 0;
147 }
148
149 static const struct hwmon_channel_info *ufs_hwmon_info[] = {
150 HWMON_CHANNEL_INFO(temp, HWMON_T_ENABLE | HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LCRIT),
151 NULL
152 };
153
154 static const struct hwmon_ops ufs_hwmon_ops = {
155 .is_visible = ufs_hwmon_is_visible,
156 .read = ufs_hwmon_read,
157 .write = ufs_hwmon_write,
158 };
159
160 static const struct hwmon_chip_info ufs_hwmon_hba_info = {
161 .ops = &ufs_hwmon_ops,
162 .info = ufs_hwmon_info,
163 };
164
ufs_hwmon_probe(struct ufs_hba * hba,u8 mask)165 void ufs_hwmon_probe(struct ufs_hba *hba, u8 mask)
166 {
167 struct device *dev = hba->dev;
168 struct ufs_hwmon_data *data;
169 struct device *hwmon;
170
171 data = kzalloc(sizeof(*data), GFP_KERNEL);
172 if (!data)
173 return;
174
175 data->hba = hba;
176 data->mask = mask;
177
178 hwmon = hwmon_device_register_with_info(dev, "ufs", data, &ufs_hwmon_hba_info, NULL);
179 if (IS_ERR(hwmon)) {
180 dev_warn(dev, "Failed to instantiate hwmon device\n");
181 kfree(data);
182 return;
183 }
184
185 hba->hwmon_device = hwmon;
186 }
187
ufs_hwmon_remove(struct ufs_hba * hba)188 void ufs_hwmon_remove(struct ufs_hba *hba)
189 {
190 struct ufs_hwmon_data *data;
191
192 if (!hba->hwmon_device)
193 return;
194
195 data = dev_get_drvdata(hba->hwmon_device);
196 hwmon_device_unregister(hba->hwmon_device);
197 hba->hwmon_device = NULL;
198 kfree(data);
199 }
200
ufs_hwmon_notify_event(struct ufs_hba * hba,u8 ee_mask)201 void ufs_hwmon_notify_event(struct ufs_hba *hba, u8 ee_mask)
202 {
203 if (!hba->hwmon_device)
204 return;
205
206 if (ee_mask & MASK_EE_TOO_HIGH_TEMP)
207 hwmon_notify_event(hba->hwmon_device, hwmon_temp, hwmon_temp_max_alarm, 0);
208
209 if (ee_mask & MASK_EE_TOO_LOW_TEMP)
210 hwmon_notify_event(hba->hwmon_device, hwmon_temp, hwmon_temp_min_alarm, 0);
211 }
212