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