File Index
Files and directories
Sign in
FILE

ufs-hwmon.c

File preview with syntax highlighting.

linux-6.19.14/drivers/ufs/core/ufs-hwmon.c C
  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,
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
150static 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
155static 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
161static const struct hwmon_chip_info ufs_hwmon_hba_info = {
162	.ops	= &ufs_hwmon_ops,
163	.info	= ufs_hwmon_info,
164};
165
166void 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
189void 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
202void 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}
~/ Read-only file preview.