Merge tag 'u-boot-atmel-fixes-2021.01-b' of https://gitlab.denx.de/u-boot/custodians...
[platform/kernel/u-boot.git] / board / freescale / common / vid.c
index d6d1bfc..2617f61 100644 (file)
@@ -1,12 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright 2014 Freescale Semiconductor, Inc.
- *
- * SPDX-License-Identifier:     GPL-2.0+
+ * Copyright 2020 NXP
  */
 
 #include <common.h>
 #include <command.h>
+#include <env.h>
 #include <i2c.h>
+#include <irq_func.h>
+#include <log.h>
 #include <asm/io.h>
 #ifdef CONFIG_FSL_LSCH2
 #include <asm/arch/immap_lsch2.h>
 #else
 #include <asm/immap_85xx.h>
 #endif
+#include <linux/delay.h>
 #include "vid.h"
 
-DECLARE_GLOBAL_DATA_PTR;
-
 int __weak i2c_multiplexer_select_vid_channel(u8 channel)
 {
        return 0;
@@ -34,6 +36,16 @@ int __weak board_vdd_drop_compensation(void)
 }
 
 /*
+ * Board specific settings for specific voltage value
+ */
+int __weak board_adjust_vdd(int vdd)
+{
+       return 0;
+}
+
+#if defined(CONFIG_VOL_MONITOR_IR36021_SET) || \
+       defined(CONFIG_VOL_MONITOR_IR36021_READ)
+/*
  * Get the i2c address configuration for the IR regulator chip
  *
  * There are some variance in the RDB HW regarding the I2C address configuration
@@ -53,18 +65,29 @@ static int find_ir_chip_on_i2c(void)
        u8 byte;
        int i;
        const int ir_i2c_addr[] = {0x38, 0x08, 0x09};
+#ifdef CONFIG_DM_I2C
+       struct udevice *dev;
+#endif
 
        /* Check all the address */
        for (i = 0; i < (sizeof(ir_i2c_addr)/sizeof(ir_i2c_addr[0])); i++) {
                i2caddress = ir_i2c_addr[i];
+#ifndef CONFIG_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;
        }
        return -1;
 }
+#endif
 
 /* Maximum loop count waiting for new voltage to take effect */
 #define MAX_LOOP_WAIT_NEW_VOL          100
@@ -94,11 +117,21 @@ static int read_voltage_from_INA220(int i2caddress)
        int i, ret, voltage_read = 0;
        u16 vol_mon;
        u8 buf[2];
+#ifdef CONFIG_DM_I2C
+       struct udevice *dev;
+#endif
 
        for (i = 0; i < NUM_READINGS; i++) {
+#ifndef CONFIG_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
                if (ret) {
                        printf("VID: failed to read core voltage\n");
                        return ret;
@@ -127,11 +160,21 @@ static int read_voltage_from_IR(int i2caddress)
        int i, ret, voltage_read = 0;
        u16 vol_mon;
        u8 buf;
+#ifdef CONFIG_DM_I2C
+       struct udevice *dev;
+#endif
 
        for (i = 0; i < NUM_READINGS; i++) {
+#ifndef CONFIG_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
                if (ret) {
                        printf("VID: failed to read vcpu\n");
                        return ret;
@@ -163,6 +206,52 @@ 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;
+
+#ifndef CONFIG_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;
+
+       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
+       if (ret) {
+               printf("VID: failed to select VDD Page 0\n");
+               return ret;
+       }
+
+#ifndef CONFIG_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);
+       if (ret) {
+               printf("VID: failed to read the volatge\n");
+               return ret;
+       }
+#endif
+       if (ret) {
+               printf("VID: failed to read the volatge\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;
+}
+#endif
+
 static int read_voltage(int i2caddress)
 {
        int voltage_read;
@@ -170,12 +259,15 @@ static int read_voltage(int i2caddress)
        voltage_read = read_voltage_from_INA220(i2caddress);
 #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);
 #else
        return -1;
 #endif
        return voltage_read;
 }
 
+#ifdef CONFIG_VOL_MONITOR_IR36021_SET
 /*
  * We need to calculate how long before the voltage stops to drop
  * or increase. It returns with the loop count. Each loop takes
@@ -235,7 +327,6 @@ static int wait_for_voltage_stable(int i2caddress)
        return vdd_current;
 }
 
-#ifdef CONFIG_VOL_MONITOR_IR36021_SET
 /* Set the voltage to the IR chip */
 static int set_voltage_to_IR(int i2caddress, int vdd)
 {
@@ -253,8 +344,18 @@ static int set_voltage_to_IR(int i2caddress, int vdd)
        vid = DIV_ROUND_UP(vdd - 245, 5);
 #endif
 
+#ifndef CONFIG_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
        if (ret) {
                printf("VID: failed to write VID\n");
                return -1;
@@ -270,6 +371,91 @@ 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)
+{
+       int ret, vdd_last, vdd_target = vdd;
+       int count = 100, temp = 0;
+       unsigned char value;
+
+       /* Scale up to the LTC resolution is 1/4096V */
+       vdd = (vdd * 4096) / 1000;
+
+       /* 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};
+
+       /* Write the desired voltage code to the regulator */
+#ifndef CONFIG_DM_I2C
+       /* Check write protect state */
+       ret = i2c_read(I2C_VOL_MONITOR_ADDR,
+                      PMBUS_CMD_WRITE_PROTECT, 1,
+                      (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,
+                               (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:
+       if (ret) {
+               printf("VID: I2C failed to write to the volatge regulator\n");
+               return -1;
+       }
+
+       /* Wait for the volatge to get to the desired value */
+       do {
+               vdd_last = read_voltage_from_LTC(i2caddress);
+               if (vdd_last < 0) {
+                       printf("VID: Couldn't read sensor abort VID adjust\n");
+                       return -1;
+               }
+               count--;
+               temp = vdd_last - vdd_target;
+       } while ((abs(temp) > 2)  && (count > 0));
+
+       return vdd_last;
+}
 #endif
 
 static int set_voltage(int i2caddress, int vdd)
@@ -278,6 +464,8 @@ 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);
 #else
        #error Specific voltage monitor must be defined
 #endif
@@ -290,11 +478,89 @@ int adjust_vdd(ulong vdd_override)
        int re_enable = disable_interrupts();
        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;
+#endif
        int vdd_target, vdd_current, vdd_last;
-       int ret, i2caddress;
+       int ret, i2caddress = 0;
        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 */
@@ -304,7 +570,7 @@ int adjust_vdd(ulong vdd_override)
                0,      /* reserved */
                0,      /* reserved */
                0,      /* reserved */
-               0,      /* reserved */
+               9000,      /* reserved */
                0,      /* reserved */
                0,      /* reserved */
                0,      /* reserved */
@@ -329,6 +595,8 @@ int adjust_vdd(ulong vdd_override)
                0,      /* reserved */
                0,      /* reserved */
        };
+#endif
+#endif
        struct vdd_drive {
                u8 vid;
                unsigned voltage;
@@ -340,6 +608,8 @@ 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();
        if (ret < 0) {
                printf("VID: Could not find voltage regulator on I2C.\n");
@@ -351,19 +621,30 @@ int adjust_vdd(ulong vdd_override)
        }
 
        /* check IR chip work on Intel mode*/
+#ifndef CONFIG_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;
        }
+#endif
 
        /* get the voltage ID from fuse status register */
        fusesr = in_le32(&gur->dcfg_fusesr);
@@ -415,6 +696,11 @@ int adjust_vdd(ulong vdd_override)
        }
        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
@@ -432,6 +718,12 @@ int adjust_vdd(ulong vdd_override)
                vdd_last = set_voltage(i2caddress, vdd_current);
        }
 
+#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);
@@ -498,6 +790,8 @@ 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();
        if (ret < 0) {
                printf("VID: Could not find voltage regulator on I2C.\n");
@@ -509,9 +803,18 @@ int adjust_vdd(ulong vdd_override)
        }
 
        /* check IR chip work on Intel mode*/
+#ifndef CONFIG_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;
@@ -522,6 +825,7 @@ int adjust_vdd(ulong vdd_override)
                ret = -1;
                goto exit;
        }
+#endif
 
        /* get the voltage ID from fuse status register */
        fusesr = in_be32(&gur->dcfg_fusesr);
@@ -625,13 +929,15 @@ exit:
 
 static int print_vdd(void)
 {
-       int vdd_last, ret, i2caddress;
+       int vdd_last, ret, i2caddress = 0;
 
        ret = i2c_multiplexer_select_vid_channel(I2C_MUX_CH_VOL_MONITOR);
        if (ret) {
                debug("VID : I2c failed to switch channel\n");
                return -1;
        }
+#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");
@@ -640,6 +946,7 @@ static int print_vdd(void)
                i2caddress = ret;
                debug("VID: IR Chip found on I2C address 0x%02x\n", i2caddress);
        }
+#endif
 
        /*
         * Read voltage monitor to check real voltage.
@@ -657,9 +964,9 @@ exit:
 
 }
 
-static int do_vdd_override(cmd_tbl_t *cmdtp,
+static int do_vdd_override(struct cmd_tbl *cmdtp,
                           int flag, int argc,
-                          char * const argv[])
+                          char *const argv[])
 {
        ulong override;
 
@@ -673,9 +980,8 @@ static int do_vdd_override(cmd_tbl_t *cmdtp,
        return 0;
 }
 
-static int do_vdd_read(cmd_tbl_t *cmdtp,
-                        int flag, int argc,
-                        char * const argv[])
+static int do_vdd_read(struct cmd_tbl *cmdtp, int flag, int argc,
+                      char *const argv[])
 {
        if (argc < 1)
                return CMD_RET_USAGE;