/*
 * Copyright (C) 2015  Siarhei Siamashka <siarhei.siamashka@gmail.com>
 * Copyright (C) 2016  Bernhard Nortmann <bernhard.nortmann@web.de>
 *
 * 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, either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef _SUNXI_TOOLS_SOC_INFO_H
#define _SUNXI_TOOLS_SOC_INFO_H

#include <stdbool.h>
#include <stdint.h>

/* SoC version information, as retrieved by the FEL protocol */
struct aw_fel_version {
	char signature[8];
	uint32_t soc_id;	/* 0x00162300 */
	uint32_t unknown_0a;	/* 1 */
	uint16_t protocol;	/* 1 */
	uint8_t  unknown_12;	/* 0x44 */
	uint8_t  unknown_13;	/* 0x08 */
	uint32_t scratchpad;	/* 0x7e00 */
	uint32_t pad[2];	/* unused */
} __attribute__((packed));

/*
 * Buffer for a SoC name string. We want at least 6 + 1 characters, to store
 * the hexadecimal ID "0xABCD" for unknown SoCs, plus the terminating NUL.
 */
typedef char soc_name_t[8];

/*
 * The 'sram_swap_buffers' structure is used to describe information about
 * pairwise memory regions in SRAM, the content of which needs to be exchanged
 * before calling the U-Boot SPL code and then exchanged again before returning
 * control back to the FEL code from the BROM.
 */
typedef struct {
	uint32_t buf1; /* BROM buffer */
	uint32_t buf2; /* backup storage location */
	uint32_t size; /* buffer size */
} sram_swap_buffers;

/*
 * Contains information on the watchdog peripheral, to enable reset
 */
typedef struct {
	/* Register that needs to be written to */
	uint32_t reg_mode;
	/* Value to write to trigger a reset */
	uint32_t reg_mode_value;
} watchdog_info;

/*
 * sunxi sid sections
 */
typedef struct {
	const char	*name;
	uint32_t	offset;
	uint32_t	size_bits;
} sid_section;

#define SID_SECTION(_name, _offset, _size_bits) {	\
	.name = _name,					\
	.offset = _offset,				\
	.size_bits = _size_bits,			\
}

/*
 * Each SoC variant may have its own list of memory buffers to be exchanged
 * and the information about the placement of the thunk code, which handles
 * the transition of execution from the BROM FEL code to the U-Boot SPL and
 * back.
 *
 * Note: the entries in the 'swap_buffers' tables need to be sorted by 'buf1'
 * addresses. And the 'buf1' addresses are the BROM data buffers, while 'buf2'
 * addresses are the intended backup locations.
 *
 * Also for performance reasons, we optionally want to have MMU enabled with
 * optimal section attributes configured (the code from the BROM should use
 * I-cache, writing data to the DRAM area should use write combining). The
 * reason is that the BROM FEL protocol implementation moves data using the
 * CPU somewhere on the performance critical path when transferring data over
 * USB. The older SoC variants (A10/A13/A20/A31/A23) already have MMU enabled
 * and we only need to adjust section attributes. The BROM in newer SoC variants
 * (A33/A83T/H3) doesn't enable MMU any more, so we need to find some 16K of
 * spare space in SRAM to place the translation table there and specify it as
 * the 'mmu_tt_addr' field in the 'soc_sram_info' structure. The 'mmu_tt_addr'
 * address must be 16K aligned.
 *
 * If an SoC has the "secure boot" fuse burned, it will enter FEL mode in
 * non-secure state, so with the SCR.NS bit set. Since in this mode the
 * secure/non-secure state restrictions are actually observed, we suffer
 * from several restrictions:
 * - No access to the SID information (both via memory mapped and "register").
 * - No access to secure SRAM (SRAM A2 on H3/A64/H5).
 * - No access to the secure side of the GIC, so it can't be configured to
 *   be accessible from non-secure world.
 * - No RMR trigger on ARMv8 cores to bring the core into AArch64.
 * However it has been found out that a simple "smc" call will immediately
 * return from monitor mode, but with the NS bit cleared, so access to all
 * secure peripherals is suddenly possible.
 * The 'needs_smc_workaround_if_zero_word_at_addr' field can be used to
 * have a check for this condition (reading from restricted addresses
 * typically returns zero) and then activate the SMC workaround if needed.
 */
typedef struct {
	uint32_t           soc_id;       /* ID of the SoC */
	const char         *name;        /* human-readable SoC name string */
	uint32_t           spl_addr;     /* SPL load address */
	uint32_t           scratch_addr; /* A safe place to upload & run code */
	uint32_t           thunk_addr;   /* Address of the thunk code */
	uint32_t           thunk_size;   /* Maximal size of the thunk code */
	bool               needs_l2en;   /* Set the L2EN bit */
	uint32_t           mmu_tt_addr;  /* MMU translation table address */
	uint32_t           sid_base;     /* base address for SID registers */
	uint32_t           sid_offset;   /* offset for SID_KEY[0-3], "root key" */
	const sid_section *sid_sections; /* sid memory maps */
	uint32_t           rvbar_reg;    /* MMIO address of RVBARADDR0_L register */
	uint32_t           rvbar_reg_alt;/* alternative MMIO address of RVBARADDR0_L register */
	uint32_t           ver_reg;      /* MMIO address of "Version Register" */
	const watchdog_info *watchdog;   /* Used for reset */
	bool               sid_fix;      /* Use SID workaround (read via register) */
	/* Use I$ workaround (disable I$ before first write to prevent stale thunk */
	bool               icache_fix;
	/* Use SMC workaround (enter secure mode) if can't read from this address */
	uint32_t           needs_smc_workaround_if_zero_word_at_addr;
	uint32_t           sram_size;	/* Usable contiguous SRAM at spl_addr */
	sram_swap_buffers *swap_buffers;
} soc_info_t;


void get_soc_name_from_id(soc_name_t buffer, uint32_t soc_id);
soc_info_t *get_soc_info_from_id(uint32_t soc_id);
soc_info_t *get_soc_info_from_version(struct aw_fel_version *buf);
const soc_info_t *get_next_soc(const soc_info_t *prev);

#endif /* _SUNXI_TOOLS_SOC_INFO_H */
