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