diff mbox series

[v1,1/3] hwmon: (pmbus) Add support for Infineon XDP710

Message ID 20240424095604.3425857-2-peteryin.openbmc@gmail.com (mailing list archive)
State Superseded
Headers show
Series hwmon: (pmbus) Add support for Infineon XDP710 | expand

Commit Message

PeterYin April 24, 2024, 9:55 a.m. UTC
Add support for xdp710 device from Infineon vendor.
This is a Hot-Swap Controller.

Signed-off-by: Peter Yin <peteryin.openbmc@gmail.com>
---
 drivers/hwmon/pmbus/Kconfig  |   9 ++
 drivers/hwmon/pmbus/Makefile |   1 +
 drivers/hwmon/pmbus/xdp710.c | 155 +++++++++++++++++++++++++++++++++++
 3 files changed, 165 insertions(+)
 create mode 100644 drivers/hwmon/pmbus/xdp710.c

Comments

Guenter Roeck April 24, 2024, 2:58 p.m. UTC | #1
On 4/24/24 02:55, Peter Yin wrote:
> Add support for xdp710 device from Infineon vendor.

Drop "vendor". Maybe "Add support for Infineon XDP710.".

> This is a Hot-Swap Controller.
> 
> Signed-off-by: Peter Yin <peteryin.openbmc@gmail.com>
> ---
>   drivers/hwmon/pmbus/Kconfig  |   9 ++
>   drivers/hwmon/pmbus/Makefile |   1 +
>   drivers/hwmon/pmbus/xdp710.c | 155 +++++++++++++++++++++++++++++++++++
>   3 files changed, 165 insertions(+)
>   create mode 100644 drivers/hwmon/pmbus/xdp710.c
> 
> diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
> index 557ae0c414b0..d72bdecf758a 100644
> --- a/drivers/hwmon/pmbus/Kconfig
> +++ b/drivers/hwmon/pmbus/Kconfig
> @@ -511,6 +511,15 @@ config SENSORS_UCD9200
>   	  This driver can also be built as a module. If so, the module will
>   	  be called ucd9200.
>   
> +config SENSORS_XDP710
> +	tristate "Infineon XDP710 family"
> +	help
> +	  If you say yes here you get hardware monitoring support for Infineon
> +	  XDP710 device.
> +
Drop "device"

> +	  This driver can also be built as a module. If so, the module will
> +	  be called xdp710.
> +
>   config SENSORS_XDPE152
>   	tristate "Infineon XDPE152 family"
>   	help
> diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
> index f14ecf03ad77..4fe630793721 100644
> --- a/drivers/hwmon/pmbus/Makefile
> +++ b/drivers/hwmon/pmbus/Makefile
> @@ -51,6 +51,7 @@ obj-$(CONFIG_SENSORS_TPS53679)	+= tps53679.o
>   obj-$(CONFIG_SENSORS_TPS546D24)	+= tps546d24.o
>   obj-$(CONFIG_SENSORS_UCD9000)	+= ucd9000.o
>   obj-$(CONFIG_SENSORS_UCD9200)	+= ucd9200.o
> +obj-$(CONFIG_SENSORS_XDP710)	+= xdp710.o
>   obj-$(CONFIG_SENSORS_XDPE122)	+= xdpe12284.o
>   obj-$(CONFIG_SENSORS_XDPE152)	+= xdpe152c4.o
>   obj-$(CONFIG_SENSORS_ZL6100)	+= zl6100.o
> diff --git a/drivers/hwmon/pmbus/xdp710.c b/drivers/hwmon/pmbus/xdp710.c
> new file mode 100644
> index 000000000000..3ed324bd0db6
> --- /dev/null
> +++ b/drivers/hwmon/pmbus/xdp710.c
> @@ -0,0 +1,155 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Driver for Infineon XDP710 Hot-Swap Controller
> + */
> +
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/bitops.h>

Include files in alphabetic order, please.

> +#include "pmbus.h"
> +
> +#define XDP710_REG_CFG		(0xD3)
> +#define XDP710_V_SNS_CFG	(0xD4)
> +#define XDP710_CS_RNG		(0xD5)

Unnecessary ( )

> +
> +struct xdp710_data {
> +	struct pmbus_driver_info info;
> +	u8 cs_rng;
> +	u8 vtlm_rng;
> +	int rsense;

I don't see those values used outside the probe function,
meaning it is unnecessary to keep them in struct xdp710_data.

> +};
> +
> +/*
> + * 0x00 to 0x09
> + * 0x0A to 0x13
> + * 0x14 to 0x1D
> + * 0x1E to 0x27
> + * 0x28 to 0x32
> + * 0x33 to 0x3F

Pointless comment. Who is supposed to understand what it means ?
Besides, if it does mean what I think it is (matching lines of values
in the array below), it is wrong and misleading. The last line in
the array starts at index 0x32 (50) and ends at 0x3b (59).

I more useful comment would be something like "table to map configuration
register values to sense resistor values" or similar.

> + */
> +const int microOhmRsense[] = {
> +	200, 250, 300, 330, 400, 470, 500, 600, 670, 700,
> +	750, 800, 900, 1000, 1100, 1200, 1250, 1300, 1400, 1500,
> +	1600, 1700, 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500,
> +	2600, 2700, 2800, 3000, 3100, 3200, 3300, 3400, 3500, 3600,
> +	3800, 3900, 4000, 4200, 4300, 4500, 4700, 4800, 4900, 5000,
> +	5500, 6000, 6500, 7000, 7500, 8000, 8500, 9000, 9500, 10000

This array has only 60 entries. The configuration register field
is 6 bits wide. This means that configuration register values
of 0x3c..0x3f will access values beyond the end of the array.

> +};
> +
> +static struct pmbus_driver_info xdp710_info = {
> +	.pages = 1,
> +	.format[PSC_VOLTAGE_IN] = direct,
> +	.format[PSC_VOLTAGE_OUT] = direct,
> +	.format[PSC_CURRENT_OUT] = direct,
> +	.format[PSC_POWER] = direct,
> +	.format[PSC_TEMPERATURE] = direct,
> +	.m[PSC_VOLTAGE_IN] = 4653,
> +	.b[PSC_VOLTAGE_IN] = 0,
> +	.R[PSC_VOLTAGE_IN] = -2,
> +	.m[PSC_VOLTAGE_OUT] = 4653,
> +	.b[PSC_VOLTAGE_OUT] = 0,
> +	.R[PSC_VOLTAGE_OUT] = -2,
> +	.m[PSC_CURRENT_OUT] = 23165,
> +	.b[PSC_CURRENT_OUT] = 0,
> +	.R[PSC_CURRENT_OUT] = -2,
> +	.m[PSC_POWER] = 4211,
> +	.b[PSC_POWER] = 0,
> +	.R[PSC_POWER] = -2,
> +	.m[PSC_TEMPERATURE] = 52,
> +	.b[PSC_TEMPERATURE] = 14321,
> +	.R[PSC_TEMPERATURE] = -1,
> +	.func[0] =
> +		PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_PIN |
> +		PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT |
> +		PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
> +};
> +
> +static int xdp710_probe(struct i2c_client *client)
> +{
> +	struct pmbus_driver_info *info;
> +	struct xdp710_data *data;
> +	int ret;
> +	int m = 0;
> +
> +	data = devm_kzalloc(&client->dev, sizeof(struct xdp710_data),
> +			    GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	memcpy(&data->info, &xdp710_info, sizeof(*info));
> +	info = &data->info;
> +
> +	/*
> +	 * Read CS_RNG Value
> +	 */

Those comments are pointless and similar to

	x = 5; /* add 5 to x */

> +	ret = i2c_smbus_read_word_data(client, XDP710_CS_RNG);
> +	if (ret < 0) {
> +		dev_err(&client->dev,
> +			"Can't get CS_RNG. Setting the CS_RNG to 0");

If this is an error, abort the probe function and return it.
If not, don't use dev_err().

> +		ret = 0;
> +	}
> +	data->cs_rng = (ret >> 6) & GENMASK(1, 0);
> +
> +	/*
> +	 * Read V_SNS_CFG Value
> +	 */
> +	ret = i2c_smbus_read_word_data(client, XDP710_V_SNS_CFG);
> +	if (ret < 0) {
> +		dev_err(&client->dev,
> +			"Can't get V_SNS_CFG. Setting the V_SNS_CFG to 0");

Same as above.

> +		ret = 0;
> +	}
> +	data->vtlm_rng = ret & GENMASK(1, 0);
> +
> +	/*
> +	 * Read RSNS_CFG Value
> +	 */
> +	ret = i2c_smbus_read_word_data(client, XDP710_REG_CFG);
> +	if (ret < 0) {
> +		dev_err(&client->dev,
> +			"Can't get REG_CFG, Setting the Rsense to 0.33mohm");

And again. Overall, I do think that those errors should be treated as fatal.
I don't see a reason for ignoring them. If accessing the chip fails on that
level, it probably fails entirely. If you want to assign defaults after
smbus errors, please explain in detail in a comment why this makes sense
and doesn't mean that the chip is not accessible.

> +		ret = 3;
> +	}
> +	ret &= GENMASK(5, 0);
> +	data->rsense = microOhmRsense[(u8)ret];

Unnecessary type cast.

> +
> +	info->m[PSC_VOLTAGE_IN] <<= data->vtlm_rng;
> +	info->m[PSC_VOLTAGE_OUT] <<= data->vtlm_rng;
> +
> +	m = info->m[PSC_CURRENT_OUT];
> +	info->m[PSC_CURRENT_OUT] = ((m >> (data->cs_rng)) *
> +				   (data->rsense)) / 1000;

Unnecessary () around data->cs_rng and data->rsense and around
the multiplication.

	info->m[PSC_CURRENT_OUT] = (m >> data->cs_rng) * data->rsense / 1000;

However, it seems to me that the right shift will result in accuracy
loss. Something like
	info->m[PSC_CURRENT_OUT] = ((m * rsense) >> data->cs_rng) / 1000;
might avoid that.

> +
> +	m = info->m[PSC_POWER];
> +	info->m[PSC_POWER] = ((m >> (data->cs_rng)) *
> +			     (data->rsense)) / 1000;

Unnecessary () around data->cs_rng and data->rsense and around
the multiplication.

Your call, but DIV_ROUND_CLOSEST() might result in better accuracy.

> +
> +	return pmbus_do_probe(client, info);
> +}
> +
> +static const struct of_device_id xdp710_of_match[] = {
> +	{ .compatible = "infineon,xdp710" },
> +	{}
> +};
> +
> +static const struct i2c_device_id xdp710_id[] = {
> +	{"xdp710", 0},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, xdp710_id);
> +
> +static struct i2c_driver xdp710_driver = {
> +	.driver = {
> +		   .name = "xdp710",
> +		   .of_match_table = xdp710_of_match,
> +	},
> +	.probe = xdp710_probe,
> +	.id_table = xdp710_id,
> +};
> +module_i2c_driver(xdp710_driver);
> +
> +MODULE_AUTHOR("Peter Yin <peter.yin@quantatw.com>");
> +MODULE_DESCRIPTION("PMBus driver for XDP710 HSC");
> +MODULE_LICENSE("GPL");
> +MODULE_IMPORT_NS(PMBUS);
diff mbox series

Patch

diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 557ae0c414b0..d72bdecf758a 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -511,6 +511,15 @@  config SENSORS_UCD9200
 	  This driver can also be built as a module. If so, the module will
 	  be called ucd9200.
 
+config SENSORS_XDP710
+	tristate "Infineon XDP710 family"
+	help
+	  If you say yes here you get hardware monitoring support for Infineon
+	  XDP710 device.
+
+	  This driver can also be built as a module. If so, the module will
+	  be called xdp710.
+
 config SENSORS_XDPE152
 	tristate "Infineon XDPE152 family"
 	help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index f14ecf03ad77..4fe630793721 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -51,6 +51,7 @@  obj-$(CONFIG_SENSORS_TPS53679)	+= tps53679.o
 obj-$(CONFIG_SENSORS_TPS546D24)	+= tps546d24.o
 obj-$(CONFIG_SENSORS_UCD9000)	+= ucd9000.o
 obj-$(CONFIG_SENSORS_UCD9200)	+= ucd9200.o
+obj-$(CONFIG_SENSORS_XDP710)	+= xdp710.o
 obj-$(CONFIG_SENSORS_XDPE122)	+= xdpe12284.o
 obj-$(CONFIG_SENSORS_XDPE152)	+= xdpe152c4.o
 obj-$(CONFIG_SENSORS_ZL6100)	+= zl6100.o
diff --git a/drivers/hwmon/pmbus/xdp710.c b/drivers/hwmon/pmbus/xdp710.c
new file mode 100644
index 000000000000..3ed324bd0db6
--- /dev/null
+++ b/drivers/hwmon/pmbus/xdp710.c
@@ -0,0 +1,155 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Infineon XDP710 Hot-Swap Controller
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/bitops.h>
+#include "pmbus.h"
+
+#define XDP710_REG_CFG		(0xD3)
+#define XDP710_V_SNS_CFG	(0xD4)
+#define XDP710_CS_RNG		(0xD5)
+
+struct xdp710_data {
+	struct pmbus_driver_info info;
+	u8 cs_rng;
+	u8 vtlm_rng;
+	int rsense;
+};
+
+/*
+ * 0x00 to 0x09
+ * 0x0A to 0x13
+ * 0x14 to 0x1D
+ * 0x1E to 0x27
+ * 0x28 to 0x32
+ * 0x33 to 0x3F
+ */
+const int microOhmRsense[] = {
+	200, 250, 300, 330, 400, 470, 500, 600, 670, 700,
+	750, 800, 900, 1000, 1100, 1200, 1250, 1300, 1400, 1500,
+	1600, 1700, 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500,
+	2600, 2700, 2800, 3000, 3100, 3200, 3300, 3400, 3500, 3600,
+	3800, 3900, 4000, 4200, 4300, 4500, 4700, 4800, 4900, 5000,
+	5500, 6000, 6500, 7000, 7500, 8000, 8500, 9000, 9500, 10000
+};
+
+static struct pmbus_driver_info xdp710_info = {
+	.pages = 1,
+	.format[PSC_VOLTAGE_IN] = direct,
+	.format[PSC_VOLTAGE_OUT] = direct,
+	.format[PSC_CURRENT_OUT] = direct,
+	.format[PSC_POWER] = direct,
+	.format[PSC_TEMPERATURE] = direct,
+	.m[PSC_VOLTAGE_IN] = 4653,
+	.b[PSC_VOLTAGE_IN] = 0,
+	.R[PSC_VOLTAGE_IN] = -2,
+	.m[PSC_VOLTAGE_OUT] = 4653,
+	.b[PSC_VOLTAGE_OUT] = 0,
+	.R[PSC_VOLTAGE_OUT] = -2,
+	.m[PSC_CURRENT_OUT] = 23165,
+	.b[PSC_CURRENT_OUT] = 0,
+	.R[PSC_CURRENT_OUT] = -2,
+	.m[PSC_POWER] = 4211,
+	.b[PSC_POWER] = 0,
+	.R[PSC_POWER] = -2,
+	.m[PSC_TEMPERATURE] = 52,
+	.b[PSC_TEMPERATURE] = 14321,
+	.R[PSC_TEMPERATURE] = -1,
+	.func[0] =
+		PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_PIN |
+		PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT |
+		PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
+};
+
+static int xdp710_probe(struct i2c_client *client)
+{
+	struct pmbus_driver_info *info;
+	struct xdp710_data *data;
+	int ret;
+	int m = 0;
+
+	data = devm_kzalloc(&client->dev, sizeof(struct xdp710_data),
+			    GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	memcpy(&data->info, &xdp710_info, sizeof(*info));
+	info = &data->info;
+
+	/*
+	 * Read CS_RNG Value
+	 */
+	ret = i2c_smbus_read_word_data(client, XDP710_CS_RNG);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"Can't get CS_RNG. Setting the CS_RNG to 0");
+		ret = 0;
+	}
+	data->cs_rng = (ret >> 6) & GENMASK(1, 0);
+
+	/*
+	 * Read V_SNS_CFG Value
+	 */
+	ret = i2c_smbus_read_word_data(client, XDP710_V_SNS_CFG);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"Can't get V_SNS_CFG. Setting the V_SNS_CFG to 0");
+		ret = 0;
+	}
+	data->vtlm_rng = ret & GENMASK(1, 0);
+
+	/*
+	 * Read RSNS_CFG Value
+	 */
+	ret = i2c_smbus_read_word_data(client, XDP710_REG_CFG);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"Can't get REG_CFG, Setting the Rsense to 0.33mohm");
+		ret = 3;
+	}
+	ret &= GENMASK(5, 0);
+	data->rsense = microOhmRsense[(u8)ret];
+
+	info->m[PSC_VOLTAGE_IN] <<= data->vtlm_rng;
+	info->m[PSC_VOLTAGE_OUT] <<= data->vtlm_rng;
+
+	m = info->m[PSC_CURRENT_OUT];
+	info->m[PSC_CURRENT_OUT] = ((m >> (data->cs_rng)) *
+				   (data->rsense)) / 1000;
+
+	m = info->m[PSC_POWER];
+	info->m[PSC_POWER] = ((m >> (data->cs_rng)) *
+			     (data->rsense)) / 1000;
+
+	return pmbus_do_probe(client, info);
+}
+
+static const struct of_device_id xdp710_of_match[] = {
+	{ .compatible = "infineon,xdp710" },
+	{}
+};
+
+static const struct i2c_device_id xdp710_id[] = {
+	{"xdp710", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, xdp710_id);
+
+static struct i2c_driver xdp710_driver = {
+	.driver = {
+		   .name = "xdp710",
+		   .of_match_table = xdp710_of_match,
+	},
+	.probe = xdp710_probe,
+	.id_table = xdp710_id,
+};
+module_i2c_driver(xdp710_driver);
+
+MODULE_AUTHOR("Peter Yin <peter.yin@quantatw.com>");
+MODULE_DESCRIPTION("PMBus driver for XDP710 HSC");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);