Merge pull request #1534 from Icenowy/sun50i_h6_pmic
Add support for Allwinner H6 + X-Powers AXP805 PMIC combination
diff --git a/drivers/allwinner/sunxi_i2c.c b/drivers/allwinner/sunxi_i2c.c
new file mode 100644
index 0000000..cc91ca5
--- /dev/null
+++ b/drivers/allwinner/sunxi_i2c.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 Icenowy Zheng <icenowy@aosc.io>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ * https://spdx.org/licenses
+ */
+
+/* This driver provides I2C support for Allwinner sunXi SoCs */
+
+#include <mmio.h>
+
+#define CONFIG_SYS_TCLK 24000000
+#define CONFIG_SYS_I2C_SPEED 100000
+#define CONFIG_SYS_I2C_SLAVE 0
+
+#define I2C_INTERRUPT_CLEAR_INVERTED
+
+struct mentor_i2c_regs {
+ uint32_t slave_address;
+ uint32_t xtnd_slave_addr;
+ uint32_t data;
+ uint32_t control;
+ uint32_t status;
+ uint32_t baudrate;
+ uint32_t soft_reset;
+};
+
+#include "../mentor/i2c/mi2cv.c"
diff --git a/drivers/marvell/i2c/a8k_i2c.c b/drivers/marvell/i2c/a8k_i2c.c
index 72a7731..1c0f922 100644
--- a/drivers/marvell/i2c/a8k_i2c.c
+++ b/drivers/marvell/i2c/a8k_i2c.c
@@ -7,57 +7,22 @@
/* This driver provides I2C support for Marvell A8K and compatible SoCs */
-#include <a8k_i2c.h>
-#include <debug.h>
-#include <delay_timer.h>
-#include <errno.h>
#include <mmio.h>
-#include <mvebu_def.h>
-
-#if LOG_LEVEL >= LOG_LEVEL_VERBOSE
-#define DEBUG_I2C
-#endif
#define CONFIG_SYS_TCLK 250000000
#define CONFIG_SYS_I2C_SPEED 100000
#define CONFIG_SYS_I2C_SLAVE 0x0
-#define I2C_TIMEOUT_VALUE 0x500
-#define I2C_MAX_RETRY_CNT 1000
-#define I2C_CMD_WRITE 0x0
-#define I2C_CMD_READ 0x1
-
-#define I2C_DATA_ADDR_7BIT_OFFS 0x1
-#define I2C_DATA_ADDR_7BIT_MASK (0xFF << I2C_DATA_ADDR_7BIT_OFFS)
-
-#define I2C_CONTROL_ACK 0x00000004
-#define I2C_CONTROL_IFLG 0x00000008
-#define I2C_CONTROL_STOP 0x00000010
-#define I2C_CONTROL_START 0x00000020
-#define I2C_CONTROL_TWSIEN 0x00000040
-#define I2C_CONTROL_INTEN 0x00000080
-#define I2C_STATUS_START 0x08
-#define I2C_STATUS_REPEATED_START 0x10
-#define I2C_STATUS_ADDR_W_ACK 0x18
-#define I2C_STATUS_DATA_W_ACK 0x28
-#define I2C_STATUS_LOST_ARB_DATA_ADDR_TRANSFER 0x38
-#define I2C_STATUS_ADDR_R_ACK 0x40
-#define I2C_STATUS_DATA_R_ACK 0x50
-#define I2C_STATUS_DATA_R_NAK 0x58
-#define I2C_STATUS_LOST_ARB_GENERAL_CALL 0x78
-#define I2C_STATUS_IDLE 0xF8
+#define I2C_CAN_UNSTUCK
-#define I2C_UNSTUCK_TRIGGER 0x1
-#define I2C_UNSTUCK_ONGOING 0x2
-#define I2C_UNSTUCK_ERROR 0x4
-struct marvell_i2c_regs {
+struct mentor_i2c_regs {
uint32_t slave_address;
uint32_t data;
uint32_t control;
union {
uint32_t status; /* when reading */
uint32_t baudrate; /* when writing */
- } u;
+ };
uint32_t xtnd_slave_addr;
uint32_t reserved[2];
uint32_t soft_reset;
@@ -65,549 +30,4 @@
uint32_t unstuck;
};
-static struct marvell_i2c_regs *base;
-
-static int marvell_i2c_lost_arbitration(uint32_t *status)
-{
- *status = mmio_read_32((uintptr_t)&base->u.status);
- if ((*status == I2C_STATUS_LOST_ARB_DATA_ADDR_TRANSFER) ||
- (*status == I2C_STATUS_LOST_ARB_GENERAL_CALL))
- return -EAGAIN;
-
- return 0;
-}
-
-static void marvell_i2c_interrupt_clear(void)
-{
- uint32_t reg;
-
- reg = mmio_read_32((uintptr_t)&base->control);
- reg &= ~(I2C_CONTROL_IFLG);
- mmio_write_32((uintptr_t)&base->control, reg);
- /* Wait for 1 us for the clear to take effect */
- udelay(1);
-}
-
-static int marvell_i2c_interrupt_get(void)
-{
- uint32_t reg;
-
- /* get the interrupt flag bit */
- reg = mmio_read_32((uintptr_t)&base->control);
- reg &= I2C_CONTROL_IFLG;
- return reg && I2C_CONTROL_IFLG;
-}
-
-static int marvell_i2c_wait_interrupt(void)
-{
- uint32_t timeout = 0;
-
- while (!marvell_i2c_interrupt_get() && (timeout++ < I2C_TIMEOUT_VALUE))
- ;
- if (timeout >= I2C_TIMEOUT_VALUE)
- return -ETIMEDOUT;
-
- return 0;
-}
-
-static int marvell_i2c_start_bit_set(void)
-{
- int is_int_flag = 0;
- uint32_t status;
-
- if (marvell_i2c_interrupt_get())
- is_int_flag = 1;
-
- /* set start bit */
- mmio_write_32((uintptr_t)&base->control,
- mmio_read_32((uintptr_t)&base->control) |
- I2C_CONTROL_START);
-
- /* in case that the int flag was set before i.e. repeated start bit */
- if (is_int_flag) {
- VERBOSE("%s: repeated start Bit\n", __func__);
- marvell_i2c_interrupt_clear();
- }
-
- if (marvell_i2c_wait_interrupt()) {
- ERROR("Start clear bit timeout\n");
- return -ETIMEDOUT;
- }
-
- /* check that start bit went down */
- if ((mmio_read_32((uintptr_t)&base->control) &
- I2C_CONTROL_START) != 0) {
- ERROR("Start bit didn't went down\n");
- return -EPERM;
- }
-
- /* check the status */
- if (marvell_i2c_lost_arbitration(&status)) {
- ERROR("%s - %d: Lost arbitration, got status %x\n",
- __func__, __LINE__, status);
- return -EAGAIN;
- }
- if ((status != I2C_STATUS_START) &&
- (status != I2C_STATUS_REPEATED_START)) {
- ERROR("Got status %x after enable start bit.\n", status);
- return -EPERM;
- }
-
- return 0;
-}
-
-static int marvell_i2c_stop_bit_set(void)
-{
- int timeout;
- uint32_t status;
-
- /* Generate stop bit */
- mmio_write_32((uintptr_t)&base->control,
- mmio_read_32((uintptr_t)&base->control) |
- I2C_CONTROL_STOP);
- marvell_i2c_interrupt_clear();
-
- timeout = 0;
- /* Read control register, check the control stop bit */
- while ((mmio_read_32((uintptr_t)&base->control) & I2C_CONTROL_STOP) &&
- (timeout++ < I2C_TIMEOUT_VALUE))
- ;
- if (timeout >= I2C_TIMEOUT_VALUE) {
- ERROR("Stop bit didn't went down\n");
- return -ETIMEDOUT;
- }
-
- /* check that stop bit went down */
- if ((mmio_read_32((uintptr_t)&base->control) & I2C_CONTROL_STOP) != 0) {
- ERROR("Stop bit didn't went down\n");
- return -EPERM;
- }
-
- /* check the status */
- if (marvell_i2c_lost_arbitration(&status)) {
- ERROR("%s - %d: Lost arbitration, got status %x\n",
- __func__, __LINE__, status);
- return -EAGAIN;
- }
- if (status != I2C_STATUS_IDLE) {
- ERROR("Got status %x after enable stop bit.\n", status);
- return -EPERM;
- }
-
- return 0;
-}
-
-static int marvell_i2c_address_set(uint8_t chain, int command)
-{
- uint32_t reg, status;
-
- reg = (chain << I2C_DATA_ADDR_7BIT_OFFS) & I2C_DATA_ADDR_7BIT_MASK;
- reg |= command;
- mmio_write_32((uintptr_t)&base->data, reg);
- udelay(1);
-
- marvell_i2c_interrupt_clear();
-
- if (marvell_i2c_wait_interrupt()) {
- ERROR("Start clear bit timeout\n");
- return -ETIMEDOUT;
- }
-
- /* check the status */
- if (marvell_i2c_lost_arbitration(&status)) {
- ERROR("%s - %d: Lost arbitration, got status %x\n",
- __func__, __LINE__, status);
- return -EAGAIN;
- }
- if (((status != I2C_STATUS_ADDR_R_ACK) && (command == I2C_CMD_READ)) ||
- ((status != I2C_STATUS_ADDR_W_ACK) && (command == I2C_CMD_WRITE))) {
- /* only in debug, since in boot we try to read the SPD
- * of both DRAM, and we don't want error messages in cas
- * DIMM doesn't exist.
- */
- INFO("%s: ERROR - status %x addr in %s mode.\n", __func__,
- status, (command == I2C_CMD_WRITE) ? "Write" : "Read");
- return -EPERM;
- }
-
- return 0;
-}
-
-/*
- * The I2C module contains a clock divider to generate the SCL clock.
- * This function calculates and sets the <N> and <M> fields in the I2C Baud
- * Rate Register (t=01) to obtain given 'requested_speed'.
- * The requested_speed will be equal to:
- * CONFIG_SYS_TCLK / (10 * (M + 1) * (2 << N))
- * Where M is the value represented by bits[6:3] and N is the value represented
- * by bits[2:0] of "I2C Baud Rate Register".
- * Therefore max M which can be set is 16 (2^4) and max N is 8 (2^3). So the
- * lowest possible baudrate is:
- * CONFIG_SYS_TCLK/(10 * (16 +1) * (2 << 8), which equals to:
- * CONFIG_SYS_TCLK/87040. Assuming that CONFIG_SYS_TCLK=250MHz, the lowest
- * possible frequency is ~2,872KHz.
- */
-static unsigned int marvell_i2c_bus_speed_set(unsigned int requested_speed)
-{
- unsigned int n, m, freq, margin, min_margin = 0xffffffff;
- unsigned int actual_n = 0, actual_m = 0;
- int val;
-
- /* Calculate N and M for the TWSI clock baud rate */
- for (n = 0; n < 8; n++) {
- for (m = 0; m < 16; m++) {
- freq = CONFIG_SYS_TCLK / (10 * (m + 1) * (2 << n));
- val = requested_speed - freq;
- margin = (val > 0) ? val : -val;
-
- if ((freq <= requested_speed) &&
- (margin < min_margin)) {
- min_margin = margin;
- actual_n = n;
- actual_m = m;
- }
- }
- }
- VERBOSE("%s: actual_n = %u, actual_m = %u\n",
- __func__, actual_n, actual_m);
- /* Set the baud rate */
- mmio_write_32((uintptr_t)&base->u.baudrate, (actual_m << 3) | actual_n);
-
- return 0;
-}
-
-#ifdef DEBUG_I2C
-static int marvell_i2c_probe(uint8_t chip)
-{
- int ret = 0;
-
- ret = marvell_i2c_start_bit_set();
- if (ret != 0) {
- marvell_i2c_stop_bit_set();
- ERROR("%s - %d: %s", __func__, __LINE__,
- "marvell_i2c_start_bit_set failed\n");
- return -EPERM;
- }
-
- ret = marvell_i2c_address_set(chip, I2C_CMD_WRITE);
- if (ret != 0) {
- marvell_i2c_stop_bit_set();
- ERROR("%s - %d: %s", __func__, __LINE__,
- "marvell_i2c_address_set failed\n");
- return -EPERM;
- }
-
- marvell_i2c_stop_bit_set();
-
- VERBOSE("%s: successful I2C probe\n", __func__);
-
- return ret;
-}
-#endif
-
-/* regular i2c transaction */
-static int marvell_i2c_data_receive(uint8_t *p_block, uint32_t block_size)
-{
- uint32_t reg, status, block_size_read = block_size;
-
- /* Wait for cause interrupt */
- if (marvell_i2c_wait_interrupt()) {
- ERROR("Start clear bit timeout\n");
- return -ETIMEDOUT;
- }
- while (block_size_read) {
- if (block_size_read == 1) {
- reg = mmio_read_32((uintptr_t)&base->control);
- reg &= ~(I2C_CONTROL_ACK);
- mmio_write_32((uintptr_t)&base->control, reg);
- }
- marvell_i2c_interrupt_clear();
-
- if (marvell_i2c_wait_interrupt()) {
- ERROR("Start clear bit timeout\n");
- return -ETIMEDOUT;
- }
- /* check the status */
- if (marvell_i2c_lost_arbitration(&status)) {
- ERROR("%s - %d: Lost arbitration, got status %x\n",
- __func__, __LINE__, status);
- return -EAGAIN;
- }
- if ((status != I2C_STATUS_DATA_R_ACK) &&
- (block_size_read != 1)) {
- ERROR("Status %x in read transaction\n", status);
- return -EPERM;
- }
- if ((status != I2C_STATUS_DATA_R_NAK) &&
- (block_size_read == 1)) {
- ERROR("Status %x in Rd Terminate\n", status);
- return -EPERM;
- }
-
- /* read the data */
- *p_block = (uint8_t) mmio_read_32((uintptr_t)&base->data);
- VERBOSE("%s: place %d read %x\n", __func__,
- block_size - block_size_read, *p_block);
- p_block++;
- block_size_read--;
- }
-
- return 0;
-}
-
-static int marvell_i2c_data_transmit(uint8_t *p_block, uint32_t block_size)
-{
- uint32_t status, block_size_write = block_size;
-
- if (marvell_i2c_wait_interrupt()) {
- ERROR("Start clear bit timeout\n");
- return -ETIMEDOUT;
- }
-
- while (block_size_write) {
- /* write the data */
- mmio_write_32((uintptr_t)&base->data, (uint32_t) *p_block);
- VERBOSE("%s: index = %d, data = %x\n", __func__,
- block_size - block_size_write, *p_block);
- p_block++;
- block_size_write--;
-
- marvell_i2c_interrupt_clear();
-
- if (marvell_i2c_wait_interrupt()) {
- ERROR("Start clear bit timeout\n");
- return -ETIMEDOUT;
- }
-
- /* check the status */
- if (marvell_i2c_lost_arbitration(&status)) {
- ERROR("%s - %d: Lost arbitration, got status %x\n",
- __func__, __LINE__, status);
- return -EAGAIN;
- }
- if (status != I2C_STATUS_DATA_W_ACK) {
- ERROR("Status %x in write transaction\n", status);
- return -EPERM;
- }
- }
-
- return 0;
-}
-
-static int marvell_i2c_target_offset_set(uint8_t chip, uint32_t addr, int alen)
-{
- uint8_t off_block[2];
- uint32_t off_size;
-
- if (alen == 2) { /* 2-byte addresses support */
- off_block[0] = (addr >> 8) & 0xff;
- off_block[1] = addr & 0xff;
- off_size = 2;
- } else { /* 1-byte addresses support */
- off_block[0] = addr & 0xff;
- off_size = 1;
- }
- VERBOSE("%s: off_size = %x addr1 = %x addr2 = %x\n", __func__,
- off_size, off_block[0], off_block[1]);
- return marvell_i2c_data_transmit(off_block, off_size);
-}
-
-static int marvell_i2c_unstuck(int ret)
-{
- uint32_t v;
-
- if (ret != -ETIMEDOUT)
- return ret;
- VERBOSE("Trying to \"unstuck i2c\"... ");
- i2c_init(base);
- mmio_write_32((uintptr_t)&base->unstuck, I2C_UNSTUCK_TRIGGER);
- do {
- v = mmio_read_32((uintptr_t)&base->unstuck);
- } while (v & I2C_UNSTUCK_ONGOING);
-
- if (v & I2C_UNSTUCK_ERROR) {
- VERBOSE("failed - soft reset i2c\n");
- ret = -EPERM;
- } else {
- VERBOSE("ok\n");
- i2c_init(base);
- ret = -EAGAIN;
- }
- return ret;
-}
-
-/*
- * API Functions
- */
-void i2c_init(void *i2c_base)
-{
- /* For I2C speed and slave address, now we do not set them since
- * we just provide the working speed and slave address in mvebu_def.h
- * for i2c_init
- */
- base = (struct marvell_i2c_regs *)i2c_base;
-
- /* Reset the I2C logic */
- mmio_write_32((uintptr_t)&base->soft_reset, 0);
-
- udelay(200);
-
- marvell_i2c_bus_speed_set(CONFIG_SYS_I2C_SPEED);
-
- /* Enable the I2C and slave */
- mmio_write_32((uintptr_t)&base->control,
- I2C_CONTROL_TWSIEN | I2C_CONTROL_ACK);
-
- /* set the I2C slave address */
- mmio_write_32((uintptr_t)&base->xtnd_slave_addr, 0);
- mmio_write_32((uintptr_t)&base->slave_address, CONFIG_SYS_I2C_SLAVE);
-
- /* unmask I2C interrupt */
- mmio_write_32((uintptr_t)&base->control,
- mmio_read_32((uintptr_t)&base->control) |
- I2C_CONTROL_INTEN);
-
- udelay(10);
-}
-
-/*
- * i2c_read: - Read multiple bytes from an i2c device
- *
- * The higher level routines take into account that this function is only
- * called with len < page length of the device (see configuration file)
- *
- * @chip: address of the chip which is to be read
- * @addr: i2c data address within the chip
- * @alen: length of the i2c data address (1..2 bytes)
- * @buffer: where to write the data
- * @len: how much byte do we want to read
- * @return: 0 in case of success
- */
-int i2c_read(uint8_t chip, uint32_t addr, int alen, uint8_t *buffer, int len)
-{
- int ret = 0;
- uint32_t counter = 0;
-
-#ifdef DEBUG_I2C
- marvell_i2c_probe(chip);
-#endif
-
- do {
- if (ret != -EAGAIN && ret) {
- ERROR("i2c transaction failed, after %d retries\n",
- counter);
- marvell_i2c_stop_bit_set();
- return ret;
- }
-
- /* wait for 1 us for the interrupt clear to take effect */
- if (counter > 0)
- udelay(1);
- counter++;
-
- ret = marvell_i2c_start_bit_set();
- if (ret) {
- ret = marvell_i2c_unstuck(ret);
- continue;
- }
-
- /* if EEPROM device */
- if (alen != 0) {
- ret = marvell_i2c_address_set(chip, I2C_CMD_WRITE);
- if (ret)
- continue;
-
- ret = marvell_i2c_target_offset_set(chip, addr, alen);
- if (ret)
- continue;
- ret = marvell_i2c_start_bit_set();
- if (ret)
- continue;
- }
-
- ret = marvell_i2c_address_set(chip, I2C_CMD_READ);
- if (ret)
- continue;
-
- ret = marvell_i2c_data_receive(buffer, len);
- if (ret)
- continue;
-
- ret = marvell_i2c_stop_bit_set();
- } while ((ret == -EAGAIN) && (counter < I2C_MAX_RETRY_CNT));
-
- if (counter == I2C_MAX_RETRY_CNT) {
- ERROR("I2C transactions failed, got EAGAIN %d times\n",
- I2C_MAX_RETRY_CNT);
- ret = -EPERM;
- }
- mmio_write_32((uintptr_t)&base->control,
- mmio_read_32((uintptr_t)&base->control) |
- I2C_CONTROL_ACK);
-
- udelay(1);
- return ret;
-}
-
-/*
- * i2c_write: - Write multiple bytes to an i2c device
- *
- * The higher level routines take into account that this function is only
- * called with len < page length of the device (see configuration file)
- *
- * @chip: address of the chip which is to be written
- * @addr: i2c data address within the chip
- * @alen: length of the i2c data address (1..2 bytes)
- * @buffer: where to find the data to be written
- * @len: how much byte do we want to read
- * @return: 0 in case of success
- */
-int i2c_write(uint8_t chip, uint32_t addr, int alen, uint8_t *buffer, int len)
-{
- int ret = 0;
- uint32_t counter = 0;
-
- do {
- if (ret != -EAGAIN && ret) {
- ERROR("i2c transaction failed\n");
- marvell_i2c_stop_bit_set();
- return ret;
- }
- /* wait for 1 us for the interrupt clear to take effect */
- if (counter > 0)
- udelay(1);
- counter++;
-
- ret = marvell_i2c_start_bit_set();
- if (ret) {
- ret = marvell_i2c_unstuck(ret);
- continue;
- }
-
- ret = marvell_i2c_address_set(chip, I2C_CMD_WRITE);
- if (ret)
- continue;
-
- /* if EEPROM device */
- if (alen != 0) {
- ret = marvell_i2c_target_offset_set(chip, addr, alen);
- if (ret)
- continue;
- }
-
- ret = marvell_i2c_data_transmit(buffer, len);
- if (ret)
- continue;
-
- ret = marvell_i2c_stop_bit_set();
- } while ((ret == -EAGAIN) && (counter < I2C_MAX_RETRY_CNT));
-
- if (counter == I2C_MAX_RETRY_CNT) {
- ERROR("I2C transactions failed, got EAGAIN %d times\n",
- I2C_MAX_RETRY_CNT);
- ret = -EPERM;
- }
-
- udelay(1);
- return ret;
-}
+#include "../../mentor/i2c/mi2cv.c"
diff --git a/drivers/mentor/i2c/mi2cv.c b/drivers/mentor/i2c/mi2cv.c
new file mode 100644
index 0000000..1b73e6f
--- /dev/null
+++ b/drivers/mentor/i2c/mi2cv.c
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ * Copyright (C) 2018 Icenowy Zheng <icenowy@aosc.io>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ * https://spdx.org/licenses
+ */
+
+/*
+ * This driver is for Mentor Graphics Inventra MI2CV IP core, which is used
+ * for Marvell and Allwinner SoCs in ATF.
+ */
+
+#include <debug.h>
+#include <delay_timer.h>
+#include <errno.h>
+#include <mentor/mi2cv.h>
+#include <mmio.h>
+
+#if LOG_LEVEL >= LOG_LEVEL_VERBOSE
+#define DEBUG_I2C
+#endif
+
+#define I2C_TIMEOUT_VALUE 0x500
+#define I2C_MAX_RETRY_CNT 1000
+#define I2C_CMD_WRITE 0x0
+#define I2C_CMD_READ 0x1
+
+#define I2C_DATA_ADDR_7BIT_OFFS 0x1
+#define I2C_DATA_ADDR_7BIT_MASK (0xFF << I2C_DATA_ADDR_7BIT_OFFS)
+
+#define I2C_CONTROL_ACK 0x00000004
+#define I2C_CONTROL_IFLG 0x00000008
+#define I2C_CONTROL_STOP 0x00000010
+#define I2C_CONTROL_START 0x00000020
+#define I2C_CONTROL_TWSIEN 0x00000040
+#define I2C_CONTROL_INTEN 0x00000080
+
+#define I2C_STATUS_START 0x08
+#define I2C_STATUS_REPEATED_START 0x10
+#define I2C_STATUS_ADDR_W_ACK 0x18
+#define I2C_STATUS_DATA_W_ACK 0x28
+#define I2C_STATUS_LOST_ARB_DATA_ADDR_TRANSFER 0x38
+#define I2C_STATUS_ADDR_R_ACK 0x40
+#define I2C_STATUS_DATA_R_ACK 0x50
+#define I2C_STATUS_DATA_R_NAK 0x58
+#define I2C_STATUS_LOST_ARB_GENERAL_CALL 0x78
+#define I2C_STATUS_IDLE 0xF8
+
+#define I2C_UNSTUCK_TRIGGER 0x1
+#define I2C_UNSTUCK_ONGOING 0x2
+#define I2C_UNSTUCK_ERROR 0x4
+
+static struct mentor_i2c_regs *base;
+
+static int mentor_i2c_lost_arbitration(uint32_t *status)
+{
+ *status = mmio_read_32((uintptr_t)&base->status);
+ if ((*status == I2C_STATUS_LOST_ARB_DATA_ADDR_TRANSFER) ||
+ (*status == I2C_STATUS_LOST_ARB_GENERAL_CALL))
+ return -EAGAIN;
+
+ return 0;
+}
+
+static void mentor_i2c_interrupt_clear(void)
+{
+ uint32_t reg;
+
+ reg = mmio_read_32((uintptr_t)&base->control);
+#ifndef I2C_INTERRUPT_CLEAR_INVERTED
+ reg &= ~(I2C_CONTROL_IFLG);
+#else
+ reg |= I2C_CONTROL_IFLG;
+#endif
+ mmio_write_32((uintptr_t)&base->control, reg);
+ /* Wait for 1 us for the clear to take effect */
+ udelay(1);
+}
+
+static int mentor_i2c_interrupt_get(void)
+{
+ uint32_t reg;
+
+ /* get the interrupt flag bit */
+ reg = mmio_read_32((uintptr_t)&base->control);
+ reg &= I2C_CONTROL_IFLG;
+ return reg && I2C_CONTROL_IFLG;
+}
+
+static int mentor_i2c_wait_interrupt(void)
+{
+ uint32_t timeout = 0;
+
+ while (!mentor_i2c_interrupt_get() && (timeout++ < I2C_TIMEOUT_VALUE))
+ ;
+ if (timeout >= I2C_TIMEOUT_VALUE)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int mentor_i2c_start_bit_set(void)
+{
+ int is_int_flag = 0;
+ uint32_t status;
+
+ if (mentor_i2c_interrupt_get())
+ is_int_flag = 1;
+
+ /* set start bit */
+ mmio_write_32((uintptr_t)&base->control,
+ mmio_read_32((uintptr_t)&base->control) |
+ I2C_CONTROL_START);
+
+ /* in case that the int flag was set before i.e. repeated start bit */
+ if (is_int_flag) {
+ VERBOSE("%s: repeated start Bit\n", __func__);
+ mentor_i2c_interrupt_clear();
+ }
+
+ if (mentor_i2c_wait_interrupt()) {
+ ERROR("Start clear bit timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ /* check that start bit went down */
+ if ((mmio_read_32((uintptr_t)&base->control) &
+ I2C_CONTROL_START) != 0) {
+ ERROR("Start bit didn't went down\n");
+ return -EPERM;
+ }
+
+ /* check the status */
+ if (mentor_i2c_lost_arbitration(&status)) {
+ ERROR("%s - %d: Lost arbitration, got status %x\n",
+ __func__, __LINE__, status);
+ return -EAGAIN;
+ }
+ if ((status != I2C_STATUS_START) &&
+ (status != I2C_STATUS_REPEATED_START)) {
+ ERROR("Got status %x after enable start bit.\n", status);
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static int mentor_i2c_stop_bit_set(void)
+{
+ int timeout;
+ uint32_t status;
+
+ /* Generate stop bit */
+ mmio_write_32((uintptr_t)&base->control,
+ mmio_read_32((uintptr_t)&base->control) |
+ I2C_CONTROL_STOP);
+ mentor_i2c_interrupt_clear();
+
+ timeout = 0;
+ /* Read control register, check the control stop bit */
+ while ((mmio_read_32((uintptr_t)&base->control) & I2C_CONTROL_STOP) &&
+ (timeout++ < I2C_TIMEOUT_VALUE))
+ ;
+ if (timeout >= I2C_TIMEOUT_VALUE) {
+ ERROR("Stop bit didn't went down\n");
+ return -ETIMEDOUT;
+ }
+
+ /* check that stop bit went down */
+ if ((mmio_read_32((uintptr_t)&base->control) & I2C_CONTROL_STOP) != 0) {
+ ERROR("Stop bit didn't went down\n");
+ return -EPERM;
+ }
+
+ /* check the status */
+ if (mentor_i2c_lost_arbitration(&status)) {
+ ERROR("%s - %d: Lost arbitration, got status %x\n",
+ __func__, __LINE__, status);
+ return -EAGAIN;
+ }
+ if (status != I2C_STATUS_IDLE) {
+ ERROR("Got status %x after enable stop bit.\n", status);
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static int mentor_i2c_address_set(uint8_t chain, int command)
+{
+ uint32_t reg, status;
+
+ reg = (chain << I2C_DATA_ADDR_7BIT_OFFS) & I2C_DATA_ADDR_7BIT_MASK;
+ reg |= command;
+ mmio_write_32((uintptr_t)&base->data, reg);
+ udelay(1);
+
+ mentor_i2c_interrupt_clear();
+
+ if (mentor_i2c_wait_interrupt()) {
+ ERROR("Start clear bit timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ /* check the status */
+ if (mentor_i2c_lost_arbitration(&status)) {
+ ERROR("%s - %d: Lost arbitration, got status %x\n",
+ __func__, __LINE__, status);
+ return -EAGAIN;
+ }
+ if (((status != I2C_STATUS_ADDR_R_ACK) && (command == I2C_CMD_READ)) ||
+ ((status != I2C_STATUS_ADDR_W_ACK) && (command == I2C_CMD_WRITE))) {
+ /* only in debug, since in boot we try to read the SPD
+ * of both DRAM, and we don't want error messages in cas
+ * DIMM doesn't exist.
+ */
+ INFO("%s: ERROR - status %x addr in %s mode.\n", __func__,
+ status, (command == I2C_CMD_WRITE) ? "Write" : "Read");
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+/*
+ * The I2C module contains a clock divider to generate the SCL clock.
+ * This function calculates and sets the <N> and <M> fields in the I2C Baud
+ * Rate Register (t=01) to obtain given 'requested_speed'.
+ * The requested_speed will be equal to:
+ * CONFIG_SYS_TCLK / (10 * (M + 1) * (2 << N))
+ * Where M is the value represented by bits[6:3] and N is the value represented
+ * by bits[2:0] of "I2C Baud Rate Register".
+ * Therefore max M which can be set is 16 (2^4) and max N is 8 (2^3). So the
+ * lowest possible baudrate is:
+ * CONFIG_SYS_TCLK/(10 * (16 +1) * (2 << 8), which equals to:
+ * CONFIG_SYS_TCLK/87040. Assuming that CONFIG_SYS_TCLK=250MHz, the lowest
+ * possible frequency is ~2,872KHz.
+ */
+static unsigned int mentor_i2c_bus_speed_set(unsigned int requested_speed)
+{
+ unsigned int n, m, freq, margin, min_margin = 0xffffffff;
+ unsigned int actual_n = 0, actual_m = 0;
+ int val;
+
+ /* Calculate N and M for the TWSI clock baud rate */
+ for (n = 0; n < 8; n++) {
+ for (m = 0; m < 16; m++) {
+ freq = CONFIG_SYS_TCLK / (10 * (m + 1) * (2 << n));
+ val = requested_speed - freq;
+ margin = (val > 0) ? val : -val;
+
+ if ((freq <= requested_speed) &&
+ (margin < min_margin)) {
+ min_margin = margin;
+ actual_n = n;
+ actual_m = m;
+ }
+ }
+ }
+ VERBOSE("%s: actual_n = %u, actual_m = %u\n",
+ __func__, actual_n, actual_m);
+ /* Set the baud rate */
+ mmio_write_32((uintptr_t)&base->baudrate, (actual_m << 3) | actual_n);
+
+ return 0;
+}
+
+#ifdef DEBUG_I2C
+static int mentor_i2c_probe(uint8_t chip)
+{
+ int ret = 0;
+
+ ret = mentor_i2c_start_bit_set();
+ if (ret != 0) {
+ mentor_i2c_stop_bit_set();
+ ERROR("%s - %d: %s", __func__, __LINE__,
+ "mentor_i2c_start_bit_set failed\n");
+ return -EPERM;
+ }
+
+ ret = mentor_i2c_address_set(chip, I2C_CMD_WRITE);
+ if (ret != 0) {
+ mentor_i2c_stop_bit_set();
+ ERROR("%s - %d: %s", __func__, __LINE__,
+ "mentor_i2c_address_set failed\n");
+ return -EPERM;
+ }
+
+ mentor_i2c_stop_bit_set();
+
+ VERBOSE("%s: successful I2C probe\n", __func__);
+
+ return ret;
+}
+#endif
+
+/* regular i2c transaction */
+static int mentor_i2c_data_receive(uint8_t *p_block, uint32_t block_size)
+{
+ uint32_t reg, status, block_size_read = block_size;
+
+ /* Wait for cause interrupt */
+ if (mentor_i2c_wait_interrupt()) {
+ ERROR("Start clear bit timeout\n");
+ return -ETIMEDOUT;
+ }
+ while (block_size_read) {
+ if (block_size_read == 1) {
+ reg = mmio_read_32((uintptr_t)&base->control);
+ reg &= ~(I2C_CONTROL_ACK);
+ mmio_write_32((uintptr_t)&base->control, reg);
+ }
+ mentor_i2c_interrupt_clear();
+
+ if (mentor_i2c_wait_interrupt()) {
+ ERROR("Start clear bit timeout\n");
+ return -ETIMEDOUT;
+ }
+ /* check the status */
+ if (mentor_i2c_lost_arbitration(&status)) {
+ ERROR("%s - %d: Lost arbitration, got status %x\n",
+ __func__, __LINE__, status);
+ return -EAGAIN;
+ }
+ if ((status != I2C_STATUS_DATA_R_ACK) &&
+ (block_size_read != 1)) {
+ ERROR("Status %x in read transaction\n", status);
+ return -EPERM;
+ }
+ if ((status != I2C_STATUS_DATA_R_NAK) &&
+ (block_size_read == 1)) {
+ ERROR("Status %x in Rd Terminate\n", status);
+ return -EPERM;
+ }
+
+ /* read the data */
+ *p_block = (uint8_t) mmio_read_32((uintptr_t)&base->data);
+ VERBOSE("%s: place %d read %x\n", __func__,
+ block_size - block_size_read, *p_block);
+ p_block++;
+ block_size_read--;
+ }
+
+ return 0;
+}
+
+static int mentor_i2c_data_transmit(uint8_t *p_block, uint32_t block_size)
+{
+ uint32_t status, block_size_write = block_size;
+
+ if (mentor_i2c_wait_interrupt()) {
+ ERROR("Start clear bit timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ while (block_size_write) {
+ /* write the data */
+ mmio_write_32((uintptr_t)&base->data, (uint32_t) *p_block);
+ VERBOSE("%s: index = %d, data = %x\n", __func__,
+ block_size - block_size_write, *p_block);
+ p_block++;
+ block_size_write--;
+
+ mentor_i2c_interrupt_clear();
+
+ if (mentor_i2c_wait_interrupt()) {
+ ERROR("Start clear bit timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ /* check the status */
+ if (mentor_i2c_lost_arbitration(&status)) {
+ ERROR("%s - %d: Lost arbitration, got status %x\n",
+ __func__, __LINE__, status);
+ return -EAGAIN;
+ }
+ if (status != I2C_STATUS_DATA_W_ACK) {
+ ERROR("Status %x in write transaction\n", status);
+ return -EPERM;
+ }
+ }
+
+ return 0;
+}
+
+static int mentor_i2c_target_offset_set(uint8_t chip, uint32_t addr, int alen)
+{
+ uint8_t off_block[2];
+ uint32_t off_size;
+
+ if (alen == 2) { /* 2-byte addresses support */
+ off_block[0] = (addr >> 8) & 0xff;
+ off_block[1] = addr & 0xff;
+ off_size = 2;
+ } else { /* 1-byte addresses support */
+ off_block[0] = addr & 0xff;
+ off_size = 1;
+ }
+ VERBOSE("%s: off_size = %x addr1 = %x addr2 = %x\n", __func__,
+ off_size, off_block[0], off_block[1]);
+ return mentor_i2c_data_transmit(off_block, off_size);
+}
+
+#ifdef I2C_CAN_UNSTUCK
+static int mentor_i2c_unstuck(int ret)
+{
+ uint32_t v;
+
+ if (ret != -ETIMEDOUT)
+ return ret;
+ VERBOSE("Trying to \"unstuck i2c\"... ");
+ i2c_init(base);
+ mmio_write_32((uintptr_t)&base->unstuck, I2C_UNSTUCK_TRIGGER);
+ do {
+ v = mmio_read_32((uintptr_t)&base->unstuck);
+ } while (v & I2C_UNSTUCK_ONGOING);
+
+ if (v & I2C_UNSTUCK_ERROR) {
+ VERBOSE("failed - soft reset i2c\n");
+ ret = -EPERM;
+ } else {
+ VERBOSE("ok\n");
+ i2c_init(base);
+ ret = -EAGAIN;
+ }
+ return ret;
+}
+#else
+static int mentor_i2c_unstuck(int ret)
+{
+ VERBOSE("Cannot \"unstuck i2c\" - soft reset i2c\n");
+ return -EPERM;
+}
+#endif
+
+/*
+ * API Functions
+ */
+void i2c_init(void *i2c_base)
+{
+ /* For I2C speed and slave address, now we do not set them since
+ * we just provide the working speed and slave address otherwhere
+ * for i2c_init
+ */
+ base = (struct mentor_i2c_regs *)i2c_base;
+
+ /* Reset the I2C logic */
+ mmio_write_32((uintptr_t)&base->soft_reset, 0);
+
+ udelay(200);
+
+ mentor_i2c_bus_speed_set(CONFIG_SYS_I2C_SPEED);
+
+ /* Enable the I2C and slave */
+ mmio_write_32((uintptr_t)&base->control,
+ I2C_CONTROL_TWSIEN | I2C_CONTROL_ACK);
+
+ /* set the I2C slave address */
+ mmio_write_32((uintptr_t)&base->xtnd_slave_addr, 0);
+ mmio_write_32((uintptr_t)&base->slave_address, CONFIG_SYS_I2C_SLAVE);
+
+ /* unmask I2C interrupt */
+ mmio_write_32((uintptr_t)&base->control,
+ mmio_read_32((uintptr_t)&base->control) |
+ I2C_CONTROL_INTEN);
+
+ udelay(10);
+}
+
+/*
+ * i2c_read: - Read multiple bytes from an i2c device
+ *
+ * The higher level routines take into account that this function is only
+ * called with len < page length of the device (see configuration file)
+ *
+ * @chip: address of the chip which is to be read
+ * @addr: i2c data address within the chip
+ * @alen: length of the i2c data address (1..2 bytes)
+ * @buffer: where to write the data
+ * @len: how much byte do we want to read
+ * @return: 0 in case of success
+ */
+int i2c_read(uint8_t chip, uint32_t addr, int alen, uint8_t *buffer, int len)
+{
+ int ret = 0;
+ uint32_t counter = 0;
+
+#ifdef DEBUG_I2C
+ mentor_i2c_probe(chip);
+#endif
+
+ do {
+ if (ret != -EAGAIN && ret) {
+ ERROR("i2c transaction failed, after %d retries\n",
+ counter);
+ mentor_i2c_stop_bit_set();
+ return ret;
+ }
+
+ /* wait for 1 us for the interrupt clear to take effect */
+ if (counter > 0)
+ udelay(1);
+ counter++;
+
+ ret = mentor_i2c_start_bit_set();
+ if (ret) {
+ ret = mentor_i2c_unstuck(ret);
+ continue;
+ }
+
+ /* if EEPROM device */
+ if (alen != 0) {
+ ret = mentor_i2c_address_set(chip, I2C_CMD_WRITE);
+ if (ret)
+ continue;
+
+ ret = mentor_i2c_target_offset_set(chip, addr, alen);
+ if (ret)
+ continue;
+ ret = mentor_i2c_start_bit_set();
+ if (ret)
+ continue;
+ }
+
+ ret = mentor_i2c_address_set(chip, I2C_CMD_READ);
+ if (ret)
+ continue;
+
+ ret = mentor_i2c_data_receive(buffer, len);
+ if (ret)
+ continue;
+
+ ret = mentor_i2c_stop_bit_set();
+ } while ((ret == -EAGAIN) && (counter < I2C_MAX_RETRY_CNT));
+
+ if (counter == I2C_MAX_RETRY_CNT) {
+ ERROR("I2C transactions failed, got EAGAIN %d times\n",
+ I2C_MAX_RETRY_CNT);
+ ret = -EPERM;
+ }
+ mmio_write_32((uintptr_t)&base->control,
+ mmio_read_32((uintptr_t)&base->control) |
+ I2C_CONTROL_ACK);
+
+ udelay(1);
+ return ret;
+}
+
+/*
+ * i2c_write: - Write multiple bytes to an i2c device
+ *
+ * The higher level routines take into account that this function is only
+ * called with len < page length of the device (see configuration file)
+ *
+ * @chip: address of the chip which is to be written
+ * @addr: i2c data address within the chip
+ * @alen: length of the i2c data address (1..2 bytes)
+ * @buffer: where to find the data to be written
+ * @len: how much byte do we want to read
+ * @return: 0 in case of success
+ */
+int i2c_write(uint8_t chip, uint32_t addr, int alen, uint8_t *buffer, int len)
+{
+ int ret = 0;
+ uint32_t counter = 0;
+
+ do {
+ if (ret != -EAGAIN && ret) {
+ ERROR("i2c transaction failed\n");
+ mentor_i2c_stop_bit_set();
+ return ret;
+ }
+ /* wait for 1 us for the interrupt clear to take effect */
+ if (counter > 0)
+ udelay(1);
+ counter++;
+
+ ret = mentor_i2c_start_bit_set();
+ if (ret) {
+ ret = mentor_i2c_unstuck(ret);
+ continue;
+ }
+
+ ret = mentor_i2c_address_set(chip, I2C_CMD_WRITE);
+ if (ret)
+ continue;
+
+ /* if EEPROM device */
+ if (alen != 0) {
+ ret = mentor_i2c_target_offset_set(chip, addr, alen);
+ if (ret)
+ continue;
+ }
+
+ ret = mentor_i2c_data_transmit(buffer, len);
+ if (ret)
+ continue;
+
+ ret = mentor_i2c_stop_bit_set();
+ } while ((ret == -EAGAIN) && (counter < I2C_MAX_RETRY_CNT));
+
+ if (counter == I2C_MAX_RETRY_CNT) {
+ ERROR("I2C transactions failed, got EAGAIN %d times\n",
+ I2C_MAX_RETRY_CNT);
+ ret = -EPERM;
+ }
+
+ udelay(1);
+ return ret;
+}
diff --git a/include/drivers/marvell/a8k_i2c.h b/include/drivers/mentor/mi2cv.h
similarity index 84%
rename from include/drivers/marvell/a8k_i2c.h
rename to include/drivers/mentor/mi2cv.h
index 8a9abe8..6b03ed7 100644
--- a/include/drivers/marvell/a8k_i2c.h
+++ b/include/drivers/mentor/mi2cv.h
@@ -1,14 +1,15 @@
/*
* Copyright (C) 2018 Marvell International Ltd.
+ * Copyright (C) 2018 Icenowy Zheng <icenowy@aosc.io>
*
* SPDX-License-Identifier: BSD-3-Clause
* https://spdx.org/licenses
*/
-/* This driver provides I2C support for Marvell A8K and compatible SoCs */
+/* This driver provides support for Mentor Graphics MI2CV IP core */
-#ifndef _A8K_I2C_H_
-#define _A8K_I2C_H_
+#ifndef _MI2CV_H_
+#define _MI2CV_H_
#include <stdint.h>
diff --git a/maintainers.rst b/maintainers.rst
index 969d5e5..3f64b83 100644
--- a/maintainers.rst
+++ b/maintainers.rst
@@ -24,6 +24,7 @@
:G: `smaeul`_
:F: docs/plat/allwinner.rst
:F: plat/allwinner/
+:F: drivers/allwinner/
Armv7-A architecture port
-------------------------
diff --git a/plat/allwinner/common/sunxi_bl31_setup.c b/plat/allwinner/common/sunxi_bl31_setup.c
index 8ecf490..7e11cec 100644
--- a/plat/allwinner/common/sunxi_bl31_setup.c
+++ b/plat/allwinner/common/sunxi_bl31_setup.c
@@ -93,6 +93,8 @@
sunxi_security_setup();
+ sunxi_pmic_setup();
+
INFO("BL31: Platform setup done\n");
}
diff --git a/plat/allwinner/common/sunxi_pm.c b/plat/allwinner/common/sunxi_pm.c
index 2a1f223..e4bb582 100644
--- a/plat/allwinner/common/sunxi_pm.c
+++ b/plat/allwinner/common/sunxi_pm.c
@@ -54,9 +54,7 @@
/* Turn off all secondary CPUs */
sunxi_disable_secondary_cpus(plat_my_core_pos());
- ERROR("PSCI: Full shutdown not implemented, halting\n");
- wfi();
- panic();
+ sunxi_power_down();
}
static void __dead2 sunxi_system_reset(void)
diff --git a/plat/allwinner/common/sunxi_private.h b/plat/allwinner/common/sunxi_private.h
index e45f494..20fa23e 100644
--- a/plat/allwinner/common/sunxi_private.h
+++ b/plat/allwinner/common/sunxi_private.h
@@ -13,6 +13,10 @@
void sunxi_disable_secondary_cpus(unsigned int primary_cpu);
uint16_t sunxi_read_soc_id(void);
+
+void sunxi_pmic_setup(void);
void sunxi_security_setup(void);
+void __dead2 sunxi_power_down(void);
+
#endif /* __SUNXI_PRIVATE_H__ */
diff --git a/plat/allwinner/sun50i_a64/platform.mk b/plat/allwinner/sun50i_a64/platform.mk
index 236464f..e2868af 100644
--- a/plat/allwinner/sun50i_a64/platform.mk
+++ b/plat/allwinner/sun50i_a64/platform.mk
@@ -30,6 +30,7 @@
${AW_PLAT}/common/sunxi_bl31_setup.c \
${AW_PLAT}/common/sunxi_cpu_ops.c \
${AW_PLAT}/common/sunxi_pm.c \
+ ${AW_PLAT}/sun50i_a64/sunxi_power.c \
${AW_PLAT}/common/sunxi_security.c \
${AW_PLAT}/common/sunxi_topology.c
diff --git a/plat/allwinner/sun50i_a64/sunxi_power.c b/plat/allwinner/sun50i_a64/sunxi_power.c
new file mode 100644
index 0000000..c1907d6
--- /dev/null
+++ b/plat/allwinner/sun50i_a64/sunxi_power.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2018, Icenowy Zheng <icenowy@aosc.io>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <debug.h>
+
+int sunxi_pmic_setup(void)
+{
+ /* STUB */
+ NOTICE("BL31: STUB PMIC setup code called\n");
+
+ return 0;
+}
+
+void __dead2 sunxi_power_down(void)
+{
+ ERROR("PSCI: Full shutdown not implemented, halting\n");
+ wfi();
+ panic();
+}
diff --git a/plat/allwinner/sun50i_h6/platform.mk b/plat/allwinner/sun50i_h6/platform.mk
index c1b26fa..c3901d0 100644
--- a/plat/allwinner/sun50i_h6/platform.mk
+++ b/plat/allwinner/sun50i_h6/platform.mk
@@ -7,6 +7,7 @@
include lib/xlat_tables_v2/xlat_tables.mk
AW_PLAT := plat/allwinner
+AW_DRIVERS := drivers/allwinner
PLAT_INCLUDES := -Iinclude/plat/arm/common \
-Iinclude/plat/arm/common/aarch64 \
@@ -15,6 +16,7 @@
PLAT_BL_COMMON_SOURCES := drivers/console/${ARCH}/console.S \
drivers/ti/uart/${ARCH}/16550_console.S \
+ ${AW_DRIVERS}/sunxi_i2c.c \
${XLAT_TABLES_LIB_SRCS} \
${AW_PLAT}/common/plat_helpers.S \
${AW_PLAT}/common/sunxi_common.c
@@ -30,6 +32,7 @@
${AW_PLAT}/common/sunxi_bl31_setup.c \
${AW_PLAT}/common/sunxi_cpu_ops.c \
${AW_PLAT}/common/sunxi_pm.c \
+ ${AW_PLAT}/sun50i_h6/sunxi_power.c \
${AW_PLAT}/common/sunxi_security.c \
${AW_PLAT}/common/sunxi_topology.c
diff --git a/plat/allwinner/sun50i_h6/sunxi_power.c b/plat/allwinner/sun50i_h6/sunxi_power.c
new file mode 100644
index 0000000..f109cce
--- /dev/null
+++ b/plat/allwinner/sun50i_h6/sunxi_power.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2018, Icenowy Zheng <icenowy@aosc.io>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <debug.h>
+#include <delay_timer.h>
+#include <errno.h>
+#include <mmio.h>
+#include <mentor/mi2cv.h>
+#include <string.h>
+#include <sunxi_mmap.h>
+
+#define AXP805_ADDR 0x36
+#define AXP805_ID 0x03
+
+enum pmic_type {
+ NO_PMIC,
+ AXP805,
+};
+
+enum pmic_type pmic;
+
+static int sunxi_init_r_i2c(void)
+{
+ uint32_t reg;
+
+ /* get currently configured function for pins PL0 and PL1 */
+ reg = mmio_read_32(SUNXI_R_PIO_BASE + 0x00);
+ if ((reg & 0xff) == 0x33) {
+ NOTICE("PMIC: already configured for TWI\n");
+ }
+
+ /* switch pins PL0 and PL1 to I2C */
+ mmio_write_32(SUNXI_R_PIO_BASE + 0x00, (reg & ~0xff) | 0x33);
+
+ /* level 2 drive strength */
+ reg = mmio_read_32(SUNXI_R_PIO_BASE + 0x14);
+ mmio_write_32(SUNXI_R_PIO_BASE + 0x14, (reg & ~0x0f) | 0xa);
+
+ /* set both ports to pull-up */
+ reg = mmio_read_32(SUNXI_R_PIO_BASE + 0x1c);
+ mmio_write_32(SUNXI_R_PIO_BASE + 0x1c, (reg & ~0x0f) | 0x5);
+
+ /* assert & de-assert reset of R_I2C */
+ reg = mmio_read_32(SUNXI_R_PRCM_BASE + 0x19c);
+ mmio_write_32(SUNXI_R_PRCM_BASE + 0x19c, 0);
+ reg = mmio_read_32(SUNXI_R_PRCM_BASE + 0x19c);
+ mmio_write_32(SUNXI_R_PRCM_BASE + 0x19c, reg | 0x00010000);
+
+ /* un-gate R_I2C clock */
+ reg = mmio_read_32(SUNXI_R_PRCM_BASE + 0x19c);
+ mmio_write_32(SUNXI_R_PRCM_BASE + 0x19c, reg | 0x00000001);
+
+ /* call mi2cv driver */
+ i2c_init((void *)SUNXI_R_I2C_BASE);
+
+ return 0;
+}
+
+int axp_i2c_read(uint8_t chip, uint8_t reg, uint8_t *val)
+{
+ int ret;
+
+ ret = i2c_write(chip, 0, 0, ®, 1);
+ if (ret)
+ return ret;
+
+ return i2c_read(chip, 0, 0, val, 1);
+}
+
+int axp_i2c_write(uint8_t chip, uint8_t reg, uint8_t val)
+{
+ return i2c_write(chip, reg, 1, &val, 1);
+}
+
+static int axp805_probe(void)
+{
+ int ret;
+ uint8_t val;
+
+ ret = axp_i2c_write(AXP805_ADDR, 0xff, 0x0);
+ if (ret) {
+ ERROR("PMIC: Cannot put AXP805 to master mode.\n");
+ return -EPERM;
+ }
+
+ ret = axp_i2c_read(AXP805_ADDR, AXP805_ID, &val);
+
+ if (!ret && ((val & 0xcf) == 0x40))
+ NOTICE("PMIC: AXP805 detected\n");
+ else if (ret) {
+ ERROR("PMIC: Cannot communicate with AXP805.\n");
+ return -EPERM;
+ } else {
+ ERROR("PMIC: Non-AXP805 chip attached at AXP805's address.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int sunxi_pmic_setup(void)
+{
+ int ret;
+
+ sunxi_init_r_i2c();
+
+ NOTICE("PMIC: Probing AXP805\n");
+ pmic = AXP805;
+
+ ret = axp805_probe();
+ if (ret)
+ pmic = NO_PMIC;
+ else
+ pmic = AXP805;
+
+ return 0;
+}
+
+void __dead2 sunxi_power_down(void)
+{
+ uint8_t val;
+
+ switch (pmic) {
+ case AXP805:
+ val = 0x26; /* Default value for REG 32H */
+ axp_i2c_read(AXP805_ADDR, 0x32, &val);
+ val |= 0x80;
+ axp_i2c_write(AXP805_ADDR, 0x32, val);
+ break;
+ default:
+ break;
+ }
+
+ udelay(1000);
+ ERROR("PSCI: Cannot communicate with PMIC, halting\n");
+ wfi();
+ panic();
+}
diff --git a/plat/marvell/a8k/a80x0/board/dram_port.c b/plat/marvell/a8k/a80x0/board/dram_port.c
index c720c11..a99bf7c 100644
--- a/plat/marvell/a8k/a80x0/board/dram_port.c
+++ b/plat/marvell/a8k/a80x0/board/dram_port.c
@@ -6,8 +6,8 @@
*/
#include <arch_helpers.h>
-#include <a8k_i2c.h>
#include <debug.h>
+#include <mentor/mi2cv.h>
#include <mmio.h>
#include <mv_ddr_if.h>
#include <mvebu_def.h>
diff --git a/plat/marvell/a8k/a80x0_mcbin/board/dram_port.c b/plat/marvell/a8k/a80x0_mcbin/board/dram_port.c
index b455b83..fa222ee 100644
--- a/plat/marvell/a8k/a80x0_mcbin/board/dram_port.c
+++ b/plat/marvell/a8k/a80x0_mcbin/board/dram_port.c
@@ -6,8 +6,8 @@
*/
#include <arch_helpers.h>
-#include <a8k_i2c.h>
#include <debug.h>
+#include <mentor/mi2cv.h>
#include <mmio.h>
#include <mv_ddr_if.h>
#include <mvebu_def.h>