Linux Hardware Monitor development
 help / color / mirror / Atom feed
From: Guenter Roeck <linux@roeck-us.net>
To: Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com>,
	linux-hwmon@vger.kernel.org, inux-kernel@vger.kernel.org
Cc: lee@kernel.org, gregkh@linuxfoundation.org, arnd@arndb.de,
	Akshay Gupta <akshay.gupta@amd.com>
Subject: Re: [PATCH 5/5] mfd/hwmon: sbrmi: Add support for APML protocols
Date: Mon, 10 Jun 2024 06:39:18 -0700	[thread overview]
Message-ID: <d057af26-85c4-4347-871f-a8a1ddab3087@roeck-us.net> (raw)
In-Reply-To: <20240530112307.3089696-6-naveenkrishna.chatradhi@amd.com>

On 5/30/24 04:23, Naveen Krishna Chatradhi wrote:
> From: Akshay Gupta <akshay.gupta@amd.com>
> 
> The present sbrmi module only support reporting power via hwmon.
> However, AMD data center range of processors support various
> system management functionality Out-of-band over
> Advanced Platform Management Link (APML).
> 
> Register a miscdevice, which creates a device /dev/sbrmiX with an IOCTL
> interface for the user space to invoke the following protocols.
>    - Mailbox read/write (already defined in sbrmi_mailbox_xfer())
>    - CPUID read
>    - MCAMSR read
> 
> Open-sourced and widely used https://github.com/amd/esmi_oob_library
> will continue to provide user-space programmable API.
> 
> Signed-off-by: Akshay Gupta <Akshay.Gupta@amd.com>
> Reviewed-by: Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com>
> ---
>   drivers/hwmon/sbrmi.c         |  43 ++--
>   drivers/mfd/sbrmi-core.c      | 461 +++++++++++++++++++++++++++++-----
>   drivers/mfd/sbrmi-core.h      |  36 +++
>   drivers/mfd/sbrmi-i2c.c       |  81 ++++--
>   include/linux/mfd/amd-sb.h    |  32 ++-
>   include/uapi/linux/amd-apml.h |  74 ++++++
>   6 files changed, 615 insertions(+), 112 deletions(-)
>   create mode 100644 drivers/mfd/sbrmi-core.h
>   create mode 100644 include/uapi/linux/amd-apml.h
> 
> diff --git a/drivers/hwmon/sbrmi.c b/drivers/hwmon/sbrmi.c
> index aaeb5050eb0c..a8bf9aea52f9 100644
> --- a/drivers/hwmon/sbrmi.c
> +++ b/drivers/hwmon/sbrmi.c
> @@ -19,42 +19,46 @@
>   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_sbrmi_device *rmi_dev = dev_get_drvdata(dev);
> +	struct apml_message msg = { 0 };
>   	int ret;
>   
>   	if (type != hwmon_power)
>   		return -EINVAL;
>   
> -	msg.read = true;
> +	mutex_lock(&rmi_dev->lock);
> +	msg.data_in.reg_in[RD_FLAG_INDEX] = 1;
>   	switch (attr) {
>   	case hwmon_power_input:
>   		msg.cmd = SBRMI_READ_PKG_PWR_CONSUMPTION;
> -		ret = rmi_mailbox_xfer(data, &msg);
> +		ret = rmi_mailbox_xfer(rmi_dev, &msg);
>   		break;
>   	case hwmon_power_cap:
>   		msg.cmd = SBRMI_READ_PKG_PWR_LIMIT;
> -		ret = rmi_mailbox_xfer(data, &msg);
> +		ret = rmi_mailbox_xfer(rmi_dev, &msg);
>   		break;
>   	case hwmon_power_cap_max:
> -		msg.data_out = data->pwr_limit_max;
>   		ret = 0;
> +		msg.data_out.mb_out[RD_WR_DATA_INDEX] = rmi_dev->pwr_limit_max;
>   		break;
>   	default:
> -		return -EINVAL;
> +		ret = -EINVAL;
>   	}
> -	if (ret < 0)
> -		return ret;
> +
>   	/* hwmon power attributes are in microWatt */
> -	*val = (long)msg.data_out * 1000;
> +	if (!ret)
> +		*val = (long)msg.data_out.mb_out[RD_WR_DATA_INDEX] * 1000;
> +
> +	mutex_unlock(&rmi_dev->lock);

For both the read and write function, there is only a single call into the rmi
code. I don't see why it would be necessary to move mutex protection out of
rmi_mailbox_xfer() and into the hwmon subsystem. IMO, from a hwmon perspective,
this change is unnecessary. If rmi_mailbox_xfer() needs a protected and an
unprotected version, both should be provided by the rmi core, and the hwmon
driver should continue calling the function which handles the mutex internally.

Thanks,
Guenter

>   	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 };
> +	struct apml_sbrmi_device *rmi_dev = dev_get_drvdata(dev);
> +	struct apml_message msg = { 0 };
> +	int ret;
>   
>   	if (type != hwmon_power && attr != hwmon_power_cap)
>   		return -EINVAL;
> @@ -64,13 +68,16 @@ static int sbrmi_write(struct device *dev, enum hwmon_sensor_types type,
>   	 */
>   	val /= 1000;
>   
> -	val = clamp_val(val, SBRMI_PWR_MIN, data->pwr_limit_max);
> +	val = clamp_val(val, SBRMI_PWR_MIN, rmi_dev->pwr_limit_max);
>   
>   	msg.cmd = SBRMI_WRITE_PKG_PWR_LIMIT;
> -	msg.data_in = val;
> -	msg.read = false;
> +	msg.data_in.mb_in[RD_WR_DATA_INDEX] = val;
> +	msg.data_in.reg_in[RD_FLAG_INDEX] = 0;
>   
> -	return rmi_mailbox_xfer(data, &msg);
> +	mutex_lock(&rmi_dev->lock);
> +	ret = rmi_mailbox_xfer(rmi_dev, &msg);
> +	mutex_unlock(&rmi_dev->lock);
> +	return ret;
>   }
>   
>   static umode_t sbrmi_is_visible(const void *data,
> @@ -114,9 +121,9 @@ static int sbrmi_probe(struct platform_device *pdev)
>   {
>   	struct device *dev = &pdev->dev;
>   	struct device *hwmon_dev;
> -	struct sbrmi_data *data  = dev_get_drvdata(pdev->dev.parent);
> +	struct apml_sbrmi_device *rmi_dev  = dev_get_drvdata(pdev->dev.parent);
>   
> -	hwmon_dev = devm_hwmon_device_register_with_info(dev, "sbrmi", data,
> +	hwmon_dev = devm_hwmon_device_register_with_info(dev, "sbrmi", rmi_dev,
>   							 &sbrmi_chip_info, NULL);
>   	return PTR_ERR_OR_ZERO(hwmon_dev);
>   }
> diff --git a/drivers/mfd/sbrmi-core.c b/drivers/mfd/sbrmi-core.c
> index d5fbe5676cab..05d6c68d78b6 100644
> --- a/drivers/mfd/sbrmi-core.c
> +++ b/drivers/mfd/sbrmi-core.c
> @@ -7,45 +7,271 @@
>    */
>   #include <linux/delay.h>
>   #include <linux/err.h>
> +#include <linux/fs.h>
>   #include <linux/mfd/amd-sb.h>
> +#include <linux/miscdevice.h>
> +#include <linux/module.h>
>   #include <linux/mutex.h>
>   #include <linux/regmap.h>
> +#include "sbrmi-core.h"
>   
>   /* Mask for Status Register bit[1] */
>   #define SW_ALERT_MASK	0x2
> +/* Mask to check H/W Alert status bit */
> +#define HW_ALERT_MASK	0x80
>   
>   /* Software Interrupt for triggering */
>   #define START_CMD	0x80
>   #define TRIGGER_MAILBOX	0x01
>   
> -/* SB-RMI registers */
> -enum sbrmi_reg {
> -	SBRMI_CTRL		= 0x01,
> -	SBRMI_STATUS,
> -	SBRMI_OUTBNDMSG0	= 0x30,
> -	SBRMI_OUTBNDMSG1,
> -	SBRMI_OUTBNDMSG2,
> -	SBRMI_OUTBNDMSG3,
> -	SBRMI_OUTBNDMSG4,
> -	SBRMI_OUTBNDMSG5,
> -	SBRMI_OUTBNDMSG6,
> -	SBRMI_OUTBNDMSG7,
> -	SBRMI_INBNDMSG0,
> -	SBRMI_INBNDMSG1,
> -	SBRMI_INBNDMSG2,
> -	SBRMI_INBNDMSG3,
> -	SBRMI_INBNDMSG4,
> -	SBRMI_INBNDMSG5,
> -	SBRMI_INBNDMSG6,
> -	SBRMI_INBNDMSG7,
> -	SBRMI_SW_INTERRUPT,
> -};
> +/* Default message lengths as per APML command protocol */
> +/* MSR */
> +#define MSR_RD_REG_LEN		0xa
> +#define MSR_WR_REG_LEN		0x8
> +#define MSR_RD_DATA_LEN		0x8
> +#define MSR_WR_DATA_LEN		0x7
> +/* CPUID */
> +#define CPUID_RD_DATA_LEN	0x8
> +#define CPUID_WR_DATA_LEN	0x8
> +#define CPUID_RD_REG_LEN	0xa
> +#define CPUID_WR_REG_LEN	0x9
> +
> +/* CPUID MSR Command Ids */
> +#define CPUID_MCA_CMD	0x73
> +#define RD_CPUID_CMD	0x91
> +#define RD_MCA_CMD	0x86
> +
> +/* input for bulk write to CPUID and MSR protocol */
> +struct cpu_msr_indata {
> +	u8 wr_len;	/* const value */
> +	u8 rd_len;	/* const value */
> +	u8 proto_cmd;	/* const value */
> +	u8 thread;	/* thread number */
> +	union {
> +		u8 reg_offset[4];	/* input value */
> +		u32 value;
> +	};
> +	u8 ext; /* extended function */
> +} __packed;
> +
> +/* output for bulk read from CPUID and MSR protocol */
> +struct cpu_msr_outdata {
> +	u8 num_bytes;	/* number of bytes return */
> +	u8 status;	/* Protocol status code */
> +	union {
> +		u64 value;
> +		u8 reg_data[8];
> +	};
> +} __packed;
> +
> +#define prepare_mca_msr_input_message(input, thread_id, data_in)	\
> +	input.rd_len = MSR_RD_DATA_LEN,					\
> +	input.wr_len = MSR_WR_DATA_LEN,					\
> +	input.proto_cmd = RD_MCA_CMD,					\
> +	input.thread = thread_id << 1,					\
> +	input.value =  data_in
> +
> +#define prepare_cpuid_input_message(input, thread_id, func, ext_func)	\
> +	input.rd_len = CPUID_RD_DATA_LEN,				\
> +	input.wr_len = CPUID_WR_DATA_LEN,				\
> +	input.proto_cmd = RD_CPUID_CMD,					\
> +	input.thread = thread_id << 1,					\
> +	input.value =  func,						\
> +	input.ext =  ext_func
> +
> +static int sbrmi_get_rev(struct apml_sbrmi_device *rmi_dev)
> +{
> +	struct apml_message msg = { 0 };
> +	int ret;
> +
> +	msg.data_in.reg_in[REG_OFF_INDEX] = SBRMI_REV;
> +	msg.data_in.reg_in[RD_FLAG_INDEX] = 1;
> +	ret = regmap_read(rmi_dev->regmap,
> +			  msg.data_in.reg_in[REG_OFF_INDEX],
> +			  &msg.data_out.mb_out[RD_WR_DATA_INDEX]);
> +	if (ret < 0)
> +		return ret;
> +
> +	rmi_dev->rev = msg.data_out.reg_out[RD_WR_DATA_INDEX];
> +	return 0;
> +}
> +
> +/*
> + * For Mailbox command software alert status bit is set by firmware
> + * to indicate command completion
> + * For RMI Rev 0x20, new h/w status bit is introduced. which is used
> + * by firmware to indicate completion of commands (0x71, 0x72, 0x73).
> + * wait for the status bit to be set by the firmware before
> + * reading the data out.
> + */
> +static int sbrmi_wait_status(struct apml_sbrmi_device *rmi_dev,
> +			     int *status, int mask)
> +{
> +	int ret, retry = 100;
> +
> +	do {
> +		ret = regmap_read(rmi_dev->regmap, SBRMI_STATUS, status);
> +		if (ret < 0)
> +			return ret;
> +
> +		if (*status & mask)
> +			break;
> +
> +		/* Wait 1~2 second for firmware to return data out */
> +		if (retry > 95)
> +			usleep_range(50, 100);
> +		else
> +			usleep_range(10000, 20000);
> +	} while (retry--);
> +
> +	if (retry < 0)
> +		ret = -ETIMEDOUT;
> +	return ret;
> +}
> +
> +/* MCA MSR protocol */
> +static int rmi_mca_msr_read(struct apml_sbrmi_device *rmi_dev,
> +			    struct apml_message *msg)
> +{
> +	struct cpu_msr_outdata output = {0};
> +	struct cpu_msr_indata input = {0};
> +	int ret, val = 0;
> +	int hw_status;
> +	u16 thread;
> +
> +	/* cache the rev value to identify if protocol is supported or not */
> +	if (!rmi_dev->rev) {
> +		ret = sbrmi_get_rev(rmi_dev);
> +		if (ret < 0)
> +			return ret;
> +	}
> +	/* MCA MSR protocol for REV 0x10 is not supported*/
> +	if (rmi_dev->rev == 0x10)
> +		return -EOPNOTSUPP;
> +
> +	thread = msg->data_in.reg_in[THREAD_LOW_INDEX] |
> +		 msg->data_in.reg_in[THREAD_HI_INDEX] << 8;
> +
> +	/* Thread > 127, Thread128 CS register, 1'b1 needs to be set to 1 */
> +	if (thread > 127) {
> +		thread -= 128;
> +		val = 1;
> +	}
> +	ret = regmap_write(rmi_dev->regmap, SBRMI_THREAD128CS, val);
> +	if (ret < 0)
> +		goto exit_unlock;
> +
> +	prepare_mca_msr_input_message(input, thread,
> +				      msg->data_in.mb_in[RD_WR_DATA_INDEX]);
> +
> +	ret = regmap_bulk_write(rmi_dev->regmap, CPUID_MCA_CMD,
> +				&input, MSR_WR_REG_LEN);
> +	if (ret < 0)
> +		goto exit_unlock;
> +
> +	ret = sbrmi_wait_status(rmi_dev, &hw_status, HW_ALERT_MASK);
> +	if (ret < 0)
> +		goto exit_unlock;
> +
> +	ret = regmap_bulk_read(rmi_dev->regmap, CPUID_MCA_CMD,
> +			       &output, MSR_RD_REG_LEN);
> +	if (ret < 0)
> +		goto exit_unlock;
> +
> +	ret = regmap_write(rmi_dev->regmap, SBRMI_STATUS,
> +			   HW_ALERT_MASK);
> +	if (ret < 0)
> +		goto exit_unlock;
> +
> +	if (output.num_bytes != MSR_RD_REG_LEN - 1) {
> +		ret = -EMSGSIZE;
> +		goto exit_unlock;
> +	}
> +	if (output.status) {
> +		ret = -EPROTOTYPE;
> +		msg->fw_ret_code = output.status;
> +		goto exit_unlock;
> +	}
> +	msg->data_out.cpu_msr_out = output.value;
> +
> +exit_unlock:
> +	return ret;
> +}
> +
> +/* Read CPUID function protocol */
> +static int rmi_cpuid_read(struct apml_sbrmi_device *rmi_dev,
> +			  struct apml_message *msg)
> +{
> +	struct cpu_msr_indata input = {0};
> +	struct cpu_msr_outdata output = {0};
> +	int val = 0;
> +	int ret, hw_status;
> +	u16 thread;
> +
> +	/* cache the rev value to identify if protocol is supported or not */
> +	if (!rmi_dev->rev) {
> +		ret = sbrmi_get_rev(rmi_dev);
> +		if (ret < 0)
> +			return ret;
> +	}
> +	/* CPUID protocol for REV 0x10 is not supported*/
> +	if (rmi_dev->rev == 0x10)
> +		return -EOPNOTSUPP;
> +
> +	thread = msg->data_in.reg_in[THREAD_LOW_INDEX] |
> +		 msg->data_in.reg_in[THREAD_HI_INDEX] << 8;
> +
> +	/* Thread > 127, Thread128 CS register, 1'b1 needs to be set to 1 */
> +	if (thread > 127) {
> +		thread -= 128;
> +		val = 1;
> +	}
> +	ret = regmap_write(rmi_dev->regmap, SBRMI_THREAD128CS, val);
> +	if (ret < 0)
> +		goto exit_unlock;
> +
> +	prepare_cpuid_input_message(input, thread,
> +				    msg->data_in.mb_in[RD_WR_DATA_INDEX],
> +				    msg->data_in.reg_in[EXT_FUNC_INDEX]);
> +
> +	ret = regmap_bulk_write(rmi_dev->regmap, CPUID_MCA_CMD,
> +				&input, CPUID_WR_REG_LEN);
> +	if (ret < 0)
> +		goto exit_unlock;
> +
> +	ret = sbrmi_wait_status(rmi_dev, &hw_status, HW_ALERT_MASK);
> +	if (ret < 0)
> +		goto exit_unlock;
> +
> +	ret = regmap_bulk_read(rmi_dev->regmap, CPUID_MCA_CMD,
> +			       &output, CPUID_RD_REG_LEN);
> +	if (ret < 0)
> +		goto exit_unlock;
> +
> +	ret = regmap_write(rmi_dev->regmap, SBRMI_STATUS,
> +			   HW_ALERT_MASK);
> +	if (ret < 0)
> +		goto exit_unlock;
> +
> +	if (output.num_bytes != CPUID_RD_REG_LEN - 1) {
> +		ret = -EMSGSIZE;
> +		goto exit_unlock;
> +	}
> +	if (output.status) {
> +		ret = -EPROTOTYPE;
> +		msg->fw_ret_code = output.status;
> +		goto exit_unlock;
> +	}
> +	msg->data_out.cpu_msr_out = output.value;
> +exit_unlock:
> +	return ret;
> +}
>   
> -static int sbrmi_clear_status_alert(struct sbrmi_data *data)
> +static int sbrmi_clear_status_alert(struct apml_sbrmi_device *rmi_dev)
>   {
>   	int sw_status, ret;
>   
> -	ret = regmap_read(data->regmap, SBRMI_STATUS,
> +	ret = regmap_read(rmi_dev->regmap, SBRMI_STATUS,
>   			  &sw_status);
>   	if (ret < 0)
>   		return ret;
> @@ -53,31 +279,31 @@ static int sbrmi_clear_status_alert(struct sbrmi_data *data)
>   	if (!(sw_status & SW_ALERT_MASK))
>   		return 0;
>   
> -	return regmap_write(data->regmap, SBRMI_STATUS,
> +	return regmap_write(rmi_dev->regmap, SBRMI_STATUS,
>   			    SW_ALERT_MASK);
>   }
>   
> -int rmi_mailbox_xfer(struct sbrmi_data *data,
> -		     struct sbrmi_mailbox_msg *msg)
> +int rmi_mailbox_xfer(struct apml_sbrmi_device *rmi_dev,
> +		     struct apml_message *msg)
>   {
> -	unsigned int bytes;
> -	int i, ret, retry = 10;
> +	unsigned int bytes, ec;
> +	int i, ret;
>   	int sw_status;
>   	u8 byte;
>   
> -	mutex_lock(&data->lock);
> +	msg->fw_ret_code = 0;
>   
> -	ret = sbrmi_clear_status_alert(data);
> +	ret = sbrmi_clear_status_alert(rmi_dev);
>   	if (ret < 0)
>   		goto exit_unlock;
>   
>   	/* Indicate firmware a command is to be serviced */
> -	ret = regmap_write(data->regmap, SBRMI_INBNDMSG7, START_CMD);
> +	ret = regmap_write(rmi_dev->regmap, SBRMI_INBNDMSG7, START_CMD);
>   	if (ret < 0)
>   		goto exit_unlock;
>   
>   	/* Write the command to SBRMI::InBndMsg_inst0 */
> -	ret = regmap_write(data->regmap, SBRMI_INBNDMSG0, msg->cmd);
> +	ret = regmap_write(rmi_dev->regmap, SBRMI_INBNDMSG0, msg->cmd);
>   	if (ret < 0)
>   		goto exit_unlock;
>   
> @@ -86,9 +312,9 @@ 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;
> -		ret = regmap_write(data->regmap, SBRMI_INBNDMSG1 + i, byte);
> +	for (i = 0; i < MB_DATA_SIZE; i++) {
> +		byte = msg->data_in.reg_in[i];
> +		ret = regmap_write(rmi_dev->regmap, SBRMI_INBNDMSG1 + i, byte);
>   		if (ret < 0)
>   			goto exit_unlock;
>   	}
> @@ -97,7 +323,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 = regmap_write(data->regmap, SBRMI_SW_INTERRUPT, TRIGGER_MAILBOX);
> +	ret = regmap_write(rmi_dev->regmap, SBRMI_SW_INTERRUPT, TRIGGER_MAILBOX);
>   	if (ret < 0)
>   		goto exit_unlock;
>   
> @@ -106,46 +332,159 @@ int rmi_mailbox_xfer(struct sbrmi_data *data,
>   	 * an ALERT (if enabled) to initiator (BMC) to indicate completion
>   	 * of the requested command
>   	 */
> -	do {
> -		ret = regmap_read(data->regmap, SBRMI_STATUS, &sw_status);
> -		if (sw_status < 0) {
> -			ret = sw_status;
> -			goto exit_unlock;
> -		}
> -		if (sw_status & SW_ALERT_MASK)
> -			break;
> -		usleep_range(50, 100);
> -	} while (retry--);
> -
> -	if (retry < 0) {
> -		ret = -EIO;
> +	ret = sbrmi_wait_status(rmi_dev, &sw_status, SW_ALERT_MASK);
> +	if (ret < 0)
>   		goto exit_unlock;
> -	}
> +
> +	ret = regmap_read(rmi_dev->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,
> +	if (msg->data_in.reg_in[RD_FLAG_INDEX]) {
> +		for (i = 0; i < MB_DATA_SIZE; i++) {
> +			ret = regmap_read(rmi_dev->regmap,
>   					  SBRMI_OUTBNDMSG1 + i, &bytes);
>   			if (ret < 0)
> -				goto exit_unlock;
> -			msg->data_out |= bytes << i * 8;
> +				break;
> +			msg->data_out.reg_out[i] = bytes;
>   		}
>   	}
> -
> +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);
> -
> +	ret = regmap_write(rmi_dev->regmap, SBRMI_STATUS,
> +			   SW_ALERT_MASK);
> +	if (ec) {
> +		ret = -EPROTOTYPE;
> +		msg->fw_ret_code = ec;
> +	}
>   exit_unlock:
> -	mutex_unlock(&data->lock);
>   	return ret;
>   }
>   EXPORT_SYMBOL(rmi_mailbox_xfer);
> +
> +static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
> +{
> +	int __user *arguser = (int  __user *)arg;
> +	struct apml_message msg = { 0 };
> +	bool read = false;
> +	int ret = -EFAULT;
> +
> +	struct apml_sbrmi_device *rmi_dev = container_of(fp->private_data, struct apml_sbrmi_device,
> +							 sbrmi_misc_dev);
> +	if (!rmi_dev)
> +		return -ENODEV;
> +
> +	/*
> +	 * If device remove/unbind is called do not allow new transaction
> +	 */
> +	if (atomic_read(&rmi_dev->no_new_trans))
> +		return -EBUSY;
> +	/* Copy the structure from user */
> +	if (copy_struct_from_user(&msg, sizeof(msg), arguser,
> +				  sizeof(struct apml_message)))
> +		return ret;
> +
> +	/*
> +	 * Only one I2C transaction can happen at
> +	 * one time. Take lock across so no two protocol is
> +	 * invoked at same time, modifying the register value.
> +	 */
> +	mutex_lock(&rmi_dev->lock);
> +	/* Verify device unbind/remove is not invoked */
> +	if (atomic_read(&rmi_dev->no_new_trans)) {
> +		mutex_unlock(&rmi_dev->lock);
> +		return -EBUSY;
> +	}
> +
> +	/* Is this a read/monitor/get request */
> +	if (msg.data_in.reg_in[RD_FLAG_INDEX])
> +		read = true;
> +
> +	/*
> +	 * Set the in_progress variable to true, to wait for
> +	 * completion during unbind/remove of driver
> +	 */
> +	atomic_set(&rmi_dev->in_progress, 1);
> +
> +	switch (msg.cmd) {
> +	case 0 ... 0x999:
> +		/* Mailbox protocol */
> +		ret = rmi_mailbox_xfer(rmi_dev, &msg);
> +		break;
> +	case APML_CPUID:
> +		ret = rmi_cpuid_read(rmi_dev, &msg);
> +		break;
> +	case APML_MCA_MSR:
> +		/* MCAMSR protocol */
> +		ret = rmi_mca_msr_read(rmi_dev, &msg);
> +		break;
> +	case APML_REG:
> +		/* REG R/W */
> +		if (read) {
> +			ret = regmap_read(rmi_dev->regmap,
> +					  msg.data_in.reg_in[REG_OFF_INDEX],
> +					  &msg.data_out.mb_out[RD_WR_DATA_INDEX]);
> +		} else {
> +			ret = regmap_write(rmi_dev->regmap,
> +					   msg.data_in.reg_in[REG_OFF_INDEX],
> +					   msg.data_in.reg_in[REG_VAL_INDEX]);
> +		}
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	/* Send complete only if device is unbinded/remove */
> +	if (atomic_read(&rmi_dev->no_new_trans))
> +		complete(&rmi_dev->misc_fops_done);
> +
> +	atomic_set(&rmi_dev->in_progress, 0);
> +	mutex_unlock(&rmi_dev->lock);
> +
> +	/* Copy results back to user only for get/monitor commands and firmware failures */
> +	if ((read && !ret) || ret == -EPROTOTYPE) {
> +		if (copy_to_user(arguser, &msg, sizeof(struct apml_message)))
> +			ret = -EFAULT;
> +	}
> +	return ret;
> +}
> +
> +static const struct file_operations sbrmi_fops = {
> +	.owner		= THIS_MODULE,
> +	.unlocked_ioctl	= sbrmi_ioctl,
> +	.compat_ioctl	= sbrmi_ioctl,
> +};
> +
> +int create_misc_rmi_device(struct apml_sbrmi_device *rmi_dev,
> +			   struct device *dev)
> +{
> +	int ret;
> +
> +	rmi_dev->sbrmi_misc_dev.name		= devm_kasprintf(dev,
> +								 GFP_KERNEL,
> +								 "sbrmi-%x",
> +								 rmi_dev->dev_static_addr);
> +	rmi_dev->sbrmi_misc_dev.minor		= MISC_DYNAMIC_MINOR;
> +	rmi_dev->sbrmi_misc_dev.fops		= &sbrmi_fops;
> +	rmi_dev->sbrmi_misc_dev.parent		= dev;
> +	rmi_dev->sbrmi_misc_dev.nodename	= devm_kasprintf(dev,
> +								 GFP_KERNEL,
> +								 "sbrmi-%x",
> +								 rmi_dev->dev_static_addr);
> +	rmi_dev->sbrmi_misc_dev.mode		= 0600;
> +
> +	ret = misc_register(&rmi_dev->sbrmi_misc_dev);
> +	if (ret)
> +		return ret;
> +
> +	dev_info(dev, "register %s device\n", rmi_dev->sbrmi_misc_dev.name);
> +	return ret;
> +}
> diff --git a/drivers/mfd/sbrmi-core.h b/drivers/mfd/sbrmi-core.h
> new file mode 100644
> index 000000000000..6339931d4eb3
> --- /dev/null
> +++ b/drivers/mfd/sbrmi-core.h
> @@ -0,0 +1,36 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2021-2024 Advanced Micro Devices, Inc.
> + */
> +
> +#ifndef _SBRMI_CORE__H_
> +#define _SBRMI_CORE__H_
> +
> +/* SB-RMI registers */
> +enum sbrmi_reg {
> +	SBRMI_REV		= 0x00,
> +	SBRMI_CTRL,
> +	SBRMI_STATUS,
> +	SBRMI_OUTBNDMSG0	= 0x30,
> +	SBRMI_OUTBNDMSG1,
> +	SBRMI_OUTBNDMSG2,
> +	SBRMI_OUTBNDMSG3,
> +	SBRMI_OUTBNDMSG4,
> +	SBRMI_OUTBNDMSG5,
> +	SBRMI_OUTBNDMSG6,
> +	SBRMI_OUTBNDMSG7,
> +	SBRMI_INBNDMSG0,
> +	SBRMI_INBNDMSG1,
> +	SBRMI_INBNDMSG2,
> +	SBRMI_INBNDMSG3,
> +	SBRMI_INBNDMSG4,
> +	SBRMI_INBNDMSG5,
> +	SBRMI_INBNDMSG6,
> +	SBRMI_INBNDMSG7,
> +	SBRMI_SW_INTERRUPT,
> +	SBRMI_THREAD128CS	= 0x4b,
> +};
> +
> +int create_misc_rmi_device(struct apml_sbrmi_device *rmi_dev,
> +			   struct device *dev);
> +#endif /*_SBRMI_CORE__H_*/
> diff --git a/drivers/mfd/sbrmi-i2c.c b/drivers/mfd/sbrmi-i2c.c
> index bdf15a7a2167..a8dcb7a92af8 100644
> --- a/drivers/mfd/sbrmi-i2c.c
> +++ b/drivers/mfd/sbrmi-i2c.c
> @@ -12,18 +12,18 @@
>   #include <linux/init.h>
>   #include <linux/mfd/core.h>
>   #include <linux/mfd/amd-sb.h>
> -#include <linux/module.h>
>   #include <linux/mutex.h>
>   #include <linux/of.h>
>   #include <linux/regmap.h>
> +#include "sbrmi-core.h"
>   
> -#define SBRMI_CTRL	0x1
> +#define MAX_WAIT_TIME_SEC	(3)
>   
>   static struct mfd_cell apml_sbrmi[] = {
>   	{ .name = "sbrmi-hwmon" },
>   };
>   
> -static int sbrmi_enable_alert(struct sbrmi_data *data)
> +static int sbrmi_enable_alert(struct apml_sbrmi_device *rmi_dev)
>   {
>   	int ctrl, ret;
>   
> @@ -31,29 +31,29 @@ static int sbrmi_enable_alert(struct sbrmi_data *data)
>   	 * Enable the SB-RMI Software alert status
>   	 * by writing 0 to bit 4 of Control register(0x1)
>   	 */
> -	ret = regmap_read(data->regmap, SBRMI_CTRL, &ctrl);
> +	ret = regmap_read(rmi_dev->regmap, SBRMI_CTRL, &ctrl);
>   	if (ret < 0)
>   		return ret;
>   
>   	if (ctrl & 0x10) {
>   		ctrl &= ~0x10;
> -		return regmap_write(data->regmap, SBRMI_CTRL, ctrl);
> +		return regmap_write(rmi_dev->regmap, SBRMI_CTRL, ctrl);
>   	}
>   
>   	return 0;
>   }
>   
> -static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data)
> +static int sbrmi_get_max_pwr_limit(struct apml_sbrmi_device *rmi_dev)
>   {
> -	struct sbrmi_mailbox_msg msg = { 0 };
> +	struct apml_message msg = { 0 };
>   	int ret;
>   
>   	msg.cmd = SBRMI_READ_PKG_MAX_PWR_LIMIT;
> -	msg.read = true;
> -	ret = rmi_mailbox_xfer(data, &msg);
> +	msg.data_in.reg_in[RD_FLAG_INDEX] = 1;
> +	ret = rmi_mailbox_xfer(rmi_dev, &msg);
>   	if (ret < 0)
>   		return ret;
> -	data->pwr_limit_max = msg.data_out;
> +	rmi_dev->pwr_limit_max = msg.data_out.mb_out[RD_WR_DATA_INDEX];
>   
>   	return ret;
>   }
> @@ -61,40 +61,76 @@ 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 sbrmi_data *data;
> +	struct apml_sbrmi_device *rmi_dev;
>   	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)
> +	rmi_dev = devm_kzalloc(dev, sizeof(struct apml_sbrmi_device), GFP_KERNEL);
> +	if (!rmi_dev)
>   		return -ENOMEM;
>   
> -	mutex_init(&data->lock);
> +	atomic_set(&rmi_dev->in_progress, 0);
> +	atomic_set(&rmi_dev->no_new_trans, 0);
> +	mutex_init(&rmi_dev->lock);
>   
> -	data->regmap = devm_regmap_init_i2c(client, &sbrmi_i2c_regmap_config);
> -	if (IS_ERR(data->regmap))
> -		return PTR_ERR(data->regmap);
> +	rmi_dev->regmap = devm_regmap_init_i2c(client, &sbrmi_i2c_regmap_config);
> +	if (IS_ERR(rmi_dev->regmap))
> +		return PTR_ERR(rmi_dev->regmap);
>   
>   	/* Enable alert for SB-RMI sequence */
> -	ret = sbrmi_enable_alert(data);
> +	ret = sbrmi_enable_alert(rmi_dev);
>   	if (ret < 0)
>   		return ret;
>   
>   	/* Cache maximum power limit */
> -	ret = sbrmi_get_max_pwr_limit(data);
> +	ret = sbrmi_get_max_pwr_limit(rmi_dev);
>   	if (ret < 0)
>   		return ret;
>   
> -	dev_set_drvdata(dev, (void *)data);
> +	dev_set_drvdata(dev, (void *)rmi_dev);
>   
>   	ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, apml_sbrmi,
>   				   ARRAY_SIZE(apml_sbrmi), NULL, 0, NULL);
> -	if (ret)
> +	if (ret) {
>   		dev_err(dev, "Failed to register for sub-devices: %d\n", ret);
> -	return ret;
> +		return ret;
> +	}
> +
> +	rmi_dev->dev_static_addr = client->addr;
> +	init_completion(&rmi_dev->misc_fops_done);
> +	return create_misc_rmi_device(rmi_dev, dev);
> +}
> +
> +static void sbrmi_i2c_remove(struct i2c_client *client)
> +{
> +	struct apml_sbrmi_device *rmi_dev = dev_get_drvdata(&client->dev);
> +
> +	if (!rmi_dev)
> +		return;
> +	/*
> +	 * Set the no_new_trans so no new transaction can
> +	 * occur in sbrmi_ioctl
> +	 */
> +	atomic_set(&rmi_dev->no_new_trans, 1);
> +	/*
> +	 * If any transaction is in progress wait for the
> +	 * transaction to get complete
> +	 * Max wait is 3 sec for any pending transaction to
> +	 * complete.
> +	 */
> +	if (atomic_read(&rmi_dev->in_progress))
> +		wait_for_completion_timeout(&rmi_dev->misc_fops_done,
> +					    MAX_WAIT_TIME_SEC * HZ);
> +	misc_deregister(&rmi_dev->sbrmi_misc_dev);
> +	/* Assign fops and parent of misc dev to NULL */
> +	rmi_dev->sbrmi_misc_dev.fops = NULL;
> +	rmi_dev->sbrmi_misc_dev.parent = NULL;
> +	dev_info(&client->dev, "Removed sbrmi driver\n");
> +
> +	return;
>   }
>   
>   static const struct i2c_device_id sbrmi_id[] = {
> @@ -117,6 +153,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/linux/mfd/amd-sb.h b/include/linux/mfd/amd-sb.h
> index 977b8228ffa1..7bb5b89bb71a 100644
> --- a/include/linux/mfd/amd-sb.h
> +++ b/include/linux/mfd/amd-sb.h
> @@ -6,8 +6,10 @@
>   #ifndef _AMD_SB_H_
>   #define _AMD_SB_H_
>   
> +#include <linux/miscdevice.h>
>   #include <linux/mutex.h>
>   #include <linux/regmap.h>
> +#include <uapi/linux/amd-apml.h>
>   /*
>    * SB-RMI supports soft mailbox service request to MP1 (power management
>    * firmware) through SBRMI inbound/outbound message registers.
> @@ -20,24 +22,32 @@ enum sbrmi_msg_id {
>   	SBRMI_READ_PKG_MAX_PWR_LIMIT,
>   };
>   
> -/* Each client has this additional data */
> -struct sbrmi_data {
> +/*
> + * Each client has this additional data
> + * in_progress: set during any transaction, mailbox/cpuid/mcamsr/readreg,
> + * to indicate a transaction is in progress.
> + * no_new_trans: set in rmmod/unbind path to indicate,
> + * not to accept new transactions
> + */
> +struct apml_sbrmi_device {
> +	struct completion misc_fops_done;
> +	struct miscdevice sbrmi_misc_dev;
>   	struct regmap *regmap;
> +	/* Mutex locking */
>   	struct mutex lock;
> +	atomic_t in_progress;
> +	atomic_t no_new_trans;
>   	u32 pwr_limit_max;
> +	u8 dev_static_addr;
> +	u8 rev;
>   } __packed;
>   
> -struct sbrmi_mailbox_msg {
> -	u8 cmd;
> -	bool read;
> -	u32 data_in;
> -	u32 data_out;
> -};
> -
>   #if IS_ENABLED(CONFIG_MFD_SBRMI_I2C)
> -int rmi_mailbox_xfer(struct sbrmi_data *data, struct sbrmi_mailbox_msg *msg);
> +int rmi_mailbox_xfer(struct apml_sbrmi_device *rmi_dev,
> +		     struct apml_message *msg);
>   #else
> -int rmi_mailbox_xfer(struct sbrmi_data *data, struct sbrmi_mailbox_msg *msg)
> +int rmi_mailbox_xfer(struct apml_sbrmi_device *rmi_dev,
> +		     struct apml_message *msg)
>   {
>   	return -EOPNOTSUPP;
>   }
> diff --git a/include/uapi/linux/amd-apml.h b/include/uapi/linux/amd-apml.h
> new file mode 100644
> index 000000000000..11d34ee83b87
> --- /dev/null
> +++ b/include/uapi/linux/amd-apml.h
> @@ -0,0 +1,74 @@
> +/* 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 <linux/types.h>
> +
> +enum apml_protocol {
> +	APML_CPUID	= 0x1000,
> +	APML_MCA_MSR,
> +	APML_REG,
> +};
> +
> +/* These are byte indexes into data_in and data_out arrays */
> +#define RD_WR_DATA_INDEX	0
> +#define REG_OFF_INDEX		0
> +#define REG_VAL_INDEX		4
> +#define THREAD_LOW_INDEX	4
> +#define THREAD_HI_INDEX		5
> +#define EXT_FUNC_INDEX		6
> +#define RD_FLAG_INDEX		7
> +
> +#define MB_DATA_SIZE		4
> +
> +struct apml_message {
> +	/* message ids:
> +	 * Mailbox Messages:	0x0 ... 0x999
> +	 * APML_CPUID:		0x1000
> +	 * APML_MCA_MSR:	0x1001
> +	 * APML_REG:		0x1002
> +	 */
> +	__u32 cmd;
> +
> +	/*
> +	 * 8 bit data for reg read,
> +	 * 32 bit data in case of mailbox,
> +	 * up to 64 bit in case of cpuid and mca msr
> +	 */
> +	union {
> +		__u64 cpu_msr_out;
> +		__u32 mb_out[2];
> +		__u8 reg_out[8];
> +	} data_out;
> +
> +	/*
> +	 * [0]...[3] mailbox 32bit input
> +	 *	     cpuid & mca msr,
> +	 *	     rmi rd/wr: reg_offset
> +	 * [4][5] cpuid & mca msr: thread
> +	 * [4] rmi reg wr: value
> +	 * [6] cpuid: ext function & read eax/ebx or ecx/edx
> +	 *	[7:0] -> bits [7:4] -> ext function &
> +	 *	bit [0] read eax/ebx or ecx/edx
> +	 * [7] read/write functionality
> +	 */
> +	union {
> +		__u64 cpu_msr_in;
> +		__u32 mb_in[2];
> +		__u8 reg_in[8];
> +	} data_in;
> +	/*
> +	 * Status code is returned in case of CPUID/MCA access
> +	 * Error code is returned in case of soft mailbox
> +	 */
> +	__u32 fw_ret_code;
> +} __attribute__((packed));
> +
> +/* ioctl command for mailbox msgs using generic _IOWR */
> +#define SBRMI_BASE_IOCTL_NR      0xF9
> +#define SBRMI_IOCTL_CMD          _IOWR(SBRMI_BASE_IOCTL_NR, 0, struct apml_message)
> +
> +#endif /*_AMD_APML_H_*/


  reply	other threads:[~2024-06-10 13:39 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-05-30 11:23 [PATCH 0/5] mfd: add amd side-band functionality Naveen Krishna Chatradhi
2024-05-30 11:23 ` [PATCH 1/5] hwmon/mfd sbrmi: Move core sbrmi from hwmon to MFD Naveen Krishna Chatradhi
2024-06-10 13:24   ` Guenter Roeck
2024-05-30 11:23 ` [PATCH 2/5] mfd: sbrmi: Add mfd cell in the probe Naveen Krishna Chatradhi
2024-05-30 11:23 ` [PATCH 3/5] mfd/hwmon sbrmi: Use regmap subsystem Naveen Krishna Chatradhi
2024-06-10 13:31   ` Guenter Roeck
2024-05-30 11:23 ` [PATCH 4/5] mfd: sbrmi: Clear sbrmi status register bit SwAlertSts Naveen Krishna Chatradhi
2024-05-30 11:23 ` [PATCH 5/5] mfd/hwmon: sbrmi: Add support for APML protocols Naveen Krishna Chatradhi
2024-06-10 13:39   ` Guenter Roeck [this message]
2024-06-13 17:05 ` [PATCH 0/5] mfd: add amd side-band functionality Lee Jones
2024-06-14 13:56   ` Chatradhi, Naveen Krishna
2024-06-14 14:49     ` Lee Jones
2024-06-18  7:17       ` Chatradhi, Naveen Krishna
2024-06-18 12:27         ` Lee Jones
2024-06-28 11:56           ` Chatradhi, Naveen Krishna

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=d057af26-85c4-4347-871f-a8a1ddab3087@roeck-us.net \
    --to=linux@roeck-us.net \
    --cc=akshay.gupta@amd.com \
    --cc=arnd@arndb.de \
    --cc=gregkh@linuxfoundation.org \
    --cc=inux-kernel@vger.kernel.org \
    --cc=lee@kernel.org \
    --cc=linux-hwmon@vger.kernel.org \
    --cc=naveenkrishna.chatradhi@amd.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox