arm: mvebu: Add Armada 38x SERDES / PHY init code from Marvell bin_hdr
This code is ported from the Marvell bin_hdr code into mainline
SPL U-Boot. It needs to be executed very early so that the devices
connected to the serdes PHY are configured correctly.
Signed-off-by: Stefan Roese <sr@denx.de>
diff --git a/arch/arm/mach-mvebu/serdes/a38x/seq_exec.c b/arch/arm/mach-mvebu/serdes/a38x/seq_exec.c
new file mode 100644
index 0000000..ee2305b
--- /dev/null
+++ b/arch/arm/mach-mvebu/serdes/a38x/seq_exec.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) Marvell International Ltd. and its affiliates
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <spl.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/soc.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;
+}