xref: /linux/drivers/misc/amd-sbi/rmi-hwmon.c (revision ab93e0dd72c37d378dd936f031ffb83ff2bd87ce)
1f4dc6406SAkshay Gupta // SPDX-License-Identifier: GPL-2.0-or-later
2f4dc6406SAkshay Gupta /*
3f4dc6406SAkshay Gupta  * rmi-hwmon.c - hwmon sensor support for side band RMI
4f4dc6406SAkshay Gupta  *
5f4dc6406SAkshay Gupta  * Copyright (C) 2025 Advanced Micro Devices, Inc.
6f4dc6406SAkshay Gupta  */
7f4dc6406SAkshay Gupta #include <linux/err.h>
8f4dc6406SAkshay Gupta #include <linux/hwmon.h>
9*35ac2034SAkshay Gupta #include <uapi/misc/amd-apml.h>
10f4dc6406SAkshay Gupta #include "rmi-core.h"
11f4dc6406SAkshay Gupta 
12f4dc6406SAkshay Gupta /* Do not allow setting negative power limit */
13f4dc6406SAkshay Gupta #define SBRMI_PWR_MIN  0
14f4dc6406SAkshay Gupta 
sbrmi_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)15f4dc6406SAkshay Gupta static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type,
16f4dc6406SAkshay Gupta 		      u32 attr, int channel, long *val)
17f4dc6406SAkshay Gupta {
18f4dc6406SAkshay Gupta 	struct sbrmi_data *data = dev_get_drvdata(dev);
19*35ac2034SAkshay Gupta 	struct apml_mbox_msg msg = { 0 };
20f4dc6406SAkshay Gupta 	int ret;
21f4dc6406SAkshay Gupta 
22f4dc6406SAkshay Gupta 	if (!data)
23f4dc6406SAkshay Gupta 		return -ENODEV;
24f4dc6406SAkshay Gupta 
25f4dc6406SAkshay Gupta 	if (type != hwmon_power)
26f4dc6406SAkshay Gupta 		return -EINVAL;
27f4dc6406SAkshay Gupta 
28f4dc6406SAkshay Gupta 	switch (attr) {
29f4dc6406SAkshay Gupta 	case hwmon_power_input:
30f4dc6406SAkshay Gupta 		msg.cmd = SBRMI_READ_PKG_PWR_CONSUMPTION;
31f4dc6406SAkshay Gupta 		ret = rmi_mailbox_xfer(data, &msg);
32f4dc6406SAkshay Gupta 		break;
33f4dc6406SAkshay Gupta 	case hwmon_power_cap:
34f4dc6406SAkshay Gupta 		msg.cmd = SBRMI_READ_PKG_PWR_LIMIT;
35f4dc6406SAkshay Gupta 		ret = rmi_mailbox_xfer(data, &msg);
36f4dc6406SAkshay Gupta 		break;
37f4dc6406SAkshay Gupta 	case hwmon_power_cap_max:
38*35ac2034SAkshay Gupta 		msg.mb_in_out = data->pwr_limit_max;
39f4dc6406SAkshay Gupta 		ret = 0;
40f4dc6406SAkshay Gupta 		break;
41f4dc6406SAkshay Gupta 	default:
42f4dc6406SAkshay Gupta 		return -EINVAL;
43f4dc6406SAkshay Gupta 	}
44f4dc6406SAkshay Gupta 	if (ret < 0)
45f4dc6406SAkshay Gupta 		return ret;
46f4dc6406SAkshay Gupta 	/* hwmon power attributes are in microWatt */
47*35ac2034SAkshay Gupta 	*val = (long)msg.mb_in_out * 1000;
48f4dc6406SAkshay Gupta 	return ret;
49f4dc6406SAkshay Gupta }
50f4dc6406SAkshay Gupta 
sbrmi_write(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long val)51f4dc6406SAkshay Gupta static int sbrmi_write(struct device *dev, enum hwmon_sensor_types type,
52f4dc6406SAkshay Gupta 		       u32 attr, int channel, long val)
53f4dc6406SAkshay Gupta {
54f4dc6406SAkshay Gupta 	struct sbrmi_data *data = dev_get_drvdata(dev);
55*35ac2034SAkshay Gupta 	struct apml_mbox_msg msg = { 0 };
56f4dc6406SAkshay Gupta 
57f4dc6406SAkshay Gupta 	if (!data)
58f4dc6406SAkshay Gupta 		return -ENODEV;
59f4dc6406SAkshay Gupta 
60f4dc6406SAkshay Gupta 	if (type != hwmon_power && attr != hwmon_power_cap)
61f4dc6406SAkshay Gupta 		return -EINVAL;
62f4dc6406SAkshay Gupta 	/*
63f4dc6406SAkshay Gupta 	 * hwmon power attributes are in microWatt
64f4dc6406SAkshay Gupta 	 * mailbox read/write is in mWatt
65f4dc6406SAkshay Gupta 	 */
66f4dc6406SAkshay Gupta 	val /= 1000;
67f4dc6406SAkshay Gupta 
68f4dc6406SAkshay Gupta 	val = clamp_val(val, SBRMI_PWR_MIN, data->pwr_limit_max);
69f4dc6406SAkshay Gupta 
70f4dc6406SAkshay Gupta 	msg.cmd = SBRMI_WRITE_PKG_PWR_LIMIT;
71*35ac2034SAkshay Gupta 	msg.mb_in_out = val;
72f4dc6406SAkshay Gupta 
73f4dc6406SAkshay Gupta 	return rmi_mailbox_xfer(data, &msg);
74f4dc6406SAkshay Gupta }
75f4dc6406SAkshay Gupta 
sbrmi_is_visible(const void * data,enum hwmon_sensor_types type,u32 attr,int channel)76f4dc6406SAkshay Gupta static umode_t sbrmi_is_visible(const void *data,
77f4dc6406SAkshay Gupta 				enum hwmon_sensor_types type,
78f4dc6406SAkshay Gupta 				u32 attr, int channel)
79f4dc6406SAkshay Gupta {
80f4dc6406SAkshay Gupta 	switch (type) {
81f4dc6406SAkshay Gupta 	case hwmon_power:
82f4dc6406SAkshay Gupta 		switch (attr) {
83f4dc6406SAkshay Gupta 		case hwmon_power_input:
84f4dc6406SAkshay Gupta 		case hwmon_power_cap_max:
85f4dc6406SAkshay Gupta 			return 0444;
86f4dc6406SAkshay Gupta 		case hwmon_power_cap:
87f4dc6406SAkshay Gupta 			return 0644;
88f4dc6406SAkshay Gupta 		}
89f4dc6406SAkshay Gupta 		break;
90f4dc6406SAkshay Gupta 	default:
91f4dc6406SAkshay Gupta 		break;
92f4dc6406SAkshay Gupta 	}
93f4dc6406SAkshay Gupta 	return 0;
94f4dc6406SAkshay Gupta }
95f4dc6406SAkshay Gupta 
96f4dc6406SAkshay Gupta static const struct hwmon_channel_info * const sbrmi_info[] = {
97f4dc6406SAkshay Gupta 	HWMON_CHANNEL_INFO(power,
98f4dc6406SAkshay Gupta 			   HWMON_P_INPUT | HWMON_P_CAP | HWMON_P_CAP_MAX),
99f4dc6406SAkshay Gupta 	NULL
100f4dc6406SAkshay Gupta };
101f4dc6406SAkshay Gupta 
102f4dc6406SAkshay Gupta static const struct hwmon_ops sbrmi_hwmon_ops = {
103f4dc6406SAkshay Gupta 	.is_visible = sbrmi_is_visible,
104f4dc6406SAkshay Gupta 	.read = sbrmi_read,
105f4dc6406SAkshay Gupta 	.write = sbrmi_write,
106f4dc6406SAkshay Gupta };
107f4dc6406SAkshay Gupta 
108f4dc6406SAkshay Gupta static const struct hwmon_chip_info sbrmi_chip_info = {
109f4dc6406SAkshay Gupta 	.ops = &sbrmi_hwmon_ops,
110f4dc6406SAkshay Gupta 	.info = sbrmi_info,
111f4dc6406SAkshay Gupta };
112f4dc6406SAkshay Gupta 
create_hwmon_sensor_device(struct device * dev,struct sbrmi_data * data)113f4dc6406SAkshay Gupta int create_hwmon_sensor_device(struct device *dev, struct sbrmi_data *data)
114f4dc6406SAkshay Gupta {
115f4dc6406SAkshay Gupta 	struct device *hwmon_dev;
116f4dc6406SAkshay Gupta 
117f4dc6406SAkshay Gupta 	hwmon_dev = devm_hwmon_device_register_with_info(dev, "sbrmi", data,
118f4dc6406SAkshay Gupta 							 &sbrmi_chip_info, NULL);
119f4dc6406SAkshay Gupta 	return PTR_ERR_OR_ZERO(hwmon_dev);
120f4dc6406SAkshay Gupta }
121