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