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