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