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