1 // SPDX-License-Identifier: GPL-2.0
3 * UFS hardware monitoring support
4 * Copyright (c) 2021, Western Digital Corporation
7 #include <linux/hwmon.h>
8 #include <linux/units.h>
10 #include <ufs/ufshcd.h>
11 #include "ufshcd-priv.h"
13 struct ufs_hwmon_data {
18 static int ufs_read_temp_enable(struct ufs_hba *hba, u8 mask, long *val)
23 err = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, QUERY_ATTR_IDN_EE_CONTROL, 0, 0,
28 *val = (mask & ee_mask & MASK_EE_TOO_HIGH_TEMP) || (mask & ee_mask & MASK_EE_TOO_LOW_TEMP);
33 static int ufs_get_temp(struct ufs_hba *hba, enum attr_idn idn, long *val)
38 err = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, idn, 0, 0, &value);
45 *val = ((long)value - 80) * MILLIDEGREE_PER_DEGREE;
50 static int ufs_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
53 struct ufs_hwmon_data *data = dev_get_drvdata(dev);
54 struct ufs_hba *hba = data->hba;
59 if (!ufshcd_is_user_access_allowed(hba)) {
64 ufshcd_rpm_get_sync(hba);
67 case hwmon_temp_enable:
68 err = ufs_read_temp_enable(hba, data->mask, val);
72 err = ufs_get_temp(hba, QUERY_ATTR_IDN_HIGH_TEMP_BOUND, val);
75 case hwmon_temp_lcrit:
76 err = ufs_get_temp(hba, QUERY_ATTR_IDN_LOW_TEMP_BOUND, val);
79 case hwmon_temp_input:
80 err = ufs_get_temp(hba, QUERY_ATTR_IDN_CASE_ROUGH_TEMP, val);
89 ufshcd_rpm_put_sync(hba);
96 static int ufs_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
99 struct ufs_hwmon_data *data = dev_get_drvdata(dev);
100 struct ufs_hba *hba = data->hba;
103 if (attr != hwmon_temp_enable)
106 if (val != 0 && val != 1)
109 down(&hba->host_sem);
111 if (!ufshcd_is_user_access_allowed(hba)) {
116 ufshcd_rpm_get_sync(hba);
119 err = ufshcd_update_ee_usr_mask(hba, MASK_EE_URGENT_TEMP, 0);
121 err = ufshcd_update_ee_usr_mask(hba, 0, MASK_EE_URGENT_TEMP);
123 ufshcd_rpm_put_sync(hba);
130 static umode_t ufs_hwmon_is_visible(const void *data,
131 enum hwmon_sensor_types type, u32 attr,
134 if (type != hwmon_temp)
138 case hwmon_temp_enable:
140 case hwmon_temp_crit:
141 case hwmon_temp_lcrit:
142 case hwmon_temp_input:
150 static const struct hwmon_channel_info *const ufs_hwmon_info[] = {
151 HWMON_CHANNEL_INFO(temp, HWMON_T_ENABLE | HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LCRIT),
155 static const struct hwmon_ops ufs_hwmon_ops = {
156 .is_visible = ufs_hwmon_is_visible,
157 .read = ufs_hwmon_read,
158 .write = ufs_hwmon_write,
161 static const struct hwmon_chip_info ufs_hwmon_hba_info = {
162 .ops = &ufs_hwmon_ops,
163 .info = ufs_hwmon_info,
166 void ufs_hwmon_probe(struct ufs_hba *hba, u8 mask)
168 struct device *dev = hba->dev;
169 struct ufs_hwmon_data *data;
170 struct device *hwmon;
172 data = kzalloc(sizeof(*data), GFP_KERNEL);
179 hwmon = hwmon_device_register_with_info(dev, "ufs", data, &ufs_hwmon_hba_info, NULL);
181 dev_warn(dev, "Failed to instantiate hwmon device\n");
186 hba->hwmon_device = hwmon;
189 void ufs_hwmon_remove(struct ufs_hba *hba)
191 struct ufs_hwmon_data *data;
193 if (!hba->hwmon_device)
196 data = dev_get_drvdata(hba->hwmon_device);
197 hwmon_device_unregister(hba->hwmon_device);
198 hba->hwmon_device = NULL;
202 void ufs_hwmon_notify_event(struct ufs_hba *hba, u8 ee_mask)
204 if (!hba->hwmon_device)
207 if (ee_mask & MASK_EE_TOO_HIGH_TEMP)
208 hwmon_notify_event(hba->hwmon_device, hwmon_temp, hwmon_temp_max_alarm, 0);
210 if (ee_mask & MASK_EE_TOO_LOW_TEMP)
211 hwmon_notify_event(hba->hwmon_device, hwmon_temp, hwmon_temp_min_alarm, 0);