de4defbaaf
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>
489 lines
14 KiB
C
489 lines
14 KiB
C
/*
|
|
* 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 <delay.h>
|
|
#include <spi_flash.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <console/console.h>
|
|
#include <soc/common.h>
|
|
#include <soc/clock.h>
|
|
|
|
/******************************************************************************
|
|
base type define
|
|
*******************************************************************************/
|
|
#define MV_SPI_REG_READ mrvl_reg_read
|
|
#define MV_SPI_REG_WRITE mrvl_reg_write
|
|
#define MV_SPI_REG_BIT_SET mrvl_reg_bit_set
|
|
#define MV_SPI_REG_BIT_RESET mrvl_reg_bit_reset
|
|
|
|
#define MV_SPI_REGS_OFFSET(unit) (0x10600 + (unit * 0x80))
|
|
#define MV_SPI_REGS_BASE(unit) (MV_SPI_REGS_OFFSET(unit))
|
|
#define MV_SPI_IF_CONFIG_REG(spi_id) (MV_SPI_REGS_BASE(spi_id) + 0x04)
|
|
#define MV_SPI_SPR_OFFSET 0
|
|
#define MV_SPI_SPR_MASK (0xF << MV_SPI_SPR_OFFSET)
|
|
#define MV_SPI_SPPR_0_OFFSET 4
|
|
#define MV_SPI_SPPR_0_MASK (0x1 << MV_SPI_SPPR_0_OFFSET)
|
|
#define MV_SPI_SPPR_HI_OFFSET 6
|
|
#define MV_SPI_SPPR_HI_MASK (0x3 << MV_SPI_SPPR_HI_OFFSET)
|
|
|
|
#define MV_SPI_BYTE_LENGTH_OFFSET 5 /* bit 5 */
|
|
#define MV_SPI_BYTE_LENGTH_MASK (0x1 << MV_SPI_BYTE_LENGTH_OFFSET)
|
|
|
|
#define MV_SPI_IF_CTRL_REG(spi_id) (MV_SPI_REGS_BASE(spi_id) + 0x00)
|
|
#define MV_SPI_CS_ENABLE_OFFSET 0 /* bit 0 */
|
|
#define MV_SPI_CS_ENABLE_MASK (0x1 << MV_SPI_CS_ENABLE_OFFSET)
|
|
|
|
#define MV_SPI_CS_NUM_OFFSET 2
|
|
#define MV_SPI_CS_NUM_MASK (0x7 << MV_SPI_CS_NUM_OFFSET)
|
|
#define MV_SPI_CPOL_OFFSET 11
|
|
#define MV_SPI_CPOL_MASK (0x1 << MV_SPI_CPOL_OFFSET)
|
|
#define MV_SPI_CPHA_OFFSET 12
|
|
#define MV_SPI_CPHA_MASK (0x1 << MV_SPI_CPHA_OFFSET)
|
|
#define MV_SPI_TXLSBF_OFFSET 13
|
|
#define MV_SPI_TXLSBF_MASK (0x1 << MV_SPI_TXLSBF_OFFSET)
|
|
#define MV_SPI_RXLSBF_OFFSET 14
|
|
#define MV_SPI_RXLSBF_MASK (0x1 << MV_SPI_RXLSBF_OFFSET)
|
|
|
|
/* SPI transfer flags */
|
|
#define SPI_XFER_BEGIN 0x01
|
|
#define SPI_XFER_END 0x02
|
|
|
|
#define MV_SPI_INT_CAUSE_REG(spi_id) (MV_SPI_REGS_BASE(spi_id) + 0x10)
|
|
#define MV_SPI_DATA_OUT_REG(spi_id) (MV_SPI_REGS_BASE(spi_id) + 0x08)
|
|
#define MV_SPI_WAIT_RDY_MAX_LOOP 100000
|
|
#define MV_SPI_DATA_IN_REG(spi_id) (MV_SPI_REGS_BASE(spi_id) + 0x0c)
|
|
|
|
#define MV_SPI_TMNG_PARAMS_REG(spi_id) (MV_SPI_REGS_BASE(spi_id) + 0x18)
|
|
#define MV_SPI_TMISO_SAMPLE_OFFSET 6
|
|
#define MV_SPI_TMISO_SAMPLE_MASK (0x3 << MV_SPI_TMISO_SAMPLE_OFFSET)
|
|
|
|
#define CONFIG_ENV_SPI_MAX_HZ 50000000
|
|
#define CONFIG_SF_DEFAULT_SPEED CONFIG_ENV_SPI_MAX_HZ
|
|
|
|
#define CMD_READ_ARRAY_FAST 0x0b
|
|
|
|
/******************************************************************************
|
|
base type define end
|
|
*******************************************************************************/
|
|
|
|
/******************************************************************************
|
|
struct define
|
|
*******************************************************************************/
|
|
typedef enum {
|
|
SPI_TYPE_FLASH = 0,
|
|
SPI_TYPE_SLIC_ZARLINK_SILABS,
|
|
SPI_TYPE_SLIC_LANTIQ,
|
|
SPI_TYPE_SLIC_ZSI,
|
|
SPI_TYPE_SLIC_ISI
|
|
} MV_SPI_TYPE;
|
|
|
|
typedef struct {
|
|
unsigned short ctrl_model;
|
|
unsigned int tclk;
|
|
} MV_SPI_HAL_DATA;
|
|
|
|
typedef struct {
|
|
int clock_pol_low;
|
|
enum { SPI_CLK_HALF_CYC, SPI_CLK_BEGIN_CYC } clock_phase;
|
|
int tx_msb_first;
|
|
int rx_msb_first;
|
|
} MV_SPI_IF_PARAMS;
|
|
|
|
typedef struct {
|
|
/* Does this device support 16 bits access */
|
|
int en16_bit;
|
|
/* should we assert / disassert CS for each byte we read / write */
|
|
int byte_cs_asrt;
|
|
int clock_pol_low;
|
|
unsigned int baud_rate;
|
|
unsigned int clk_phase;
|
|
} MV_SPI_TYPE_INFO;
|
|
|
|
/******************************************************************************
|
|
struct define end
|
|
*******************************************************************************/
|
|
|
|
/******************************************************************************
|
|
param define
|
|
*******************************************************************************/
|
|
static MV_SPI_HAL_DATA spi_hal_data;
|
|
static MV_SPI_TYPE_INFO *curr_spi_info = NULL;
|
|
static MV_SPI_TYPE_INFO spi_types[] = { {.en16_bit = MV_TRUE,
|
|
.clock_pol_low = MV_TRUE,
|
|
.byte_cs_asrt = MV_FALSE,
|
|
.baud_rate = (20 << 20), /* 20_m */
|
|
.clk_phase = SPI_CLK_BEGIN_CYC},
|
|
{.en16_bit = MV_FALSE,
|
|
.clock_pol_low = MV_TRUE,
|
|
.byte_cs_asrt = MV_TRUE,
|
|
.baud_rate = 0x00800000,
|
|
.clk_phase = SPI_CLK_BEGIN_CYC},
|
|
{.en16_bit = MV_FALSE,
|
|
.clock_pol_low = MV_TRUE,
|
|
.byte_cs_asrt = MV_FALSE,
|
|
.baud_rate = 0x00800000,
|
|
.clk_phase = SPI_CLK_BEGIN_CYC},
|
|
{.en16_bit = MV_FALSE,
|
|
.clock_pol_low = MV_TRUE,
|
|
.byte_cs_asrt = MV_TRUE,
|
|
.baud_rate = 0x00800000,
|
|
.clk_phase = SPI_CLK_HALF_CYC},
|
|
{.en16_bit = MV_FALSE,
|
|
.clock_pol_low = MV_FALSE,
|
|
.byte_cs_asrt = MV_TRUE,
|
|
.baud_rate = 0x00200000,
|
|
.clk_phase = SPI_CLK_HALF_CYC} };
|
|
|
|
/******************************************************************************
|
|
param define end
|
|
*******************************************************************************/
|
|
|
|
static struct spi_slave s_spi;
|
|
|
|
static int mv_spi_baud_rate_set(unsigned char spi_id,
|
|
unsigned int serial_baud_rate);
|
|
static void mv_spi_cs_deassert(unsigned char spi_id);
|
|
static int mv_spi_cs_set(unsigned char spi_id, unsigned char cs_id);
|
|
static int mv_spi_if_config_set(unsigned char spi_id,
|
|
MV_SPI_IF_PARAMS *if_params);
|
|
static int mv_spi_params_set(unsigned char spi_id,
|
|
unsigned char cs_id,
|
|
MV_SPI_TYPE type);
|
|
static int mv_spi_init(unsigned char spi_id,
|
|
unsigned char cs_id,
|
|
unsigned int serial_baud_rate,
|
|
MV_SPI_HAL_DATA *hal_data);
|
|
static int mv_spi_sys_init(unsigned char spi_id,
|
|
unsigned char cs_id,
|
|
unsigned int serial_baud_rate);
|
|
static void mv_spi_cs_assert(unsigned char spi_id);
|
|
static int mv_spi_8bit_data_tx_rx(unsigned char spi_id,
|
|
unsigned char tx_data,
|
|
unsigned char *p_rx_data);
|
|
|
|
int mv_spi_baud_rate_set(unsigned char spi_id, unsigned int serial_baud_rate)
|
|
{
|
|
unsigned int spr, sppr;
|
|
unsigned int divider;
|
|
unsigned int best_spr = 0, best_sppr = 0;
|
|
unsigned char exact_match = 0;
|
|
unsigned int min_baud_offset = 0xFFFFFFFF;
|
|
unsigned int cpu_clk = spi_hal_data.tclk; /*mv_cpu_pclk_get();*/
|
|
unsigned int temp_reg;
|
|
|
|
assert(cpu_clk != serial_baud_rate);
|
|
/* Find the best prescale configuration - less or equal */
|
|
for (spr = 1; spr <= 15; spr++) {
|
|
for (sppr = 0; sppr <= 7; sppr++) {
|
|
divider = spr * (1 << sppr);
|
|
/* check for higher - irrelevant */
|
|
if ((cpu_clk / divider) > serial_baud_rate)
|
|
continue;
|
|
|
|
/* check for exact fit */
|
|
if ((cpu_clk / divider) == serial_baud_rate) {
|
|
best_spr = spr;
|
|
best_sppr = sppr;
|
|
exact_match = 1;
|
|
break;
|
|
}
|
|
|
|
/* check if this is better than the previous one */
|
|
if ((serial_baud_rate - (cpu_clk / divider)) <
|
|
min_baud_offset) {
|
|
min_baud_offset =
|
|
serial_baud_rate - cpu_clk / divider;
|
|
best_spr = spr;
|
|
best_sppr = sppr;
|
|
}
|
|
}
|
|
|
|
if (exact_match == 1)
|
|
break;
|
|
}
|
|
|
|
if (best_spr == 0) {
|
|
printk(BIOS_INFO, "%s ERROR: SPI baud rate prescale error!\n",
|
|
__func__);
|
|
return MV_OUT_OF_RANGE;
|
|
}
|
|
|
|
/* configure the Prescale */
|
|
temp_reg = MV_SPI_REG_READ(MV_SPI_IF_CONFIG_REG(spi_id)) &
|
|
~(MV_SPI_SPR_MASK | MV_SPI_SPPR_0_MASK | MV_SPI_SPPR_HI_MASK);
|
|
temp_reg |= ((best_spr << MV_SPI_SPR_OFFSET) |
|
|
((best_sppr & 0x1) << MV_SPI_SPPR_0_OFFSET) |
|
|
((best_sppr >> 1) << MV_SPI_SPPR_HI_OFFSET));
|
|
MV_SPI_REG_WRITE(MV_SPI_IF_CONFIG_REG(spi_id), temp_reg);
|
|
|
|
return MV_OK;
|
|
}
|
|
|
|
void mv_spi_cs_deassert(unsigned char spi_id)
|
|
{
|
|
MV_SPI_REG_BIT_RESET(MV_SPI_IF_CTRL_REG(spi_id), MV_SPI_CS_ENABLE_MASK);
|
|
}
|
|
|
|
int mv_spi_cs_set(unsigned char spi_id, unsigned char cs_id)
|
|
{
|
|
unsigned int ctrl_reg;
|
|
static unsigned char last_cs_id = 0xFF;
|
|
static unsigned char last_spi_id = 0xFF;
|
|
|
|
if (cs_id > 7)
|
|
return MV_BAD_PARAM;
|
|
|
|
if ((last_spi_id == spi_id) && (last_cs_id == cs_id))
|
|
return MV_OK;
|
|
|
|
ctrl_reg = MV_SPI_REG_READ(MV_SPI_IF_CTRL_REG(spi_id));
|
|
ctrl_reg &= ~MV_SPI_CS_NUM_MASK;
|
|
ctrl_reg |= (cs_id << MV_SPI_CS_NUM_OFFSET);
|
|
MV_SPI_REG_WRITE(MV_SPI_IF_CTRL_REG(spi_id), ctrl_reg);
|
|
|
|
last_spi_id = spi_id;
|
|
last_cs_id = cs_id;
|
|
|
|
return MV_OK;
|
|
}
|
|
|
|
int mv_spi_if_config_set(unsigned char spi_id, MV_SPI_IF_PARAMS *if_params)
|
|
{
|
|
unsigned int ctrl_reg;
|
|
|
|
ctrl_reg = MV_SPI_REG_READ(MV_SPI_IF_CONFIG_REG(spi_id));
|
|
|
|
/* Set Clock Polarity */
|
|
ctrl_reg &= ~(MV_SPI_CPOL_MASK | MV_SPI_CPHA_MASK | MV_SPI_TXLSBF_MASK |
|
|
MV_SPI_RXLSBF_MASK);
|
|
if (if_params->clock_pol_low)
|
|
ctrl_reg |= MV_SPI_CPOL_MASK;
|
|
|
|
if (if_params->clock_phase == SPI_CLK_BEGIN_CYC)
|
|
ctrl_reg |= MV_SPI_CPHA_MASK;
|
|
|
|
if (if_params->tx_msb_first)
|
|
ctrl_reg |= MV_SPI_TXLSBF_MASK;
|
|
|
|
if (if_params->rx_msb_first)
|
|
ctrl_reg |= MV_SPI_RXLSBF_MASK;
|
|
|
|
MV_SPI_REG_WRITE(MV_SPI_IF_CONFIG_REG(spi_id), ctrl_reg);
|
|
|
|
return MV_OK;
|
|
}
|
|
|
|
int mv_spi_params_set(unsigned char spi_id,
|
|
unsigned char cs_id,
|
|
MV_SPI_TYPE type)
|
|
{
|
|
MV_SPI_IF_PARAMS if_params;
|
|
|
|
if (MV_OK != mv_spi_cs_set(spi_id, cs_id)) {
|
|
printk(BIOS_INFO, "Error, setting SPI CS failed\n");
|
|
return MV_ERROR;
|
|
}
|
|
|
|
if (curr_spi_info != (&(spi_types[type]))) {
|
|
curr_spi_info = &(spi_types[type]);
|
|
mv_spi_baud_rate_set(spi_id, curr_spi_info->baud_rate);
|
|
|
|
if_params.clock_pol_low = curr_spi_info->clock_pol_low;
|
|
if_params.clock_phase = curr_spi_info->clk_phase;
|
|
if_params.tx_msb_first = MV_FALSE;
|
|
if_params.rx_msb_first = MV_FALSE;
|
|
mv_spi_if_config_set(spi_id, &if_params);
|
|
}
|
|
|
|
return MV_OK;
|
|
}
|
|
|
|
int mv_spi_init(unsigned char spi_id,
|
|
unsigned char cs_id,
|
|
unsigned int serial_baud_rate,
|
|
MV_SPI_HAL_DATA *hal_data)
|
|
{
|
|
int ret;
|
|
unsigned int timing_reg;
|
|
|
|
spi_hal_data.ctrl_model = hal_data->ctrl_model;
|
|
spi_hal_data.tclk = hal_data->tclk;
|
|
|
|
/* Set the serial clock */
|
|
ret = mv_spi_baud_rate_set(spi_id, serial_baud_rate);
|
|
if (ret != MV_OK)
|
|
return ret;
|
|
|
|
/* Configure the default SPI mode to be 8bit */
|
|
MV_SPI_REG_BIT_RESET(MV_SPI_IF_CONFIG_REG(spi_id),
|
|
MV_SPI_BYTE_LENGTH_MASK);
|
|
|
|
timing_reg = MV_SPI_REG_READ(MV_SPI_TMNG_PARAMS_REG(spi_id));
|
|
timing_reg &= ~MV_SPI_TMISO_SAMPLE_MASK;
|
|
timing_reg |= (0x2) << MV_SPI_TMISO_SAMPLE_OFFSET;
|
|
MV_SPI_REG_WRITE(MV_SPI_TMNG_PARAMS_REG(spi_id), timing_reg);
|
|
|
|
/* Verify that the CS is deasserted */
|
|
mv_spi_cs_deassert(spi_id);
|
|
|
|
mv_spi_params_set(spi_id, cs_id, SPI_TYPE_FLASH);
|
|
|
|
return MV_OK;
|
|
}
|
|
|
|
int mv_spi_sys_init(unsigned char spi_id,
|
|
unsigned char cs_id,
|
|
unsigned int serial_baud_rate)
|
|
{
|
|
MV_SPI_HAL_DATA hal_data;
|
|
|
|
hal_data.ctrl_model = MV_6810_DEV_ID;
|
|
hal_data.tclk = mv_tclk_get();
|
|
|
|
return mv_spi_init(spi_id, cs_id, serial_baud_rate, &hal_data);
|
|
}
|
|
|
|
void mv_spi_cs_assert(unsigned char spi_id)
|
|
{
|
|
MV_SPI_REG_BIT_SET(MV_SPI_IF_CTRL_REG(spi_id), MV_SPI_CS_ENABLE_MASK);
|
|
}
|
|
|
|
int mv_spi_8bit_data_tx_rx(unsigned char spi_id,
|
|
unsigned char tx_data,
|
|
unsigned char *p_rx_data)
|
|
{
|
|
unsigned int i;
|
|
int ready = MV_FALSE;
|
|
|
|
if (curr_spi_info->byte_cs_asrt)
|
|
mv_spi_cs_assert(spi_id);
|
|
|
|
/* First clear the bit in the interrupt cause register */
|
|
MV_SPI_REG_WRITE(MV_SPI_INT_CAUSE_REG(spi_id), 0x0);
|
|
|
|
/* Transmit data */
|
|
MV_SPI_REG_WRITE(MV_SPI_DATA_OUT_REG(spi_id), tx_data);
|
|
|
|
/* wait with timeout for memory ready */
|
|
for (i = 0; i < MV_SPI_WAIT_RDY_MAX_LOOP; i++) {
|
|
if (MV_SPI_REG_READ(MV_SPI_INT_CAUSE_REG(spi_id))) {
|
|
ready = MV_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!ready) {
|
|
if (curr_spi_info->byte_cs_asrt) {
|
|
mv_spi_cs_deassert(spi_id);
|
|
/* WA to compansate Zarlink SLIC CS off time */
|
|
udelay(4);
|
|
}
|
|
return MV_TIMEOUT;
|
|
}
|
|
|
|
/* check that the RX data is needed */
|
|
if (p_rx_data)
|
|
*p_rx_data = MV_SPI_REG_READ(MV_SPI_DATA_IN_REG(spi_id));
|
|
|
|
if (curr_spi_info->byte_cs_asrt) {
|
|
mv_spi_cs_deassert(spi_id);
|
|
/* WA to compansate Zarlink SLIC CS off time */
|
|
udelay(4);
|
|
}
|
|
|
|
return MV_OK;
|
|
}
|
|
|
|
static int mrvl_spi_xfer(struct spi_slave *slave,
|
|
unsigned int bitlen,
|
|
const void *dout,
|
|
void *din)
|
|
{
|
|
int ret;
|
|
unsigned char *pdout = (unsigned char *)dout;
|
|
unsigned char *pdin = (unsigned char *)din;
|
|
int tmp_bitlen = bitlen;
|
|
unsigned char tmp_dout = 0;
|
|
|
|
/* Verify that the SPI mode is in 8bit mode */
|
|
MV_SPI_REG_BIT_RESET(MV_SPI_IF_CONFIG_REG(slave->bus),
|
|
MV_SPI_BYTE_LENGTH_MASK);
|
|
|
|
while (tmp_bitlen > 0) {
|
|
if (pdout)
|
|
tmp_dout = (*pdout) & 0xff;
|
|
|
|
/* Transmitted and wait for the transfer to be completed */
|
|
ret = mv_spi_8bit_data_tx_rx(slave->bus, tmp_dout, pdin);
|
|
if (ret != MV_OK)
|
|
return ret;
|
|
|
|
/* increment the pointers */
|
|
if (pdin)
|
|
pdin++;
|
|
if (pdout)
|
|
pdout++;
|
|
|
|
tmp_bitlen -= 8;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs)
|
|
{
|
|
struct spi_slave *slave = &s_spi;
|
|
|
|
slave->bus = bus;
|
|
slave->cs = cs;
|
|
mv_spi_sys_init(bus, cs, CONFIG_SF_DEFAULT_SPEED);
|
|
return slave;
|
|
}
|
|
|
|
int spi_claim_bus(struct spi_slave *slave)
|
|
{
|
|
mv_spi_cs_set(slave->bus, slave->cs);
|
|
mv_spi_cs_assert(slave->bus);
|
|
return 0;
|
|
}
|
|
|
|
void spi_release_bus(struct spi_slave *slave)
|
|
{
|
|
mv_spi_cs_deassert(slave->bus);
|
|
}
|
|
|
|
unsigned int spi_crop_chunk(unsigned int cmd_len, unsigned int buf_len)
|
|
{
|
|
return buf_len;
|
|
}
|
|
|
|
int spi_xfer(struct spi_slave *slave,
|
|
const void *dout,
|
|
unsigned out_bytes,
|
|
void *din,
|
|
unsigned in_bytes)
|
|
{
|
|
int ret = -1;
|
|
|
|
if (out_bytes)
|
|
ret = mrvl_spi_xfer(slave, out_bytes * 8, dout, din);
|
|
else if (in_bytes)
|
|
ret = mrvl_spi_xfer(slave, in_bytes * 8, dout, din);
|
|
else
|
|
die("Unexpected condition in spi_xfer\n");
|
|
return ret;
|
|
}
|