diff options
| -rw-r--r-- | drivers/marvell/i2c/a8k_i2c.c | 587 | ||||
| -rw-r--r-- | drivers/mentor/i2c/mi2cv.c | 607 | ||||
| -rw-r--r-- | include/drivers/mentor/mi2cv.h (renamed from include/drivers/marvell/a8k_i2c.h) | 7 | ||||
| -rw-r--r-- | plat/marvell/a8k/a80x0/board/dram_port.c | 2 | ||||
| -rw-r--r-- | plat/marvell/a8k/a80x0_mcbin/board/dram_port.c | 2 | 
5 files changed, 616 insertions, 589 deletions
| diff --git a/drivers/marvell/i2c/a8k_i2c.c b/drivers/marvell/i2c/a8k_i2c.c index fe8bc52e..1c0f922c 100644 --- a/drivers/marvell/i2c/a8k_i2c.c +++ b/drivers/marvell/i2c/a8k_i2c.c @@ -7,51 +7,15 @@  /* 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 +#define I2C_CAN_UNSTUCK -struct  marvell_i2c_regs { +struct  mentor_i2c_regs {  	uint32_t slave_address;  	uint32_t data;  	uint32_t control; @@ -66,549 +30,4 @@ struct  marvell_i2c_regs {  	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->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->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 00000000..618836ba --- /dev/null +++ b/drivers/mentor/i2c/mi2cv.c @@ -0,0 +1,607 @@ +/* + * 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); +	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 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 index 8a9abe8d..6b03ed7f 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/plat/marvell/a8k/a80x0/board/dram_port.c b/plat/marvell/a8k/a80x0/board/dram_port.c index c720c117..a99bf7c2 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 b455b837..fa222eec 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> | 
