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