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);