1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2020-2021 Intel Corporation <www.intel.com>
4 *
5 */
6
7#include <asm/arch/handoff_soc64.h>
8#include <asm/io.h>
9#include <common.h>
10#include <errno.h>
11#include "log.h"
12
13#ifndef __ASSEMBLY__
14enum endianness {
15	LITTLE_ENDIAN = 0,
16	BIG_ENDIAN,
17	UNKNOWN_ENDIANNESS
18};
19#endif
20
21static enum endianness check_endianness(u32 handoff)
22{
23	switch (handoff) {
24	case SOC64_HANDOFF_MAGIC_BOOT:
25	case SOC64_HANDOFF_MAGIC_MUX:
26	case SOC64_HANDOFF_MAGIC_IOCTL:
27	case SOC64_HANDOFF_MAGIC_FPGA:
28	case SOC64_HANDOFF_MAGIC_DELAY:
29	case SOC64_HANDOFF_MAGIC_CLOCK:
30	case SOC64_HANDOFF_MAGIC_MISC:
31		return BIG_ENDIAN;
32#if IS_ENABLED(CONFIG_TARGET_SOCFPGA_N5X)
33	case SOC64_HANDOFF_DDR_UMCTL2_MAGIC:
34		debug("%s: umctl2 handoff data\n", __func__);
35		return LITTLE_ENDIAN;
36	case SOC64_HANDOFF_DDR_PHY_MAGIC:
37		debug("%s: PHY handoff data\n", __func__);
38		return LITTLE_ENDIAN;
39	case SOC64_HANDOFF_DDR_PHY_INIT_ENGINE_MAGIC:
40		debug("%s: PHY engine handoff data\n", __func__);
41		return LITTLE_ENDIAN;
42#endif
43	default:
44		debug("%s: Unknown endianness!!\n", __func__);
45		return UNKNOWN_ENDIANNESS;
46	}
47}
48
49static int getting_endianness(void *handoff_address, enum endianness *endian_t)
50{
51	/* Checking handoff data is little endian ? */
52	*endian_t = check_endianness(readl(handoff_address));
53
54	if (*endian_t == UNKNOWN_ENDIANNESS) {
55		/* Trying to check handoff data is big endian? */
56		*endian_t = check_endianness(swab32(readl(handoff_address)));
57		if (*endian_t == UNKNOWN_ENDIANNESS) {
58			debug("%s: Cannot find HANDOFF MAGIC ", __func__);
59			debug("at addr 0x%p\n", (u32 *)handoff_address);
60			return -EPERM;
61		}
62	}
63
64	return 0;
65}
66
67int socfpga_get_handoff_size(void *handoff_address)
68{
69	u32 size;
70	int ret;
71	enum endianness endian_t;
72
73	ret = getting_endianness(handoff_address, &endian_t);
74	if (ret)
75		return ret;
76
77	size = readl(handoff_address + SOC64_HANDOFF_OFFSET_LENGTH);
78	if (endian_t == BIG_ENDIAN)
79		size = swab32(size);
80
81	size = (size - SOC64_HANDOFF_OFFSET_DATA) / sizeof(u32);
82
83	debug("%s: handoff address = 0x%p handoff size = 0x%08x\n", __func__,
84	      (u32 *)handoff_address, size);
85
86	return size;
87}
88
89int socfpga_handoff_read(void *handoff_address, void *table, u32 table_len)
90{
91	u32 temp;
92	u32 *table_x32 = table;
93	u32 i = 0;
94	int ret;
95	enum endianness endian_t;
96
97	ret = getting_endianness(handoff_address, &endian_t);
98	if (ret)
99		return ret;
100
101	temp = readl(handoff_address + SOC64_HANDOFF_OFFSET_DATA +
102		    (i * sizeof(u32)));
103
104	if (endian_t == BIG_ENDIAN) {
105		debug("%s: Handoff addr = 0x%p ", __func__, (u32 *)handoff_address);
106		debug("Handoff table address = 0x%p ", table_x32);
107		debug("table length = 0x%x\n", table_len);
108		debug("%s: handoff data =\n{\n", __func__);
109		*table_x32 = swab32(temp);
110	} else if (endian_t == LITTLE_ENDIAN) {
111		debug(" {\n");
112		*table_x32 = temp;
113	}
114
115	debug(" No.%d Addr 0x%08x: ", i, *table_x32);
116
117	for (i = 1; i < table_len; i++) {
118		table_x32++;
119
120		temp = readl(handoff_address +
121			     SOC64_HANDOFF_OFFSET_DATA +
122			     (i * sizeof(u32)));
123
124		if (endian_t == BIG_ENDIAN)
125			*table_x32 = swab32(temp);
126		else if (endian_t == LITTLE_ENDIAN)
127			*table_x32 = temp;
128
129		if (!(i % 2))
130			debug(" No.%d Addr 0x%08x: ", i,
131			      *table_x32);
132		else
133			debug(" 0x%08x\n", *table_x32);
134	}
135	debug("\n}\n");
136
137	return 0;
138}
139