ufs-hwmon.c (4269B)
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 13struct ufs_hwmon_data { 14 struct ufs_hba *hba; 15 u8 mask; 16}; 17 18static 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 33static 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 50static 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 96static 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 130static umode_t ufs_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr, 131 int channel) 132{ 133 if (type != hwmon_temp) 134 return 0; 135 136 switch (attr) { 137 case hwmon_temp_enable: 138 return 0644; 139 case hwmon_temp_crit: 140 case hwmon_temp_lcrit: 141 case hwmon_temp_input: 142 return 0444; 143 default: 144 break; 145 } 146 return 0; 147} 148 149static const struct hwmon_channel_info *ufs_hwmon_info[] = { 150 HWMON_CHANNEL_INFO(temp, HWMON_T_ENABLE | HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LCRIT), 151 NULL 152}; 153 154static const struct hwmon_ops ufs_hwmon_ops = { 155 .is_visible = ufs_hwmon_is_visible, 156 .read = ufs_hwmon_read, 157 .write = ufs_hwmon_write, 158}; 159 160static const struct hwmon_chip_info ufs_hwmon_hba_info = { 161 .ops = &ufs_hwmon_ops, 162 .info = ufs_hwmon_info, 163}; 164 165void ufs_hwmon_probe(struct ufs_hba *hba, u8 mask) 166{ 167 struct device *dev = hba->dev; 168 struct ufs_hwmon_data *data; 169 struct device *hwmon; 170 171 data = kzalloc(sizeof(*data), GFP_KERNEL); 172 if (!data) 173 return; 174 175 data->hba = hba; 176 data->mask = mask; 177 178 hwmon = hwmon_device_register_with_info(dev, "ufs", data, &ufs_hwmon_hba_info, NULL); 179 if (IS_ERR(hwmon)) { 180 dev_warn(dev, "Failed to instantiate hwmon device\n"); 181 kfree(data); 182 return; 183 } 184 185 hba->hwmon_device = hwmon; 186} 187 188void ufs_hwmon_remove(struct ufs_hba *hba) 189{ 190 struct ufs_hwmon_data *data; 191 192 if (!hba->hwmon_device) 193 return; 194 195 data = dev_get_drvdata(hba->hwmon_device); 196 hwmon_device_unregister(hba->hwmon_device); 197 hba->hwmon_device = NULL; 198 kfree(data); 199} 200 201void ufs_hwmon_notify_event(struct ufs_hba *hba, u8 ee_mask) 202{ 203 if (!hba->hwmon_device) 204 return; 205 206 if (ee_mask & MASK_EE_TOO_HIGH_TEMP) 207 hwmon_notify_event(hba->hwmon_device, hwmon_temp, hwmon_temp_max_alarm, 0); 208 209 if (ee_mask & MASK_EE_TOO_LOW_TEMP) 210 hwmon_notify_event(hba->hwmon_device, hwmon_temp, hwmon_temp_min_alarm, 0); 211}