From b5ee48c099e4036fc0336cf66a0d324b8225d53e Mon Sep 17 00:00:00 2001 From: Stephen Carlson Date: Mon, 8 Feb 2021 11:11:29 +0100 Subject: [PATCH] arm: fsl: common: Improve NXP VID driver PMBus support This patch adds support for more PMBus compatible devices to the NXP drivers for its QorIQ family devices. At runtime, the voltage regulator is queried over I2C, and the required voltage multiplier determined. This change supports the DIRECT and LINEAR PMBus voltage reporting modes. Previously, the driver only supported a few specific devices such as the IR36021 and LTC3882, so this change allows the QorIQ series to be used with a much larger variety of core voltage regulator devices. checkpatch warning "Use if (IS_DEFINED (...))" was ignored to maintain consistency with the existing code. Signed-off-by: Stephen Carlson Signed-off-by: Wasim Khan Tested-by: Wasim Khan [Rebased] Signed-off-by: Priyanka Jain --- board/freescale/common/Kconfig | 27 +- board/freescale/common/vid.c | 820 ++++++++++-------------- board/freescale/common/vid.h | 11 +- board/freescale/ls1088a/ls1088a.c | 40 ++ board/freescale/ls2080ardb/ls2080ardb.c | 42 ++ board/freescale/lx2160a/lx2160a.c | 42 ++ include/configs/ls1088aqds.h | 6 - include/configs/ls1088ardb.h | 8 +- 8 files changed, 477 insertions(+), 519 deletions(-) diff --git a/board/freescale/common/Kconfig b/board/freescale/common/Kconfig index 1b1fd69cb2..17db755951 100644 --- a/board/freescale/common/Kconfig +++ b/board/freescale/common/Kconfig @@ -21,18 +21,37 @@ config CMD_ESBC_VALIDATE esbc_validate - validate signature using RSA verification esbc_halt - put the core in spin loop (Secure Boot Only) +config VID + depends on DM_I2C + bool "Enable Freescale VID" + help + This option enables setting core voltage based on individual + values saved in SoC fuses. + config VOL_MONITOR_LTC3882_READ depends on VID bool "Enable the LTC3882 voltage monitor read" - default n help This option enables LTC3882 voltage monitor read - functionality. It is used by common VID driver. + functionality. It is used by the common VID driver. config VOL_MONITOR_LTC3882_SET depends on VID bool "Enable the LTC3882 voltage monitor set" - default n help This option enables LTC3882 voltage monitor set - functionality. It is used by common VID driver. + functionality. It is used by the common VID driver. + +config VOL_MONITOR_ISL68233_READ + depends on VID + bool "Enable the ISL68233 voltage monitor read" + help + This option enables ISL68233 voltage monitor read + functionality. It is used by the common VID driver. + +config VOL_MONITOR_ISL68233_SET + depends on VID + bool "Enable the ISL68233 voltage monitor set" + help + This option enables ISL68233 voltage monitor set + functionality. It is used by the common VID driver. diff --git a/board/freescale/common/vid.c b/board/freescale/common/vid.c index 20f5421da0..6e8296293b 100644 --- a/board/freescale/common/vid.c +++ b/board/freescale/common/vid.c @@ -2,6 +2,7 @@ /* * Copyright 2014 Freescale Semiconductor, Inc. * Copyright 2020 NXP + * Copyright 2020 Stephen Carlson */ #include @@ -21,14 +22,22 @@ #include #include "vid.h" +/* Voltages are generally handled in mV to keep them as integers */ +#define MV_PER_V 1000 + +/* + * Select the channel on the I2C mux (on some NXP boards) that contains + * the voltage regulator to use for VID. Return 0 for success or nonzero + * for failure. + */ int __weak i2c_multiplexer_select_vid_channel(u8 channel) { return 0; } /* - * Compensate for a board specific voltage drop between regulator and SoC - * return a value in mV + * Compensate for a board specific voltage drop between regulator and SoC. + * Returns the voltage offset in mV. */ int __weak board_vdd_drop_compensation(void) { @@ -36,13 +45,94 @@ int __weak board_vdd_drop_compensation(void) } /* - * Board specific settings for specific voltage value + * Performs any board specific adjustments after the VID voltage has been + * set. Return 0 for success or nonzero for failure. */ int __weak board_adjust_vdd(int vdd) { return 0; } +/* + * Processor specific method of converting the fuse value read from VID + * registers into the core voltage to supply. Return the voltage in mV. + */ +u16 __weak soc_get_fuse_vid(int vid_index) +{ + /* Default VDD for Layerscape Chassis 1 devices */ + static const u16 vdd[32] = { + 0, /* unused */ + 9875, /* 0.9875V */ + 9750, + 9625, + 9500, + 9375, + 9250, + 9125, + 9000, + 8875, + 8750, + 8625, + 8500, + 8375, + 8250, + 8125, + 10000, /* 1.0000V */ + 10125, + 10250, + 10375, + 10500, + 10625, + 10750, + 10875, + 11000, + 0, /* reserved */ + }; + return vdd[vid_index]; +} + +#ifndef I2C_VOL_MONITOR_ADDR +#define I2C_VOL_MONITOR_ADDR 0 +#endif + +#if CONFIG_IS_ENABLED(DM_I2C) +#define DEVICE_HANDLE_T struct udevice * + +#ifndef I2C_VOL_MONITOR_BUS +#define I2C_VOL_MONITOR_BUS 0 +#endif + +/* If DM is in use, retrieve the udevice chip for the specified bus number */ +static int vid_get_device(int address, DEVICE_HANDLE_T *dev) +{ + int ret = i2c_get_chip_for_busnum(I2C_VOL_MONITOR_BUS, address, 1, dev); + + if (ret) + printf("VID: Bus %d has no device with address 0x%02X\n", + I2C_VOL_MONITOR_BUS, address); + return ret; +} + +#define I2C_READ(dev, register, data, length) \ + dm_i2c_read(dev, register, data, length) +#define I2C_WRITE(dev, register, data, length) \ + dm_i2c_write(dev, register, data, length) +#else +#define DEVICE_HANDLE_T int + +/* If DM is not in use, I2C addresses are passed directly */ +static int vid_get_device(int address, DEVICE_HANDLE_T *dev) +{ + *dev = address; + return 0; +} + +#define I2C_READ(dev, register, data, length) \ + i2c_read(dev, register, 1, data, length) +#define I2C_WRITE(dev, register, data, length) \ + i2c_write(dev, register, 1, data, length) +#endif + #if defined(CONFIG_VOL_MONITOR_IR36021_SET) || \ defined(CONFIG_VOL_MONITOR_IR36021_READ) /* @@ -60,30 +150,22 @@ int __weak board_adjust_vdd(int vdd) */ static int find_ir_chip_on_i2c(void) { - int i2caddress; - int ret; - u8 byte; - int i; + int i2caddress, ret, i; + u8 mfrID; const int ir_i2c_addr[] = {0x38, 0x08, 0x09}; -#if CONFIG_IS_ENABLED(DM_I2C) - struct udevice *dev; -#endif + DEVICE_HANDLE_T dev; /* Check all the address */ for (i = 0; i < (sizeof(ir_i2c_addr)/sizeof(ir_i2c_addr[0])); i++) { i2caddress = ir_i2c_addr[i]; -#if !CONFIG_IS_ENABLED(DM_I2C) - ret = i2c_read(i2caddress, - IR36021_MFR_ID_OFFSET, 1, (void *)&byte, - sizeof(byte)); -#else - ret = i2c_get_chip_for_busnum(0, i2caddress, 1, &dev); - if (!ret) - ret = dm_i2c_read(dev, IR36021_MFR_ID_OFFSET, - (void *)&byte, sizeof(byte)); -#endif - if ((ret >= 0) && (byte == IR36021_MFR_ID)) - return i2caddress; + ret = vid_get_device(i2caddress, &dev); + if (!ret) { + ret = I2C_READ(dev, IR36021_MFR_ID_OFFSET, + (void *)&mfrID, sizeof(mfrID)); + /* If manufacturer ID matches the IR36021 */ + if (!ret && mfrID == IR36021_MFR_ID) + return i2caddress; + } } return -1; } @@ -117,35 +199,33 @@ static int read_voltage_from_INA220(int i2caddress) int i, ret, voltage_read = 0; u16 vol_mon; u8 buf[2]; -#if CONFIG_IS_ENABLED(DM_I2C) - struct udevice *dev; -#endif + DEVICE_HANDLE_T dev; + + /* Open device handle */ + ret = vid_get_device(i2caddress, &dev); + if (ret) + return ret; for (i = 0; i < NUM_READINGS; i++) { -#if !CONFIG_IS_ENABLED(DM_I2C) - ret = i2c_read(I2C_VOL_MONITOR_ADDR, - I2C_VOL_MONITOR_BUS_V_OFFSET, 1, - (void *)&buf, 2); -#else - ret = i2c_get_chip_for_busnum(0, I2C_VOL_MONITOR_ADDR, 1, &dev); - if (!ret) - ret = dm_i2c_read(dev, I2C_VOL_MONITOR_BUS_V_OFFSET, - (void *)&buf, 2); -#endif + ret = I2C_READ(dev, I2C_VOL_MONITOR_BUS_V_OFFSET, + (void *)&buf[0], sizeof(buf)); if (ret) { printf("VID: failed to read core voltage\n"); return ret; } + vol_mon = (buf[0] << 8) | buf[1]; if (vol_mon & I2C_VOL_MONITOR_BUS_V_OVF) { printf("VID: Core voltage sensor error\n"); return -1; } + debug("VID: bus voltage reads 0x%04x\n", vol_mon); /* LSB = 4mv */ voltage_read += (vol_mon >> I2C_VOL_MONITOR_BUS_V_SHIFT) * 4; udelay(WAIT_FOR_ADC); } + /* calculate the average */ voltage_read /= NUM_READINGS; @@ -153,30 +233,25 @@ static int read_voltage_from_INA220(int i2caddress) } #endif -/* read voltage from IR */ #ifdef CONFIG_VOL_MONITOR_IR36021_READ +/* read voltage from IR */ static int read_voltage_from_IR(int i2caddress) { int i, ret, voltage_read = 0; u16 vol_mon; u8 buf; -#if CONFIG_IS_ENABLED(DM_I2C) - struct udevice *dev; -#endif + DEVICE_HANDLE_T dev; + + /* Open device handle */ + ret = vid_get_device(i2caddress, &dev); + if (ret) + return ret; for (i = 0; i < NUM_READINGS; i++) { -#if !CONFIG_IS_ENABLED(DM_I2C) - ret = i2c_read(i2caddress, - IR36021_LOOP1_VOUT_OFFSET, - 1, (void *)&buf, 1); -#else - ret = i2c_get_chip_for_busnum(0, i2caddress, 1, &dev); - if (!ret) - ret = dm_i2c_read(dev, IR36021_LOOP1_VOUT_OFFSET, - (void *)&buf, 1); -#endif + ret = I2C_READ(dev, IR36021_LOOP1_VOUT_OFFSET, (void *)&buf, + sizeof(buf)); if (ret) { - printf("VID: failed to read vcpu\n"); + printf("VID: failed to read core voltage\n"); return ret; } vol_mon = buf; @@ -188,7 +263,7 @@ static int read_voltage_from_IR(int i2caddress) /* Resolution is 1/128V. We scale up here to get 1/128mV * and divide at the end */ - voltage_read += vol_mon * 1000; + voltage_read += vol_mon * MV_PER_V; udelay(WAIT_FOR_ADC); } /* Scale down to the real mV as IR resolution is 1/128V, rounding up */ @@ -206,49 +281,94 @@ static int read_voltage_from_IR(int i2caddress) } #endif -#ifdef CONFIG_VOL_MONITOR_LTC3882_READ -/* read the current value of the LTC Regulator Voltage */ -static int read_voltage_from_LTC(int i2caddress) -{ - int ret, vcode = 0; - u8 chan = PWM_CHANNEL0; +#if defined(CONFIG_VOL_MONITOR_ISL68233_READ) || \ + defined(CONFIG_VOL_MONITOR_LTC3882_READ) || \ + defined(CONFIG_VOL_MONITOR_ISL68233_SET) || \ + defined(CONFIG_VOL_MONITOR_LTC3882_SET) -#if !CONFIG_IS_ENABLED(DM_I2C) - /* select the PAGE 0 using PMBus commands PAGE for VDD*/ - ret = i2c_write(I2C_VOL_MONITOR_ADDR, - PMBUS_CMD_PAGE, 1, &chan, 1); -#else - struct udevice *dev; +/* + * The message displayed if the VOUT exponent causes a resolution + * worse than 1.0 V (if exponent is >= 0). + */ +#define VOUT_WARNING "VID: VOUT_MODE exponent has resolution worse than 1 V!\n" - ret = i2c_get_chip_for_busnum(0, I2C_VOL_MONITOR_ADDR, 1, &dev); - if (!ret) - ret = dm_i2c_write(dev, PMBUS_CMD_PAGE, &chan, 1); -#endif +/* Checks the PMBus voltage monitor for the format used for voltage values */ +static int get_pmbus_multiplier(DEVICE_HANDLE_T dev) +{ + u8 mode; + int exponent, multiplier, ret; + + ret = I2C_READ(dev, PMBUS_CMD_VOUT_MODE, &mode, sizeof(mode)); if (ret) { - printf("VID: failed to select VDD Page 0\n"); + printf("VID: unable to determine voltage multiplier\n"); + return 1; + } + + /* Upper 3 bits is mode, lower 5 bits is exponent */ + exponent = (int)mode & 0x1F; + mode >>= 5; + switch (mode) { + case 0: + /* Linear, 5 bit twos component exponent */ + if (exponent & 0x10) { + multiplier = 1 << (16 - (exponent & 0xF)); + } else { + /* If exponent is >= 0, then resolution is 1 V! */ + printf(VOUT_WARNING); + multiplier = 1; + } + break; + case 1: + /* VID code identifier */ + printf("VID: custom VID codes are not supported\n"); + multiplier = MV_PER_V; + break; + default: + /* Direct, in mV */ + multiplier = MV_PER_V; + break; + } + + debug("VID: calculated multiplier is %d\n", multiplier); + return multiplier; +} +#endif + +#if defined(CONFIG_VOL_MONITOR_ISL68233_READ) || \ + defined(CONFIG_VOL_MONITOR_LTC3882_READ) +static int read_voltage_from_pmbus(int i2caddress) +{ + int ret, multiplier, vout; + u8 channel = PWM_CHANNEL0; + u16 vcode; + DEVICE_HANDLE_T dev; + + /* Open device handle */ + ret = vid_get_device(i2caddress, &dev); + if (ret) return ret; - } -#if !CONFIG_IS_ENABLED(DM_I2C) - /*read the output voltage using PMBus command READ_VOUT*/ - ret = i2c_read(I2C_VOL_MONITOR_ADDR, - PMBUS_CMD_READ_VOUT, 1, (void *)&vcode, 2); -#else - ret = dm_i2c_read(dev, PMBUS_CMD_READ_VOUT, (void *)&vcode, 2); + /* Select the right page */ + ret = I2C_WRITE(dev, PMBUS_CMD_PAGE, &channel, sizeof(channel)); if (ret) { - printf("VID: failed to read the volatge\n"); + printf("VID: failed to select VDD page %d\n", channel); return ret; } -#endif + + /* VOUT is little endian */ + ret = I2C_READ(dev, PMBUS_CMD_READ_VOUT, (void *)&vcode, sizeof(vcode)); if (ret) { - printf("VID: failed to read the volatge\n"); + printf("VID: failed to read core voltage\n"); return ret; } - /* Scale down to the real mV as LTC resolution is 1/4096V,rounding up */ - vcode = DIV_ROUND_UP(vcode * 1000, 4096); - - return vcode; + /* Scale down to the real mV */ + multiplier = get_pmbus_multiplier(dev); + vout = (int)vcode; + /* Multiplier 1000 (direct mode) requires no change to convert */ + if (multiplier != MV_PER_V) + vout = DIV_ROUND_UP(vout * MV_PER_V, multiplier); + return vout - board_vdd_drop_compensation(); } #endif @@ -256,13 +376,14 @@ static int read_voltage(int i2caddress) { int voltage_read; #ifdef CONFIG_VOL_MONITOR_INA220 - voltage_read = read_voltage_from_INA220(i2caddress); + voltage_read = read_voltage_from_INA220(I2C_VOL_MONITOR_ADDR); #elif defined CONFIG_VOL_MONITOR_IR36021_READ voltage_read = read_voltage_from_IR(i2caddress); -#elif defined CONFIG_VOL_MONITOR_LTC3882_READ - voltage_read = read_voltage_from_LTC(i2caddress); +#elif defined(CONFIG_VOL_MONITOR_ISL68233_READ) || \ + defined(CONFIG_VOL_MONITOR_LTC3882_READ) + voltage_read = read_voltage_from_pmbus(i2caddress); #else - return -1; + voltage_read = -1; #endif return voltage_read; } @@ -300,7 +421,7 @@ static int wait_for_new_voltage(int vdd, int i2caddress) } /* - * this function keeps reading the voltage until it is stable or until the + * Blocks and reads the VID voltage until it stabilizes, or the * timeout expires */ static int wait_for_voltage_stable(int i2caddress) @@ -310,9 +431,9 @@ static int wait_for_voltage_stable(int i2caddress) vdd = read_voltage(i2caddress); udelay(NUM_READINGS * WAIT_FOR_ADC); - /* wait until voltage is stable */ vdd_current = read_voltage(i2caddress); - /* The maximum timeout is + /* + * The maximum timeout is * MAX_LOOP_WAIT_VOL_STABLE * NUM_READINGS * WAIT_FOR_ADC */ for (timeout = MAX_LOOP_WAIT_VOL_STABLE; @@ -327,12 +448,18 @@ static int wait_for_voltage_stable(int i2caddress) return vdd_current; } -/* Set the voltage to the IR chip */ +/* Sets the VID voltage using the IR36021 */ static int set_voltage_to_IR(int i2caddress, int vdd) { int wait, vdd_last; int ret; u8 vid; + DEVICE_HANDLE_T dev; + + /* Open device handle */ + ret = vid_get_device(i2caddress, &dev); + if (ret) + return ret; /* Compensate for a board specific voltage drop between regulator and * SoC before converting into an IR VID value @@ -344,20 +471,10 @@ static int set_voltage_to_IR(int i2caddress, int vdd) vid = DIV_ROUND_UP(vdd - 245, 5); #endif -#if !CONFIG_IS_ENABLED(DM_I2C) - ret = i2c_write(i2caddress, IR36021_LOOP1_MANUAL_ID_OFFSET, - 1, (void *)&vid, sizeof(vid)); -#else - struct udevice *dev; - - ret = i2c_get_chip_for_busnum(0, i2caddress, 1, &dev); - if (!ret) - ret = dm_i2c_write(dev, IR36021_LOOP1_MANUAL_ID_OFFSET, - (void *)&vid, sizeof(vid)); - -#endif + ret = I2C_WRITE(dev, IR36021_LOOP1_MANUAL_ID_OFFSET, (void *)&vid, + sizeof(vid)); if (ret) { - printf("VID: failed to write VID\n"); + printf("VID: failed to write new voltage\n"); return -1; } wait = wait_for_new_voltage(vdd, i2caddress); @@ -371,81 +488,59 @@ static int set_voltage_to_IR(int i2caddress, int vdd) debug("VID: Current voltage is %d mV\n", vdd_last); return vdd_last; } - #endif -#ifdef CONFIG_VOL_MONITOR_LTC3882_SET -/* this function sets the VDD and returns the value set */ -static int set_voltage_to_LTC(int i2caddress, int vdd) +#if defined(CONFIG_VOL_MONITOR_ISL68233_SET) || \ + defined(CONFIG_VOL_MONITOR_LTC3882_SET) +static int set_voltage_to_pmbus(int i2caddress, int vdd) { int ret, vdd_last, vdd_target = vdd; - int count = 100, temp = 0; + int count = MAX_LOOP_WAIT_NEW_VOL, temp = 0, multiplier; unsigned char value; - /* Scale up to the LTC resolution is 1/4096V */ - vdd = (vdd * 4096) / 1000; + /* The data to be sent with the PMBus command PAGE_PLUS_WRITE */ + u8 buffer[5] = { 0x04, PWM_CHANNEL0, PMBUS_CMD_VOUT_COMMAND, 0, 0 }; + DEVICE_HANDLE_T dev; - /* 5-byte buffer which needs to be sent following the - * PMBus command PAGE_PLUS_WRITE. - */ - u8 buff[5] = {0x04, PWM_CHANNEL0, PMBUS_CMD_VOUT_COMMAND, - vdd & 0xFF, (vdd & 0xFF00) >> 8}; + /* Open device handle */ + ret = vid_get_device(i2caddress, &dev); + if (ret) + return ret; + + /* Scale up to the proper value for the VOUT command, little endian */ + multiplier = get_pmbus_multiplier(dev); + vdd += board_vdd_drop_compensation(); + if (multiplier != MV_PER_V) + vdd = DIV_ROUND_UP(vdd * multiplier, MV_PER_V); + buffer[3] = vdd & 0xFF; + buffer[4] = (vdd & 0xFF00) >> 8; - /* Write the desired voltage code to the regulator */ -#if !CONFIG_IS_ENABLED(DM_I2C) /* Check write protect state */ - ret = i2c_read(I2C_VOL_MONITOR_ADDR, - PMBUS_CMD_WRITE_PROTECT, 1, - (void *)&value, sizeof(value)); + ret = I2C_READ(dev, PMBUS_CMD_WRITE_PROTECT, (void *)&value, + sizeof(value)); if (ret) goto exit; if (value != EN_WRITE_ALL_CMD) { value = EN_WRITE_ALL_CMD; - ret = i2c_write(I2C_VOL_MONITOR_ADDR, - PMBUS_CMD_WRITE_PROTECT, 1, + ret = I2C_WRITE(dev, PMBUS_CMD_WRITE_PROTECT, (void *)&value, sizeof(value)); if (ret) goto exit; } - ret = i2c_write(I2C_VOL_MONITOR_ADDR, - PMBUS_CMD_PAGE_PLUS_WRITE, 1, - (void *)&buff, sizeof(buff)); -#else - struct udevice *dev; - - ret = i2c_get_chip_for_busnum(0, I2C_VOL_MONITOR_ADDR, 1, &dev); - if (!ret) { - /* Check write protect state */ - ret = dm_i2c_read(dev, - PMBUS_CMD_WRITE_PROTECT, - (void *)&value, sizeof(value)); - if (ret) - goto exit; - - if (value != EN_WRITE_ALL_CMD) { - value = EN_WRITE_ALL_CMD; - ret = dm_i2c_write(dev, - PMBUS_CMD_WRITE_PROTECT, - (void *)&value, sizeof(value)); - if (ret) - goto exit; - } - - ret = dm_i2c_write(dev, PMBUS_CMD_PAGE_PLUS_WRITE, - (void *)&buff, sizeof(buff)); - } -#endif -exit: + /* Write the desired voltage code to the regulator */ + ret = I2C_WRITE(dev, PMBUS_CMD_PAGE_PLUS_WRITE, (void *)&buffer[0], + sizeof(buffer)); if (ret) { - printf("VID: I2C failed to write to the volatge regulator\n"); + printf("VID: I2C failed to write to the voltage regulator\n"); return -1; } - /* Wait for the volatge to get to the desired value */ +exit: + /* Wait for the voltage to get to the desired value */ do { - vdd_last = read_voltage_from_LTC(i2caddress); + vdd_last = read_voltage_from_pmbus(i2caddress); if (vdd_last < 0) { printf("VID: Couldn't read sensor abort VID adjust\n"); return -1; @@ -464,325 +559,80 @@ static int set_voltage(int i2caddress, int vdd) #ifdef CONFIG_VOL_MONITOR_IR36021_SET vdd_last = set_voltage_to_IR(i2caddress, vdd); -#elif defined CONFIG_VOL_MONITOR_LTC3882_SET - vdd_last = set_voltage_to_LTC(i2caddress, vdd); +#elif defined(CONFIG_VOL_MONITOR_ISL68233_SET) || \ + defined(CONFIG_VOL_MONITOR_LTC3882_SET) + vdd_last = set_voltage_to_pmbus(i2caddress, vdd); #else #error Specific voltage monitor must be defined #endif return vdd_last; } -#ifdef CONFIG_FSL_LSCH3 int adjust_vdd(ulong vdd_override) { int re_enable = disable_interrupts(); +#if defined(CONFIG_FSL_LSCH2) || defined(CONFIG_FSL_LSCH3) struct ccsr_gur *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR); - u32 fusesr; -#if defined(CONFIG_VOL_MONITOR_IR36021_SET) || \ - defined(CONFIG_VOL_MONITOR_IR36021_READ) - u8 vid, buf; #else - u8 vid; + ccsr_gur_t __iomem *gur = + (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); #endif - int vdd_target, vdd_current, vdd_last; - int ret, i2caddress = 0; + u8 vid; + u32 fusesr; + int vdd_current, vdd_last, vdd_target; + int ret, i2caddress = I2C_VOL_MONITOR_ADDR; unsigned long vdd_string_override; char *vdd_string; -#if defined(CONFIG_ARCH_LX2160A) || defined(CONFIG_ARCH_LX2162A) - static const u16 vdd[32] = { - 8250, - 7875, - 7750, - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 8000, - 8125, - 8250, - 0, /* reserved */ - 8500, - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - }; -#else -#ifdef CONFIG_ARCH_LS1088A - static const uint16_t vdd[32] = { - 10250, - 9875, - 9750, - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 9000, - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 10000, /* 1.0000V */ - 10125, - 10250, - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - }; -#else - static const uint16_t vdd[32] = { - 10500, - 0, /* reserved */ - 9750, - 0, /* reserved */ - 9500, - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 9000, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 10000, /* 1.0000V */ - 0, /* reserved */ - 10250, - 0, /* reserved */ - 10500, - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - 0, /* reserved */ - }; -#endif -#endif - struct vdd_drive { - u8 vid; - unsigned voltage; - }; - - ret = i2c_multiplexer_select_vid_channel(I2C_MUX_CH_VOL_MONITOR); - if (ret) { - debug("VID: I2C failed to switch channel\n"); - ret = -1; - goto exit; - } #if defined(CONFIG_VOL_MONITOR_IR36021_SET) || \ defined(CONFIG_VOL_MONITOR_IR36021_READ) - ret = find_ir_chip_on_i2c(); - if (ret < 0) { - printf("VID: Could not find voltage regulator on I2C.\n"); - ret = -1; - goto exit; - } else { - i2caddress = ret; - debug("VID: IR Chip found on I2C address 0x%02x\n", i2caddress); - } - - /* check IR chip work on Intel mode*/ -#if !CONFIG_IS_ENABLED(DM_I2C) - ret = i2c_read(i2caddress, - IR36021_INTEL_MODE_OOFSET, - 1, (void *)&buf, 1); -#else - struct udevice *dev; - - ret = i2c_get_chip_for_busnum(0, i2caddress, 1, &dev); - if (!ret) - ret = dm_i2c_read(dev, IR36021_INTEL_MODE_OOFSET, - (void *)&buf, 1); -#endif - if (ret) { - printf("VID: failed to read IR chip mode.\n"); - ret = -1; - goto exit; - } - - if ((buf & IR36021_MODE_MASK) != IR36021_INTEL_MODE) { - printf("VID: IR Chip is not used in Intel mode.\n"); - ret = -1; - goto exit; - } + u8 buf; + DEVICE_HANDLE_T dev; #endif - /* get the voltage ID from fuse status register */ + /* + * VID is used according to the table below + * --------------------------------------- + * | DA_V | + * |-------------------------------------| + * | 5b00000 | 5b00001-5b11110 | 5b11111 | + * ---------------+---------+-----------------+---------| + * | D | 5b00000 | NO VID | VID = DA_V | NO VID | + * | A |----------+---------+-----------------+---------| + * | _ | 5b00001 |VID = | VID = |VID = | + * | V | ~ | DA_V_ALT| DA_V_ALT | DA_A_VLT| + * | _ | 5b11110 | | | | + * | A |----------+---------+-----------------+---------| + * | L | 5b11111 | No VID | VID = DA_V | NO VID | + * | T | | | | | + * ------------------------------------------------------ + */ +#if defined(CONFIG_FSL_LSCH3) fusesr = in_le32(&gur->dcfg_fusesr); vid = (fusesr >> FSL_CHASSIS3_DCFG_FUSESR_ALTVID_SHIFT) & - FSL_CHASSIS3_DCFG_FUSESR_ALTVID_MASK; - if ((vid == 0) || (vid == FSL_CHASSIS3_DCFG_FUSESR_ALTVID_MASK)) { + FSL_CHASSIS3_DCFG_FUSESR_ALTVID_MASK; + if (vid == 0 || vid == FSL_CHASSIS3_DCFG_FUSESR_ALTVID_MASK) { vid = (fusesr >> FSL_CHASSIS3_DCFG_FUSESR_VID_SHIFT) & - FSL_CHASSIS3_DCFG_FUSESR_VID_MASK; - } - vdd_target = vdd[vid]; - - /* check override variable for overriding VDD */ - vdd_string = env_get(CONFIG_VID_FLS_ENV); - if (vdd_override == 0 && vdd_string && - !strict_strtoul(vdd_string, 10, &vdd_string_override)) - vdd_override = vdd_string_override; - - if (vdd_override >= VDD_MV_MIN && vdd_override <= VDD_MV_MAX) { - vdd_target = vdd_override * 10; /* convert to 1/10 mV */ - debug("VDD override is %lu\n", vdd_override); - } else if (vdd_override != 0) { - printf("Invalid value.\n"); + FSL_CHASSIS3_DCFG_FUSESR_VID_MASK; } - - /* divide and round up by 10 to get a value in mV */ - vdd_target = DIV_ROUND_UP(vdd_target, 10); - if (vdd_target == 0) { - debug("VID: VID not used\n"); - ret = 0; - goto exit; - } else if (vdd_target < VDD_MV_MIN || vdd_target > VDD_MV_MAX) { - /* Check vdd_target is in valid range */ - printf("VID: Target VID %d mV is not in range.\n", - vdd_target); - ret = -1; - goto exit; - } else { - debug("VID: vid = %d mV\n", vdd_target); - } - - /* - * Read voltage monitor to check real voltage. - */ - vdd_last = read_voltage(i2caddress); - if (vdd_last < 0) { - printf("VID: Couldn't read sensor abort VID adjustment\n"); - ret = -1; - goto exit; +#elif defined(CONFIG_FSL_LSCH2) + fusesr = in_be32(&gur->dcfg_fusesr); + vid = (fusesr >> FSL_CHASSIS2_DCFG_FUSESR_ALTVID_SHIFT) & + FSL_CHASSIS2_DCFG_FUSESR_ALTVID_MASK; + if (vid == 0 || vid == FSL_CHASSIS2_DCFG_FUSESR_ALTVID_MASK) { + vid = (fusesr >> FSL_CHASSIS2_DCFG_FUSESR_VID_SHIFT) & + FSL_CHASSIS2_DCFG_FUSESR_VID_MASK; } - vdd_current = vdd_last; - debug("VID: Core voltage is currently at %d mV\n", vdd_last); - -#ifdef CONFIG_VOL_MONITOR_LTC3882_SET - /* Set the target voltage */ - vdd_last = vdd_current = set_voltage(i2caddress, vdd_target); #else - /* - * Adjust voltage to at or one step above target. - * As measurements are less precise than setting the values - * we may run through dummy steps that cancel each other - * when stepping up and then down. - */ - while (vdd_last > 0 && - vdd_last < vdd_target) { - vdd_current += IR_VDD_STEP_UP; - vdd_last = set_voltage(i2caddress, vdd_current); - } - while (vdd_last > 0 && - vdd_last > vdd_target + (IR_VDD_STEP_DOWN - 1)) { - vdd_current -= IR_VDD_STEP_DOWN; - vdd_last = set_voltage(i2caddress, vdd_current); + fusesr = in_be32(&gur->dcfg_fusesr); + vid = (fusesr >> FSL_CORENET_DCFG_FUSESR_ALTVID_SHIFT) & + FSL_CORENET_DCFG_FUSESR_ALTVID_MASK; + if (vid == 0 || vid == FSL_CORENET_DCFG_FUSESR_ALTVID_MASK) { + vid = (fusesr >> FSL_CORENET_DCFG_FUSESR_VID_SHIFT) & + FSL_CORENET_DCFG_FUSESR_VID_MASK; } - #endif - if (board_adjust_vdd(vdd_target) < 0) { - ret = -1; - goto exit; - } - - if (vdd_last > 0) - printf("VID: Core voltage after adjustment is at %d mV\n", - vdd_last); - else - ret = -1; -exit: - if (re_enable) - enable_interrupts(); - i2c_multiplexer_select_vid_channel(I2C_MUX_CH_DEFAULT); - return ret; -} -#else /* !CONFIG_FSL_LSCH3 */ -int adjust_vdd(ulong vdd_override) -{ - int re_enable = disable_interrupts(); -#if defined(CONFIG_FSL_LSCH2) - struct ccsr_gur *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR); -#else - ccsr_gur_t __iomem *gur = - (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); -#endif - u32 fusesr; - u8 vid, buf; - int vdd_target, vdd_current, vdd_last; - int ret, i2caddress; - unsigned long vdd_string_override; - char *vdd_string; - static const uint16_t vdd[32] = { - 0, /* unused */ - 9875, /* 0.9875V */ - 9750, - 9625, - 9500, - 9375, - 9250, - 9125, - 9000, - 8875, - 8750, - 8625, - 8500, - 8375, - 8250, - 8125, - 10000, /* 1.0000V */ - 10125, - 10250, - 10375, - 10500, - 10625, - 10750, - 10875, - 11000, - 0, /* reserved */ - }; - struct vdd_drive { - u8 vid; - unsigned voltage; - }; + vdd_target = soc_get_fuse_vid((int)vid); ret = i2c_multiplexer_select_vid_channel(I2C_MUX_CH_VOL_MONITOR); if (ret) { @@ -790,6 +640,7 @@ int adjust_vdd(ulong vdd_override) ret = -1; goto exit; } + #if defined(CONFIG_VOL_MONITOR_IR36021_SET) || \ defined(CONFIG_VOL_MONITOR_IR36021_READ) ret = find_ir_chip_on_i2c(); @@ -802,19 +653,13 @@ int adjust_vdd(ulong vdd_override) debug("VID: IR Chip found on I2C address 0x%02x\n", i2caddress); } - /* check IR chip work on Intel mode*/ -#if !CONFIG_IS_ENABLED(DM_I2C) - ret = i2c_read(i2caddress, - IR36021_INTEL_MODE_OOFSET, - 1, (void *)&buf, 1); -#else - struct udevice *dev; + ret = vid_get_device(i2caddress, &dev); + if (ret) + return ret; - ret = i2c_get_chip_for_busnum(0, i2caddress, 1, &dev); - if (!ret) - ret = dm_i2c_read(dev, IR36021_INTEL_MODE_OOFSET, - (void *)&buf, 1); -#endif + /* check IR chip work on Intel mode */ + ret = I2C_READ(dev, IR36021_INTEL_MODE_OFFSET, (void *)&buf, + sizeof(buf)); if (ret) { printf("VID: failed to read IR chip mode.\n"); ret = -1; @@ -827,52 +672,18 @@ int adjust_vdd(ulong vdd_override) } #endif - /* get the voltage ID from fuse status register */ - fusesr = in_be32(&gur->dcfg_fusesr); - /* - * VID is used according to the table below - * --------------------------------------- - * | DA_V | - * |-------------------------------------| - * | 5b00000 | 5b00001-5b11110 | 5b11111 | - * ---------------+---------+-----------------+---------| - * | D | 5b00000 | NO VID | VID = DA_V | NO VID | - * | A |----------+---------+-----------------+---------| - * | _ | 5b00001 |VID = | VID = |VID = | - * | V | ~ | DA_V_ALT| DA_V_ALT | DA_A_VLT| - * | _ | 5b11110 | | | | - * | A |----------+---------+-----------------+---------| - * | L | 5b11111 | No VID | VID = DA_V | NO VID | - * | T | | | | | - * ------------------------------------------------------ - */ -#ifdef CONFIG_FSL_LSCH2 - vid = (fusesr >> FSL_CHASSIS2_DCFG_FUSESR_ALTVID_SHIFT) & - FSL_CHASSIS2_DCFG_FUSESR_ALTVID_MASK; - if ((vid == 0) || (vid == FSL_CHASSIS2_DCFG_FUSESR_ALTVID_MASK)) { - vid = (fusesr >> FSL_CHASSIS2_DCFG_FUSESR_VID_SHIFT) & - FSL_CHASSIS2_DCFG_FUSESR_VID_MASK; - } -#else - vid = (fusesr >> FSL_CORENET_DCFG_FUSESR_ALTVID_SHIFT) & - FSL_CORENET_DCFG_FUSESR_ALTVID_MASK; - if ((vid == 0) || (vid == FSL_CORENET_DCFG_FUSESR_ALTVID_MASK)) { - vid = (fusesr >> FSL_CORENET_DCFG_FUSESR_VID_SHIFT) & - FSL_CORENET_DCFG_FUSESR_VID_MASK; - } -#endif - vdd_target = vdd[vid]; - /* check override variable for overriding VDD */ vdd_string = env_get(CONFIG_VID_FLS_ENV); + debug("VID: Initial VDD value is %d mV\n", + DIV_ROUND_UP(vdd_target, 10)); if (vdd_override == 0 && vdd_string && !strict_strtoul(vdd_string, 10, &vdd_string_override)) vdd_override = vdd_string_override; if (vdd_override >= VDD_MV_MIN && vdd_override <= VDD_MV_MAX) { vdd_target = vdd_override * 10; /* convert to 1/10 mV */ - debug("VDD override is %lu\n", vdd_override); + debug("VID: VDD override is %lu\n", vdd_override); } else if (vdd_override != 0) { - printf("Invalid value.\n"); + printf("VID: Invalid VDD value.\n"); } if (vdd_target == 0) { debug("VID: VID not used\n"); @@ -895,6 +706,13 @@ int adjust_vdd(ulong vdd_override) } vdd_current = vdd_last; debug("VID: Core voltage is currently at %d mV\n", vdd_last); + +#if defined(CONFIG_VOL_MONITOR_LTC3882_SET) || \ + defined(CONFIG_VOL_MONITOR_ISL68233_SET) + /* Set the target voltage */ + vdd_current = set_voltage(i2caddress, vdd_target); + vdd_last = vdd_current; +#else /* * Adjust voltage to at or one step above target. * As measurements are less precise than setting the values @@ -911,6 +729,13 @@ int adjust_vdd(ulong vdd_override) vdd_current -= IR_VDD_STEP_DOWN; vdd_last = set_voltage(i2caddress, vdd_current); } +#endif + + /* Board specific adjustments */ + if (board_adjust_vdd(vdd_target) < 0) { + ret = -1; + goto exit; + } if (vdd_last > 0) printf("VID: Core voltage after adjustment is at %d mV\n", @@ -925,11 +750,10 @@ exit: return ret; } -#endif static int print_vdd(void) { - int vdd_last, ret, i2caddress = 0; + int vdd_last, ret, i2caddress = I2C_VOL_MONITOR_ADDR; ret = i2c_multiplexer_select_vid_channel(I2C_MUX_CH_VOL_MONITOR); if (ret) { diff --git a/board/freescale/common/vid.h b/board/freescale/common/vid.h index 5bbaecace4..b34c080b4b 100644 --- a/board/freescale/common/vid.h +++ b/board/freescale/common/vid.h @@ -7,16 +7,17 @@ #ifndef __VID_H_ #define __VID_H_ +/* IR36021 command codes */ #define IR36021_LOOP1_MANUAL_ID_OFFSET 0x6A #define IR36021_LOOP1_VOUT_OFFSET 0x9A #define IR36021_MFR_ID_OFFSET 0x92 #define IR36021_MFR_ID 0x43 -#define IR36021_INTEL_MODE_OOFSET 0x14 +#define IR36021_INTEL_MODE_OFFSET 0x14 #define IR36021_MODE_MASK 0x20 #define IR36021_INTEL_MODE 0x00 #define IR36021_AMD_MODE 0x20 -/* step the IR regulator in 5mV increments */ +/* Step the IR regulator in 5mV increments */ #define IR_VDD_STEP_DOWN 5 #define IR_VDD_STEP_UP 5 @@ -50,15 +51,16 @@ #define VDD_MV_MAX 925 #endif -#if defined(CONFIG_TARGET_LX2160AQDS) || defined(CONFIG_TARGET_LX2162AQDS) || \ -defined(CONFIG_TARGET_LX2160ARDB) /* PM Bus commands code for LTC3882*/ #define PWM_CHANNEL0 0x0 #define PMBUS_CMD_PAGE 0x0 #define PMBUS_CMD_READ_VOUT 0x8B +#define PMBUS_CMD_VOUT_MODE 0x20 #define PMBUS_CMD_VOUT_COMMAND 0x21 #define PMBUS_CMD_PAGE_PLUS_WRITE 0x05 +#if defined(CONFIG_TARGET_LX2160AQDS) || defined(CONFIG_TARGET_LX2162AQDS) || \ +defined(CONFIG_TARGET_LX2160ARDB) /* Voltage monitor on channel 2*/ #define I2C_VOL_MONITOR_BUS_V_OFFSET 0x2 #define I2C_VOL_MONITOR_BUS_V_OVF 0x1 @@ -68,5 +70,6 @@ defined(CONFIG_TARGET_LX2160ARDB) #endif int adjust_vdd(ulong vdd_override); +u16 soc_get_fuse_vid(int vid_index); #endif /* __VID_H_ */ diff --git a/board/freescale/ls1088a/ls1088a.c b/board/freescale/ls1088a/ls1088a.c index e76ea01914..f5dc449d89 100644 --- a/board/freescale/ls1088a/ls1088a.c +++ b/board/freescale/ls1088a/ls1088a.c @@ -186,6 +186,46 @@ int init_func_vid(void) return 0; } + +u16 soc_get_fuse_vid(int vid_index) +{ + static const u16 vdd[32] = { + 10250, + 9875, + 9750, + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 9000, + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 10000, /* 1.0000V */ + 10125, + 10250, + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + }; + + return vdd[vid_index]; +}; #endif int is_pb_board(void) diff --git a/board/freescale/ls2080ardb/ls2080ardb.c b/board/freescale/ls2080ardb/ls2080ardb.c index c5ae02bc93..3a026b0827 100644 --- a/board/freescale/ls2080ardb/ls2080ardb.c +++ b/board/freescale/ls2080ardb/ls2080ardb.c @@ -43,6 +43,48 @@ enum { MUX_TYPE_DSPI, }; +#ifdef CONFIG_VID +u16 soc_get_fuse_vid(int vid_index) +{ + static const u16 vdd[32] = { + 10500, + 0, /* reserved */ + 9750, + 0, /* reserved */ + 9500, + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 9000, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 10000, /* 1.0000V */ + 0, /* reserved */ + 10250, + 0, /* reserved */ + 10500, + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + }; + + return vdd[vid_index]; +}; +#endif + unsigned long long get_qixis_addr(void) { unsigned long long addr; diff --git a/board/freescale/lx2160a/lx2160a.c b/board/freescale/lx2160a/lx2160a.c index b32e487e76..47a7024f33 100644 --- a/board/freescale/lx2160a/lx2160a.c +++ b/board/freescale/lx2160a/lx2160a.c @@ -649,6 +649,48 @@ int misc_init_r(void) } #endif +#ifdef CONFIG_VID +u16 soc_get_fuse_vid(int vid_index) +{ + static const u16 vdd[32] = { + 8250, + 7875, + 7750, + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 8000, + 8125, + 8250, + 0, /* reserved */ + 8500, + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + 0, /* reserved */ + }; + + return vdd[vid_index]; +}; +#endif + #ifdef CONFIG_FSL_MC_ENET extern int fdt_fixup_board_phy(void *fdt); diff --git a/include/configs/ls1088aqds.h b/include/configs/ls1088aqds.h index 0dcf844303..4d04833c50 100644 --- a/include/configs/ls1088aqds.h +++ b/include/configs/ls1088aqds.h @@ -326,12 +326,6 @@ unsigned long get_board_ddr_clk(void); #define CONFIG_VOL_MONITOR_LTC3882_SET #define CONFIG_VOL_MONITOR_LTC3882_READ -/* PM Bus commands code for LTC3882*/ -#define PMBUS_CMD_PAGE 0x0 -#define PMBUS_CMD_READ_VOUT 0x8B -#define PMBUS_CMD_PAGE_PLUS_WRITE 0x05 -#define PMBUS_CMD_VOUT_COMMAND 0x21 - #define PWM_CHANNEL0 0x0 /* diff --git a/include/configs/ls1088ardb.h b/include/configs/ls1088ardb.h index f59a9f5574..6f36dd417a 100644 --- a/include/configs/ls1088ardb.h +++ b/include/configs/ls1088ardb.h @@ -203,7 +203,7 @@ #define CONFIG_SYS_LS_MC_BOOT_TIMEOUT_MS 5000 -#define I2C_MUX_CH_VOL_MONITOR 0xA +#define I2C_MUX_CH_VOL_MONITOR 0xA /* Voltage monitor on channel 2*/ #define I2C_VOL_MONITOR_ADDR 0x63 #define I2C_VOL_MONITOR_BUS_V_OFFSET 0x2 @@ -221,12 +221,6 @@ #define CONFIG_VOL_MONITOR_LTC3882_SET #define CONFIG_VOL_MONITOR_LTC3882_READ -/* PM Bus commands code for LTC3882*/ -#define PMBUS_CMD_PAGE 0x0 -#define PMBUS_CMD_READ_VOUT 0x8B -#define PMBUS_CMD_PAGE_PLUS_WRITE 0x05 -#define PMBUS_CMD_VOUT_COMMAND 0x21 - #define PWM_CHANNEL0 0x0 /* -- 2.39.5