2018-05-06 21:58:06 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2015-04-20 07:31:27 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) Marvell International Ltd. and its affiliates
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <common.h>
|
|
|
|
#include <spl.h>
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include <asm/arch/cpu.h>
|
|
|
|
#include <asm/arch/soc.h>
|
2020-05-10 17:40:11 +00:00
|
|
|
#include <linux/delay.h>
|
2015-04-20 07:31:27 +00:00
|
|
|
|
|
|
|
#include "seq_exec.h"
|
|
|
|
#include "high_speed_env_spec.h"
|
|
|
|
|
|
|
|
#include "../../../drivers/ddr/marvell/a38x/ddr3_init.h"
|
|
|
|
|
|
|
|
#if defined(MV_DEBUG_INIT_FULL) || defined(MV_DEBUG)
|
|
|
|
#define DB(x) x
|
|
|
|
#else
|
|
|
|
#define DB(x)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Array for mapping the operation (write, poll or delay) functions */
|
|
|
|
op_execute_func_ptr op_execute_func_arr[] = {
|
|
|
|
write_op_execute,
|
|
|
|
delay_op_execute,
|
|
|
|
poll_op_execute
|
|
|
|
};
|
|
|
|
|
|
|
|
int write_op_execute(u32 serdes_num, struct op_params *params, u32 data_arr_idx)
|
|
|
|
{
|
|
|
|
u32 unit_base_reg, unit_offset, data, mask, reg_data, reg_addr;
|
|
|
|
|
|
|
|
/* Getting write op params from the input parameter */
|
|
|
|
data = params->data[data_arr_idx];
|
|
|
|
mask = params->mask;
|
|
|
|
|
|
|
|
/* an empty operation */
|
|
|
|
if (data == NO_DATA)
|
|
|
|
return MV_OK;
|
|
|
|
|
|
|
|
/* get updated base address since it can be different between Serdes */
|
|
|
|
CHECK_STATUS(hws_get_ext_base_addr(serdes_num, params->unit_base_reg,
|
|
|
|
params->unit_offset,
|
|
|
|
&unit_base_reg, &unit_offset));
|
|
|
|
|
|
|
|
/* Address calculation */
|
|
|
|
reg_addr = unit_base_reg + unit_offset * serdes_num;
|
|
|
|
|
|
|
|
#ifdef SEQ_DEBUG
|
|
|
|
printf("Write: 0x%x: 0x%x (mask 0x%x) - ", reg_addr, data, mask);
|
|
|
|
#endif
|
|
|
|
/* Reading old value */
|
|
|
|
reg_data = reg_read(reg_addr);
|
|
|
|
reg_data &= (~mask);
|
|
|
|
|
|
|
|
/* Writing new data */
|
|
|
|
data &= mask;
|
|
|
|
reg_data |= data;
|
|
|
|
reg_write(reg_addr, reg_data);
|
|
|
|
|
|
|
|
#ifdef SEQ_DEBUG
|
|
|
|
printf(" - 0x%x\n", reg_data);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return MV_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int delay_op_execute(u32 serdes_num, struct op_params *params, u32 data_arr_idx)
|
|
|
|
{
|
|
|
|
u32 delay;
|
|
|
|
|
|
|
|
/* Getting delay op params from the input parameter */
|
|
|
|
delay = params->wait_time;
|
|
|
|
#ifdef SEQ_DEBUG
|
|
|
|
printf("Delay: %d\n", delay);
|
|
|
|
#endif
|
|
|
|
mdelay(delay);
|
|
|
|
|
|
|
|
return MV_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int poll_op_execute(u32 serdes_num, struct op_params *params, u32 data_arr_idx)
|
|
|
|
{
|
|
|
|
u32 unit_base_reg, unit_offset, data, mask, num_of_loops, wait_time;
|
|
|
|
u32 poll_counter = 0;
|
|
|
|
u32 reg_addr, reg_data;
|
|
|
|
|
|
|
|
/* Getting poll op params from the input parameter */
|
|
|
|
data = params->data[data_arr_idx];
|
|
|
|
mask = params->mask;
|
|
|
|
num_of_loops = params->num_of_loops;
|
|
|
|
wait_time = params->wait_time;
|
|
|
|
|
|
|
|
/* an empty operation */
|
|
|
|
if (data == NO_DATA)
|
|
|
|
return MV_OK;
|
|
|
|
|
|
|
|
/* get updated base address since it can be different between Serdes */
|
|
|
|
CHECK_STATUS(hws_get_ext_base_addr(serdes_num, params->unit_base_reg,
|
|
|
|
params->unit_offset,
|
|
|
|
&unit_base_reg, &unit_offset));
|
|
|
|
|
|
|
|
/* Address calculation */
|
|
|
|
reg_addr = unit_base_reg + unit_offset * serdes_num;
|
|
|
|
|
|
|
|
/* Polling */
|
|
|
|
#ifdef SEQ_DEBUG
|
|
|
|
printf("Poll: 0x%x: 0x%x (mask 0x%x)\n", reg_addr, data, mask);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
do {
|
|
|
|
reg_data = reg_read(reg_addr) & mask;
|
|
|
|
poll_counter++;
|
|
|
|
udelay(wait_time);
|
|
|
|
} while ((reg_data != data) && (poll_counter < num_of_loops));
|
|
|
|
|
|
|
|
if ((poll_counter >= num_of_loops) && (reg_data != data)) {
|
|
|
|
DEBUG_INIT_S("poll_op_execute: TIMEOUT\n");
|
|
|
|
return MV_TIMEOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return MV_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum mv_op get_cfg_seq_op(struct op_params *params)
|
|
|
|
{
|
|
|
|
if (params->wait_time == 0)
|
|
|
|
return WRITE_OP;
|
|
|
|
else if (params->num_of_loops == 0)
|
|
|
|
return DELAY_OP;
|
|
|
|
|
|
|
|
return POLL_OP;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mv_seq_exec(u32 serdes_num, u32 seq_id)
|
|
|
|
{
|
|
|
|
u32 seq_idx;
|
|
|
|
struct op_params *seq_arr;
|
|
|
|
u32 seq_size;
|
|
|
|
u32 data_arr_idx;
|
|
|
|
enum mv_op curr_op;
|
|
|
|
|
|
|
|
DB(printf("\n### mv_seq_exec ###\n"));
|
|
|
|
DB(printf("seq id: %d\n", seq_id));
|
|
|
|
|
|
|
|
if (hws_is_serdes_active(serdes_num) != 1) {
|
|
|
|
printf("mv_seq_exec_ext:Error: SerDes lane %d is not valid\n",
|
|
|
|
serdes_num);
|
|
|
|
return MV_BAD_PARAM;
|
|
|
|
}
|
|
|
|
|
|
|
|
seq_arr = serdes_seq_db[seq_id].op_params_ptr;
|
|
|
|
seq_size = serdes_seq_db[seq_id].cfg_seq_size;
|
|
|
|
data_arr_idx = serdes_seq_db[seq_id].data_arr_idx;
|
|
|
|
|
|
|
|
DB(printf("seq_size: %d\n", seq_size));
|
|
|
|
DB(printf("data_arr_idx: %d\n", data_arr_idx));
|
|
|
|
|
|
|
|
/* Executing the sequence operations */
|
|
|
|
for (seq_idx = 0; seq_idx < seq_size; seq_idx++) {
|
|
|
|
curr_op = get_cfg_seq_op(&seq_arr[seq_idx]);
|
|
|
|
op_execute_func_arr[curr_op](serdes_num, &seq_arr[seq_idx],
|
|
|
|
data_arr_idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
return MV_OK;
|
|
|
|
}
|