blob: 9a1bbba7f2f4db97210672b590323385ef49a35c [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) Marvell International Ltd. and its affiliates
*/
#include <spl.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/soc.h>
#include <linux/delay.h>
#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;
}