stm32mp1: Add PMIC support
If a PMIC companion chip is present on board, it has to be configured
for regulators supplies.
This check is done with board DT configuration.
Signed-off-by: Yann Gautier <yann.gautier@st.com>
Signed-off-by: Pascal Paillet <p.paillet@st.com>
diff --git a/drivers/st/pmic/stm32_i2c.c b/drivers/st/pmic/stm32_i2c.c
new file mode 100644
index 0000000..0980139
--- /dev/null
+++ b/drivers/st/pmic/stm32_i2c.c
@@ -0,0 +1,851 @@
+/*
+ * Copyright (c) 2016-2018, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <delay_timer.h>
+#include <errno.h>
+#include <mmio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stm32_i2c.h>
+
+/* STM32 I2C registers offsets */
+#define I2C_CR1 0x00U
+#define I2C_CR2 0x04U
+#define I2C_OAR1 0x08U
+#define I2C_OAR2 0x0CU
+#define I2C_TIMINGR 0x10U
+#define I2C_TIMEOUTR 0x14U
+#define I2C_ISR 0x18U
+#define I2C_ICR 0x1CU
+#define I2C_PECR 0x20U
+#define I2C_RXDR 0x24U
+#define I2C_TXDR 0x28U
+
+#define MAX_DELAY 0xFFFFFFFFU
+
+/* I2C TIMING clear register Mask */
+#define TIMING_CLEAR_MASK 0xF0FFFFFFU
+/* Timeout 25 ms */
+#define I2C_TIMEOUT_BUSY 25U
+
+#define MAX_NBYTE_SIZE 255U
+
+static int i2c_request_memory_write(struct i2c_handle_s *hi2c,
+ uint16_t dev_addr, uint16_t mem_addr,
+ uint16_t mem_add_size, uint32_t timeout,
+ uint32_t tick_start);
+static int i2c_request_memory_read(struct i2c_handle_s *hi2c, uint16_t dev_addr,
+ uint16_t mem_addr, uint16_t mem_add_size,
+ uint32_t timeout, uint32_t tick_start);
+
+/* Private functions to handle flags during polling transfer */
+static int i2c_wait_flag(struct i2c_handle_s *hi2c, uint32_t flag,
+ uint8_t awaited_value, uint32_t timeout,
+ uint32_t tick_start);
+static int i2c_wait_txis(struct i2c_handle_s *hi2c, uint32_t timeout,
+ uint32_t tick_start);
+static int i2c_wait_stop(struct i2c_handle_s *hi2c, uint32_t timeout,
+ uint32_t tick_start);
+static int i2c_ack_failed(struct i2c_handle_s *hi2c, uint32_t timeout,
+ uint32_t tick_start);
+
+/* Private function to flush TXDR register */
+static void i2c_flush_txdr(struct i2c_handle_s *hi2c);
+
+/* Private function to start, restart or stop a transfer */
+static void i2c_transfer_config(struct i2c_handle_s *hi2c, uint16_t dev_addr,
+ uint16_t size, uint32_t i2c_mode,
+ uint32_t request);
+
+/*
+ * @brief Initialize the I2C device.
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
+ * the configuration information for the specified I2C.
+ * @retval 0 if OK, negative value else
+ */
+int stm32_i2c_init(struct i2c_handle_s *hi2c)
+{
+ if (hi2c == NULL) {
+ return -ENOENT;
+ }
+
+ if (hi2c->i2c_state == I2C_STATE_RESET) {
+ hi2c->lock = 0;
+ }
+
+ hi2c->i2c_state = I2C_STATE_BUSY;
+
+ /* Disable the selected I2C peripheral */
+ mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR1, I2C_CR1_PE);
+
+ /* Configure I2Cx: Frequency range */
+ mmio_write_32(hi2c->i2c_base_addr + I2C_TIMINGR,
+ hi2c->i2c_init.timing & TIMING_CLEAR_MASK);
+
+ /* Disable Own Address1 before set the Own Address1 configuration */
+ mmio_clrbits_32(hi2c->i2c_base_addr + I2C_OAR1, I2C_OAR1_OA1EN);
+
+ /* Configure I2Cx: Own Address1 and ack own address1 mode */
+ if (hi2c->i2c_init.addressing_mode == I2C_ADDRESSINGMODE_7BIT) {
+ mmio_write_32(hi2c->i2c_base_addr + I2C_OAR1,
+ I2C_OAR1_OA1EN | hi2c->i2c_init.own_address1);
+ } else { /* I2C_ADDRESSINGMODE_10BIT */
+ mmio_write_32(hi2c->i2c_base_addr + I2C_OAR1,
+ I2C_OAR1_OA1EN | I2C_OAR1_OA1MODE |
+ hi2c->i2c_init.own_address1);
+ }
+
+ /* Configure I2Cx: Addressing Master mode */
+ if (hi2c->i2c_init.addressing_mode == I2C_ADDRESSINGMODE_10BIT) {
+ mmio_write_32(hi2c->i2c_base_addr + I2C_CR2, I2C_CR2_ADD10);
+ }
+
+ /*
+ * Enable the AUTOEND by default, and enable NACK
+ * (should be disable only during Slave process)
+ */
+ mmio_setbits_32(hi2c->i2c_base_addr + I2C_CR2,
+ I2C_CR2_AUTOEND | I2C_CR2_NACK);
+
+ /* Disable Own Address2 before set the Own Address2 configuration */
+ mmio_clrbits_32(hi2c->i2c_base_addr + I2C_OAR2, I2C_DUALADDRESS_ENABLE);
+
+ /* Configure I2Cx: Dual mode and Own Address2 */
+ mmio_write_32(hi2c->i2c_base_addr + I2C_OAR2,
+ hi2c->i2c_init.dual_address_mode |
+ hi2c->i2c_init.own_address2 |
+ (hi2c->i2c_init.own_address2_masks << 8));
+
+ /* Configure I2Cx: Generalcall and NoStretch mode */
+ mmio_write_32(hi2c->i2c_base_addr + I2C_CR1,
+ hi2c->i2c_init.general_call_mode |
+ hi2c->i2c_init.no_stretch_mode);
+
+ /* Enable the selected I2C peripheral */
+ mmio_setbits_32(hi2c->i2c_base_addr + I2C_CR1, I2C_CR1_PE);
+
+ hi2c->i2c_err = I2C_ERROR_NONE;
+ hi2c->i2c_state = I2C_STATE_READY;
+ hi2c->i2c_mode = I2C_MODE_NONE;
+
+ return 0;
+}
+
+/*
+ * @brief Write an amount of data in blocking mode to a specific memory address
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
+ * the configuration information for the specified I2C.
+ * @param dev_addr: Target device address
+ * @param mem_addr: Internal memory address
+ * @param mem_add_size: size of internal memory address
+ * @param p_data: Pointer to data buffer
+ * @param size: Amount of data to be sent
+ * @param timeout: timeout duration
+ * @retval 0 if OK, negative value else
+ */
+int stm32_i2c_mem_write(struct i2c_handle_s *hi2c, uint16_t dev_addr,
+ uint16_t mem_addr, uint16_t mem_add_size,
+ uint8_t *p_data, uint16_t size, uint32_t timeout)
+{
+ uint32_t tickstart;
+
+ if ((hi2c->i2c_state != I2C_STATE_READY) || (hi2c->lock != 0U)) {
+ return -EBUSY;
+ }
+
+ if ((p_data == NULL) || (size == 0U)) {
+ return -EINVAL;
+ }
+
+ hi2c->lock = 1;
+
+ tickstart = (uint32_t)read_cntpct_el0();
+
+ if (i2c_wait_flag(hi2c, I2C_FLAG_BUSY, 1, I2C_TIMEOUT_BUSY,
+ tickstart) != 0) {
+ return -EIO;
+ }
+
+ hi2c->i2c_state = I2C_STATE_BUSY_TX;
+ hi2c->i2c_mode = I2C_MODE_MEM;
+ hi2c->i2c_err = I2C_ERROR_NONE;
+
+ hi2c->p_buff = p_data;
+ hi2c->xfer_count = size;
+
+ /* Send Slave Address and Memory Address */
+ if (i2c_request_memory_write(hi2c, dev_addr, mem_addr, mem_add_size,
+ timeout, tickstart) != 0) {
+ hi2c->lock = 0;
+ return -EIO;
+ }
+
+ /*
+ * Set NBYTES to write and reload
+ * if hi2c->xfer_count > MAX_NBYTE_SIZE
+ */
+ if (hi2c->xfer_count > MAX_NBYTE_SIZE) {
+ hi2c->xfer_size = MAX_NBYTE_SIZE;
+ i2c_transfer_config(hi2c, dev_addr, hi2c->xfer_size,
+ I2C_RELOAD_MODE, I2C_NO_STARTSTOP);
+ } else {
+ hi2c->xfer_size = hi2c->xfer_count;
+ i2c_transfer_config(hi2c, dev_addr, hi2c->xfer_size,
+ I2C_AUTOEND_MODE, I2C_NO_STARTSTOP);
+ }
+
+ do {
+ if (i2c_wait_txis(hi2c, timeout, tickstart) != 0) {
+ return -EIO;
+ }
+
+ mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR, *hi2c->p_buff);
+ hi2c->p_buff++;
+ hi2c->xfer_count--;
+ hi2c->xfer_size--;
+
+ if ((hi2c->xfer_count != 0U) && (hi2c->xfer_size == 0U)) {
+ /* Wait until TCR flag is set */
+ if (i2c_wait_flag(hi2c, I2C_FLAG_TCR, 0, timeout,
+ tickstart) != 0) {
+ return -EIO;
+ }
+
+ if (hi2c->xfer_count > MAX_NBYTE_SIZE) {
+ hi2c->xfer_size = MAX_NBYTE_SIZE;
+ i2c_transfer_config(hi2c, dev_addr,
+ hi2c->xfer_size,
+ I2C_RELOAD_MODE,
+ I2C_NO_STARTSTOP);
+ } else {
+ hi2c->xfer_size = hi2c->xfer_count;
+ i2c_transfer_config(hi2c, dev_addr,
+ hi2c->xfer_size,
+ I2C_AUTOEND_MODE,
+ I2C_NO_STARTSTOP);
+ }
+ }
+
+ } while (hi2c->xfer_count > 0U);
+
+ /*
+ * No need to Check TC flag, with AUTOEND mode the stop
+ * is automatically generated.
+ * Wait until STOPF flag is reset.
+ */
+ if (i2c_wait_stop(hi2c, timeout, tickstart) != 0) {
+ return -EIO;
+ }
+
+ mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, I2C_FLAG_STOPF);
+
+ mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR2, I2C_RESET_CR2);
+
+ hi2c->i2c_state = I2C_STATE_READY;
+ hi2c->i2c_mode = I2C_MODE_NONE;
+
+ hi2c->lock = 0;
+
+ return 0;
+}
+
+/*
+ * @brief Read an amount of data in blocking mode from a specific memory
+ * address
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
+ * the configuration information for the specified I2C.
+ * @param dev_addr: Target device address
+ * @param mem_addr: Internal memory address
+ * @param mem_add_size: size of internal memory address
+ * @param p_data: Pointer to data buffer
+ * @param size: Amount of data to be sent
+ * @param timeout: timeout duration
+ * @retval 0 if OK, negative value else
+ */
+int stm32_i2c_mem_read(struct i2c_handle_s *hi2c, uint16_t dev_addr,
+ uint16_t mem_addr, uint16_t mem_add_size,
+ uint8_t *p_data, uint16_t size, uint32_t timeout)
+{
+ uint32_t tickstart;
+
+ if ((hi2c->i2c_state != I2C_STATE_READY) || (hi2c->lock != 0U)) {
+ return -EBUSY;
+ }
+
+ if ((p_data == NULL) || (size == 0U)) {
+ return -EINVAL;
+ }
+
+ hi2c->lock = 1;
+
+ tickstart = (uint32_t)read_cntpct_el0();
+
+ if (i2c_wait_flag(hi2c, I2C_FLAG_BUSY, 1, I2C_TIMEOUT_BUSY,
+ tickstart) != 0) {
+ return -EIO;
+ }
+
+ hi2c->i2c_state = I2C_STATE_BUSY_RX;
+ hi2c->i2c_mode = I2C_MODE_MEM;
+ hi2c->i2c_err = I2C_ERROR_NONE;
+
+ hi2c->p_buff = p_data;
+ hi2c->xfer_count = size;
+
+ /* Send Slave Address and Memory Address */
+ if (i2c_request_memory_read(hi2c, dev_addr, mem_addr, mem_add_size,
+ timeout, tickstart) != 0) {
+ hi2c->lock = 0;
+ return -EIO;
+ }
+
+ /*
+ * Send Slave Address.
+ * Set NBYTES to write and reload if hi2c->xfer_count > MAX_NBYTE_SIZE
+ * and generate RESTART.
+ */
+ if (hi2c->xfer_count > MAX_NBYTE_SIZE) {
+ hi2c->xfer_size = MAX_NBYTE_SIZE;
+ i2c_transfer_config(hi2c, dev_addr, hi2c->xfer_size,
+ I2C_RELOAD_MODE, I2C_GENERATE_START_READ);
+ } else {
+ hi2c->xfer_size = hi2c->xfer_count;
+ i2c_transfer_config(hi2c, dev_addr, hi2c->xfer_size,
+ I2C_AUTOEND_MODE, I2C_GENERATE_START_READ);
+ }
+
+ do {
+ if (i2c_wait_flag(hi2c, I2C_FLAG_RXNE, 0, timeout,
+ tickstart) != 0) {
+ return -EIO;
+ }
+
+ *hi2c->p_buff = mmio_read_8(hi2c->i2c_base_addr + I2C_RXDR);
+ hi2c->p_buff++;
+ hi2c->xfer_size--;
+ hi2c->xfer_count--;
+
+ if ((hi2c->xfer_count != 0U) && (hi2c->xfer_size == 0U)) {
+ if (i2c_wait_flag(hi2c, I2C_FLAG_TCR, 0, timeout,
+ tickstart) != 0) {
+ return -EIO;
+ }
+
+ if (hi2c->xfer_count > MAX_NBYTE_SIZE) {
+ hi2c->xfer_size = MAX_NBYTE_SIZE;
+ i2c_transfer_config(hi2c, dev_addr,
+ hi2c->xfer_size,
+ I2C_RELOAD_MODE,
+ I2C_NO_STARTSTOP);
+ } else {
+ hi2c->xfer_size = hi2c->xfer_count;
+ i2c_transfer_config(hi2c, dev_addr,
+ hi2c->xfer_size,
+ I2C_AUTOEND_MODE,
+ I2C_NO_STARTSTOP);
+ }
+ }
+ } while (hi2c->xfer_count > 0U);
+
+ /*
+ * No need to Check TC flag, with AUTOEND mode the stop
+ * is automatically generated
+ * Wait until STOPF flag is reset
+ */
+ if (i2c_wait_stop(hi2c, timeout, tickstart) != 0) {
+ return -EIO;
+ }
+
+ mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, I2C_FLAG_STOPF);
+
+ mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR2, I2C_RESET_CR2);
+
+ hi2c->i2c_state = I2C_STATE_READY;
+ hi2c->i2c_mode = I2C_MODE_NONE;
+
+ hi2c->lock = 0;
+
+ return 0;
+}
+
+/*
+ * @brief Checks if target device is ready for communication.
+ * @note This function is used with Memory devices
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
+ * the configuration information for the specified I2C.
+ * @param dev_addr: Target device address
+ * @param trials: Number of trials
+ * @param timeout: timeout duration
+ * @retval 0 if OK, negative value else
+ */
+int stm32_i2c_is_device_ready(struct i2c_handle_s *hi2c,
+ uint16_t dev_addr, uint32_t trials,
+ uint32_t timeout)
+{
+ uint32_t i2c_trials = 0U;
+
+ if ((hi2c->i2c_state != I2C_STATE_READY) || (hi2c->lock != 0U)) {
+ return -EBUSY;
+ }
+
+ if ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) & I2C_FLAG_BUSY) !=
+ 0U) {
+ return -EBUSY;
+ }
+
+ hi2c->lock = 1;
+
+ hi2c->i2c_state = I2C_STATE_BUSY;
+ hi2c->i2c_err = I2C_ERROR_NONE;
+
+ do {
+ uint32_t tickstart;
+
+ /* Generate Start */
+ if (hi2c->i2c_init.addressing_mode == I2C_ADDRESSINGMODE_7BIT) {
+ mmio_write_32(hi2c->i2c_base_addr + I2C_CR2,
+ (((uint32_t)dev_addr & I2C_CR2_SADD) |
+ I2C_CR2_START | I2C_CR2_AUTOEND) &
+ ~I2C_CR2_RD_WRN);
+ } else {
+ mmio_write_32(hi2c->i2c_base_addr + I2C_CR2,
+ (((uint32_t)dev_addr & I2C_CR2_SADD) |
+ I2C_CR2_START | I2C_CR2_ADD10) &
+ ~I2C_CR2_RD_WRN);
+ }
+
+ /*
+ * No need to Check TC flag, with AUTOEND mode the stop
+ * is automatically generated
+ * Wait until STOPF flag is set or a NACK flag is set
+ */
+ tickstart = (uint32_t)read_cntpct_el0();
+ while (((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) &
+ (I2C_FLAG_STOPF | I2C_FLAG_AF)) == 0U) &&
+ (hi2c->i2c_state != I2C_STATE_TIMEOUT)) {
+ if (timeout != MAX_DELAY) {
+ if ((((uint32_t)read_cntpct_el0() - tickstart) >
+ timeout) || (timeout == 0U)) {
+ hi2c->i2c_state = I2C_STATE_READY;
+
+ hi2c->i2c_err |=
+ I2C_ERROR_TIMEOUT;
+
+ hi2c->lock = 0;
+
+ return -EIO;
+ }
+ }
+ }
+
+ /* Check if the NACKF flag has not been set */
+ if ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) &
+ I2C_FLAG_AF) == 0U) {
+ if (i2c_wait_flag(hi2c, I2C_FLAG_STOPF, 0, timeout,
+ tickstart) != 0) {
+ return -EIO;
+ }
+
+ mmio_write_32(hi2c->i2c_base_addr + I2C_ICR,
+ I2C_FLAG_STOPF);
+
+ hi2c->i2c_state = I2C_STATE_READY;
+
+ hi2c->lock = 0;
+
+ return 0;
+ }
+
+ if (i2c_wait_flag(hi2c, I2C_FLAG_STOPF, 0, timeout,
+ tickstart) != 0) {
+ return -EIO;
+ }
+
+ mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, I2C_FLAG_AF);
+
+ mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, I2C_FLAG_STOPF);
+
+ if (i2c_trials == trials) {
+ mmio_setbits_32(hi2c->i2c_base_addr + I2C_CR2,
+ I2C_CR2_STOP);
+
+ if (i2c_wait_flag(hi2c, I2C_FLAG_STOPF, 0, timeout,
+ tickstart) != 0) {
+ return -EIO;
+ }
+
+ mmio_write_32(hi2c->i2c_base_addr + I2C_ICR,
+ I2C_FLAG_STOPF);
+ }
+
+ i2c_trials++;
+ } while (i2c_trials < trials);
+
+ hi2c->i2c_state = I2C_STATE_READY;
+
+ hi2c->i2c_err |= I2C_ERROR_TIMEOUT;
+
+ hi2c->lock = 0;
+
+ return -EIO;
+}
+
+/*
+ * @brief Master sends target device address followed by internal memory
+ * address for write request.
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
+ * the configuration information for the specified I2C.
+ * @param dev_addr: Target device address
+ * @param mem_addr: Internal memory address
+ * @param mem_add_size: size of internal memory address
+ * @param timeout: timeout duration
+ * @param tick_start Tick start value
+ * @retval 0 if OK, negative value else
+ */
+static int i2c_request_memory_write(struct i2c_handle_s *hi2c,
+ uint16_t dev_addr, uint16_t mem_addr,
+ uint16_t mem_add_size, uint32_t timeout,
+ uint32_t tick_start)
+{
+ i2c_transfer_config(hi2c, dev_addr, mem_add_size, I2C_RELOAD_MODE,
+ I2C_GENERATE_START_WRITE);
+
+ if (i2c_wait_txis(hi2c, timeout, tick_start) != 0) {
+ return -EIO;
+ }
+
+ if (mem_add_size == I2C_MEMADD_SIZE_8BIT) {
+ /* Send Memory Address */
+ mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR,
+ (uint8_t)(mem_addr & 0x00FFU));
+ } else {
+ /* Send MSB of Memory Address */
+ mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR,
+ (uint8_t)((mem_addr & 0xFF00U) >> 8));
+
+ /* Wait until TXIS flag is set */
+ if (i2c_wait_txis(hi2c, timeout, tick_start) != 0) {
+ return -EIO;
+ }
+
+ /* Send LSB of Memory Address */
+ mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR,
+ (uint8_t)(mem_addr & 0x00FFU));
+ }
+
+ if (i2c_wait_flag(hi2c, I2C_FLAG_TCR, 0, timeout, tick_start) !=
+ 0) {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * @brief Master sends target device address followed by internal memory
+ * address for read request.
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
+ * the configuration information for the specified I2C.
+ * @param dev_addr: Target device address
+ * @param mem_addr: Internal memory address
+ * @param mem_add_size: size of internal memory address
+ * @param timeout: timeout duration
+ * @param tick_start Tick start value
+ * @retval 0 if OK, negative value else
+ */
+static int i2c_request_memory_read(struct i2c_handle_s *hi2c, uint16_t dev_addr,
+ uint16_t mem_addr, uint16_t mem_add_size,
+ uint32_t timeout, uint32_t tick_start)
+{
+ i2c_transfer_config(hi2c, dev_addr, mem_add_size, I2C_SOFTEND_MODE,
+ I2C_GENERATE_START_WRITE);
+
+ if (i2c_wait_txis(hi2c, timeout, tick_start) != 0) {
+ return -EIO;
+ }
+
+ if (mem_add_size == I2C_MEMADD_SIZE_8BIT) {
+ /* Send Memory Address */
+ mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR,
+ (uint8_t)(mem_addr & 0x00FFU));
+ } else {
+ /* Send MSB of Memory Address */
+ mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR,
+ (uint8_t)((mem_addr & 0xFF00U) >> 8));
+
+ /* Wait until TXIS flag is set */
+ if (i2c_wait_txis(hi2c, timeout, tick_start) != 0) {
+ return -EIO;
+ }
+
+ /* Send LSB of Memory Address */
+ mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR,
+ (uint8_t)(mem_addr & 0x00FFU));
+ }
+
+ if (i2c_wait_flag(hi2c, I2C_FLAG_TC, 0, timeout, tick_start) != 0) {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * @brief I2C Tx data register flush process.
+ * @param hi2c: I2C handle.
+ * @retval None
+ */
+static void i2c_flush_txdr(struct i2c_handle_s *hi2c)
+{
+ /*
+ * If a pending TXIS flag is set,
+ * write a dummy data in TXDR to clear it.
+ */
+ if ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) & I2C_FLAG_TXIS) !=
+ 0U) {
+ mmio_write_32(hi2c->i2c_base_addr + I2C_TXDR, 0);
+ }
+
+ /* Flush TX register if not empty */
+ if ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) & I2C_FLAG_TXE) ==
+ 0U) {
+ mmio_setbits_32(hi2c->i2c_base_addr + I2C_ISR,
+ I2C_FLAG_TXE);
+ }
+}
+
+/*
+ * @brief This function handles I2C Communication timeout.
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
+ * the configuration information for the specified I2C.
+ * @param flag: Specifies the I2C flag to check.
+ * @param awaited_value: The awaited bit value for the flag (0 or 1).
+ * @param timeout: timeout duration
+ * @param tick_start: Tick start value
+ * @retval 0 if OK, negative value else
+ */
+static int i2c_wait_flag(struct i2c_handle_s *hi2c, uint32_t flag,
+ uint8_t awaited_value, uint32_t timeout,
+ uint32_t tick_start)
+{
+ uint8_t flag_check;
+
+ do {
+ flag_check = ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) &
+ flag) == flag) ? 1U : 0U;
+
+ if (timeout != MAX_DELAY) {
+ if ((((uint32_t)read_cntpct_el0() - tick_start) >
+ timeout) || (timeout == 0U)) {
+ hi2c->i2c_err |= I2C_ERROR_TIMEOUT;
+ hi2c->i2c_state = I2C_STATE_READY;
+ hi2c->i2c_mode = I2C_MODE_NONE;
+
+ hi2c->lock = 0;
+ return -EIO;
+ }
+ }
+ } while (flag_check == awaited_value);
+
+ return 0;
+}
+
+/*
+ * @brief This function handles I2C Communication timeout for specific usage
+ * of TXIS flag.
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
+ * the configuration information for the specified I2C.
+ * @param timeout: timeout duration
+ * @param tick_start: Tick start value
+ * @retval 0 if OK, negative value else
+ */
+static int i2c_wait_txis(struct i2c_handle_s *hi2c, uint32_t timeout,
+ uint32_t tick_start)
+{
+ while ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) &
+ I2C_FLAG_TXIS) == 0U) {
+ if (i2c_ack_failed(hi2c, timeout, tick_start) != 0) {
+ return -EIO;
+ }
+
+ if (timeout != MAX_DELAY) {
+ if ((((uint32_t)read_cntpct_el0() - tick_start) >
+ timeout) || (timeout == 0U)) {
+ hi2c->i2c_err |= I2C_ERROR_TIMEOUT;
+ hi2c->i2c_state = I2C_STATE_READY;
+ hi2c->i2c_mode = I2C_MODE_NONE;
+
+ hi2c->lock = 0;
+
+ return -EIO;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * @brief This function handles I2C Communication timeout for specific
+ * usage of STOP flag.
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
+ * the configuration information for the specified I2C.
+ * @param timeout: timeout duration
+ * @param tick_start: Tick start value
+ * @retval 0 if OK, negative value else
+ */
+static int i2c_wait_stop(struct i2c_handle_s *hi2c, uint32_t timeout,
+ uint32_t tick_start)
+{
+ while ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) &
+ I2C_FLAG_STOPF) == 0U) {
+ if (i2c_ack_failed(hi2c, timeout, tick_start) != 0) {
+ return -EIO;
+ }
+
+ if ((((uint32_t)read_cntpct_el0() - tick_start) > timeout) ||
+ (timeout == 0U)) {
+ hi2c->i2c_err |= I2C_ERROR_TIMEOUT;
+ hi2c->i2c_state = I2C_STATE_READY;
+ hi2c->i2c_mode = I2C_MODE_NONE;
+
+ hi2c->lock = 0;
+
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * @brief This function handles Acknowledge failed detection during
+ * an I2C Communication.
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
+ * the configuration information for the specified I2C.
+ * @param timeout: timeout duration
+ * @param tick_start: Tick start value
+ * @retval 0 if OK, negative value else
+ */
+static int i2c_ack_failed(struct i2c_handle_s *hi2c, uint32_t timeout,
+ uint32_t tick_start)
+{
+ if ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) & I2C_FLAG_AF) == 0U) {
+ return 0;
+ }
+
+ /*
+ * Wait until STOP Flag is reset.
+ * AutoEnd should be initiate after AF.
+ */
+ while ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) &
+ I2C_FLAG_STOPF) == 0U) {
+ if (timeout != MAX_DELAY) {
+ if ((((uint32_t)read_cntpct_el0() - tick_start) >
+ timeout) || (timeout == 0U)) {
+ hi2c->i2c_err |= I2C_ERROR_TIMEOUT;
+ hi2c->i2c_state = I2C_STATE_READY;
+ hi2c->i2c_mode = I2C_MODE_NONE;
+
+ hi2c->lock = 0;
+
+ return -EIO;
+ }
+ }
+ }
+
+ mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, I2C_FLAG_AF);
+
+ mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, I2C_FLAG_STOPF);
+
+ i2c_flush_txdr(hi2c);
+
+ mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR2, I2C_RESET_CR2);
+
+ hi2c->i2c_err |= I2C_ERROR_AF;
+ hi2c->i2c_state = I2C_STATE_READY;
+ hi2c->i2c_mode = I2C_MODE_NONE;
+
+ hi2c->lock = 0;
+
+ return -EIO;
+}
+
+/*
+ * @brief Handles I2Cx communication when starting transfer or during transfer
+ * (TC or TCR flag are set).
+ * @param hi2c: I2C handle.
+ * @param dev_addr: Specifies the slave address to be programmed.
+ * @param size: Specifies the number of bytes to be programmed.
+ * This parameter must be a value between 0 and 255.
+ * @param i2c_mode: New state of the I2C START condition generation.
+ * This parameter can be one of the following values:
+ * @arg @ref I2C_RELOAD_MODE: Enable Reload mode .
+ * @arg @ref I2C_AUTOEND_MODE: Enable Automatic end mode.
+ * @arg @ref I2C_SOFTEND_MODE: Enable Software end mode.
+ * @param request: New state of the I2C START condition generation.
+ * This parameter can be one of the following values:
+ * @arg @ref I2C_NO_STARTSTOP: Don't Generate stop and start condition.
+ * @arg @ref I2C_GENERATE_STOP: Generate stop condition
+ * (size should be set to 0).
+ * @arg @ref I2C_GENERATE_START_READ: Generate Restart for read request.
+ * @arg @ref I2C_GENERATE_START_WRITE: Generate Restart for write request.
+ * @retval None
+ */
+static void i2c_transfer_config(struct i2c_handle_s *hi2c, uint16_t dev_addr,
+ uint16_t size, uint32_t i2c_mode,
+ uint32_t request)
+{
+ uint32_t clr_value, set_value;
+
+ clr_value = (I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD |
+ I2C_CR2_AUTOEND | I2C_CR2_START | I2C_CR2_STOP) |
+ (I2C_CR2_RD_WRN & (request >> (31U - I2C_CR2_RD_WRN_OFFSET)));
+
+ set_value = ((uint32_t)dev_addr & I2C_CR2_SADD) |
+ (((uint32_t)size << I2C_CR2_NBYTES_OFFSET) & I2C_CR2_NBYTES) |
+ i2c_mode | request;
+
+ mmio_clrsetbits_32(hi2c->i2c_base_addr + I2C_CR2, clr_value, set_value);
+}
+
+/*
+ * @brief Configure I2C Analog noise filter.
+ * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
+ * the configuration information for the specified I2Cx peripheral
+ * @param analog_filter: New state of the Analog filter.
+ * @retval 0 if OK, negative value else
+ */
+int stm32_i2c_config_analog_filter(struct i2c_handle_s *hi2c,
+ uint32_t analog_filter)
+{
+ if ((hi2c->i2c_state != I2C_STATE_READY) || (hi2c->lock != 0U)) {
+ return -EBUSY;
+ }
+
+ hi2c->lock = 1;
+
+ hi2c->i2c_state = I2C_STATE_BUSY;
+
+ /* Disable the selected I2C peripheral */
+ mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR1, I2C_CR1_PE);
+
+ /* Reset I2Cx ANOFF bit */
+ mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR1, I2C_CR1_ANFOFF);
+
+ /* Set analog filter bit*/
+ mmio_setbits_32(hi2c->i2c_base_addr + I2C_CR1, analog_filter);
+
+ /* Enable the selected I2C peripheral */
+ mmio_setbits_32(hi2c->i2c_base_addr + I2C_CR1, I2C_CR1_PE);
+
+ hi2c->i2c_state = I2C_STATE_READY;
+
+ hi2c->lock = 0;
+
+ return 0;
+}
diff --git a/drivers/st/pmic/stm32mp1_pmic.c b/drivers/st/pmic/stm32mp1_pmic.c
new file mode 100644
index 0000000..958de08
--- /dev/null
+++ b/drivers/st/pmic/stm32mp1_pmic.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2017-2018, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <debug.h>
+#include <delay_timer.h>
+#include <errno.h>
+#include <libfdt.h>
+#include <mmio.h>
+#include <mmio.h>
+#include <platform_def.h>
+#include <stdbool.h>
+#include <stm32_gpio.h>
+#include <stm32mp1_clk.h>
+#include <stm32mp1_dt.h>
+#include <stm32mp1_pmic.h>
+#include <stpmu1.h>
+#include <utils_def.h>
+
+/* I2C Timing hard-coded value, for I2C clock source is HSI at 64MHz */
+#define I2C_TIMING 0x10D07DB5
+
+#define I2C_TIMEOUT 0xFFFFF
+
+#define MASK_RESET_BUCK3 BIT(2)
+
+#define STPMU1_LDO12356_OUTPUT_MASK (uint8_t)(GENMASK(6, 2))
+#define STPMU1_LDO12356_OUTPUT_SHIFT 2
+#define STPMU1_LDO3_MODE (uint8_t)(BIT(7))
+#define STPMU1_LDO3_DDR_SEL 31U
+#define STPMU1_LDO3_1800000 (9U << STPMU1_LDO12356_OUTPUT_SHIFT)
+
+#define STPMU1_BUCK_OUTPUT_SHIFT 2
+#define STPMU1_BUCK3_1V8 (39U << STPMU1_BUCK_OUTPUT_SHIFT)
+
+#define STPMU1_DEFAULT_START_UP_DELAY_MS 1
+
+static struct i2c_handle_s i2c_handle;
+static uint32_t pmic_i2c_addr;
+
+static int dt_get_pmic_node(void *fdt)
+{
+ return fdt_node_offset_by_compatible(fdt, -1, "st,stpmu1");
+}
+
+bool dt_check_pmic(void)
+{
+ int node;
+ void *fdt;
+
+ if (fdt_get_address(&fdt) == 0) {
+ return false;
+ }
+
+ node = dt_get_pmic_node(fdt);
+ if (node < 0) {
+ VERBOSE("%s: No PMIC node found in DT\n", __func__);
+ return false;
+ }
+
+ return fdt_check_status(node);
+}
+
+static int dt_pmic_i2c_config(struct dt_node_info *i2c_info)
+{
+ int pmic_node, i2c_node;
+ void *fdt;
+ const fdt32_t *cuint;
+
+ if (fdt_get_address(&fdt) == 0) {
+ return -ENOENT;
+ }
+
+ pmic_node = dt_get_pmic_node(fdt);
+ if (pmic_node < 0) {
+ return -FDT_ERR_NOTFOUND;
+ }
+
+ cuint = fdt_getprop(fdt, pmic_node, "reg", NULL);
+ if (cuint == NULL) {
+ return -FDT_ERR_NOTFOUND;
+ }
+
+ pmic_i2c_addr = fdt32_to_cpu(*cuint) << 1;
+ if (pmic_i2c_addr > UINT16_MAX) {
+ return -EINVAL;
+ }
+
+ i2c_node = fdt_parent_offset(fdt, pmic_node);
+ if (i2c_node < 0) {
+ return -FDT_ERR_NOTFOUND;
+ }
+
+ dt_fill_device_info(i2c_info, i2c_node);
+ if (i2c_info->base == 0U) {
+ return -FDT_ERR_NOTFOUND;
+ }
+
+ return dt_set_pinctrl_config(i2c_node);
+}
+
+int dt_pmic_enable_boot_on_regulators(void)
+{
+ int pmic_node, regulators_node, regulator_node;
+ void *fdt;
+
+ if (fdt_get_address(&fdt) == 0) {
+ return -ENOENT;
+ }
+
+ pmic_node = dt_get_pmic_node(fdt);
+ if (pmic_node < 0) {
+ return -FDT_ERR_NOTFOUND;
+ }
+
+ regulators_node = fdt_subnode_offset(fdt, pmic_node, "regulators");
+
+ fdt_for_each_subnode(regulator_node, fdt, regulators_node) {
+ const fdt32_t *cuint;
+ const char *node_name;
+ uint16_t voltage;
+
+ if (fdt_getprop(fdt, regulator_node, "regulator-boot-on",
+ NULL) == NULL) {
+ continue;
+ }
+
+ cuint = fdt_getprop(fdt, regulator_node,
+ "regulator-min-microvolt", NULL);
+ if (cuint == NULL) {
+ continue;
+ }
+
+ /* DT uses microvolts, whereas driver awaits millivolts */
+ voltage = (uint16_t)(fdt32_to_cpu(*cuint) / 1000U);
+ node_name = fdt_get_name(fdt, regulator_node, NULL);
+
+ if (stpmu1_is_regulator_enabled(node_name) == 0U) {
+ int status;
+
+ status = stpmu1_regulator_voltage_set(node_name,
+ voltage);
+ if (status != 0) {
+ return status;
+ }
+
+ status = stpmu1_regulator_enable(node_name);
+ if (status != 0) {
+ return status;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void initialize_pmic_i2c(void)
+{
+ int ret;
+ struct dt_node_info i2c_info;
+
+ if (dt_pmic_i2c_config(&i2c_info) != 0) {
+ ERROR("I2C configuration failed\n");
+ panic();
+ }
+
+ if (stm32mp1_clk_enable((uint32_t)i2c_info.clock) < 0) {
+ ERROR("I2C clock enable failed\n");
+ panic();
+ }
+
+ /* Initialize PMIC I2C */
+ i2c_handle.i2c_base_addr = i2c_info.base;
+ i2c_handle.i2c_init.timing = I2C_TIMING;
+ i2c_handle.i2c_init.own_address1 = pmic_i2c_addr;
+ i2c_handle.i2c_init.addressing_mode = I2C_ADDRESSINGMODE_7BIT;
+ i2c_handle.i2c_init.dual_address_mode = I2C_DUALADDRESS_DISABLE;
+ i2c_handle.i2c_init.own_address2 = 0;
+ i2c_handle.i2c_init.own_address2_masks = I2C_OAR2_OA2NOMASK;
+ i2c_handle.i2c_init.general_call_mode = I2C_GENERALCALL_DISABLE;
+ i2c_handle.i2c_init.no_stretch_mode = I2C_NOSTRETCH_DISABLE;
+
+ ret = stm32_i2c_init(&i2c_handle);
+ if (ret != 0) {
+ ERROR("Cannot initialize I2C %x (%d)\n",
+ i2c_handle.i2c_base_addr, ret);
+ panic();
+ }
+
+ ret = stm32_i2c_config_analog_filter(&i2c_handle,
+ I2C_ANALOGFILTER_ENABLE);
+ if (ret != 0) {
+ ERROR("Cannot initialize I2C analog filter (%d)\n", ret);
+ panic();
+ }
+
+ ret = stm32_i2c_is_device_ready(&i2c_handle, (uint16_t)pmic_i2c_addr, 1,
+ I2C_TIMEOUT);
+ if (ret != 0) {
+ ERROR("I2C device not ready (%d)\n", ret);
+ panic();
+ }
+
+ stpmu1_bind_i2c(&i2c_handle, (uint16_t)pmic_i2c_addr);
+}
+
+void initialize_pmic(void)
+{
+ int status;
+ uint8_t read_val;
+
+ initialize_pmic_i2c();
+
+ status = stpmu1_register_read(VERSION_STATUS_REG, &read_val);
+ if (status != 0) {
+ panic();
+ }
+
+ INFO("PMIC version = 0x%x\n", read_val);
+
+ /* Keep VDD on during the reset cycle */
+ status = stpmu1_register_update(MASK_RESET_BUCK_REG,
+ MASK_RESET_BUCK3,
+ MASK_RESET_BUCK3);
+ if (status != 0) {
+ panic();
+ }
+}
+
+int pmic_ddr_power_init(enum ddr_type ddr_type)
+{
+ bool buck3_at_1v8 = false;
+ uint8_t read_val;
+ int status;
+
+ switch (ddr_type) {
+ case STM32MP_DDR3:
+ /* Set LDO3 to sync mode */
+ status = stpmu1_register_read(LDO3_CONTROL_REG, &read_val);
+ if (status != 0) {
+ return status;
+ }
+
+ read_val &= ~STPMU1_LDO3_MODE;
+ read_val &= ~STPMU1_LDO12356_OUTPUT_MASK;
+ read_val |= STPMU1_LDO3_DDR_SEL << STPMU1_LDO12356_OUTPUT_SHIFT;
+
+ status = stpmu1_register_write(LDO3_CONTROL_REG, read_val);
+ if (status != 0) {
+ return status;
+ }
+
+ status = stpmu1_regulator_voltage_set("buck2", 1350);
+ if (status != 0) {
+ return status;
+ }
+
+ status = stpmu1_regulator_enable("buck2");
+ if (status != 0) {
+ return status;
+ }
+
+ mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS);
+
+ status = stpmu1_regulator_enable("vref_ddr");
+ if (status != 0) {
+ return status;
+ }
+
+ mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS);
+
+ status = stpmu1_regulator_enable("ldo3");
+ if (status != 0) {
+ return status;
+ }
+
+ mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS);
+ break;
+
+ case STM32MP_LPDDR2:
+ /*
+ * Set LDO3 to 1.8V
+ * Set LDO3 to bypass mode if BUCK3 = 1.8V
+ * Set LDO3 to normal mode if BUCK3 != 1.8V
+ */
+ status = stpmu1_register_read(BUCK3_CONTROL_REG, &read_val);
+ if (status != 0) {
+ return status;
+ }
+
+ if ((read_val & STPMU1_BUCK3_1V8) == STPMU1_BUCK3_1V8) {
+ buck3_at_1v8 = true;
+ }
+
+ status = stpmu1_register_read(LDO3_CONTROL_REG, &read_val);
+ if (status != 0) {
+ return status;
+ }
+
+ read_val &= ~STPMU1_LDO3_MODE;
+ read_val &= ~STPMU1_LDO12356_OUTPUT_MASK;
+ read_val |= STPMU1_LDO3_1800000;
+ if (buck3_at_1v8) {
+ read_val |= STPMU1_LDO3_MODE;
+ }
+
+ status = stpmu1_register_write(LDO3_CONTROL_REG, read_val);
+ if (status != 0) {
+ return status;
+ }
+
+ status = stpmu1_regulator_voltage_set("buck2", 1200);
+ if (status != 0) {
+ return status;
+ }
+
+ status = stpmu1_regulator_enable("ldo3");
+ if (status != 0) {
+ return status;
+ }
+
+ mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS);
+
+ status = stpmu1_regulator_enable("buck2");
+ if (status != 0) {
+ return status;
+ }
+
+ mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS);
+
+ status = stpmu1_regulator_enable("vref_ddr");
+ if (status != 0) {
+ return status;
+ }
+
+ mdelay(STPMU1_DEFAULT_START_UP_DELAY_MS);
+ break;
+
+ default:
+ break;
+ };
+
+ return 0;
+}
diff --git a/drivers/st/pmic/stpmu1.c b/drivers/st/pmic/stpmu1.c
new file mode 100644
index 0000000..5951899
--- /dev/null
+++ b/drivers/st/pmic/stpmu1.c
@@ -0,0 +1,600 @@
+/*
+ * Copyright (c) 2016-2018, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <debug.h>
+#include <platform.h>
+#include <stpmu1.h>
+#include <string.h>
+
+struct regul_struct {
+ const char *dt_node_name;
+ const uint16_t *voltage_table;
+ uint8_t voltage_table_size;
+ uint8_t control_reg;
+ uint8_t low_power_reg;
+};
+
+static struct i2c_handle_s *stpmu_i2c_handle;
+static uint16_t stpmu_i2c_addr;
+
+/* Voltage tables in mV */
+static const uint16_t buck1_voltage_table[] = {
+ 600,
+ 625,
+ 650,
+ 675,
+ 700,
+ 725,
+ 750,
+ 775,
+ 800,
+ 825,
+ 850,
+ 875,
+ 900,
+ 925,
+ 950,
+ 975,
+ 1000,
+ 1025,
+ 1050,
+ 1075,
+ 1100,
+ 1125,
+ 1150,
+ 1175,
+ 1200,
+ 1225,
+ 1250,
+ 1275,
+ 1300,
+ 1325,
+ 1350,
+ 1350,
+};
+
+static const uint16_t buck2_voltage_table[] = {
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1050,
+ 1050,
+ 1100,
+ 1100,
+ 1150,
+ 1150,
+ 1200,
+ 1200,
+ 1250,
+ 1250,
+ 1300,
+ 1300,
+ 1350,
+ 1350,
+ 1400,
+ 1400,
+ 1450,
+ 1450,
+ 1500,
+};
+
+static const uint16_t buck3_voltage_table[] = {
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ 1100,
+ 1100,
+ 1100,
+ 1100,
+ 1200,
+ 1200,
+ 1200,
+ 1200,
+ 1300,
+ 1300,
+ 1300,
+ 1300,
+ 1400,
+ 1400,
+ 1400,
+ 1400,
+ 1500,
+ 1600,
+ 1700,
+ 1800,
+ 1900,
+ 2000,
+ 2100,
+ 2200,
+ 2300,
+ 2400,
+ 2500,
+ 2600,
+ 2700,
+ 2800,
+ 2900,
+ 3000,
+ 3100,
+ 3200,
+ 3300,
+ 3400,
+};
+
+static const uint16_t buck4_voltage_table[] = {
+ 600,
+ 625,
+ 650,
+ 675,
+ 700,
+ 725,
+ 750,
+ 775,
+ 800,
+ 825,
+ 850,
+ 875,
+ 900,
+ 925,
+ 950,
+ 975,
+ 1000,
+ 1025,
+ 1050,
+ 1075,
+ 1100,
+ 1125,
+ 1150,
+ 1175,
+ 1200,
+ 1225,
+ 1250,
+ 1275,
+ 1300,
+ 1300,
+ 1350,
+ 1350,
+ 1400,
+ 1400,
+ 1450,
+ 1450,
+ 1500,
+ 1600,
+ 1700,
+ 1800,
+ 1900,
+ 2000,
+ 2100,
+ 2200,
+ 2300,
+ 2400,
+ 2500,
+ 2600,
+ 2700,
+ 2800,
+ 2900,
+ 3000,
+ 3100,
+ 3200,
+ 3300,
+ 3400,
+ 3500,
+ 3600,
+ 3700,
+ 3800,
+ 3900,
+};
+
+static const uint16_t ldo1_voltage_table[] = {
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1800,
+ 1900,
+ 2000,
+ 2100,
+ 2200,
+ 2300,
+ 2400,
+ 2500,
+ 2600,
+ 2700,
+ 2800,
+ 2900,
+ 3000,
+ 3100,
+ 3200,
+ 3300,
+};
+
+static const uint16_t ldo2_voltage_table[] = {
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1800,
+ 1900,
+ 2000,
+ 2100,
+ 2200,
+ 2300,
+ 2400,
+ 2500,
+ 2600,
+ 2700,
+ 2800,
+ 2900,
+ 3000,
+ 3100,
+ 3200,
+ 3300,
+};
+
+static const uint16_t ldo3_voltage_table[] = {
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1800,
+ 1900,
+ 2000,
+ 2100,
+ 2200,
+ 2300,
+ 2400,
+ 2500,
+ 2600,
+ 2700,
+ 2800,
+ 2900,
+ 3000,
+ 3100,
+ 3200,
+ 3300,
+ 3300,
+ 3300,
+ 3300,
+ 3300,
+ 3300,
+ 3300,
+ 0xFFFF, /* VREFDDR */
+};
+
+static const uint16_t ldo5_voltage_table[] = {
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1700,
+ 1800,
+ 1900,
+ 2000,
+ 2100,
+ 2200,
+ 2300,
+ 2400,
+ 2500,
+ 2600,
+ 2700,
+ 2800,
+ 2900,
+ 3000,
+ 3100,
+ 3200,
+ 3300,
+ 3400,
+ 3500,
+ 3600,
+ 3700,
+ 3800,
+ 3900,
+};
+
+static const uint16_t ldo6_voltage_table[] = {
+ 900,
+ 1000,
+ 1100,
+ 1200,
+ 1300,
+ 1400,
+ 1500,
+ 1600,
+ 1700,
+ 1800,
+ 1900,
+ 2000,
+ 2100,
+ 2200,
+ 2300,
+ 2400,
+ 2500,
+ 2600,
+ 2700,
+ 2800,
+ 2900,
+ 3000,
+ 3100,
+ 3200,
+ 3300,
+};
+
+static const uint16_t ldo4_voltage_table[] = {
+ 3300,
+};
+
+static const uint16_t vref_ddr_voltage_table[] = {
+ 3300,
+};
+
+/* Table of Regulators in PMIC SoC */
+static const struct regul_struct regulators_table[] = {
+ {
+ .dt_node_name = "buck1",
+ .voltage_table = buck1_voltage_table,
+ .voltage_table_size = ARRAY_SIZE(buck1_voltage_table),
+ .control_reg = BUCK1_CONTROL_REG,
+ .low_power_reg = BUCK1_PWRCTRL_REG,
+ },
+ {
+ .dt_node_name = "buck2",
+ .voltage_table = buck2_voltage_table,
+ .voltage_table_size = ARRAY_SIZE(buck2_voltage_table),
+ .control_reg = BUCK2_CONTROL_REG,
+ .low_power_reg = BUCK2_PWRCTRL_REG,
+ },
+ {
+ .dt_node_name = "buck3",
+ .voltage_table = buck3_voltage_table,
+ .voltage_table_size = ARRAY_SIZE(buck3_voltage_table),
+ .control_reg = BUCK3_CONTROL_REG,
+ .low_power_reg = BUCK3_PWRCTRL_REG,
+ },
+ {
+ .dt_node_name = "buck4",
+ .voltage_table = buck4_voltage_table,
+ .voltage_table_size = ARRAY_SIZE(buck4_voltage_table),
+ .control_reg = BUCK4_CONTROL_REG,
+ .low_power_reg = BUCK4_PWRCTRL_REG,
+ },
+ {
+ .dt_node_name = "ldo1",
+ .voltage_table = ldo1_voltage_table,
+ .voltage_table_size = ARRAY_SIZE(ldo1_voltage_table),
+ .control_reg = LDO1_CONTROL_REG,
+ .low_power_reg = LDO1_PWRCTRL_REG,
+ },
+ {
+ .dt_node_name = "ldo2",
+ .voltage_table = ldo2_voltage_table,
+ .voltage_table_size = ARRAY_SIZE(ldo2_voltage_table),
+ .control_reg = LDO2_CONTROL_REG,
+ .low_power_reg = LDO2_PWRCTRL_REG,
+ },
+ {
+ .dt_node_name = "ldo3",
+ .voltage_table = ldo3_voltage_table,
+ .voltage_table_size = ARRAY_SIZE(ldo3_voltage_table),
+ .control_reg = LDO3_CONTROL_REG,
+ .low_power_reg = LDO3_PWRCTRL_REG,
+ },
+ {
+ .dt_node_name = "ldo4",
+ .voltage_table = ldo4_voltage_table,
+ .voltage_table_size = ARRAY_SIZE(ldo4_voltage_table),
+ .control_reg = LDO4_CONTROL_REG,
+ .low_power_reg = LDO4_PWRCTRL_REG,
+ },
+ {
+ .dt_node_name = "ldo5",
+ .voltage_table = ldo5_voltage_table,
+ .voltage_table_size = ARRAY_SIZE(ldo5_voltage_table),
+ .control_reg = LDO5_CONTROL_REG,
+ .low_power_reg = LDO5_PWRCTRL_REG,
+ },
+ {
+ .dt_node_name = "ldo6",
+ .voltage_table = ldo6_voltage_table,
+ .voltage_table_size = ARRAY_SIZE(ldo6_voltage_table),
+ .control_reg = LDO6_CONTROL_REG,
+ .low_power_reg = LDO6_PWRCTRL_REG,
+ },
+ {
+ .dt_node_name = "vref_ddr",
+ .voltage_table = vref_ddr_voltage_table,
+ .voltage_table_size = ARRAY_SIZE(vref_ddr_voltage_table),
+ .control_reg = VREF_DDR_CONTROL_REG,
+ .low_power_reg = VREF_DDR_PWRCTRL_REG,
+ },
+};
+
+#define MAX_REGUL ARRAY_SIZE(regulators_table)
+
+static const struct regul_struct *stpmu1_get_regulator_data(const char *name)
+{
+ uint8_t i;
+
+ for (i = 0 ; i < MAX_REGUL ; i++) {
+ if (strncmp(name, regulators_table[i].dt_node_name,
+ strlen(regulators_table[i].dt_node_name)) == 0) {
+ return ®ulators_table[i];
+ }
+ }
+
+ /* Regulator not found */
+ panic();
+ return NULL;
+}
+
+static uint8_t stpmu1_voltage_find_index(const char *name,
+ uint16_t millivolts)
+{
+ const struct regul_struct *regul = stpmu1_get_regulator_data(name);
+ uint8_t i;
+
+ for (i = 0 ; i < regul->voltage_table_size ; i++) {
+ if (regul->voltage_table[i] == millivolts) {
+ return i;
+ }
+ }
+
+ /* Voltage not found */
+ panic();
+
+ return 0;
+}
+
+int stpmu1_switch_off(void)
+{
+ return stpmu1_register_update(MAIN_CONTROL_REG, 1,
+ SOFTWARE_SWITCH_OFF_ENABLED);
+}
+
+int stpmu1_regulator_enable(const char *name)
+{
+ const struct regul_struct *regul = stpmu1_get_regulator_data(name);
+
+ return stpmu1_register_update(regul->control_reg, BIT(0), BIT(0));
+}
+
+int stpmu1_regulator_disable(const char *name)
+{
+ const struct regul_struct *regul = stpmu1_get_regulator_data(name);
+
+ return stpmu1_register_update(regul->control_reg, 0, BIT(0));
+}
+
+uint8_t stpmu1_is_regulator_enabled(const char *name)
+{
+ uint8_t val;
+ const struct regul_struct *regul = stpmu1_get_regulator_data(name);
+
+ if (stpmu1_register_read(regul->control_reg, &val) != 0) {
+ panic();
+ }
+
+ return (val & 0x1U);
+}
+
+int stpmu1_regulator_voltage_set(const char *name, uint16_t millivolts)
+{
+ uint8_t voltage_index = stpmu1_voltage_find_index(name, millivolts);
+ const struct regul_struct *regul = stpmu1_get_regulator_data(name);
+
+ return stpmu1_register_update(regul->control_reg, voltage_index << 2,
+ 0xFC);
+}
+
+int stpmu1_register_read(uint8_t register_id, uint8_t *value)
+{
+ return stm32_i2c_mem_read(stpmu_i2c_handle, stpmu_i2c_addr,
+ (uint16_t)register_id, I2C_MEMADD_SIZE_8BIT,
+ value, 1, 100000);
+}
+
+int stpmu1_register_write(uint8_t register_id, uint8_t value)
+{
+ int status;
+
+ status = stm32_i2c_mem_write(stpmu_i2c_handle, stpmu_i2c_addr,
+ (uint16_t)register_id,
+ I2C_MEMADD_SIZE_8BIT, &value, 1, 100000);
+
+ if (status != 0) {
+ return status;
+ }
+
+ if ((register_id != WATCHDOG_CONTROL_REG) && (register_id <= 0x40U)) {
+ uint8_t readval;
+
+ status = stpmu1_register_read(register_id, &readval);
+ if (status != 0) {
+ return status;
+ }
+
+ if (readval != value) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int stpmu1_register_update(uint8_t register_id, uint8_t value, uint8_t mask)
+{
+ int status;
+ uint8_t val;
+
+ status = stpmu1_register_read(register_id, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Clear bits to update */
+ val &= ~mask;
+
+ /* Update appropriate bits*/
+ val |= (value & mask);
+
+ /* Send new value on I2C Bus */
+ return stpmu1_register_write(register_id, val);
+}
+
+void stpmu1_bind_i2c(struct i2c_handle_s *i2c_handle, uint16_t i2c_addr)
+{
+ stpmu_i2c_handle = i2c_handle;
+ stpmu_i2c_addr = i2c_addr;
+}