Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * UFS hardware monitoring support
0004  * Copyright (c) 2021, Western Digital Corporation
0005  */
0006 
0007 #include <linux/hwmon.h>
0008 #include <linux/units.h>
0009 
0010 #include <ufs/ufshcd.h>
0011 #include "ufshcd-priv.h"
0012 
0013 struct ufs_hwmon_data {
0014     struct ufs_hba *hba;
0015     u8 mask;
0016 };
0017 
0018 static int ufs_read_temp_enable(struct ufs_hba *hba, u8 mask, long *val)
0019 {
0020     u32 ee_mask;
0021     int err;
0022 
0023     err = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, QUERY_ATTR_IDN_EE_CONTROL, 0, 0,
0024                 &ee_mask);
0025     if (err)
0026         return err;
0027 
0028     *val = (mask & ee_mask & MASK_EE_TOO_HIGH_TEMP) || (mask & ee_mask & MASK_EE_TOO_LOW_TEMP);
0029 
0030     return 0;
0031 }
0032 
0033 static int ufs_get_temp(struct ufs_hba *hba, enum attr_idn idn, long *val)
0034 {
0035     u32 value;
0036     int err;
0037 
0038     err = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, idn, 0, 0, &value);
0039     if (err)
0040         return err;
0041 
0042     if (value == 0)
0043         return -ENODATA;
0044 
0045     *val = ((long)value - 80) * MILLIDEGREE_PER_DEGREE;
0046 
0047     return 0;
0048 }
0049 
0050 static int ufs_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
0051               long *val)
0052 {
0053     struct ufs_hwmon_data *data = dev_get_drvdata(dev);
0054     struct ufs_hba *hba = data->hba;
0055     int err;
0056 
0057     down(&hba->host_sem);
0058 
0059     if (!ufshcd_is_user_access_allowed(hba)) {
0060         up(&hba->host_sem);
0061         return -EBUSY;
0062     }
0063 
0064     ufshcd_rpm_get_sync(hba);
0065 
0066     switch (attr) {
0067     case hwmon_temp_enable:
0068         err = ufs_read_temp_enable(hba, data->mask, val);
0069 
0070         break;
0071     case hwmon_temp_crit:
0072         err = ufs_get_temp(hba, QUERY_ATTR_IDN_HIGH_TEMP_BOUND, val);
0073 
0074         break;
0075     case hwmon_temp_lcrit:
0076         err = ufs_get_temp(hba, QUERY_ATTR_IDN_LOW_TEMP_BOUND, val);
0077 
0078         break;
0079     case hwmon_temp_input:
0080         err = ufs_get_temp(hba, QUERY_ATTR_IDN_CASE_ROUGH_TEMP, val);
0081 
0082         break;
0083     default:
0084         err = -EOPNOTSUPP;
0085 
0086         break;
0087     }
0088 
0089     ufshcd_rpm_put_sync(hba);
0090 
0091     up(&hba->host_sem);
0092 
0093     return err;
0094 }
0095 
0096 static int ufs_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
0097                long val)
0098 {
0099     struct ufs_hwmon_data *data = dev_get_drvdata(dev);
0100     struct ufs_hba *hba = data->hba;
0101     int err;
0102 
0103     if (attr != hwmon_temp_enable)
0104         return -EINVAL;
0105 
0106     if (val != 0 && val != 1)
0107         return -EINVAL;
0108 
0109     down(&hba->host_sem);
0110 
0111     if (!ufshcd_is_user_access_allowed(hba)) {
0112         up(&hba->host_sem);
0113         return -EBUSY;
0114     }
0115 
0116     ufshcd_rpm_get_sync(hba);
0117 
0118     if (val == 1)
0119         err = ufshcd_update_ee_usr_mask(hba, MASK_EE_URGENT_TEMP, 0);
0120     else
0121         err = ufshcd_update_ee_usr_mask(hba, 0, MASK_EE_URGENT_TEMP);
0122 
0123     ufshcd_rpm_put_sync(hba);
0124 
0125     up(&hba->host_sem);
0126 
0127     return err;
0128 }
0129 
0130 static umode_t ufs_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr,
0131                     int channel)
0132 {
0133     if (type != hwmon_temp)
0134         return 0;
0135 
0136     switch (attr) {
0137     case hwmon_temp_enable:
0138         return 0644;
0139     case hwmon_temp_crit:
0140     case hwmon_temp_lcrit:
0141     case hwmon_temp_input:
0142         return 0444;
0143     default:
0144         break;
0145     }
0146     return 0;
0147 }
0148 
0149 static const struct hwmon_channel_info *ufs_hwmon_info[] = {
0150     HWMON_CHANNEL_INFO(temp, HWMON_T_ENABLE | HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LCRIT),
0151     NULL
0152 };
0153 
0154 static const struct hwmon_ops ufs_hwmon_ops = {
0155     .is_visible = ufs_hwmon_is_visible,
0156     .read       = ufs_hwmon_read,
0157     .write      = ufs_hwmon_write,
0158 };
0159 
0160 static const struct hwmon_chip_info ufs_hwmon_hba_info = {
0161     .ops    = &ufs_hwmon_ops,
0162     .info   = ufs_hwmon_info,
0163 };
0164 
0165 void ufs_hwmon_probe(struct ufs_hba *hba, u8 mask)
0166 {
0167     struct device *dev = hba->dev;
0168     struct ufs_hwmon_data *data;
0169     struct device *hwmon;
0170 
0171     data = kzalloc(sizeof(*data), GFP_KERNEL);
0172     if (!data)
0173         return;
0174 
0175     data->hba = hba;
0176     data->mask = mask;
0177 
0178     hwmon = hwmon_device_register_with_info(dev, "ufs", data, &ufs_hwmon_hba_info, NULL);
0179     if (IS_ERR(hwmon)) {
0180         dev_warn(dev, "Failed to instantiate hwmon device\n");
0181         kfree(data);
0182         return;
0183     }
0184 
0185     hba->hwmon_device = hwmon;
0186 }
0187 
0188 void ufs_hwmon_remove(struct ufs_hba *hba)
0189 {
0190     struct ufs_hwmon_data *data;
0191 
0192     if (!hba->hwmon_device)
0193         return;
0194 
0195     data = dev_get_drvdata(hba->hwmon_device);
0196     hwmon_device_unregister(hba->hwmon_device);
0197     hba->hwmon_device = NULL;
0198     kfree(data);
0199 }
0200 
0201 void ufs_hwmon_notify_event(struct ufs_hba *hba, u8 ee_mask)
0202 {
0203     if (!hba->hwmon_device)
0204         return;
0205 
0206     if (ee_mask & MASK_EE_TOO_HIGH_TEMP)
0207         hwmon_notify_event(hba->hwmon_device, hwmon_temp, hwmon_temp_max_alarm, 0);
0208 
0209     if (ee_mask & MASK_EE_TOO_LOW_TEMP)
0210         hwmon_notify_event(hba->hwmon_device, hwmon_temp, hwmon_temp_min_alarm, 0);
0211 }