/*
 *
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#ifndef _SOC_TIGERLAKE_MEMINIT_H_
#define _SOC_TIGERLAKE_MEMINIT_H_

#include <stddef.h>
#include <stdint.h>
#include <fsp/soc_binding.h>

#define BITS_PER_BYTE			8

#define LPDDR4X_CHANNELS		8
#define LPDDR4X_BYTES_PER_CHANNEL	2

#define DDR4_CHANNELS			2
#define DDR4_BYTES_PER_CHANNEL		8

enum mem_topology {
	MEMORY_DOWN,	/* Supports reading SPD from CBFS or in-memory pointer. */
	SODIMM,		/* Supports reading SPD using SMBus (only for DDR4). */
	MIXED,		/* CH0 = MD, CH1 = SODIMM (only for DDR4). */
};

enum md_spd_loc {
	 /* Read SPD from pointer provided to memory location. */
	SPD_MEMPTR,
	/* Read SPD using index into spd.bin in CBFS.  */
	SPD_CBFS,
};

struct spd_info {
	enum mem_topology topology;

	/* SPD info for Memory down topology */
	enum md_spd_loc md_spd_loc;
	union {
		/* Used for SPD_CBFS */
		uint8_t cbfs_index;

		struct {
			/* Used for SPD_MEMPTR */
			uintptr_t data_ptr;
			size_t data_len;
		};
	};

	/*
	 * SPD info for SODIMM topology.
	 * Leave addr_dimmN as 0 for any DIMMs that are not populated.
	 */
	struct {
		/* SMBus address for DIMM0 within the channel. */
		uint8_t addr_dimm0;
		/* SMBus address for DIMM1 within the channel. */
		uint8_t addr_dimm1;
	} smbus_info[DDR4_CHANNELS];
};

/* Board-specific memory configuration information */
struct lpddr4x_cfg {
	/*
	 * DQ CPU<>DRAM map:
	 * LPDDR4x memory interface has 2 DQs per channel. Each DQ consists of 8 bits(1
	 * byte). Thus, dq_map is represented as DDR[7-0]_DQ[1-0][7:0], where
	 * DDR[7-0] : LPDDR4x channel #
	 * DQ[1-0]  : DQ # within the channel
	 * [7:0]    : Bits within the DQ
	 *
	 * Index of the array represents DQ pin# on the CPU, whereas value in
	 * the array represents DQ pin# on the memory part.
	 */
	uint8_t dq_map[LPDDR4X_CHANNELS][LPDDR4X_BYTES_PER_CHANNEL][BITS_PER_BYTE];

	/*
	 * DQS CPU<>DRAM map:
	 * LPDDR4x memory interface has 2 DQS pairs(P/N) per channel. Thus, dqs_map is
	 * represented as DDR[7-0]_DQS[1:0], where
	 * DDR[7-0]  : LPDDR4x channel #
	 * DQS[1-0]  : DQS # within the channel
	 *
	 * Index of the array represents DQS pin# on the CPU, whereas value in
	 * the array represents DQ pin# on the memory part.
	 */
	uint8_t dqs_map[LPDDR4X_CHANNELS][LPDDR4X_BYTES_PER_CHANNEL];
	/*
	 * Early Command Training Enable/Disable Control
	 * 1 = enable, 0 = disable
	 */
	uint8_t ect;
};

/* Board-specific memory configuration information for DDR4 memory variant */
struct mb_ddr4_cfg {
	/*
	 * DQ CPU<>DRAM map:
	 * DDR4 memory interface has 8 DQs per channel. Each DQ consists of 8 bits(1
	 * byte). Thus, dq_map is represented as DDR[1-0]_DQ[7-0][7:0], where
	 * DDR[1-0] : DDR4 channel #
	 * DQ[7-0]  : DQ # within the channel
	 * [7:0]    : Bits within the DQ
	 *
	 * Index of the array represents DQ pin# on the CPU, whereas value in
	 * the array represents DQ pin# on the memory part.
	 */
	uint8_t dq_map[DDR4_CHANNELS][DDR4_BYTES_PER_CHANNEL][BITS_PER_BYTE];
	/*
	 * DQS CPU<>DRAM map:
	 * DDR4 memory interface has 8 DQS pairs per channel. Thus, dqs_map is represented as
	 * DDR[1-0]_DQS[7-0], where
	 * DDR[1-0]  : DDR4 channel #
	 * DQS[7-0]  : DQS # within the channel
	 *
	 * Index of the array represents DQS pin# on the CPU, whereas value in
	 * the array represents DQS pin# on the memory part.
	 */
	uint8_t dqs_map[DDR4_CHANNELS][DDR4_BYTES_PER_CHANNEL];
	/*
	 * Indicates whether memory is interleaved.
	 * Set to 1 for an interleaved design,
	 * set to 0 for non-interleaved design.
	 */
	uint8_t dq_pins_interleaved;
	/*
	 * Early Command Training Enable/Disable Control
	 * 1 = enable, 0 = disable
	 */
	uint8_t ect;
};

void meminit_lpddr4x(FSP_M_CONFIG *mem_cfg, const struct lpddr4x_cfg *board_cfg,
		const struct spd_info *spd, bool half_populated);
/* Initialize DDR4 memory configurations */
void meminit_ddr4(FSP_M_CONFIG *mem_cfg, const struct mb_ddr4_cfg *board_cfg,
			  const struct spd_info *spd, const bool half_populated);
#endif /* _SOC_TIGERLAKE_MEMINIT_H_ */
