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