cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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}