Browse Source
Add i2c driver for A8K SoC family. Change-Id: I5932b2fce286d84fc3ad5a74c4c456001faa3196 Signed-off-by: Hanna Hawa <hannah@marvell.com> Signed-off-by: Konstantin Porotchkin <kostap@marvell.com>pull/1450/head
Konstantin Porotchkin
7 years ago
3 changed files with 670 additions and 0 deletions
@ -0,0 +1,613 @@ |
|||
/*
|
|||
* Copyright (C) 2018 Marvell International Ltd. |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
* https://spdx.org/licenses
|
|||
*/ |
|||
|
|||
/* 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_UNSTUCK_TRIGGER 0x1 |
|||
#define I2C_UNSTUCK_ONGOING 0x2 |
|||
#define I2C_UNSTUCK_ERROR 0x4 |
|||
struct marvell_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; |
|||
uint8_t reserved2[0xa0 - 0x20]; |
|||
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 plat_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,38 @@ |
|||
/*
|
|||
* Copyright (C) 2018 Marvell International Ltd. |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
* https://spdx.org/licenses
|
|||
*/ |
|||
|
|||
/* This driver provides I2C support for Marvell A8K and compatible SoCs */ |
|||
|
|||
#ifndef _A8K_I2C_H_ |
|||
#define _A8K_I2C_H_ |
|||
|
|||
#include <stdint.h> |
|||
|
|||
/*
|
|||
* Initialization, must be called once on start up, may be called |
|||
* repeatedly to change the speed and slave addresses. |
|||
*/ |
|||
void i2c_init(void *i2c_base); |
|||
|
|||
/*
|
|||
* Read/Write interface: |
|||
* chip: I2C chip address, range 0..127 |
|||
* addr: Memory (register) address within the chip |
|||
* alen: Number of bytes to use for addr (typically 1, 2 for larger |
|||
* memories, 0 for register type devices with only one |
|||
* register) |
|||
* buffer: Where to read/write the data |
|||
* len: How many bytes to read/write |
|||
* |
|||
* Returns: 0 on success, not 0 on failure |
|||
*/ |
|||
int i2c_read(uint8_t chip, |
|||
unsigned int addr, int alen, uint8_t *buffer, int len); |
|||
|
|||
int i2c_write(uint8_t chip, |
|||
unsigned int addr, int alen, uint8_t *buffer, int len); |
|||
#endif |
@ -0,0 +1,19 @@ |
|||
/*
|
|||
* Copyright (C) 2018 Marvell International Ltd. |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
* https://spdx.org/licenses
|
|||
*/ |
|||
|
|||
#ifndef _I2C_H_ |
|||
#define _I2C_H_ |
|||
|
|||
|
|||
void i2c_init(void); |
|||
|
|||
int i2c_read(uint8_t chip, |
|||
unsigned int addr, int alen, uint8_t *buffer, int len); |
|||
|
|||
int i2c_write(uint8_t chip, |
|||
unsigned int addr, int alen, uint8_t *buffer, int len); |
|||
#endif |
Loading…
Reference in new issue