xref: /openbmc/linux/drivers/ufs/core/ufs-hwmon.c (revision 7effbd18)
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, 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 
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 
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 
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