stpmic1: add NVM update support in fuse command
authorPatrick Delaunay <patrick.delaunay@st.com>
Mon, 4 Feb 2019 10:26:22 +0000 (11:26 +0100)
committerPatrick Delaunay <patrick.delaunay@st.com>
Fri, 12 Apr 2019 14:09:13 +0000 (16:09 +0200)
Add functions to read/update the non volatile memory of STPMIC1
(8 bytes-register at 0xF8 address) and allow access
with fuse command (bank=1, word > 0xF8).

For example:

STM32MP> fuse read 1 0xf8 8
Reading bank 1:

Word 0x000000f8: 000000ee 00000092 000000c0 00000002
Word 0x000000fc: 000000f2 00000080 00000002 00000033

Signed-off-by: Patrick Delaunay <patrick.delaunay@st.com>
drivers/misc/stm32mp_fuse.c
drivers/power/pmic/stpmic1.c
include/power/stpmic1.h

index 33943a231b1008e935f741e7de9e35cd88d03f4f..8dc246b0dbe8292fc2991634f61d1a1203407252 100644 (file)
@@ -9,8 +9,10 @@
 #include <errno.h>
 #include <dm/device.h>
 #include <dm/uclass.h>
+#include <power/stpmic1.h>
 
 #define STM32MP_OTP_BANK       0
+#define STM32MP_NVM_BANK       1
 
 /*
  * The 'fuse' command API
@@ -34,6 +36,13 @@ int fuse_read(u32 bank, u32 word, u32 *val)
                ret = 0;
                break;
 
+#ifdef CONFIG_PMIC_STPMIC1
+       case STM32MP_NVM_BANK:
+               *val = 0;
+               ret = stpmic1_shadow_read_byte(word, (u8 *)val);
+               break;
+#endif /* CONFIG_PMIC_STPMIC1 */
+
        default:
                printf("stm32mp %s: wrong value for bank %i\n", __func__, bank);
                ret = -EINVAL;
@@ -62,6 +71,12 @@ int fuse_prog(u32 bank, u32 word, u32 val)
                ret = 0;
                break;
 
+#ifdef CONFIG_PMIC_STPMIC1
+       case STM32MP_NVM_BANK:
+               ret = stpmic1_nvm_write_byte(word, (u8 *)&val);
+               break;
+#endif /* CONFIG_PMIC_STPMIC1 */
+
        default:
                printf("stm32mp %s: wrong value for bank %i\n", __func__, bank);
                ret = -EINVAL;
@@ -89,6 +104,13 @@ int fuse_sense(u32 bank, u32 word, u32 *val)
                ret = 0;
                break;
 
+#ifdef CONFIG_PMIC_STPMIC1
+       case STM32MP_NVM_BANK:
+               *val = 0;
+               ret = stpmic1_nvm_read_byte(word, (u8 *)val);
+               break;
+#endif /* CONFIG_PMIC_STPMIC1 */
+
        default:
                printf("stm32mp %s: wrong value for bank %i\n", __func__, bank);
                ret = -EINVAL;
@@ -117,6 +139,12 @@ int fuse_override(u32 bank, u32 word, u32 val)
                ret = 0;
                break;
 
+#ifdef CONFIG_PMIC_STPMIC1
+       case STM32MP_NVM_BANK:
+               ret = stpmic1_shadow_write_byte(word, (u8 *)&val);
+               break;
+#endif /* CONFIG_PMIC_STPMIC1 */
+
        default:
                printf("stm32mp %s: wrong value for bank %i\n",
                       __func__, bank);
index c962160307b1df7f53c5cad3dea88ee9b76cf547..65296c5fc3a0dc26c334910e169e4f2431c44c4e 100644 (file)
 
 #define STPMIC1_NUM_OF_REGS 0x100
 
+#define STPMIC1_NVM_SIZE 8
+#define STPMIC1_NVM_POLL_TIMEOUT 100000
+#define STPMIC1_NVM_START_ADDRESS 0xf8
+
+enum pmic_nvm_op {
+       SHADOW_READ,
+       SHADOW_WRITE,
+       NVM_READ,
+       NVM_WRITE,
+};
+
 #if CONFIG_IS_ENABLED(DM_REGULATOR)
 static const struct pmic_child_info stpmic1_children_info[] = {
        { .prefix = "ldo", .driver = "stpmic1_ldo" },
@@ -101,6 +112,109 @@ U_BOOT_DRIVER(pmic_stpmic1) = {
        .ops = &stpmic1_ops,
 };
 
+#ifndef CONFIG_SPL_BUILD
+static int stpmic1_nvm_rw(u8 addr, u8 *buf, int buf_len, enum pmic_nvm_op op)
+{
+       struct udevice *dev;
+       unsigned long timeout;
+       u8 cmd = STPMIC1_NVM_CMD_READ;
+       int ret;
+
+       ret = uclass_get_device_by_driver(UCLASS_PMIC,
+                                         DM_GET_DRIVER(pmic_stpmic1), &dev);
+       if (ret)
+               /* No PMIC on power discrete board */
+               return -EOPNOTSUPP;
+
+       if (addr < STPMIC1_NVM_START_ADDRESS)
+               return -EACCES;
+
+       if (op == SHADOW_READ)
+               return pmic_read(dev, addr, buf, buf_len);
+
+       if (op == SHADOW_WRITE)
+               return pmic_write(dev, addr, buf, buf_len);
+
+       if (op == NVM_WRITE) {
+               cmd = STPMIC1_NVM_CMD_PROGRAM;
+
+               ret = pmic_write(dev, addr, buf, buf_len);
+               if (ret < 0)
+                       return ret;
+       }
+
+       ret = pmic_reg_read(dev, STPMIC1_NVM_CR);
+       if (ret < 0)
+               return ret;
+
+       ret = pmic_reg_write(dev, STPMIC1_NVM_CR, ret | cmd);
+       if (ret < 0)
+               return ret;
+
+       timeout = timer_get_us() + STPMIC1_NVM_POLL_TIMEOUT;
+       for (;;) {
+               ret = pmic_reg_read(dev, STPMIC1_NVM_SR);
+               if (ret < 0)
+                       return ret;
+
+               if (!(ret & STPMIC1_NVM_BUSY))
+                       break;
+
+               if (time_after(timer_get_us(), timeout))
+                       break;
+       }
+
+       if (ret & STPMIC1_NVM_BUSY)
+               return -ETIMEDOUT;
+
+       if (op == NVM_READ) {
+               ret = pmic_read(dev, addr, buf, buf_len);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+int stpmic1_shadow_read_byte(u8 addr, u8 *buf)
+{
+       return stpmic1_nvm_rw(addr, buf, 1, SHADOW_READ);
+}
+
+int stpmic1_shadow_write_byte(u8 addr, u8 *buf)
+{
+       return stpmic1_nvm_rw(addr, buf, 1, SHADOW_WRITE);
+}
+
+int stpmic1_nvm_read_byte(u8 addr, u8 *buf)
+{
+       return stpmic1_nvm_rw(addr, buf, 1, NVM_READ);
+}
+
+int stpmic1_nvm_write_byte(u8 addr, u8 *buf)
+{
+       return stpmic1_nvm_rw(addr, buf, 1, NVM_WRITE);
+}
+
+int stpmic1_nvm_read_all(u8 *buf, int buf_len)
+{
+       if (buf_len != STPMIC1_NVM_SIZE)
+               return -EINVAL;
+
+       return stpmic1_nvm_rw(STPMIC1_NVM_START_ADDRESS,
+                            buf, buf_len, NVM_READ);
+}
+
+int stpmic1_nvm_write_all(u8 *buf, int buf_len)
+{
+       if (buf_len != STPMIC1_NVM_SIZE)
+               return -EINVAL;
+
+       return stpmic1_nvm_rw(STPMIC1_NVM_START_ADDRESS,
+                            buf, buf_len, NVM_WRITE);
+}
+#endif /* CONFIG_SPL_BUILD */
+
 #ifdef CONFIG_SYSRESET
 static int stpmic1_sysreset_request(struct udevice *dev, enum sysreset_t type)
 {
index ea91b75cfd21aa43daba927d349063f4ff8a1fa9..0e6721d852afd31a57ad3192a6508df0fb25c781 100644 (file)
@@ -107,4 +107,11 @@ enum {
        STPMIC1_PWR_SW2,
        STPMIC1_MAX_PWR_SW,
 };
+
+int stpmic1_shadow_read_byte(u8 addr, u8 *buf);
+int stpmic1_shadow_write_byte(u8 addr, u8 *buf);
+int stpmic1_nvm_read_byte(u8 addr, u8 *buf);
+int stpmic1_nvm_write_byte(u8 addr, u8 *buf);
+int stpmic1_nvm_read_all(u8 *buf, int buf_len);
+int stpmic1_nvm_write_all(u8 *buf, int buf_len);
 #endif