Merge pull request #1534 from Icenowy/sun50i_h6_pmic
Add support for Allwinner H6 + X-Powers AXP805 PMIC combination
This commit is contained in:
commit
e636812dce
|
@ -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"
|
|
@ -7,57 +7,22 @@
|
||||||
|
|
||||||
/* This driver provides I2C support for Marvell A8K and compatible SoCs */
|
/* 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 <mmio.h>
|
||||||
#include <mvebu_def.h>
|
|
||||||
|
|
||||||
#if LOG_LEVEL >= LOG_LEVEL_VERBOSE
|
|
||||||
#define DEBUG_I2C
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CONFIG_SYS_TCLK 250000000
|
#define CONFIG_SYS_TCLK 250000000
|
||||||
#define CONFIG_SYS_I2C_SPEED 100000
|
#define CONFIG_SYS_I2C_SPEED 100000
|
||||||
#define CONFIG_SYS_I2C_SLAVE 0x0
|
#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_CAN_UNSTUCK
|
||||||
#define I2C_DATA_ADDR_7BIT_MASK (0xFF << I2C_DATA_ADDR_7BIT_OFFS)
|
|
||||||
|
|
||||||
#define I2C_CONTROL_ACK 0x00000004
|
struct mentor_i2c_regs {
|
||||||
#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
|
|
||||||
struct marvell_i2c_regs {
|
|
||||||
uint32_t slave_address;
|
uint32_t slave_address;
|
||||||
uint32_t data;
|
uint32_t data;
|
||||||
uint32_t control;
|
uint32_t control;
|
||||||
union {
|
union {
|
||||||
uint32_t status; /* when reading */
|
uint32_t status; /* when reading */
|
||||||
uint32_t baudrate; /* when writing */
|
uint32_t baudrate; /* when writing */
|
||||||
} u;
|
};
|
||||||
uint32_t xtnd_slave_addr;
|
uint32_t xtnd_slave_addr;
|
||||||
uint32_t reserved[2];
|
uint32_t reserved[2];
|
||||||
uint32_t soft_reset;
|
uint32_t soft_reset;
|
||||||
|
@ -65,549 +30,4 @@ struct marvell_i2c_regs {
|
||||||
uint32_t unstuck;
|
uint32_t unstuck;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct marvell_i2c_regs *base;
|
#include "../../mentor/i2c/mi2cv.c"
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -1,14 +1,15 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2018 Marvell International Ltd.
|
* Copyright (C) 2018 Marvell International Ltd.
|
||||||
|
* Copyright (C) 2018 Icenowy Zheng <icenowy@aosc.io>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-3-Clause
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
* https://spdx.org/licenses
|
* 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_
|
#ifndef _MI2CV_H_
|
||||||
#define _A8K_I2C_H_
|
#define _MI2CV_H_
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
|
@ -24,6 +24,7 @@ Allwinner ARMv8 platform port
|
||||||
:G: `smaeul`_
|
:G: `smaeul`_
|
||||||
:F: docs/plat/allwinner.rst
|
:F: docs/plat/allwinner.rst
|
||||||
:F: plat/allwinner/
|
:F: plat/allwinner/
|
||||||
|
:F: drivers/allwinner/
|
||||||
|
|
||||||
Armv7-A architecture port
|
Armv7-A architecture port
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
|
@ -93,6 +93,8 @@ void bl31_platform_setup(void)
|
||||||
|
|
||||||
sunxi_security_setup();
|
sunxi_security_setup();
|
||||||
|
|
||||||
|
sunxi_pmic_setup();
|
||||||
|
|
||||||
INFO("BL31: Platform setup done\n");
|
INFO("BL31: Platform setup done\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,9 +54,7 @@ static void __dead2 sunxi_system_off(void)
|
||||||
/* Turn off all secondary CPUs */
|
/* Turn off all secondary CPUs */
|
||||||
sunxi_disable_secondary_cpus(plat_my_core_pos());
|
sunxi_disable_secondary_cpus(plat_my_core_pos());
|
||||||
|
|
||||||
ERROR("PSCI: Full shutdown not implemented, halting\n");
|
sunxi_power_down();
|
||||||
wfi();
|
|
||||||
panic();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __dead2 sunxi_system_reset(void)
|
static void __dead2 sunxi_system_reset(void)
|
||||||
|
|
|
@ -13,6 +13,10 @@ void sunxi_cpu_on(unsigned int cluster, unsigned int core);
|
||||||
void sunxi_disable_secondary_cpus(unsigned int primary_cpu);
|
void sunxi_disable_secondary_cpus(unsigned int primary_cpu);
|
||||||
|
|
||||||
uint16_t sunxi_read_soc_id(void);
|
uint16_t sunxi_read_soc_id(void);
|
||||||
|
|
||||||
|
void sunxi_pmic_setup(void);
|
||||||
void sunxi_security_setup(void);
|
void sunxi_security_setup(void);
|
||||||
|
|
||||||
|
void __dead2 sunxi_power_down(void);
|
||||||
|
|
||||||
#endif /* __SUNXI_PRIVATE_H__ */
|
#endif /* __SUNXI_PRIVATE_H__ */
|
||||||
|
|
|
@ -30,6 +30,7 @@ BL31_SOURCES += drivers/arm/gic/common/gic_common.c \
|
||||||
${AW_PLAT}/common/sunxi_bl31_setup.c \
|
${AW_PLAT}/common/sunxi_bl31_setup.c \
|
||||||
${AW_PLAT}/common/sunxi_cpu_ops.c \
|
${AW_PLAT}/common/sunxi_cpu_ops.c \
|
||||||
${AW_PLAT}/common/sunxi_pm.c \
|
${AW_PLAT}/common/sunxi_pm.c \
|
||||||
|
${AW_PLAT}/sun50i_a64/sunxi_power.c \
|
||||||
${AW_PLAT}/common/sunxi_security.c \
|
${AW_PLAT}/common/sunxi_security.c \
|
||||||
${AW_PLAT}/common/sunxi_topology.c
|
${AW_PLAT}/common/sunxi_topology.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();
|
||||||
|
}
|
|
@ -7,6 +7,7 @@
|
||||||
include lib/xlat_tables_v2/xlat_tables.mk
|
include lib/xlat_tables_v2/xlat_tables.mk
|
||||||
|
|
||||||
AW_PLAT := plat/allwinner
|
AW_PLAT := plat/allwinner
|
||||||
|
AW_DRIVERS := drivers/allwinner
|
||||||
|
|
||||||
PLAT_INCLUDES := -Iinclude/plat/arm/common \
|
PLAT_INCLUDES := -Iinclude/plat/arm/common \
|
||||||
-Iinclude/plat/arm/common/aarch64 \
|
-Iinclude/plat/arm/common/aarch64 \
|
||||||
|
@ -15,6 +16,7 @@ PLAT_INCLUDES := -Iinclude/plat/arm/common \
|
||||||
|
|
||||||
PLAT_BL_COMMON_SOURCES := drivers/console/${ARCH}/console.S \
|
PLAT_BL_COMMON_SOURCES := drivers/console/${ARCH}/console.S \
|
||||||
drivers/ti/uart/${ARCH}/16550_console.S \
|
drivers/ti/uart/${ARCH}/16550_console.S \
|
||||||
|
${AW_DRIVERS}/sunxi_i2c.c \
|
||||||
${XLAT_TABLES_LIB_SRCS} \
|
${XLAT_TABLES_LIB_SRCS} \
|
||||||
${AW_PLAT}/common/plat_helpers.S \
|
${AW_PLAT}/common/plat_helpers.S \
|
||||||
${AW_PLAT}/common/sunxi_common.c
|
${AW_PLAT}/common/sunxi_common.c
|
||||||
|
@ -30,6 +32,7 @@ BL31_SOURCES += drivers/arm/gic/common/gic_common.c \
|
||||||
${AW_PLAT}/common/sunxi_bl31_setup.c \
|
${AW_PLAT}/common/sunxi_bl31_setup.c \
|
||||||
${AW_PLAT}/common/sunxi_cpu_ops.c \
|
${AW_PLAT}/common/sunxi_cpu_ops.c \
|
||||||
${AW_PLAT}/common/sunxi_pm.c \
|
${AW_PLAT}/common/sunxi_pm.c \
|
||||||
|
${AW_PLAT}/sun50i_h6/sunxi_power.c \
|
||||||
${AW_PLAT}/common/sunxi_security.c \
|
${AW_PLAT}/common/sunxi_security.c \
|
||||||
${AW_PLAT}/common/sunxi_topology.c
|
${AW_PLAT}/common/sunxi_topology.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();
|
||||||
|
}
|
|
@ -6,8 +6,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <arch_helpers.h>
|
#include <arch_helpers.h>
|
||||||
#include <a8k_i2c.h>
|
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
|
#include <mentor/mi2cv.h>
|
||||||
#include <mmio.h>
|
#include <mmio.h>
|
||||||
#include <mv_ddr_if.h>
|
#include <mv_ddr_if.h>
|
||||||
#include <mvebu_def.h>
|
#include <mvebu_def.h>
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <arch_helpers.h>
|
#include <arch_helpers.h>
|
||||||
#include <a8k_i2c.h>
|
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
|
#include <mentor/mi2cv.h>
|
||||||
#include <mmio.h>
|
#include <mmio.h>
|
||||||
#include <mv_ddr_if.h>
|
#include <mv_ddr_if.h>
|
||||||
#include <mvebu_def.h>
|
#include <mvebu_def.h>
|
||||||
|
|
Loading…
Reference in New Issue