aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRuilin Hao <rlhao@marvell.com>2015-11-10 00:18:59 -0800
committerPatrick Georgi <pgeorgi@google.com>2016-02-04 11:31:45 +0100
commitde4defbaaf76a8bf7a705e3d1b265e1936cd7481 (patch)
tree8b1184e4496851be3a9a98d8a37527be4891bab9 /src
parentc1b9e7934c0b100b8ce558669df8b57a03b2271f (diff)
soc/marvell/armada38x: Add i2c driver for armada38x
Port i2c driver from uboot to coreboot BUG=chrome-os-partner:47462 TEST=emerge-cyclone coreboot BRANCH=tot Change-Id: I8ce2a965acaed68ad0f0518648490ec471c6810b Signed-off-by: Patrick Georgi <pgeorgi@chromium.org> Original-Commit-Id: 4c2e9592662787ebed1d0aa8cafaa00fd12c2e9c Original-Change-Id: If791228edf29405fa4b2f959a21510bd7da9865b Original-Signed-off-by: Ruilin Hao <rlhao@marvell.com> Original-Reviewed-on: https://chromium-review.googlesource.com/313342 Original-Commit-Ready: Kan Yan <kyan@google.com> Original-Tested-by: Kan Yan <kyan@google.com> Original-Reviewed-by: Furquan Shaikh <furquan@chromium.org> Reviewed-on: https://review.coreboot.org/13113 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
Diffstat (limited to 'src')
-rw-r--r--src/soc/marvell/armada38x/Makefile.inc7
-rw-r--r--src/soc/marvell/armada38x/clock.c35
-rw-r--r--src/soc/marvell/armada38x/i2c.c1251
-rw-r--r--src/soc/marvell/armada38x/include/soc/clock.h21
-rw-r--r--src/soc/marvell/armada38x/include/soc/i2c.h120
-rw-r--r--src/soc/marvell/armada38x/spi.c3
-rw-r--r--src/soc/marvell/armada38x/uart.c3
7 files changed, 1437 insertions, 3 deletions
diff --git a/src/soc/marvell/armada38x/Makefile.inc b/src/soc/marvell/armada38x/Makefile.inc
index bc72d10dc9..c62d95f96e 100644
--- a/src/soc/marvell/armada38x/Makefile.inc
+++ b/src/soc/marvell/armada38x/Makefile.inc
@@ -5,6 +5,7 @@ bootblock-y += bootblock_asm.S
bootblock-y += spi.c
bootblock-y += gpio.c
bootblock-y += monotonic_timer.c
+bootblock-y += clock.c
ifeq ($(CONFIG_BOOTBLOCK_CONSOLE),y)
bootblock-$(CONFIG_DRIVERS_UART) += uart.c
endif
@@ -13,12 +14,15 @@ verstage-$(CONFIG_DRIVERS_UART) += uart.c
verstage-y += monotonic_timer.c
verstage-y += spi.c
verstage-y += gpio.c
+verstage-y += i2c.c
+verstage-y += clock.c
romstage-y += spi.c
romstage-y += gpio.c
romstage-y += cbmem.c
romstage-y += monotonic_timer.c
romstage-$(CONFIG_DRIVERS_UART) += uart.c
+romstage-y += clock.c
ramstage-y += spi.c
ramstage-y += gpio.c
@@ -26,8 +30,9 @@ ramstage-y += cbmem.c
ramstage-y += monotonic_timer.c
ramstage-y += soc.c
ramstage-$(CONFIG_DRIVERS_UART) += uart.c
+ramstage-y += clock.c
-CPPFLAGS_common += -Isrc/soc/marvell/armada38x/include/
+CPPFLAGS_common += -Isrc/soc/marvell/armada38x/include/ -Isrc/commonlib/include/commonlib/
BIN_HDR = 3rdparty/blobs/cpu/marvell/armada38x/bin_hdr.bin
DOIMAGE = 3rdparty/blobs/cpu/marvell/armada38x/doimage
diff --git a/src/soc/marvell/armada38x/clock.c b/src/soc/marvell/armada38x/clock.c
new file mode 100644
index 0000000000..579e1a3d2b
--- /dev/null
+++ b/src/soc/marvell/armada38x/clock.c
@@ -0,0 +1,35 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2015 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <soc/common.h>
+#include <soc/clock.h>
+
+uint32_t mv_tclk_get(void)
+{
+ uint32_t tclk_reg_value;
+ uint32_t tclk;
+
+ tclk_reg_value = (mrvl_reg_read(MPP_SAMPLE_AT_RESET));
+ tclk_reg_value = ((tclk_reg_value & (1 << 15)) >> 15);
+ switch (tclk_reg_value) {
+ case 0:
+ tclk = MV_BOARD_TCLK_250MHZ;
+ case 1:
+ tclk = MV_BOARD_TCLK_200MHZ;
+ default:
+ tclk = MV_BOARD_TCLK_250MHZ;
+ }
+ return tclk;
+}
diff --git a/src/soc/marvell/armada38x/i2c.c b/src/soc/marvell/armada38x/i2c.c
new file mode 100644
index 0000000000..723d3b4b1d
--- /dev/null
+++ b/src/soc/marvell/armada38x/i2c.c
@@ -0,0 +1,1251 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2015 Marvell Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <arch/io.h>
+#include <arch/cpu.h>
+#include <console/console.h>
+#include <delay.h>
+#include <device/i2c.h>
+#include <soc/common.h>
+#include <soc/i2c.h>
+#include <soc/clock.h>
+#include <helpers.h>
+
+#undef MV_DEBUG
+//#define MV_DEBUG
+#ifdef MV_DEBUG
+#define DB(x) x
+#else
+#define DB(x)
+#endif
+#define mv_os_printf(args...) printk(BIOS_INFO, args)
+
+/* The TWSI interface supports both 7-bit and 10-bit addressing. */
+/* This enumerator describes addressing type. */
+typedef enum _mv_twsi_addr_type {
+ ADDR7_BIT, /* 7 bit address */
+ ADDR10_BIT /* 10 bit address */
+} MV_TWSI_ADDR_TYPE;
+
+/* This structure describes TWSI address. */
+typedef struct _mv_twsi_addr {
+ uint32_t address; /* address */
+ MV_TWSI_ADDR_TYPE type; /* Address type */
+} MV_TWSI_ADDR;
+
+/* This structure describes a TWSI slave. */
+typedef struct _mv_twsi_slave {
+ MV_TWSI_ADDR slave_addr;
+ int valid_offset; /* whether the slave has offset (i.e. Eeprom etc.) */
+ uint32_t offset; /* offset in the slave. */
+ int more_than256; /* whether the ofset is bigger then 256 */
+} MV_TWSI_SLAVE;
+
+/* This enumerator describes TWSI protocol commands. */
+typedef enum _mv_twsi_cmd {
+ MV_TWSI_WRITE, /* TWSI write command - 0 according to spec */
+ MV_TWSI_READ /* TWSI read command - 1 according to spec */
+} MV_TWSI_CMD;
+
+static void twsi_int_flg_clr(uint8_t chan_num);
+static uint8_t twsi_main_int_get(uint8_t chan_num);
+static void twsi_ack_bit_set(uint8_t chan_num);
+static uint32_t twsi_sts_get(uint8_t chan_num);
+static void twsi_reset(uint8_t chan_num);
+static int twsi_addr7_bit_set(uint8_t chan_num,
+ uint32_t device_address,
+ MV_TWSI_CMD command);
+static int twsi_addr10_bit_set(uint8_t chan_num,
+ uint32_t device_address,
+ MV_TWSI_CMD command);
+static int twsi_data_transmit(uint8_t chan_num,
+ uint8_t *p_block,
+ uint32_t block_size);
+static int twsi_data_receive(uint8_t chan_num,
+ uint8_t *p_block,
+ uint32_t block_size);
+static int twsi_target_offs_set(uint8_t chan_num,
+ uint32_t offset,
+ uint8_t more_than256);
+static int mv_twsi_start_bit_set(uint8_t chan_num);
+static int mv_twsi_stop_bit_set(uint8_t chan_num);
+static int mv_twsi_addr_set(uint8_t chan_num,
+ MV_TWSI_ADDR *twsi_addr,
+ MV_TWSI_CMD command);
+static uint32_t mv_twsi_init(uint8_t chan_num,
+ uint32_t frequency,
+ uint32_t Tclk,
+ MV_TWSI_ADDR *twsi_addr,
+ uint8_t general_call_enable);
+static int mv_twsi_read(uint8_t chan_num,
+ MV_TWSI_SLAVE *twsi_slave,
+ uint8_t *p_block,
+ uint32_t block_size);
+static int mv_twsi_write(uint8_t chan_num,
+ MV_TWSI_SLAVE *twsi_slave,
+ uint8_t *p_block,
+ uint32_t block_size);
+static uint32_t who_am_i(void);
+static int i2c_init(unsigned bus);
+static void i2c_reset(unsigned bus);
+
+static int m_initialized[MAX_I2C_NUM] = {0, 0};
+
+static uint8_t twsi_timeout_chk(uint32_t timeout, const char *p_string)
+{
+ if (timeout >= TWSI_TIMEOUT_VALUE) {
+ DB(mv_os_printf("%s", p_string));
+ return MV_TRUE;
+ }
+ return MV_FALSE;
+}
+
+/*******************************************************************************
+* mv_twsi_start_bit_set - Set start bit on the bus
+*
+* DESCRIPTION:
+* This routine sets the start bit on the TWSI bus.
+* The routine first checks for interrupt flag condition, then it sets
+* the start bit in the TWSI Control register.
+* If the interrupt flag condition check previously was set, the function
+* will clear it.
+* The function then wait for the start bit to be cleared by the HW.
+* Then it waits for the interrupt flag to be set and eventually, the
+* TWSI status is checked to be 0x8 or 0x10(repeated start bit).
+*
+* INPUT:
+* chan_num - TWSI channel.
+*
+* OUTPUT:
+* None.
+*
+* RETURN:
+* MV_OK if start bit was set successfuly on the bus.
+* MV_FAIL if start_bit not set or status does not indicate start
+* condition trasmitted.
+*
+*******************************************************************************/
+static int mv_twsi_start_bit_set(uint8_t chan_num)
+{
+ uint8_t is_int_flag = MV_FALSE;
+ uint32_t timeout, temp;
+
+ DB(mv_os_printf("TWSI: mv_twsi_start_bit_set\n"));
+ /* check Int flag */
+ if (twsi_main_int_get(chan_num))
+ is_int_flag = MV_TRUE;
+ /* set start Bit */
+ mrvl_reg_bit_set(TWSI_CONTROL_REG(chan_num), TWSI_CONTROL_START_BIT);
+
+ /* in case that the int flag was set before i.e. repeated start bit */
+ if (is_int_flag) {
+ DB(mv_os_printf(
+ "TWSI: mv_twsi_start_bit_set repeated start Bit\n"));
+ twsi_int_flg_clr(chan_num);
+ }
+
+ /* wait for interrupt */
+ timeout = 0;
+ while (!twsi_main_int_get(chan_num) && (timeout++ < TWSI_TIMEOUT_VALUE))
+ ;
+
+ /* check for timeout */
+ if (MV_TRUE ==
+ twsi_timeout_chk(timeout,
+ (const char *)"TWSI: Start Clear bit time_out.\n"))
+ return MV_TIMEOUT;
+
+ /* check that start bit went down */
+ if ((mrvl_reg_read(TWSI_CONTROL_REG(chan_num)) &
+ TWSI_CONTROL_START_BIT) != 0) {
+ mv_os_printf("TWSI: start bit didn't go down\n");
+ return MV_FAIL;
+ }
+
+ /* check the status */
+ temp = twsi_sts_get(chan_num);
+ if ((TWSI_M_LOST_ARB_DUR_AD_OR_DATA_TRA == temp) ||
+ (TWSI_M_LOST_ARB_DUR_AD_TRA_GNL_CALL_AD_REC_ACK_TRA == temp)) {
+ DB(mv_os_printf("TWSI: Lost Arb, status %x\n", temp));
+ return MV_RETRY;
+ } else if ((temp != TWSI_START_CON_TRA) &&
+ (temp != TWSI_REPEATED_START_CON_TRA)) {
+ mv_os_printf("TWSI: status %x after Set Start Bit.\n", temp);
+ return MV_FAIL;
+ }
+
+ return MV_OK;
+}
+
+/*******************************************************************************
+* mv_twsi_stop_bit_set - Set stop bit on the bus
+*
+* DESCRIPTION:
+* This routine set the stop bit on the TWSI bus.
+* The function then wait for the stop bit to be cleared by the HW.
+* Finally the function checks for status of 0xF8.
+*
+* INPUT:
+* chan_num - TWSI channel
+*
+* OUTPUT:
+* None.
+*
+* RETURN:
+* MV_TRUE is stop bit was set successfuly on the bus.
+*
+*******************************************************************************/
+static int mv_twsi_stop_bit_set(uint8_t chan_num)
+{
+ uint32_t timeout, temp;
+
+ /* Generate stop bit */
+ mrvl_reg_bit_set(TWSI_CONTROL_REG(chan_num), TWSI_CONTROL_STOP_BIT);
+
+ twsi_int_flg_clr(chan_num);
+
+ /* wait for stop bit to come down */
+ timeout = 0;
+ while (((mrvl_reg_read(TWSI_CONTROL_REG(chan_num)) &
+ TWSI_CONTROL_STOP_BIT) != 0) &&
+ (timeout++ < TWSI_TIMEOUT_VALUE))
+ ;
+
+ /* check for timeout */
+ if (MV_TRUE ==
+ twsi_timeout_chk(timeout,
+ (const char *)"TWSI: ERROR - Stop bit timeout\n"))
+ return MV_TIMEOUT;
+
+ /* check that the stop bit went down */
+ if ((mrvl_reg_read(TWSI_CONTROL_REG(chan_num)) &
+ TWSI_CONTROL_STOP_BIT) != 0) {
+ mv_os_printf(
+ "TWSI: ERROR - stop bit not went down\n");
+ return MV_FAIL;
+ }
+
+ /* check the status */
+ temp = twsi_sts_get(chan_num);
+ if ((TWSI_M_LOST_ARB_DUR_AD_OR_DATA_TRA == temp) ||
+ (TWSI_M_LOST_ARB_DUR_AD_TRA_GNL_CALL_AD_REC_ACK_TRA == temp)) {
+ DB(mv_os_printf("TWSI: Lost Arb, status %x\n", temp));
+ return MV_RETRY;
+ } else if (temp != TWSI_NO_REL_STS_INT_FLAG_IS_KEPT_0) {
+ mv_os_printf(
+ "TWSI: ERROR - status %x after Stop Bit\n",
+ temp);
+ return MV_FAIL;
+ }
+
+ return MV_OK;
+}
+
+/*******************************************************************************
+* twsi_main_int_get - Get twsi bit from main Interrupt cause.
+*
+* DESCRIPTION:
+* This routine returns the twsi interrupt flag value.
+*
+* INPUT:
+* None.
+*
+* OUTPUT:
+* None.
+*
+* RETURN:
+* MV_TRUE is interrupt flag is set, MV_FALSE otherwise.
+*
+*******************************************************************************/
+static uint32_t who_am_i(void)
+{
+ return (read_mpidr() & 0x1);
+}
+
+static uint8_t twsi_main_int_get(uint8_t chan_num)
+{
+ uint32_t temp;
+
+ /* get the int flag bit */
+ temp = mrvl_reg_read(MV_TWSI_CPU_MAIN_INT_CAUSE(chan_num, who_am_i()));
+ if (temp & (1 << CPU_MAIN_INT_TWSI_OFFS(chan_num)))
+ return MV_TRUE;
+
+ return MV_FALSE;
+}
+
+/*******************************************************************************
+* twsi_int_flg_clr - Clear Interrupt flag.
+*
+* DESCRIPTION:
+* This routine clears the interrupt flag. It does NOT poll the interrupt
+* to make sure the clear. After clearing the interrupt, it waits for at
+* least 1 miliseconds.
+*
+* INPUT:
+* chan_num - TWSI channel
+*
+* OUTPUT:
+* None.
+*
+* RETURN:
+* None.
+*
+*******************************************************************************/
+static void twsi_int_flg_clr(uint8_t chan_num)
+{
+ /* wait for 1ms to prevent TWSI register write after write problems */
+ mdelay(1);
+ /* clear the int flag bit */
+ mrvl_reg_bit_reset(
+ TWSI_CONTROL_REG(chan_num), TWSI_CONTROL_INT_FLAG_SET);
+ /* wait for 1 mili sec for the clear to take effect */
+ mdelay(1);
+}
+
+/*******************************************************************************
+* twsi_ack_bit_set - Set acknowledge bit on the bus
+*
+* DESCRIPTION:
+* This routine set the acknowledge bit on the TWSI bus.
+*
+* INPUT:
+* None.
+*
+* OUTPUT:
+* None.
+*
+* RETURN:
+* None.
+*
+*******************************************************************************/
+static void twsi_ack_bit_set(uint8_t chan_num)
+{
+ /*Set the Ack bit */
+ mrvl_reg_bit_set(TWSI_CONTROL_REG(chan_num), TWSI_CONTROL_ACK);
+ /* Add delay of 1ms */
+ mdelay(1);
+}
+
+/*******************************************************************************
+* twsi_init - Initialize TWSI interface
+*
+* DESCRIPTION:
+* This routine:
+* -Reset the TWSI.
+* -Initialize the TWSI clock baud rate according to given frequency
+* parameter based on Tclk frequency and enables TWSI slave.
+* -Set the ack bit.
+* -Assign the TWSI slave address according to the TWSI address Type.
+*
+* INPUT:
+* chan_num - TWSI channel
+* frequency - TWSI frequency in KHz. (up to 100_kHZ)
+*
+* OUTPUT:
+* None.
+*
+* RETURN:
+* Actual frequency.
+*
+*******************************************************************************/
+static uint32_t mv_twsi_init(uint8_t chan_num,
+ uint32_t frequency,
+ uint32_t Tclk,
+ MV_TWSI_ADDR *p_twsi_addr,
+ uint8_t general_call_enable)
+{
+ uint32_t n, m, freq, margin, min_margin = 0xffffffff;
+ uint32_t power;
+ uint32_t actual_freq = 0, actual_n = 0, actual_m = 0, val;
+
+ if (frequency > 100000)
+ die("TWSI frequency is too high!");
+
+ DB(mv_os_printf("TWSI: mv_twsi_init - Tclk = %d freq = %d\n", Tclk,
+ frequency));
+ /* Calucalte N and M for the TWSI clock baud rate */
+ for (n = 0; n < 8; n++) {
+ for (m = 0; m < 16; m++) {
+ power = 2 << n; /* power = 2^(n+1) */
+ freq = Tclk / (10 * (m + 1) * power);
+ margin = ABS(frequency - freq);
+
+ if ((freq <= frequency) && (margin < min_margin)) {
+ min_margin = margin;
+ actual_freq = freq;
+ actual_n = n;
+ actual_m = m;
+ }
+ }
+ }
+ DB(mv_os_printf("TWSI: mv_twsi_init - act_n %u act_m %u act_freq %u\n",
+ actual_n, actual_m, actual_freq));
+ /* Reset the TWSI logic */
+ twsi_reset(chan_num);
+
+ /* Set the baud rate */
+ val = ((actual_m << TWSI_BAUD_RATE_M_OFFS) |
+ actual_n << TWSI_BAUD_RATE_N_OFFS);
+ mrvl_reg_write(TWSI_STATUS_BAUDE_RATE_REG(chan_num), val);
+
+ /* Enable the TWSI and slave */
+ mrvl_reg_write(TWSI_CONTROL_REG(chan_num),
+ TWSI_CONTROL_ENA | TWSI_CONTROL_ACK);
+
+ /* set the TWSI slave address */
+ if (p_twsi_addr->type == ADDR10_BIT) {
+ /* writing the 2 most significant bits of the 10 bit address */
+ val = ((p_twsi_addr->address & TWSI_SLAVE_ADDR_10_BIT_MASK) >>
+ TWSI_SLAVE_ADDR_10_BIT_OFFS);
+ /* bits 7:3 must be 0x11110 */
+ val |= TWSI_SLAVE_ADDR_10_BIT_CONST;
+ /* set GCE bit */
+ if (general_call_enable)
+ val |= TWSI_SLAVE_ADDR_GCE_ENA;
+ /* write slave address */
+ mrvl_reg_write(TWSI_SLAVE_ADDR_REG(chan_num), val);
+
+ /* writing the 8 least significant bits of the 10 bit address */
+ val = (p_twsi_addr->address << TWSI_EXTENDED_SLAVE_OFFS) &
+ TWSI_EXTENDED_SLAVE_MASK;
+ mrvl_reg_write(TWSI_EXTENDED_SLAVE_ADDR_REG(chan_num), val);
+ } else {
+ /* set the 7 Bits address */
+ mrvl_reg_write(TWSI_EXTENDED_SLAVE_ADDR_REG(chan_num), 0x0);
+ val = (p_twsi_addr->address << TWSI_SLAVE_ADDR_7_BIT_OFFS) &
+ TWSI_SLAVE_ADDR_7_BIT_MASK;
+ mrvl_reg_write(TWSI_SLAVE_ADDR_REG(chan_num), val);
+ }
+
+ /* unmask twsi int */
+ mrvl_reg_bit_set(TWSI_CONTROL_REG(chan_num), TWSI_CONTROL_INT_ENA);
+
+ /* unmask twsi int in Interrupt source control register */
+ mrvl_reg_bit_set(CPU_INT_SOURCE_CONTROL_REG(
+ CPU_MAIN_INT_CAUSE_TWSI(chan_num)), (
+ 1 << CPU_INT_SOURCE_CONTROL_IRQ_OFFS));
+
+ /* Add delay of 1ms */
+ mdelay(1);
+
+ return actual_freq;
+}
+
+/*******************************************************************************
+* twsi_sts_get - Get the TWSI status value.
+*
+* DESCRIPTION:
+* This routine returns the TWSI status value.
+*
+* INPUT:
+* chan_num - TWSI channel
+*
+* OUTPUT:
+* None.
+*
+* RETURN:
+* uint32_t - the TWSI status.
+*
+*******************************************************************************/
+static uint32_t twsi_sts_get(uint8_t chan_num)
+{
+ return mrvl_reg_read(TWSI_STATUS_BAUDE_RATE_REG(chan_num));
+}
+
+/*******************************************************************************
+* twsi_reset - Reset the TWSI.
+*
+* DESCRIPTION:
+* Resets the TWSI logic and sets all TWSI registers to their reset values.
+*
+* INPUT:
+* chan_num - TWSI channel
+*
+* OUTPUT:
+* None.
+*
+* RETURN:
+* None
+*
+*******************************************************************************/
+static void twsi_reset(uint8_t chan_num)
+{
+ /* Reset the TWSI logic */
+ mrvl_reg_write(TWSI_SOFT_RESET_REG(chan_num), 0);
+
+ /* wait for 2 mili sec */
+ mdelay(2);
+}
+
+/*******************************************************************************
+* mv_twsi_addr_set - Set address on TWSI bus.
+*
+* DESCRIPTION:
+* This function Set address (7 or 10 Bit address) on the Twsi Bus.
+*
+* INPUT:
+* chan_num - TWSI channel
+* p_twsi_addr - twsi address.
+* command - read / write .
+*
+* OUTPUT:
+* None.
+*
+* RETURN:
+* MV_OK - if setting the address completed successfully.
+* MV_FAIL otherwmise.
+*
+*******************************************************************************/
+static int mv_twsi_addr_set(uint8_t chan_num,
+ MV_TWSI_ADDR *p_twsi_addr,
+ MV_TWSI_CMD command)
+{
+ DB(mv_os_printf(
+ "TWSI: mv_twsi_addr7_bit_set addr %x , type %d, cmd is %s\n",
+ p_twsi_addr->address, p_twsi_addr->type,
+ ((command == MV_TWSI_WRITE) ? "Write" : "Read")));
+ /* 10 Bit address */
+ if (p_twsi_addr->type == ADDR10_BIT)
+ return twsi_addr10_bit_set(chan_num, p_twsi_addr->address,
+ command);
+ /* 7 Bit address */
+ else
+ return twsi_addr7_bit_set(chan_num, p_twsi_addr->address,
+ command);
+}
+
+/*******************************************************************************
+* twsi_addr10_bit_set - Set 10 Bit address on TWSI bus.
+*
+* DESCRIPTION:
+* There are two address phases:
+* 1) Write '11110' to data register bits [7:3] and 10-bit address MSB
+* (bits [9:8]) to data register bits [2:1] plus a write(0) or read(1)
+*bit
+* to the Data register. Then it clears interrupt flag which drive
+* the address on the TWSI bus. The function then waits for interrupt
+* flag to be active and status 0x18 (write) or 0x40 (read) to be set.
+* 2) write the rest of 10-bit address to data register and clears
+* interrupt flag which drive the address on the TWSI bus. The
+* function then waits for interrupt flag to be active and status
+* 0xD0 (write) or 0xE0 (read) to be set.
+*
+* INPUT:
+* chan_num - TWSI channel
+* device_address - twsi address.
+* command - read / write .
+*
+* OUTPUT:
+* None.
+*
+* RETURN:
+* MV_OK - if setting the address completed successfully.
+* MV_FAIL otherwmise.
+*
+*******************************************************************************/
+static int twsi_addr10_bit_set(uint8_t chan_num,
+ uint32_t device_address,
+ MV_TWSI_CMD command)
+{
+ uint32_t val, timeout;
+
+ /* writing the 2 most significant bits of the 10 bit address */
+ val = ((device_address & TWSI_DATA_ADDR_10_BIT_MASK) >>
+ TWSI_DATA_ADDR_10_BIT_OFFS);
+ /* bits 7:3 must be 0x11110 */
+ val |= TWSI_DATA_ADDR_10_BIT_CONST;
+ /* set command */
+ val |= command;
+ mrvl_reg_write(TWSI_DATA_REG(chan_num), val);
+ /* WA add a delay */
+ mdelay(1);
+
+ /* clear Int flag */
+ twsi_int_flg_clr(chan_num);
+
+ /* wait for Int to be Set */
+ timeout = 0;
+ while (!twsi_main_int_get(chan_num) && (timeout++ < TWSI_TIMEOUT_VALUE))
+ ;
+
+ /* check for timeout */
+ if (MV_TRUE ==
+ twsi_timeout_chk(
+ timeout, (const char *)"TWSI: addr (10_bit) Int time_out.\n"))
+ return MV_TIMEOUT;
+
+ /* check the status */
+ val = twsi_sts_get(chan_num);
+ if ((TWSI_M_LOST_ARB_DUR_AD_OR_DATA_TRA == val) ||
+ (TWSI_M_LOST_ARB_DUR_AD_TRA_GNL_CALL_AD_REC_ACK_TRA == val)) {
+ DB(mv_os_printf("TWSI: Lost Arb, status %x\n", val));
+ return MV_RETRY;
+ } else if (((val != TWSI_AD_PLS_RD_BIT_TRA_ACK_REC) &&
+ (command == MV_TWSI_READ)) ||
+ ((val != TWSI_AD_PLS_WR_BIT_TRA_ACK_REC) &&
+ (command == MV_TWSI_WRITE))) {
+ mv_os_printf("TWSI: status %x 1st addr (10 Bit) in %s mode.\n",
+ val,
+ ((command == MV_TWSI_WRITE) ? "Write" : "Read"));
+ return MV_FAIL;
+ }
+
+ /* set 8 LSB of the address */
+ val = (device_address << TWSI_DATA_ADDR_7_BIT_OFFS) &
+ TWSI_DATA_ADDR_7_BIT_MASK;
+ mrvl_reg_write(TWSI_DATA_REG(chan_num), val);
+
+ /* clear Int flag */
+ twsi_int_flg_clr(chan_num);
+
+ /* wait for Int to be Set */
+ timeout = 0;
+ while (!twsi_main_int_get(chan_num) && (timeout++ < TWSI_TIMEOUT_VALUE))
+ ;
+
+ /* check for timeout */
+ if (MV_TRUE ==
+ twsi_timeout_chk(timeout,
+ (const char *)"TWSI: 2nd (10 Bit) Int tim_out.\n"))
+ return MV_TIMEOUT;
+
+ /* check the status */
+ val = twsi_sts_get(chan_num);
+ if ((TWSI_M_LOST_ARB_DUR_AD_OR_DATA_TRA == val) ||
+ (TWSI_M_LOST_ARB_DUR_AD_TRA_GNL_CALL_AD_REC_ACK_TRA == val)) {
+ DB(mv_os_printf("TWSI: Lost Arb, status %x\n", val));
+ return MV_RETRY;
+ } else if (((val != TWSI_SEC_AD_PLS_RD_BIT_TRA_ACK_REC) &&
+ (command == MV_TWSI_READ)) ||
+ ((val != TWSI_SEC_AD_PLS_WR_BIT_TRA_ACK_REC) &&
+ (command == MV_TWSI_WRITE))) {
+ mv_os_printf("TWSI: status %x 2nd addr(10 Bit) in %s mode.\n",
+ val,
+ ((command == MV_TWSI_WRITE) ? "Write" : "Read"));
+ return MV_FAIL;
+ }
+
+ return MV_OK;
+}
+
+/*******************************************************************************
+* twsi_addr7_bit_set - Set 7 Bit address on TWSI bus.
+*
+* DESCRIPTION:
+* This function writes 7 bit address plus a write or read bit to the
+* Data register. Then it clears interrupt flag which drive the address on
+* the TWSI bus. The function then waits for interrupt flag to be active
+* and status 0x18 (write) or 0x40 (read) to be set.
+*
+* INPUT:
+* chan_num - TWSI channel
+* device_address - twsi address.
+* command - read / write .
+*
+* OUTPUT:
+* None.
+*
+* RETURN:
+* MV_OK - if setting the address completed successfully.
+* MV_FAIL otherwmise.
+*
+*******************************************************************************/
+static int twsi_addr7_bit_set(uint8_t chan_num,
+ uint32_t device_address,
+ MV_TWSI_CMD command)
+{
+ uint32_t val, timeout;
+
+ /* set the address */
+ val = (device_address << TWSI_DATA_ADDR_7_BIT_OFFS) &
+ TWSI_DATA_ADDR_7_BIT_MASK;
+ /* set command */
+ val |= command;
+ mrvl_reg_write(TWSI_DATA_REG(chan_num), val);
+ /* WA add a delay */
+ mdelay(1);
+
+ /* clear Int flag */
+ twsi_int_flg_clr(chan_num);
+
+ /* wait for Int to be Set */
+ timeout = 0;
+ while (!twsi_main_int_get(chan_num) && (timeout++ < TWSI_TIMEOUT_VALUE))
+ ;
+
+ /* check for timeout */
+ if (MV_TRUE ==
+ twsi_timeout_chk(
+ timeout, (const char *)"TWSI: Addr (7 Bit) int time_out.\n"))
+ return MV_TIMEOUT;
+
+ /* check the status */
+ val = twsi_sts_get(chan_num);
+ if ((TWSI_M_LOST_ARB_DUR_AD_OR_DATA_TRA == val) ||
+ (TWSI_M_LOST_ARB_DUR_AD_TRA_GNL_CALL_AD_REC_ACK_TRA == val)) {
+ DB(mv_os_printf("TWSI: Lost Arb, status %x\n", val));
+ return MV_RETRY;
+ } else if (((val != TWSI_AD_PLS_RD_BIT_TRA_ACK_REC) &&
+ (command == MV_TWSI_READ)) ||
+ ((val != TWSI_AD_PLS_WR_BIT_TRA_ACK_REC) &&
+ (command == MV_TWSI_WRITE))) {
+ /* only in debug, since in boot we try to read the SPD of both
+ DRAM, and we don't
+ want error messeges in case DIMM doesn't exist. */
+ DB(mv_os_printf(
+ "TWSI: status %x addr (7 Bit) in %s mode.\n", val,
+ ((command == MV_TWSI_WRITE) ? "Write" : "Read")));
+ return MV_FAIL;
+ }
+
+ return MV_OK;
+}
+
+/*******************************************************************************
+* twsi_data_write - Trnasmit a data block over TWSI bus.
+*
+* DESCRIPTION:
+* This function writes a given data block to TWSI bus in 8 bit
+* granularity.
+* first The function waits for interrupt flag to be active then
+* For each 8-bit data:
+* The function writes data to data register. It then clears
+* interrupt flag which drives the data on the TWSI bus.
+* The function then waits for interrupt flag to be active and status
+* 0x28 to be set.
+*
+*
+* INPUT:
+* chan_num - TWSI channel
+* p_block - Data block.
+* block_size - number of chars in p_block.
+*
+* OUTPUT:
+* None.
+*
+* RETURN:
+* MV_OK - if transmiting the block completed successfully,
+* MV_BAD_PARAM - if p_block is NULL,
+* MV_FAIL otherwmise.
+*
+*******************************************************************************/
+static int twsi_data_transmit(uint8_t chan_num,
+ uint8_t *p_block,
+ uint32_t block_size)
+{
+ uint32_t timeout, temp, block_size_wr = block_size;
+
+ if (NULL == p_block)
+ return MV_BAD_PARAM;
+
+ /* wait for Int to be Set */
+ timeout = 0;
+ while (!twsi_main_int_get(chan_num) && (timeout++ < TWSI_TIMEOUT_VALUE))
+ ;
+
+ /* check for timeout */
+ if (MV_TRUE ==
+ twsi_timeout_chk(timeout,
+ (const char *)"TWSI: Read Data Int time_out.\n"))
+ return MV_TIMEOUT;
+
+ while (block_size_wr) {
+ /* write the data */
+ mrvl_reg_write(TWSI_DATA_REG(chan_num), (uint32_t)*p_block);
+ DB(mv_os_printf(
+ "TWSI: twsi_data_transmit place = %d write %x\n",
+ block_size - block_size_wr, *p_block));
+ p_block++;
+ block_size_wr--;
+
+ twsi_int_flg_clr(chan_num);
+
+ /* wait for Int to be Set */
+ timeout = 0;
+ while (!twsi_main_int_get(chan_num) &&
+ (timeout++ < TWSI_TIMEOUT_VALUE))
+ ;
+
+ /* check for timeout */
+ if (MV_TRUE == twsi_timeout_chk(
+ timeout, (const char *)"TWSI: time_out.\n"))
+ return MV_TIMEOUT;
+
+ /* check the status */
+ temp = twsi_sts_get(chan_num);
+ if ((TWSI_M_LOST_ARB_DUR_AD_OR_DATA_TRA == temp) ||
+ (TWSI_M_LOST_ARB_DUR_AD_TRA_GNL_CALL_AD_REC_ACK_TRA ==
+ temp)) {
+ DB(mv_os_printf("TWSI: Lost Arb, status %x\n", temp));
+ return MV_RETRY;
+ } else if (temp != TWSI_M_TRAN_DATA_BYTE_ACK_REC) {
+ mv_os_printf("TWSI: status %x in write trans\n", temp);
+ return MV_FAIL;
+ }
+ }
+
+ return MV_OK;
+}
+
+/*******************************************************************************
+* twsi_data_receive - Receive data block from TWSI bus.
+*
+* DESCRIPTION:
+* This function receive data block from TWSI bus in 8bit granularity
+* into p_block buffer.
+* first The function waits for interrupt flag to be active then
+* For each 8-bit data:
+* It clears the interrupt flag which allows the next data to be
+* received from TWSI bus.
+* The function waits for interrupt flag to be active,
+* and status reg is 0x50.
+* Then the function reads data from data register, and copies it to
+* the given buffer.
+*
+* INPUT:
+* chan_num - TWSI channel
+* block_size - number of bytes to read.
+*
+* OUTPUT:
+* p_block - Data block.
+*
+* RETURN:
+* MV_OK - if receive transaction completed successfully,
+* MV_BAD_PARAM - if p_block is NULL,
+* MV_FAIL otherwmise.
+*
+*******************************************************************************/
+static int twsi_data_receive(uint8_t chan_num,
+ uint8_t *p_block,
+ uint32_t block_size)
+{
+ uint32_t timeout, temp, block_size_rd = block_size;
+
+ if (NULL == p_block)
+ return MV_BAD_PARAM;
+
+ /* wait for Int to be Set */
+ timeout = 0;
+ while (!twsi_main_int_get(chan_num) && (timeout++ < TWSI_TIMEOUT_VALUE))
+ ;
+
+ /* check for timeout */
+ if (MV_TRUE ==
+ twsi_timeout_chk(timeout,
+ (const char *)"TWSI: Read Data int Time out .\n"))
+ return MV_TIMEOUT;
+
+ while (block_size_rd) {
+ if (block_size_rd == 1)
+ /* clear ack and Int flag */
+ mrvl_reg_bit_reset(
+ TWSI_CONTROL_REG(chan_num), TWSI_CONTROL_ACK);
+
+ twsi_int_flg_clr(chan_num);
+ /* wait for Int to be Set */
+ timeout = 0;
+ while ((!twsi_main_int_get(chan_num)) &&
+ (timeout++ < TWSI_TIMEOUT_VALUE))
+ ;
+
+ /* check for timeout */
+ if (MV_TRUE ==
+ twsi_timeout_chk(timeout, (const char *)"TWSI: Timeout.\n"))
+ return MV_TIMEOUT;
+
+ /* check the status */
+ temp = twsi_sts_get(chan_num);
+ if ((TWSI_M_LOST_ARB_DUR_AD_OR_DATA_TRA == temp) ||
+ (TWSI_M_LOST_ARB_DUR_AD_TRA_GNL_CALL_AD_REC_ACK_TRA ==
+ temp)) {
+ DB(mv_os_printf("TWSI: Lost Arb, status %x\n", temp));
+ return MV_RETRY;
+ } else if ((temp != TWSI_M_REC_RD_DATA_ACK_TRA) &&
+ (block_size_rd != 1)) {
+ mv_os_printf("TWSI: status %x in read trans\n", temp);
+ return MV_FAIL;
+ } else if ((temp != TWSI_M_REC_RD_DATA_ACK_NOT_TRA) &&
+ (block_size_rd == 1)) {
+ mv_os_printf("TWSI: status %x in Rd Terminate\n", temp);
+ return MV_FAIL;
+ }
+
+ /* read the data */
+ *p_block = (uint8_t)mrvl_reg_read(TWSI_DATA_REG(chan_num));
+ DB(mv_os_printf("TWSI: twsi_data_receive place %d read %x\n",
+ block_size - block_size_rd, *p_block));
+ p_block++;
+ block_size_rd--;
+ }
+
+ return MV_OK;
+}
+
+/*******************************************************************************
+* twsi_target_offs_set - Set TWST target offset on TWSI bus.
+*
+* DESCRIPTION:
+* The function support TWSI targets that have inside address space (for
+* example EEPROMs). The function:
+* 1) Convert the given offset into p_block and size.
+* in case the offset should be set to a TWSI slave which support
+* more then 256 bytes offset, the offset setting will be done
+* in 2 transactions.
+* 2) Use twsi_data_transmit to place those on the bus.
+*
+* INPUT:
+* chan_num - TWSI channel
+* offset - offset to be set on the EEPROM device.
+* more_than256 - whether the EEPROM device support more then 256 byte
+*offset.
+*
+* OUTPUT:
+* None.
+*
+* RETURN:
+* MV_OK - if setting the offset completed successfully.
+* MV_FAIL otherwmise.
+*
+*******************************************************************************/
+static int twsi_target_offs_set(uint8_t chan_num,
+ uint32_t offset,
+ uint8_t more_than256)
+{
+ uint8_t off_block[2];
+ uint32_t off_size;
+
+ if (more_than256 == MV_TRUE) {
+ off_block[0] = (offset >> 8) & 0xff;
+ off_block[1] = offset & 0xff;
+ off_size = 2;
+ } else {
+ off_block[0] = offset & 0xff;
+ off_size = 1;
+ }
+ DB(mv_os_printf(
+ "TWSI: twsi_target_offs_set off_size = %x addr1 = %x addr2 = %x\n",
+ off_size, off_block[0], off_block[1]));
+ return twsi_data_transmit(chan_num, off_block, off_size);
+}
+
+/*******************************************************************************
+* mv_twsi_read - Read data block from a TWSI Slave.
+*
+* DESCRIPTION:
+* The function calls the following functions:
+* -) mv_twsi_start_bit_set();
+* if (EEPROM device)
+* -) mv_twsi_addr_set(w);
+* -) twsi_target_offs_set();
+* -) mv_twsi_start_bit_set();
+* -) mv_twsi_addr_set(r);
+* -) twsi_data_receive();
+* -) mv_twsi_stop_bit_set();
+*
+* INPUT:
+* chan_num - TWSI channel
+* p_twsi_slave - Twsi Slave structure.
+* block_size - number of bytes to read.
+*
+* OUTPUT:
+* p_block - Data block.
+*
+* RETURN:
+* MV_OK - if EEPROM read transaction completed successfully,
+* MV_BAD_PARAM - if p_block is NULL,
+* MV_FAIL otherwmise.
+*
+*******************************************************************************/
+static int mv_twsi_read(uint8_t chan_num,
+ MV_TWSI_SLAVE *p_twsi_slave,
+ uint8_t *p_block,
+ uint32_t block_size)
+{
+ int rc;
+ int ret = MV_FAIL;
+ uint32_t counter = 0;
+
+ if ((NULL == p_block) || (NULL == p_twsi_slave))
+ return MV_BAD_PARAM;
+
+ do {
+ /* wait for 1 mili sec for the clear to take effect */
+ if (counter > 0)
+ mdelay(1);
+ ret = mv_twsi_start_bit_set(chan_num);
+
+ if (MV_RETRY == ret)
+ continue;
+ else if (MV_OK != ret) {
+ mv_twsi_stop_bit_set(chan_num);
+ DB(mv_os_printf(
+ "mv_twsi_read:mv_twsi_start_bit_set failed\n"));
+ return MV_FAIL;
+ }
+
+ DB(mv_os_printf(
+ "TWSI: mv_twsi_eeprom_read after mv_twsi_start_bit_set\n"));
+
+ /* in case offset exsist (i.e. eeprom ) */
+ if (MV_TRUE == p_twsi_slave->valid_offset) {
+ rc = mv_twsi_addr_set(chan_num,
+ &(p_twsi_slave->slave_addr),
+ MV_TWSI_WRITE);
+ if (MV_RETRY == rc)
+ continue;
+ else if (MV_OK != rc) {
+ mv_twsi_stop_bit_set(chan_num);
+ DB(mv_os_printf(
+ "mv_twsi_addr_set(%d,0x%x,%d) rc=%d\n",
+ chan_num,
+ (uint32_t) &(p_twsi_slave->slave_addr),
+ MV_TWSI_WRITE, rc));
+ return MV_FAIL;
+ }
+
+ ret =
+ twsi_target_offs_set(chan_num, p_twsi_slave->offset,
+ p_twsi_slave->more_than256);
+ if (MV_RETRY == ret)
+ continue;
+ else if (MV_OK != ret) {
+ mv_twsi_stop_bit_set(chan_num);
+ DB(mv_os_printf(
+ "TWSI: twsi_target_offs_set Failed\n"));
+ return MV_FAIL;
+ }
+ DB(mv_os_printf("TWSI: after twsi_target_offs_set\n"));
+ ret = mv_twsi_start_bit_set(chan_num);
+ if (MV_RETRY == ret)
+ continue;
+ else if (MV_OK != ret) {
+ mv_twsi_stop_bit_set(chan_num);
+ DB(mv_os_printf(
+ "TWSI: mv_twsi_start_bit_set failed\n"));
+ return MV_FAIL;
+ }
+ DB(mv_os_printf("TWSI: after mv_twsi_start_bit_set\n"));
+ }
+ ret = mv_twsi_addr_set(chan_num, &(p_twsi_slave->slave_addr),
+ MV_TWSI_READ);
+ if (MV_RETRY == ret)
+ continue;
+ else if (MV_OK != ret) {
+ mv_twsi_stop_bit_set(chan_num);
+ DB(mv_os_printf(
+ "mv_twsi_read: mv_twsi_addr_set 2 Failed\n"));
+ return MV_FAIL;
+ }
+ DB(mv_os_printf(
+ "TWSI: mv_twsi_eeprom_read after mv_twsi_addr_set\n"));
+
+ ret = twsi_data_receive(chan_num, p_block, block_size);
+ if (MV_RETRY == ret)
+ continue;
+ else if (MV_OK != ret) {
+ mv_twsi_stop_bit_set(chan_num);
+ DB(mv_os_printf(
+ "mv_twsi_read: twsi_data_receive Failed\n"));
+ return MV_FAIL;
+ }
+ DB(mv_os_printf(
+ "TWSI: mv_twsi_eeprom_read after twsi_data_receive\n"));
+
+ ret = mv_twsi_stop_bit_set(chan_num);
+ if (MV_RETRY == ret)
+ continue;
+ else if (MV_OK != ret) {
+ DB(mv_os_printf(
+ "mv_twsi_read: mv_twsi_stop_bit_set 3 Failed\n"));
+ return MV_FAIL;
+ }
+ counter++;
+ } while ((MV_RETRY == ret) && (counter < MAX_RETRY_CNT));
+
+ if (counter == MAX_RETRY_CNT)
+ DB(mv_os_printf("mv_twsi_write: Retry Expire\n"));
+
+ twsi_ack_bit_set(chan_num);
+
+ DB(mv_os_printf(
+ "TWSI: mv_twsi_eeprom_read after mv_twsi_stop_bit_set\n"));
+
+ return MV_OK;
+}
+
+/*******************************************************************************
+* mv_twsi_write - Write data block to a TWSI Slave.
+*
+* DESCRIPTION:
+* The function calls the following functions:
+* -) mv_twsi_start_bit_set();
+* -) mv_twsi_addr_set();
+* -)if (EEPROM device)
+* -) twsi_target_offs_set();
+* -) twsi_data_transmit();
+* -) mv_twsi_stop_bit_set();
+*
+* INPUT:
+* chan_num - TWSI channel
+* eeprom_address - eeprom address.
+* block_size - number of bytes to write.
+* p_block - Data block.
+*
+* OUTPUT:
+* None
+*
+* RETURN:
+* MV_OK - if EEPROM read transaction completed successfully.
+* MV_BAD_PARAM - if p_block is NULL,
+* MV_FAIL otherwmise.
+*
+* NOTE: Part of the EEPROM, required that the offset will be aligned to the
+* max write burst supported.
+*******************************************************************************/
+static int mv_twsi_write(uint8_t chan_num,
+ MV_TWSI_SLAVE *p_twsi_slave,
+ uint8_t *p_block,
+ uint32_t block_size)
+{
+ int ret = MV_FAIL;
+ uint32_t counter = 0;
+
+ if ((NULL == p_block) || (NULL == p_twsi_slave))
+ return MV_BAD_PARAM;
+
+ do {
+ if (counter >
+ 0) /* wait for 1 mili sec for the clear to take effect */
+ mdelay(1);
+ ret = mv_twsi_start_bit_set(chan_num);
+
+ if (MV_RETRY == ret)
+ continue;
+
+ else if (MV_OK != ret) {
+ mv_twsi_stop_bit_set(chan_num);
+ DB(mv_os_printf(
+ "mv_twsi_write: mv_twsi_start_bit_set failed\n"));
+ return MV_FAIL;
+ }
+
+ ret = mv_twsi_addr_set(chan_num, &(p_twsi_slave->slave_addr),
+ MV_TWSI_WRITE);
+ if (MV_RETRY == ret)
+ continue;
+ else if (MV_OK != ret) {
+ mv_twsi_stop_bit_set(chan_num);
+ DB(mv_os_printf(
+ "mv_twsi_write: mv_twsi_addr_set failed\n"));
+ return MV_FAIL;
+ }
+
+ /* in case offset exsist (i.e. eeprom ) */
+ if (MV_TRUE == p_twsi_slave->valid_offset) {
+ ret =
+ twsi_target_offs_set(chan_num, p_twsi_slave->offset,
+ p_twsi_slave->more_than256);
+ if (MV_RETRY == ret)
+ continue;
+ else if (MV_OK != ret) {
+ mv_twsi_stop_bit_set(chan_num);
+ DB(mv_os_printf(
+ "TWSI: twsi_target_offs_set failed\n"));
+ return MV_FAIL;
+ }
+ }
+
+ ret = twsi_data_transmit(chan_num, p_block, block_size);
+ if (MV_RETRY == ret)
+ continue;
+ else if (MV_OK != ret) {
+ mv_twsi_stop_bit_set(chan_num);
+ DB(mv_os_printf(
+ "mv_twsi_write: twsi_data_transmit failed\n"));
+ return MV_FAIL;
+ }
+ ret = mv_twsi_stop_bit_set(chan_num);
+ if (MV_RETRY == ret)
+ continue;
+ else if (MV_OK != ret) {
+ DB(mv_os_printf(
+ "mv_twsi_write: failed to set stopbit\n"));
+ return MV_FAIL;
+ }
+ counter++;
+ } while ((MV_RETRY == ret) && (counter < MAX_RETRY_CNT));
+
+ if (counter == MAX_RETRY_CNT)
+ DB(mv_os_printf("mv_twsi_write: Retry Expire\n"));
+
+ return MV_OK;
+}
+
+static int i2c_init(unsigned bus)
+{
+ if (bus >= MAX_I2C_NUM)
+ return 1;
+
+ if (!m_initialized[bus]) {
+ /* TWSI init */
+ MV_TWSI_ADDR slave;
+
+ slave.type = ADDR7_BIT;
+ slave.address = 0;
+ mv_twsi_init(bus, TWSI_SPEED, mv_tclk_get(), &slave, 0);
+ m_initialized[bus] = 1;
+ }
+
+ return 0;
+}
+
+static void i2c_reset(unsigned bus)
+{
+ if (bus < MAX_I2C_NUM)
+ m_initialized[bus] = 0;
+}
+
+int platform_i2c_transfer(unsigned bus, struct i2c_seg *segments, int seg_count)
+{
+ struct i2c_seg *seg = segments;
+ int ret = 0;
+ MV_TWSI_SLAVE twsi_slave;
+
+ if (i2c_init(bus))
+ return 1;
+
+ while (!ret && seg_count--) {
+ twsi_slave.slave_addr.address = seg->chip;
+ twsi_slave.slave_addr.type = ADDR7_BIT;
+ twsi_slave.more_than256 = MV_FALSE;
+ twsi_slave.valid_offset = MV_FALSE;
+ if (seg->read)
+ ret =
+ mv_twsi_read(bus, &twsi_slave, seg->buf, seg->len);
+ else
+ ret =
+ mv_twsi_write(bus, &twsi_slave, seg->buf, seg->len);
+ seg++;
+ }
+
+ if (ret) {
+ i2c_reset(bus);
+ DB(mv_os_printf("mv_twsi_read/mv_twsi_write failed\n"));
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/soc/marvell/armada38x/include/soc/clock.h b/src/soc/marvell/armada38x/include/soc/clock.h
new file mode 100644
index 0000000000..82af93724b
--- /dev/null
+++ b/src/soc/marvell/armada38x/include/soc/clock.h
@@ -0,0 +1,21 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2015 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SOC_MARVELL_ARMADA38X_CLOCK_H_
+#define __SOC_MARVELL_ARMADA38X_CLOCK_H_
+
+uint32_t mv_tclk_get(void);
+
+#endif // __SOC_MARVELL_ARMADA38X_CLOCK_H_
diff --git a/src/soc/marvell/armada38x/include/soc/i2c.h b/src/soc/marvell/armada38x/include/soc/i2c.h
new file mode 100644
index 0000000000..7de29534b6
--- /dev/null
+++ b/src/soc/marvell/armada38x/include/soc/i2c.h
@@ -0,0 +1,120 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2015 Marvell Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SOC_MARVELL_ARMADA38X_I2C_H_
+#define __SOC_MARVELL_ARMADA38X_I2C_H_
+
+#include <types.h>
+
+#define TWSI_SPEED 100000
+
+#define MAX_I2C_NUM 2
+#define MAX_RETRY_CNT 1000
+#define TWSI_TIMEOUT_VALUE 0x500
+
+#define MV_TWSI_SLAVE_REGS_OFFSET(chan_num) (0x11000 + (chan_num * 0x100))
+#define MV_TWSI_SLAVE_REGS_BASE(unit) (MV_TWSI_SLAVE_REGS_OFFSET(unit))
+#define TWSI_SLAVE_ADDR_REG(chan_num) (MV_TWSI_SLAVE_REGS_BASE(chan_num) + 0x00)
+
+#define MV_CPUIF_REGS_OFFSET(cpu) (0x21800 + (cpu)*0x100)
+#define MV_CPUIF_REGS_BASE(cpu) (MV_CPUIF_REGS_OFFSET(cpu))
+#define CPU_MAIN_INT_CAUSE_REG(vec, cpu) \
+ (MV_CPUIF_REGS_BASE(cpu) + 0x80 + (vec * 0x4))
+#define CPU_MAIN_INT_TWSI_OFFS(i) (2 + i)
+#define CPU_MAIN_INT_CAUSE_TWSI(i) (31 + i)
+#define TWSI_CPU_MAIN_INT_CAUSE_REG(cpu) CPU_MAIN_INT_CAUSE_REG(1, (cpu))
+#define MV_TWSI_CPU_MAIN_INT_CAUSE(ch_num, cpu) TWSI_CPU_MAIN_INT_CAUSE_REG(cpu)
+
+#define MV_MBUS_REGS_OFFSET (0x20000)
+#define MV_CPUIF_SHARED_REGS_BASE (MV_MBUS_REGS_OFFSET)
+#define CPU_INT_SOURCE_CONTROL_REG(i) \
+ (MV_CPUIF_SHARED_REGS_BASE + 0xB00 + (i * 0x4))
+
+#define CPU_INT_SOURCE_CONTROL_IRQ_OFFS 28
+#define CPU_INT_SOURCE_CONTROL_IRQ_MASK (1 << CPU_INT_SOURCE_CONTROL_IRQ_OFFS)
+
+#define TWSI_SLAVE_ADDR_GCE_ENA BIT(0)
+#define TWSI_SLAVE_ADDR_7_BIT_OFFS 0x1
+#define TWSI_SLAVE_ADDR_7_BIT_MASK (0xFF << TWSI_SLAVE_ADDR_7_BIT_OFFS)
+#define TWSI_SLAVE_ADDR_10_BIT_OFFS 0x7
+#define TWSI_SLAVE_ADDR_10_BIT_MASK 0x300
+#define TWSI_SLAVE_ADDR_10_BIT_CONST 0xF0
+
+#define TWSI_DATA_REG(chan_num) (MV_TWSI_SLAVE_REGS_BASE(chan_num) + 0x04)
+#define TWSI_DATA_COMMAND_OFFS 0x0
+#define TWSI_DATA_COMMAND_MASK (0x1 << TWSI_DATA_COMMAND_OFFS)
+#define TWSI_DATA_COMMAND_WR (0x1 << TWSI_DATA_COMMAND_OFFS)
+#define TWSI_DATA_COMMAND_RD (0x0 << TWSI_DATA_COMMAND_OFFS)
+#define TWSI_DATA_ADDR_7_BIT_OFFS 0x1
+#define TWSI_DATA_ADDR_7_BIT_MASK (0xFF << TWSI_DATA_ADDR_7_BIT_OFFS)
+#define TWSI_DATA_ADDR_10_BIT_OFFS 0x7
+#define TWSI_DATA_ADDR_10_BIT_MASK 0x300
+#define TWSI_DATA_ADDR_10_BIT_CONST 0xF0
+
+#define TWSI_CONTROL_REG(chan_num) (MV_TWSI_SLAVE_REGS_BASE(chan_num) + 0x08)
+#define TWSI_CONTROL_ACK BIT(2)
+#define TWSI_CONTROL_INT_FLAG_SET BIT(3)
+#define TWSI_CONTROL_STOP_BIT BIT(4)
+#define TWSI_CONTROL_START_BIT BIT(5)
+#define TWSI_CONTROL_ENA BIT(6)
+#define TWSI_CONTROL_INT_ENA BIT(7)
+
+#define TWSI_STATUS_BAUDE_RATE_REG(chan_num) \
+ (MV_TWSI_SLAVE_REGS_BASE(chan_num) + 0x0c)
+#define TWSI_BAUD_RATE_N_OFFS 0
+#define TWSI_BAUD_RATE_N_MASK (0x7 << TWSI_BAUD_RATE_N_OFFS)
+#define TWSI_BAUD_RATE_M_OFFS 3
+#define TWSI_BAUD_RATE_M_MASK (0xF << TWSI_BAUD_RATE_M_OFFS)
+
+#define TWSI_EXTENDED_SLAVE_ADDR_REG(chan_num) \
+ (MV_TWSI_SLAVE_REGS_BASE(chan_num) + 0x10)
+#define TWSI_EXTENDED_SLAVE_OFFS 0
+#define TWSI_EXTENDED_SLAVE_MASK (0xFF << TWSI_EXTENDED_SLAVE_OFFS)
+
+#define TWSI_SOFT_RESET_REG(chan_num) (MV_TWSI_SLAVE_REGS_BASE(chan_num) + 0x1c)
+
+#define TWSI_BUS_ERROR 0x00
+#define TWSI_START_CON_TRA 0x08
+#define TWSI_REPEATED_START_CON_TRA 0x10
+#define TWSI_AD_PLS_WR_BIT_TRA_ACK_REC 0x18
+#define TWSI_AD_PLS_WR_BIT_TRA_ACK_NOT_REC 0x20
+#define TWSI_M_TRAN_DATA_BYTE_ACK_REC 0x28
+#define TWSI_M_TRAN_DATA_BYTE_ACK_NOT_REC 0x30
+#define TWSI_M_LOST_ARB_DUR_AD_OR_DATA_TRA 0x38
+#define TWSI_AD_PLS_RD_BIT_TRA_ACK_REC 0x40
+#define TWSI_AD_PLS_RD_BIT_TRA_ACK_NOT_REC 0x48
+#define TWSI_M_REC_RD_DATA_ACK_TRA 0x50
+#define TWSI_M_REC_RD_DATA_ACK_NOT_TRA 0x58
+#define TWSI_SLA_REC_AD_PLS_WR_BIT_ACK_TRA 0x60
+#define TWSI_M_LOST_ARB_DUR_AD_TRA_AD_IS_TRGT_TO_SLA_ACK_TRA_W 0x68
+#define TWSI_GNL_CALL_REC_ACK_TRA 0x70
+#define TWSI_M_LOST_ARB_DUR_AD_TRA_GNL_CALL_AD_REC_ACK_TRA 0x78
+#define TWSI_SLA_REC_WR_DATA_AF_REC_SLA_AD_ACK_TRAN 0x80
+#define TWSI_SLA_REC_WR_DATA_AF_REC_SLA_AD_ACK_NOT_TRAN 0x88
+#define TWSI_SLA_REC_WR_DATA_AF_REC_GNL_CALL_ACK_TRAN 0x90
+#define TWSI_SLA_REC_WR_DATA_AF_REC_GNL_CALL_ACK_NOT_TRAN 0x98
+#define TWSI_SLA_REC_STOP_OR_REPEATED_STRT_CON 0xA0
+#define TWSI_SLA_REC_AD_PLS_RD_BIT_ACK_TRA 0xA8
+#define TWSI_M_LOST_ARB_DUR_AD_TRA_AD_IS_TRGT_TO_SLA_ACK_TRA_R 0xB0
+#define TWSI_SLA_TRA_RD_DATA_ACK_REC 0xB8
+#define TWSI_SLA_TRA_RD_DATA_ACK_NOT_REC 0xC0
+#define TWSI_SLA_TRA_LAST_RD_DATA_ACK_REC 0xC8
+#define TWSI_SEC_AD_PLS_WR_BIT_TRA_ACK_REC 0xD0
+#define TWSI_SEC_AD_PLS_WR_BIT_TRA_ACK_NOT_REC 0xD8
+#define TWSI_SEC_AD_PLS_RD_BIT_TRA_ACK_REC 0xE0
+#define TWSI_SEC_AD_PLS_RD_BIT_TRA_ACK_NOT_REC 0xE8
+#define TWSI_NO_REL_STS_INT_FLAG_IS_KEPT_0 0xF8
+
+#endif // __SOC_MARVELL_ARMADA38X_I2C_H__
diff --git a/src/soc/marvell/armada38x/spi.c b/src/soc/marvell/armada38x/spi.c
index ed5d519dd0..6a0e062327 100644
--- a/src/soc/marvell/armada38x/spi.c
+++ b/src/soc/marvell/armada38x/spi.c
@@ -21,6 +21,7 @@
#include <assert.h>
#include <console/console.h>
#include <soc/common.h>
+#include <soc/clock.h>
/******************************************************************************
base type define
@@ -352,7 +353,7 @@ int mv_spi_sys_init(unsigned char spi_id,
MV_SPI_HAL_DATA hal_data;
hal_data.ctrl_model = MV_6810_DEV_ID;
- hal_data.tclk = MV_BOARD_TCLK_250MHZ;
+ hal_data.tclk = mv_tclk_get();
return mv_spi_init(spi_id, cs_id, serial_baud_rate, &hal_data);
}
diff --git a/src/soc/marvell/armada38x/uart.c b/src/soc/marvell/armada38x/uart.c
index e535aebf67..dbd8dca78f 100644
--- a/src/soc/marvell/armada38x/uart.c
+++ b/src/soc/marvell/armada38x/uart.c
@@ -21,6 +21,7 @@
#include <stdint.h>
#include <assert.h>
#include <soc/common.h>
+#include <soc/clock.h>
struct armada38x_uart {
union {
@@ -94,7 +95,7 @@ static int armada38x_uart_tst_byte(struct armada38x_uart *uart_ptr)
unsigned int uart_platform_refclk(void)
{
- return MV_BOARD_TCLK_250MHZ;
+ return mv_tclk_get();
}
uintptr_t uart_platform_base(int idx)