From 04032d3b6424804da72d6daa39694fd0d8c58de7 Mon Sep 17 00:00:00 2001 From: brandonchuang Date: Fri, 26 Aug 2022 16:33:39 +0800 Subject: [PATCH] [Edgecore][as7816-64x] Support new PSU model "DPS-850A" 1. Modify pddf-device.json to add pddf_custom_psu into custom_kos 2. Implement pddf_custom_psu to support serial number and vout value of all models Signed-off-by: brandonchuang --- .../pddf/pddf-device.json | 3 +- .../as7816-64x/modules/Makefile | 4 +- .../as7816-64x/modules/pddf_custom_psu.c | 273 ++++++++++++++++++ 3 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/pddf_custom_psu.c diff --git a/device/accton/x86_64-accton_as7816_64x-r0/pddf/pddf-device.json b/device/accton/x86_64-accton_as7816_64x-r0/pddf/pddf-device.json index f373f39601e4..fcd9988344f0 100644 --- a/device/accton/x86_64-accton_as7816_64x-r0/pddf/pddf-device.json +++ b/device/accton/x86_64-accton_as7816_64x-r0/pddf/pddf-device.json @@ -52,7 +52,8 @@ ], "custom_kos": [ - "pddf_custom_fan" + "pddf_custom_fan", + "pddf_custom_psu" ] diff --git a/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/Makefile b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/Makefile index 3e3a9ac38a3e..9dd8ba685653 100644 --- a/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/Makefile +++ b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/Makefile @@ -1,6 +1,8 @@ obj-m:=x86-64-accton-as7816-64x-fan.o x86-64-accton-as7816-64x-leds.o \ - x86-64-accton-as7816-64x-psu.o accton_i2c_cpld.o ym2651y.o pddf_custom_fan.o + x86-64-accton-as7816-64x-psu.o accton_i2c_cpld.o ym2651y.o pddf_custom_fan.o \ + pddf_custom_psu.o CFLAGS_pddf_custom_fan.o := -I$(M)/../../../../pddf/i2c/modules/include +CFLAGS_pddf_custom_psu.o := -I$(M)/../../../../pddf/i2c/modules/include KBUILD_EXTRA_SYMBOLS := $(M)/../../../../pddf/i2c/Module.symvers.PDDF diff --git a/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/pddf_custom_psu.c b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/pddf_custom_psu.c new file mode 100644 index 000000000000..4d2f3849f3f6 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-accton/as7816-64x/modules/pddf_custom_psu.c @@ -0,0 +1,273 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pddf_psu_defs.h" + +extern PSU_SYSFS_ATTR_DATA access_psu_serial_num; +extern PSU_SYSFS_ATTR_DATA access_psu_v_out; + +#define MAX_MODEL_NAME 10 +#define MAX_SERIAL_NUMBER 19 + +#define PSU_REG_VOUT_MODE 0x20 +#define PSU_REG_READ_VOUT 0x8B + +enum psu_type { + PSU_TYPE_AC_D850AB_5A, + PSU_TYPE_AC_D850AB_5B, + PSU_TYPE_AC_YM2851F, + PSU_TYPE_DC_YM2851J +}; + +struct model_name_info { + enum psu_type type; + u8 offset; + u8 length; + u8 chk_length; + char* model_name; +}; + +struct serial_number_info { + enum psu_type type; + u8 offset; + u8 length; + u8 chk_length; + char* serial_number; +}; + +struct model_name_info models[] = { + { PSU_TYPE_AC_D850AB_5A, 0x15, 10, 10, "D850AB-5 A" }, + { PSU_TYPE_AC_D850AB_5B, 0x15, 10, 10, "D850AB-5 B" }, + { PSU_TYPE_AC_YM2851F, 0x20, 8, 8, "YM-2851F" }, + { PSU_TYPE_DC_YM2851J, 0x20, 8, 8, "YM-2851J" } +}; + +struct serial_number_info serials[] = { + { PSU_TYPE_AC_D850AB_5A, 0x2e, 11, 11, "D850AB-5 A" }, + { PSU_TYPE_AC_D850AB_5B, 0x2e, 11, 11, "D850AB-5 B" }, + { PSU_TYPE_AC_YM2851F, 0x35, 18, 18, "YM-2851F" }, + { PSU_TYPE_DC_YM2851J, 0x35, 18, 18, "YM-2851J" } +}; + +struct pddf_psu_data { + char model_name[MAX_MODEL_NAME+1]; + char serial_number[MAX_SERIAL_NUMBER+1]; +}; + +static int pddf_psu_read_byte(struct i2c_client *client, u8 reg) +{ + int result = 0; + int retry_count = 10; + + while (retry_count) { + retry_count--; + + result = i2c_smbus_read_byte_data(client, reg); + if (unlikely(result < 0)) { + msleep(10); + continue; + } + + break; + } + + return result; +} + +static int pddf_psu_read_word(struct i2c_client *client, u8 reg) +{ + int result = 0; + int retry_count = 10; + + while (retry_count) { + retry_count--; + + result = i2c_smbus_read_word_data(client, reg); + if (unlikely(result < 0)) { + msleep(10); + continue; + } + + break; + } + + return result; +} + +static int pddf_psu_read_block(struct i2c_client *client, u8 command, u8 *data, + int data_len) +{ + int result = 0; + int retry_count = 10; + + while (retry_count) { + retry_count--; + + result = i2c_smbus_read_i2c_block_data(client, command, data_len, data); + if (unlikely(result < 0)) { + msleep(10); + continue; + } + + if (unlikely(result != data_len)) { + result = -EIO; + msleep(10); + continue; + } + + result = 0; + break; + } + + return result; +} + +ssize_t pddf_get_custom_psu_serial_num(struct device *dev, struct device_attribute *da, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct pddf_psu_data data; + int i, status; + + for (i = 0; i < ARRAY_SIZE(models); i++) { + memset(data.serial_number, 0, sizeof(data.serial_number)); + + status = pddf_psu_read_block(client, models[i].offset, + data.model_name, models[i].length); + if (status < 0) { + data.model_name[0] = '\0'; + dev_dbg(&client->dev, "unable to read model name from (0x%x) offset(0x%x)\n", + client->addr, models[i].offset); + return status; + } + else { + data.model_name[models[i].length] = '\0'; + } + + /* Determine if the model name is known, if not, read next index + */ + if (strncmp(data.model_name, models[i].model_name, models[i].chk_length) == 0) { + status = pddf_psu_read_block(client, serials[i].offset, + data.serial_number, serials[i].length); + + if (status < 0) { + data.serial_number[0] = '\0'; + dev_dbg(&client->dev, "unable to read serial num from (0x%x) offset(0x%x)\n", + client->addr, serials[i].offset); + return status; + } + else { + data.serial_number[serials[i].length] = '\0'; + return sprintf(buf, "%s\n", data.serial_number); + } + + return 0; + } + else { + data.serial_number[0] = '\0'; + } + } + + return -ENODATA; +} + +static int twos_complement_to_int(u16 data, u8 valid_bit, int mask) +{ + u16 valid_data = data & mask; + bool is_negative = valid_data >> (valid_bit - 1); + + return is_negative ? (-(((~valid_data) & mask) + 1)) : valid_data; +} + +static ssize_t show_vout_linear(char *buf, u8 vout_mode, u16 read_vout) +{ + int exponent = 0, mantissa = read_vout; + int multiplier = 1000; + + exponent = twos_complement_to_int(vout_mode, 5, 0x1f); + return (exponent > 0) ? sprintf(buf, "%d\n", (mantissa << exponent) * multiplier) : + sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); +} + +static ssize_t show_vout_literal(char *buf, u16 read_vout) +{ + int exponent, mantissa; + int multiplier = 1000; + + exponent = twos_complement_to_int(read_vout >> 11, 5, 0x1F); + mantissa = twos_complement_to_int(read_vout & 0x7FF, 11, 0x7FF); + + return (exponent >= 0) ? sprintf(buf, "%d\n", (mantissa << exponent) * multiplier) : + sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); +} + +ssize_t pddf_get_custom_psu_v_out(struct device *dev, struct device_attribute *da, char *buf) +{ + u8 reg = 0; + u8 vout_mode = 0xFF; + u16 read_vout = 0; + int status = 0; + struct i2c_client *client = to_i2c_client(dev); + + reg = PSU_REG_VOUT_MODE; /* PMBus VOUT_MODE */ + status = pddf_psu_read_byte(client, reg); + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", reg, status); + return status; + } + else { + vout_mode = status; + } + + reg = PSU_REG_READ_VOUT; /* PMBus READ_VOUT */ + status = pddf_psu_read_word(client, reg); + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", reg, status); + return status; + } + else { + read_vout = status; + } + + if ((vout_mode & 0xE0) == 0x20) { + return -ENODATA; + } + else if (!(vout_mode & 0xE0)) { + /* Linear mode */ + return show_vout_linear(buf, vout_mode, read_vout); + } + else { + /* Default literal format */ + return show_vout_literal(buf, read_vout); + } +} + +static int __init pddf_custom_psu_init(void) +{ + access_psu_serial_num.show = pddf_get_custom_psu_serial_num; + access_psu_serial_num.do_get = NULL; + + access_psu_v_out.show = pddf_get_custom_psu_v_out; + access_psu_v_out.do_get = NULL; + + return 0; +} + +static void __exit pddf_custom_psu_exit(void) +{ + return; +} + +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("pddf custom psu api"); +MODULE_LICENSE("GPL"); + +module_init(pddf_custom_psu_init); +module_exit(pddf_custom_psu_exit);