From 43470595e72bd4448fcf0cd720c4d37c34035eb7 Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Mon, 28 Apr 2025 06:30:26 +0000 Subject: misc: amd-sbi: Move protocol functionality to core file - This is done to utilize the protocol functionality into other domains. - Increase the scalability of the module with different bus(i2c/i3c) Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: Akshay Gupta Link: https://lore.kernel.org/r/20250428063034.2145566-3-akshay.gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/amd-sbi/rmi-i2c.c | 209 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 drivers/misc/amd-sbi/rmi-i2c.c (limited to 'drivers/misc/amd-sbi/rmi-i2c.c') diff --git a/drivers/misc/amd-sbi/rmi-i2c.c b/drivers/misc/amd-sbi/rmi-i2c.c new file mode 100644 index 000000000000..6412f00eb381 --- /dev/null +++ b/drivers/misc/amd-sbi/rmi-i2c.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * rmi-i2c.c - Side band RMI over I2C support for AMD out + * of band management + * + * Copyright (C) 2024 Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "rmi-core.h" + +/* Do not allow setting negative power limit */ +#define SBRMI_PWR_MIN 0 + +static int sbrmi_enable_alert(struct i2c_client *client) +{ + int ctrl; + + /* + * Enable the SB-RMI Software alert status + * by writing 0 to bit 4 of Control register(0x1) + */ + ctrl = i2c_smbus_read_byte_data(client, SBRMI_CTRL); + if (ctrl < 0) + return ctrl; + + if (ctrl & 0x10) { + ctrl &= ~0x10; + return i2c_smbus_write_byte_data(client, + SBRMI_CTRL, ctrl); + } + + return 0; +} + +static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct sbrmi_data *data = dev_get_drvdata(dev); + struct sbrmi_mailbox_msg msg = { 0 }; + int ret; + + if (type != hwmon_power) + return -EINVAL; + + msg.read = true; + switch (attr) { + case hwmon_power_input: + msg.cmd = SBRMI_READ_PKG_PWR_CONSUMPTION; + ret = rmi_mailbox_xfer(data, &msg); + break; + case hwmon_power_cap: + msg.cmd = SBRMI_READ_PKG_PWR_LIMIT; + ret = rmi_mailbox_xfer(data, &msg); + break; + case hwmon_power_cap_max: + msg.data_out = data->pwr_limit_max; + ret = 0; + break; + default: + return -EINVAL; + } + if (ret < 0) + return ret; + /* hwmon power attributes are in microWatt */ + *val = (long)msg.data_out * 1000; + return ret; +} + +static int sbrmi_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct sbrmi_data *data = dev_get_drvdata(dev); + struct sbrmi_mailbox_msg msg = { 0 }; + + if (type != hwmon_power && attr != hwmon_power_cap) + return -EINVAL; + /* + * hwmon power attributes are in microWatt + * mailbox read/write is in mWatt + */ + val /= 1000; + + val = clamp_val(val, SBRMI_PWR_MIN, data->pwr_limit_max); + + msg.cmd = SBRMI_WRITE_PKG_PWR_LIMIT; + msg.data_in = val; + msg.read = false; + + return rmi_mailbox_xfer(data, &msg); +} + +static umode_t sbrmi_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_power: + switch (attr) { + case hwmon_power_input: + case hwmon_power_cap_max: + return 0444; + case hwmon_power_cap: + return 0644; + } + break; + default: + break; + } + return 0; +} + +static const struct hwmon_channel_info * const sbrmi_info[] = { + HWMON_CHANNEL_INFO(power, + HWMON_P_INPUT | HWMON_P_CAP | HWMON_P_CAP_MAX), + NULL +}; + +static const struct hwmon_ops sbrmi_hwmon_ops = { + .is_visible = sbrmi_is_visible, + .read = sbrmi_read, + .write = sbrmi_write, +}; + +static const struct hwmon_chip_info sbrmi_chip_info = { + .ops = &sbrmi_hwmon_ops, + .info = sbrmi_info, +}; + +static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data) +{ + struct sbrmi_mailbox_msg msg = { 0 }; + int ret; + + msg.cmd = SBRMI_READ_PKG_MAX_PWR_LIMIT; + msg.read = true; + ret = rmi_mailbox_xfer(data, &msg); + if (ret < 0) + return ret; + data->pwr_limit_max = msg.data_out; + + return ret; +} + +static int sbrmi_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct sbrmi_data *data; + int ret; + + data = devm_kzalloc(dev, sizeof(struct sbrmi_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->lock); + + /* Enable alert for SB-RMI sequence */ + ret = sbrmi_enable_alert(client); + if (ret < 0) + return ret; + + /* Cache maximum power limit */ + ret = sbrmi_get_max_pwr_limit(data); + if (ret < 0) + return ret; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &sbrmi_chip_info, NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id sbrmi_id[] = { + {"sbrmi-i2c"}, + {} +}; +MODULE_DEVICE_TABLE(i2c, sbrmi_id); + +static const struct of_device_id __maybe_unused sbrmi_of_match[] = { + { + .compatible = "amd,sbrmi", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, sbrmi_of_match); + +static struct i2c_driver sbrmi_driver = { + .driver = { + .name = "sbrmi-i2c", + .of_match_table = of_match_ptr(sbrmi_of_match), + }, + .probe = sbrmi_i2c_probe, + .id_table = sbrmi_id, +}; + +module_i2c_driver(sbrmi_driver); + +MODULE_AUTHOR("Akshay Gupta "); +MODULE_AUTHOR("Naveen Krishna Chatradhi "); +MODULE_DESCRIPTION("Hwmon driver for AMD SB-RMI emulated sensor"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From f4dc6406631ec46f608be7078f50777990c87b47 Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Mon, 28 Apr 2025 06:30:27 +0000 Subject: misc: amd-sbi: Move hwmon device sensor as separate entity - Move hwmon device sensor to misc as only power is reported through hwmon sensor. Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: Akshay Gupta Link: https://lore.kernel.org/r/20250428063034.2145566-4-akshay.gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/amd-sbi/Kconfig | 10 +++- drivers/misc/amd-sbi/Makefile | 3 +- drivers/misc/amd-sbi/rmi-core.h | 8 +++ drivers/misc/amd-sbi/rmi-hwmon.c | 121 +++++++++++++++++++++++++++++++++++++++ drivers/misc/amd-sbi/rmi-i2c.c | 104 +-------------------------------- 5 files changed, 142 insertions(+), 104 deletions(-) create mode 100644 drivers/misc/amd-sbi/rmi-hwmon.c (limited to 'drivers/misc/amd-sbi/rmi-i2c.c') diff --git a/drivers/misc/amd-sbi/Kconfig b/drivers/misc/amd-sbi/Kconfig index 0c8981f97f25..4840831c84ca 100644 --- a/drivers/misc/amd-sbi/Kconfig +++ b/drivers/misc/amd-sbi/Kconfig @@ -2,9 +2,17 @@ config AMD_SBRMI_I2C tristate "AMD side band RMI support" depends on I2C - depends on HWMON help Side band RMI over I2C support for AMD out of band management. This driver can also be built as a module. If so, the module will be called sbrmi-i2c. + +config AMD_SBRMI_HWMON + bool "SBRMI hardware monitoring" + depends on AMD_SBRMI_I2C && HWMON + depends on !(AMD_SBRMI_I2C=y && HWMON=m) + help + This provides support for RMI device hardware monitoring. If enabled, + a hardware monitoring device will be created for each socket in + the system. diff --git a/drivers/misc/amd-sbi/Makefile b/drivers/misc/amd-sbi/Makefile index 7cd8e0a1aa5d..38eaaa651fd9 100644 --- a/drivers/misc/amd-sbi/Makefile +++ b/drivers/misc/amd-sbi/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -sbrmi-i2c-objs := rmi-i2c.o rmi-core.o +sbrmi-i2c-objs += rmi-i2c.o rmi-core.o +sbrmi-i2c-$(CONFIG_AMD_SBRMI_HWMON) += rmi-hwmon.o obj-$(CONFIG_AMD_SBRMI_I2C) += sbrmi-i2c.o diff --git a/drivers/misc/amd-sbi/rmi-core.h b/drivers/misc/amd-sbi/rmi-core.h index 8e30a43ec714..977ee05af6a6 100644 --- a/drivers/misc/amd-sbi/rmi-core.h +++ b/drivers/misc/amd-sbi/rmi-core.h @@ -60,4 +60,12 @@ struct sbrmi_mailbox_msg { }; int rmi_mailbox_xfer(struct sbrmi_data *data, struct sbrmi_mailbox_msg *msg); +#ifdef CONFIG_AMD_SBRMI_HWMON +int create_hwmon_sensor_device(struct device *dev, struct sbrmi_data *data); +#else +static inline int create_hwmon_sensor_device(struct device *dev, struct sbrmi_data *data) +{ + return 0; +} +#endif #endif /*_SBRMI_CORE_H_*/ diff --git a/drivers/misc/amd-sbi/rmi-hwmon.c b/drivers/misc/amd-sbi/rmi-hwmon.c new file mode 100644 index 000000000000..720e800db1f0 --- /dev/null +++ b/drivers/misc/amd-sbi/rmi-hwmon.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * rmi-hwmon.c - hwmon sensor support for side band RMI + * + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ +#include +#include +#include "rmi-core.h" + +/* Do not allow setting negative power limit */ +#define SBRMI_PWR_MIN 0 + +static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct sbrmi_data *data = dev_get_drvdata(dev); + struct sbrmi_mailbox_msg msg = { 0 }; + int ret; + + if (!data) + return -ENODEV; + + if (type != hwmon_power) + return -EINVAL; + + msg.read = true; + switch (attr) { + case hwmon_power_input: + msg.cmd = SBRMI_READ_PKG_PWR_CONSUMPTION; + ret = rmi_mailbox_xfer(data, &msg); + break; + case hwmon_power_cap: + msg.cmd = SBRMI_READ_PKG_PWR_LIMIT; + ret = rmi_mailbox_xfer(data, &msg); + break; + case hwmon_power_cap_max: + msg.data_out = data->pwr_limit_max; + ret = 0; + break; + default: + return -EINVAL; + } + if (ret < 0) + return ret; + /* hwmon power attributes are in microWatt */ + *val = (long)msg.data_out * 1000; + return ret; +} + +static int sbrmi_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct sbrmi_data *data = dev_get_drvdata(dev); + struct sbrmi_mailbox_msg msg = { 0 }; + + if (!data) + return -ENODEV; + + if (type != hwmon_power && attr != hwmon_power_cap) + return -EINVAL; + /* + * hwmon power attributes are in microWatt + * mailbox read/write is in mWatt + */ + val /= 1000; + + val = clamp_val(val, SBRMI_PWR_MIN, data->pwr_limit_max); + + msg.cmd = SBRMI_WRITE_PKG_PWR_LIMIT; + msg.data_in = val; + msg.read = false; + + return rmi_mailbox_xfer(data, &msg); +} + +static umode_t sbrmi_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_power: + switch (attr) { + case hwmon_power_input: + case hwmon_power_cap_max: + return 0444; + case hwmon_power_cap: + return 0644; + } + break; + default: + break; + } + return 0; +} + +static const struct hwmon_channel_info * const sbrmi_info[] = { + HWMON_CHANNEL_INFO(power, + HWMON_P_INPUT | HWMON_P_CAP | HWMON_P_CAP_MAX), + NULL +}; + +static const struct hwmon_ops sbrmi_hwmon_ops = { + .is_visible = sbrmi_is_visible, + .read = sbrmi_read, + .write = sbrmi_write, +}; + +static const struct hwmon_chip_info sbrmi_chip_info = { + .ops = &sbrmi_hwmon_ops, + .info = sbrmi_info, +}; + +int create_hwmon_sensor_device(struct device *dev, struct sbrmi_data *data) +{ + struct device *hwmon_dev; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, "sbrmi", data, + &sbrmi_chip_info, NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); +} diff --git a/drivers/misc/amd-sbi/rmi-i2c.c b/drivers/misc/amd-sbi/rmi-i2c.c index 6412f00eb381..9ad4c8093399 100644 --- a/drivers/misc/amd-sbi/rmi-i2c.c +++ b/drivers/misc/amd-sbi/rmi-i2c.c @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -16,9 +15,6 @@ #include #include "rmi-core.h" -/* Do not allow setting negative power limit */ -#define SBRMI_PWR_MIN 0 - static int sbrmi_enable_alert(struct i2c_client *client) { int ctrl; @@ -40,100 +36,6 @@ static int sbrmi_enable_alert(struct i2c_client *client) return 0; } -static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long *val) -{ - struct sbrmi_data *data = dev_get_drvdata(dev); - struct sbrmi_mailbox_msg msg = { 0 }; - int ret; - - if (type != hwmon_power) - return -EINVAL; - - msg.read = true; - switch (attr) { - case hwmon_power_input: - msg.cmd = SBRMI_READ_PKG_PWR_CONSUMPTION; - ret = rmi_mailbox_xfer(data, &msg); - break; - case hwmon_power_cap: - msg.cmd = SBRMI_READ_PKG_PWR_LIMIT; - ret = rmi_mailbox_xfer(data, &msg); - break; - case hwmon_power_cap_max: - msg.data_out = data->pwr_limit_max; - ret = 0; - break; - default: - return -EINVAL; - } - if (ret < 0) - return ret; - /* hwmon power attributes are in microWatt */ - *val = (long)msg.data_out * 1000; - return ret; -} - -static int sbrmi_write(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long val) -{ - struct sbrmi_data *data = dev_get_drvdata(dev); - struct sbrmi_mailbox_msg msg = { 0 }; - - if (type != hwmon_power && attr != hwmon_power_cap) - return -EINVAL; - /* - * hwmon power attributes are in microWatt - * mailbox read/write is in mWatt - */ - val /= 1000; - - val = clamp_val(val, SBRMI_PWR_MIN, data->pwr_limit_max); - - msg.cmd = SBRMI_WRITE_PKG_PWR_LIMIT; - msg.data_in = val; - msg.read = false; - - return rmi_mailbox_xfer(data, &msg); -} - -static umode_t sbrmi_is_visible(const void *data, - enum hwmon_sensor_types type, - u32 attr, int channel) -{ - switch (type) { - case hwmon_power: - switch (attr) { - case hwmon_power_input: - case hwmon_power_cap_max: - return 0444; - case hwmon_power_cap: - return 0644; - } - break; - default: - break; - } - return 0; -} - -static const struct hwmon_channel_info * const sbrmi_info[] = { - HWMON_CHANNEL_INFO(power, - HWMON_P_INPUT | HWMON_P_CAP | HWMON_P_CAP_MAX), - NULL -}; - -static const struct hwmon_ops sbrmi_hwmon_ops = { - .is_visible = sbrmi_is_visible, - .read = sbrmi_read, - .write = sbrmi_write, -}; - -static const struct hwmon_chip_info sbrmi_chip_info = { - .ops = &sbrmi_hwmon_ops, - .info = sbrmi_info, -}; - static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data) { struct sbrmi_mailbox_msg msg = { 0 }; @@ -152,7 +54,6 @@ static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data) static int sbrmi_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; - struct device *hwmon_dev; struct sbrmi_data *data; int ret; @@ -173,9 +74,8 @@ static int sbrmi_i2c_probe(struct i2c_client *client) if (ret < 0) return ret; - hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, - &sbrmi_chip_info, NULL); - return PTR_ERR_OR_ZERO(hwmon_dev); + dev_set_drvdata(dev, data); + return create_hwmon_sensor_device(dev, data); } static const struct i2c_device_id sbrmi_id[] = { -- cgit v1.2.3 From 013f7e7131bd84a83ed3b70855b2667a89bb3c98 Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Mon, 28 Apr 2025 06:30:28 +0000 Subject: misc: amd-sbi: Use regmap subsystem - regmap subsystem provides multiple benefits over direct smbus APIs - subsystem adds another abstraction layer on top of struct i2c_client to make it easy to read or write registers. - The subsystem can be helpful in following cases - Different types of bus (i2c/i3c), we have plans to support i3c. - Different Register address size (1byte/2byte) Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: Akshay Gupta Link: https://lore.kernel.org/r/20250428063034.2145566-5-akshay.gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/amd-sbi/rmi-core.c | 29 ++++++++++++----------------- drivers/misc/amd-sbi/rmi-core.h | 3 ++- drivers/misc/amd-sbi/rmi-i2c.c | 25 ++++++++++++++++--------- 3 files changed, 30 insertions(+), 27 deletions(-) (limited to 'drivers/misc/amd-sbi/rmi-i2c.c') diff --git a/drivers/misc/amd-sbi/rmi-core.c b/drivers/misc/amd-sbi/rmi-core.c index 74456756270c..663ab9176d95 100644 --- a/drivers/misc/amd-sbi/rmi-core.c +++ b/drivers/misc/amd-sbi/rmi-core.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "rmi-core.h" /* Mask for Status Register bit[1] */ @@ -21,6 +22,7 @@ int rmi_mailbox_xfer(struct sbrmi_data *data, struct sbrmi_mailbox_msg *msg) { + unsigned int bytes; int i, ret, retry = 10; int sw_status; u8 byte; @@ -28,14 +30,12 @@ int rmi_mailbox_xfer(struct sbrmi_data *data, mutex_lock(&data->lock); /* Indicate firmware a command is to be serviced */ - ret = i2c_smbus_write_byte_data(data->client, - SBRMI_INBNDMSG7, START_CMD); + ret = regmap_write(data->regmap, SBRMI_INBNDMSG7, START_CMD); if (ret < 0) goto exit_unlock; /* Write the command to SBRMI::InBndMsg_inst0 */ - ret = i2c_smbus_write_byte_data(data->client, - SBRMI_INBNDMSG0, msg->cmd); + ret = regmap_write(data->regmap, SBRMI_INBNDMSG0, msg->cmd); if (ret < 0) goto exit_unlock; @@ -46,8 +46,7 @@ int rmi_mailbox_xfer(struct sbrmi_data *data, */ for (i = 0; i < 4; i++) { byte = (msg->data_in >> i * 8) & 0xff; - ret = i2c_smbus_write_byte_data(data->client, - SBRMI_INBNDMSG1 + i, byte); + ret = regmap_write(data->regmap, SBRMI_INBNDMSG1 + i, byte); if (ret < 0) goto exit_unlock; } @@ -56,8 +55,7 @@ int rmi_mailbox_xfer(struct sbrmi_data *data, * Write 0x01 to SBRMI::SoftwareInterrupt to notify firmware to * perform the requested read or write command */ - ret = i2c_smbus_write_byte_data(data->client, - SBRMI_SW_INTERRUPT, TRIGGER_MAILBOX); + ret = regmap_write(data->regmap, SBRMI_SW_INTERRUPT, TRIGGER_MAILBOX); if (ret < 0) goto exit_unlock; @@ -67,8 +65,7 @@ int rmi_mailbox_xfer(struct sbrmi_data *data, * of the requested command */ do { - sw_status = i2c_smbus_read_byte_data(data->client, - SBRMI_STATUS); + ret = regmap_read(data->regmap, SBRMI_STATUS, &sw_status); if (sw_status < 0) { ret = sw_status; goto exit_unlock; @@ -79,8 +76,6 @@ int rmi_mailbox_xfer(struct sbrmi_data *data, } while (retry--); if (retry < 0) { - dev_err(&data->client->dev, - "Firmware fail to indicate command completion\n"); ret = -EIO; goto exit_unlock; } @@ -92,11 +87,11 @@ int rmi_mailbox_xfer(struct sbrmi_data *data, */ if (msg->read) { for (i = 0; i < 4; i++) { - ret = i2c_smbus_read_byte_data(data->client, - SBRMI_OUTBNDMSG1 + i); + ret = regmap_read(data->regmap, + SBRMI_OUTBNDMSG1 + i, &bytes); if (ret < 0) goto exit_unlock; - msg->data_out |= ret << i * 8; + msg->data_out |= bytes << i * 8; } } @@ -104,8 +99,8 @@ int rmi_mailbox_xfer(struct sbrmi_data *data, * BMC must write 1'b1 to SBRMI::Status[SwAlertSts] to clear the * ALERT to initiator */ - ret = i2c_smbus_write_byte_data(data->client, SBRMI_STATUS, - sw_status | SW_ALERT_MASK); + ret = regmap_write(data->regmap, SBRMI_STATUS, + sw_status | SW_ALERT_MASK); exit_unlock: mutex_unlock(&data->lock); diff --git a/drivers/misc/amd-sbi/rmi-core.h b/drivers/misc/amd-sbi/rmi-core.h index 977ee05af6a6..3a6028306d10 100644 --- a/drivers/misc/amd-sbi/rmi-core.h +++ b/drivers/misc/amd-sbi/rmi-core.h @@ -9,6 +9,7 @@ #include #include #include +#include /* SB-RMI registers */ enum sbrmi_reg { @@ -47,7 +48,7 @@ enum sbrmi_msg_id { /* Each client has this additional data */ struct sbrmi_data { - struct i2c_client *client; + struct regmap *regmap; struct mutex lock; u32 pwr_limit_max; }; diff --git a/drivers/misc/amd-sbi/rmi-i2c.c b/drivers/misc/amd-sbi/rmi-i2c.c index 9ad4c8093399..7a9801273a4c 100644 --- a/drivers/misc/amd-sbi/rmi-i2c.c +++ b/drivers/misc/amd-sbi/rmi-i2c.c @@ -13,24 +13,24 @@ #include #include #include +#include #include "rmi-core.h" -static int sbrmi_enable_alert(struct i2c_client *client) +static int sbrmi_enable_alert(struct sbrmi_data *data) { - int ctrl; + int ctrl, ret; /* * Enable the SB-RMI Software alert status * by writing 0 to bit 4 of Control register(0x1) */ - ctrl = i2c_smbus_read_byte_data(client, SBRMI_CTRL); - if (ctrl < 0) - return ctrl; + ret = regmap_read(data->regmap, SBRMI_CTRL, &ctrl); + if (ret < 0) + return ret; if (ctrl & 0x10) { ctrl &= ~0x10; - return i2c_smbus_write_byte_data(client, - SBRMI_CTRL, ctrl); + return regmap_write(data->regmap, SBRMI_CTRL, ctrl); } return 0; @@ -55,17 +55,24 @@ static int sbrmi_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct sbrmi_data *data; + struct regmap_config sbrmi_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + }; int ret; data = devm_kzalloc(dev, sizeof(struct sbrmi_data), GFP_KERNEL); if (!data) return -ENOMEM; - data->client = client; mutex_init(&data->lock); + data->regmap = devm_regmap_init_i2c(client, &sbrmi_i2c_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + /* Enable alert for SB-RMI sequence */ - ret = sbrmi_enable_alert(client); + ret = sbrmi_enable_alert(data); if (ret < 0) return ret; -- cgit v1.2.3 From 35ac2034db72bbbc73609aab5f05ff6e0d38fdd0 Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Mon, 28 Apr 2025 06:30:30 +0000 Subject: misc: amd-sbi: Add support for AMD_SBI IOCTL The present sbrmi module only support reporting power via hwmon. However, AMD data center range of processors support various system management functionality using custom protocols defined in Advanced Platform Management Link (APML) specification. Register a miscdevice, which creates a device /dev/sbrmiX with an IOCTL interface for the user space to invoke the APML Mailbox protocol, which is already defined in sbrmi_mailbox_xfer(). The APML protocols depend on a set of RMI registers. Having an IOCTL as a single entry point will help in providing synchronization among these protocols as multiple transactions on RMI register set may create race condition. Support for other protocols will be added in subsequent patches. APML mailbox protocol returns additional error codes written by SMU firmware in the out-bound register 0x37. These errors include, invalid core, message not supported over platform and others. This additional error codes can be used to provide more details to user space. Open-sourced and widely used https://github.com/amd/esmi_oob_library will continue to provide user-space programmable API. Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: Akshay Gupta Link: https://lore.kernel.org/r/20250428063034.2145566-7-akshay.gupta@amd.com Signed-off-by: Greg Kroah-Hartman --- Documentation/misc-devices/index.rst | 1 + Documentation/userspace-api/ioctl/ioctl-number.rst | 2 + drivers/misc/amd-sbi/rmi-core.c | 92 +++++++++++++++++++--- drivers/misc/amd-sbi/rmi-core.h | 15 ++-- drivers/misc/amd-sbi/rmi-hwmon.c | 13 ++- drivers/misc/amd-sbi/rmi-i2c.c | 25 +++++- include/uapi/misc/amd-apml.h | 51 ++++++++++++ 7 files changed, 167 insertions(+), 32 deletions(-) create mode 100644 include/uapi/misc/amd-apml.h (limited to 'drivers/misc/amd-sbi/rmi-i2c.c') diff --git a/Documentation/misc-devices/index.rst b/Documentation/misc-devices/index.rst index 8c5b226d8313..081e79415e38 100644 --- a/Documentation/misc-devices/index.rst +++ b/Documentation/misc-devices/index.rst @@ -12,6 +12,7 @@ fit into other categories. :maxdepth: 2 ad525x_dpot + amd-sbi apds990x bh1770glc c2port diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 7a1409ecc238..3191d96ea4da 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -397,6 +397,8 @@ Code Seq# Include File Comments 0xF8 all arch/x86/include/uapi/asm/amd_hsmp.h AMD HSMP EPYC system management interface driver +0xF9 00-0F uapi/misc/amd-apml.h AMD side band system management interface driver + 0xFD all linux/dm-ioctl.h 0xFE all linux/isst_if.h ==== ===== ======================================================= ================================================================ diff --git a/drivers/misc/amd-sbi/rmi-core.c b/drivers/misc/amd-sbi/rmi-core.c index 1d5e2556ab88..7d13c049c98d 100644 --- a/drivers/misc/amd-sbi/rmi-core.c +++ b/drivers/misc/amd-sbi/rmi-core.c @@ -7,7 +7,10 @@ */ #include #include +#include #include +#include +#include #include #include #include "rmi-core.h" @@ -20,15 +23,17 @@ #define TRIGGER_MAILBOX 0x01 int rmi_mailbox_xfer(struct sbrmi_data *data, - struct sbrmi_mailbox_msg *msg) + struct apml_mbox_msg *msg) { - unsigned int bytes; + unsigned int bytes, ec; int i, ret; int sw_status; u8 byte; mutex_lock(&data->lock); + msg->fw_ret_code = 0; + /* Indicate firmware a command is to be serviced */ ret = regmap_write(data->regmap, SBRMI_INBNDMSG7, START_CMD); if (ret < 0) @@ -44,8 +49,8 @@ int rmi_mailbox_xfer(struct sbrmi_data *data, * Command Data In[31:0] to SBRMI::InBndMsg_inst[4:1] * SBRMI_x3C(MSB):SBRMI_x39(LSB) */ - for (i = 0; i < 4; i++) { - byte = (msg->data_in >> i * 8) & 0xff; + for (i = 0; i < AMD_SBI_MB_DATA_SIZE; i++) { + byte = (msg->mb_in_out >> i * 8) & 0xff; ret = regmap_write(data->regmap, SBRMI_INBNDMSG1 + i, byte); if (ret < 0) goto exit_unlock; @@ -69,29 +74,90 @@ int rmi_mailbox_xfer(struct sbrmi_data *data, if (ret) goto exit_unlock; + ret = regmap_read(data->regmap, SBRMI_OUTBNDMSG7, &ec); + if (ret || ec) + goto exit_clear_alert; /* * For a read operation, the initiator (BMC) reads the firmware * response Command Data Out[31:0] from SBRMI::OutBndMsg_inst[4:1] * {SBRMI_x34(MSB):SBRMI_x31(LSB)}. */ - if (msg->read) { - for (i = 0; i < 4; i++) { - ret = regmap_read(data->regmap, - SBRMI_OUTBNDMSG1 + i, &bytes); - if (ret < 0) - goto exit_unlock; - msg->data_out |= bytes << i * 8; - } + for (i = 0; i < AMD_SBI_MB_DATA_SIZE; i++) { + ret = regmap_read(data->regmap, + SBRMI_OUTBNDMSG1 + i, &bytes); + if (ret < 0) + break; + msg->mb_in_out |= bytes << i * 8; } +exit_clear_alert: /* * BMC must write 1'b1 to SBRMI::Status[SwAlertSts] to clear the * ALERT to initiator */ ret = regmap_write(data->regmap, SBRMI_STATUS, sw_status | SW_ALERT_MASK); - + if (ec) { + ret = -EPROTOTYPE; + msg->fw_ret_code = ec; + } exit_unlock: mutex_unlock(&data->lock); return ret; } + +static int apml_mailbox_xfer(struct sbrmi_data *data, struct apml_mbox_msg __user *arg) +{ + struct apml_mbox_msg msg = { 0 }; + int ret; + + /* Copy the structure from user */ + if (copy_from_user(&msg, arg, sizeof(struct apml_mbox_msg))) + return -EFAULT; + + /* Mailbox protocol */ + ret = rmi_mailbox_xfer(data, &msg); + if (ret && ret != -EPROTOTYPE) + return ret; + + return copy_to_user(arg, &msg, sizeof(struct apml_mbox_msg)); +} + +static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct sbrmi_data *data; + + data = container_of(fp->private_data, struct sbrmi_data, sbrmi_misc_dev); + switch (cmd) { + case SBRMI_IOCTL_MBOX_CMD: + return apml_mailbox_xfer(data, argp); + default: + return -ENOTTY; + } +} + +static const struct file_operations sbrmi_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = sbrmi_ioctl, + .compat_ioctl = compat_ptr_ioctl, +}; + +int create_misc_rmi_device(struct sbrmi_data *data, + struct device *dev) +{ + data->sbrmi_misc_dev.name = devm_kasprintf(dev, + GFP_KERNEL, + "sbrmi-%x", + data->dev_static_addr); + data->sbrmi_misc_dev.minor = MISC_DYNAMIC_MINOR; + data->sbrmi_misc_dev.fops = &sbrmi_fops; + data->sbrmi_misc_dev.parent = dev; + data->sbrmi_misc_dev.nodename = devm_kasprintf(dev, + GFP_KERNEL, + "sbrmi-%x", + data->dev_static_addr); + data->sbrmi_misc_dev.mode = 0600; + + return misc_register(&data->sbrmi_misc_dev); +} diff --git a/drivers/misc/amd-sbi/rmi-core.h b/drivers/misc/amd-sbi/rmi-core.h index 3a6028306d10..8ab31c6852d1 100644 --- a/drivers/misc/amd-sbi/rmi-core.h +++ b/drivers/misc/amd-sbi/rmi-core.h @@ -6,10 +6,12 @@ #ifndef _SBRMI_CORE_H_ #define _SBRMI_CORE_H_ +#include #include #include #include #include +#include /* SB-RMI registers */ enum sbrmi_reg { @@ -48,19 +50,15 @@ enum sbrmi_msg_id { /* Each client has this additional data */ struct sbrmi_data { + struct miscdevice sbrmi_misc_dev; struct regmap *regmap; + /* Mutex locking */ struct mutex lock; u32 pwr_limit_max; + u8 dev_static_addr; }; -struct sbrmi_mailbox_msg { - u8 cmd; - bool read; - u32 data_in; - u32 data_out; -}; - -int rmi_mailbox_xfer(struct sbrmi_data *data, struct sbrmi_mailbox_msg *msg); +int rmi_mailbox_xfer(struct sbrmi_data *data, struct apml_mbox_msg *msg); #ifdef CONFIG_AMD_SBRMI_HWMON int create_hwmon_sensor_device(struct device *dev, struct sbrmi_data *data); #else @@ -69,4 +67,5 @@ static inline int create_hwmon_sensor_device(struct device *dev, struct sbrmi_da return 0; } #endif +int create_misc_rmi_device(struct sbrmi_data *data, struct device *dev); #endif /*_SBRMI_CORE_H_*/ diff --git a/drivers/misc/amd-sbi/rmi-hwmon.c b/drivers/misc/amd-sbi/rmi-hwmon.c index 720e800db1f0..f4f015605daa 100644 --- a/drivers/misc/amd-sbi/rmi-hwmon.c +++ b/drivers/misc/amd-sbi/rmi-hwmon.c @@ -6,6 +6,7 @@ */ #include #include +#include #include "rmi-core.h" /* Do not allow setting negative power limit */ @@ -15,7 +16,7 @@ static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { struct sbrmi_data *data = dev_get_drvdata(dev); - struct sbrmi_mailbox_msg msg = { 0 }; + struct apml_mbox_msg msg = { 0 }; int ret; if (!data) @@ -24,7 +25,6 @@ static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type, if (type != hwmon_power) return -EINVAL; - msg.read = true; switch (attr) { case hwmon_power_input: msg.cmd = SBRMI_READ_PKG_PWR_CONSUMPTION; @@ -35,7 +35,7 @@ static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type, ret = rmi_mailbox_xfer(data, &msg); break; case hwmon_power_cap_max: - msg.data_out = data->pwr_limit_max; + msg.mb_in_out = data->pwr_limit_max; ret = 0; break; default: @@ -44,7 +44,7 @@ static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type, if (ret < 0) return ret; /* hwmon power attributes are in microWatt */ - *val = (long)msg.data_out * 1000; + *val = (long)msg.mb_in_out * 1000; return ret; } @@ -52,7 +52,7 @@ static int sbrmi_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { struct sbrmi_data *data = dev_get_drvdata(dev); - struct sbrmi_mailbox_msg msg = { 0 }; + struct apml_mbox_msg msg = { 0 }; if (!data) return -ENODEV; @@ -68,8 +68,7 @@ static int sbrmi_write(struct device *dev, enum hwmon_sensor_types type, val = clamp_val(val, SBRMI_PWR_MIN, data->pwr_limit_max); msg.cmd = SBRMI_WRITE_PKG_PWR_LIMIT; - msg.data_in = val; - msg.read = false; + msg.mb_in_out = val; return rmi_mailbox_xfer(data, &msg); } diff --git a/drivers/misc/amd-sbi/rmi-i2c.c b/drivers/misc/amd-sbi/rmi-i2c.c index 7a9801273a4c..f891f5af4bc6 100644 --- a/drivers/misc/amd-sbi/rmi-i2c.c +++ b/drivers/misc/amd-sbi/rmi-i2c.c @@ -38,15 +38,14 @@ static int sbrmi_enable_alert(struct sbrmi_data *data) static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data) { - struct sbrmi_mailbox_msg msg = { 0 }; + struct apml_mbox_msg msg = { 0 }; int ret; msg.cmd = SBRMI_READ_PKG_MAX_PWR_LIMIT; - msg.read = true; ret = rmi_mailbox_xfer(data, &msg); if (ret < 0) return ret; - data->pwr_limit_max = msg.data_out; + data->pwr_limit_max = msg.mb_in_out; return ret; } @@ -81,8 +80,25 @@ static int sbrmi_i2c_probe(struct i2c_client *client) if (ret < 0) return ret; + data->dev_static_addr = client->addr; dev_set_drvdata(dev, data); - return create_hwmon_sensor_device(dev, data); + + ret = create_hwmon_sensor_device(dev, data); + if (ret < 0) + return ret; + return create_misc_rmi_device(data, dev); +} + +static void sbrmi_i2c_remove(struct i2c_client *client) +{ + struct sbrmi_data *data = dev_get_drvdata(&client->dev); + + misc_deregister(&data->sbrmi_misc_dev); + /* Assign fops and parent of misc dev to NULL */ + data->sbrmi_misc_dev.fops = NULL; + data->sbrmi_misc_dev.parent = NULL; + dev_info(&client->dev, "Removed sbrmi-i2c driver\n"); + return; } static const struct i2c_device_id sbrmi_id[] = { @@ -105,6 +121,7 @@ static struct i2c_driver sbrmi_driver = { .of_match_table = of_match_ptr(sbrmi_of_match), }, .probe = sbrmi_i2c_probe, + .remove = sbrmi_i2c_remove, .id_table = sbrmi_id, }; diff --git a/include/uapi/misc/amd-apml.h b/include/uapi/misc/amd-apml.h new file mode 100644 index 000000000000..a5f086f84b06 --- /dev/null +++ b/include/uapi/misc/amd-apml.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (C) 2021-2024 Advanced Micro Devices, Inc. + */ +#ifndef _AMD_APML_H_ +#define _AMD_APML_H_ + +#include + +/* Mailbox data size for data_in and data_out */ +#define AMD_SBI_MB_DATA_SIZE 4 + +struct apml_mbox_msg { + /* + * Mailbox Message ID + */ + __u32 cmd; + /* + * [0]...[3] mailbox 32bit input/output data + */ + __u32 mb_in_out; + /* + * Error code is returned in case of soft mailbox error + */ + __u32 fw_ret_code; +}; + +/* + * AMD sideband interface base IOCTL + */ +#define SB_BASE_IOCTL_NR 0xF9 + +/** + * DOC: SBRMI_IOCTL_MBOX_CMD + * + * @Parameters + * + * @struct apml_mbox_msg + * Pointer to the &struct apml_mbox_msg that will contain the protocol + * information + * + * @Description + * IOCTL command for APML messages using generic _IOWR + * The IOCTL provides userspace access to AMD sideband mailbox protocol + * - Mailbox message read/write(0x0~0xFF) + * - returning "-EFAULT" if none of the above + * "-EPROTOTYPE" error is returned to provide additional error details + */ +#define SBRMI_IOCTL_MBOX_CMD _IOWR(SB_BASE_IOCTL_NR, 0, struct apml_mbox_msg) + +#endif /*_AMD_APML_H_*/ -- cgit v1.2.3