#include "i2c.h"
#include "t210.h"
#include "mc.h"
//#include "emc.h"
#include "pmc.h"
#include "util.h"
#include "fuse.h"

#include "sdram.inl"

static u32 _get_sdram_id()
{
	return (fuse_read_odm(4) & 0x38) >> 3;
}

static void _sdram_config(const u32 *_cfg)
{
	const u32 *_cfg_120 = _cfg + 0x120;
	const u32 *_cfg_100 = _cfg + 0x100;

	PMC(0x45C) = (((4 * _cfg[0x12F] >> 2) + 0x80000000) ^ 0xFFFF) & 0xC000FFFF;
	sleep(_cfg[0x111]);

	u32 req = (4 * _cfg_120[0x10] >> 2) + 0x80000000;
	PMC(APBDEV_PMC_IO_DPD4_REQ) = (req ^ 0x3FFF0000) >> 16 << 16;
	sleep(_cfg_100[0x12]);
	PMC(APBDEV_PMC_IO_DPD4_REQ) = (req ^ 0xFFFF) & 0xC000FFFF;
	sleep(_cfg_100[0x12]);
	PMC(APBDEV_PMC_WEAK_BIAS) = 0;
	sleep(1);

	CLOCK(0x98) = _cfg[4];
	CLOCK(0x9C) = 0;
	CLOCK(0x90) = (_cfg[2] << 8) | (*((u16 *)_cfg + 0xA) << 20) | _cfg[1] | 0x40000000;

	u32 wait_end = TMR(0x10) + 300;
	while (!((CLOCK(0x90) >> 27) & 1))
	{
		if (TMR(0x10) >= wait_end)
			goto break_nosleep;
	}
	sleep(10);
break_nosleep:

	CLOCK(0x19C) = _cfg[0x16] & 0xFFFEFFFF | (_cfg[0x175] >> 11) & 0x10000;
	if (_cfg[0x17])
		CLOCK(0x664) = _cfg[0x17];
	if (_cfg[0x1A])
		CLOCK(0x44C) = 0x40000000;
	CLOCK(0x328) = 0x2000001;
	CLOCK(0x284) = 0x4000;
	CLOCK(0x30C) = 0x2000001;
	EMC(0xC34) = _cfg_120[0x13];
	EMC(0xC38) = _cfg_120[0x14];
	EMC(0xCF0) = _cfg_120[0x15];
	EMC(0x28) = 1;
	sleep(1);
	EMC(0x8) = _cfg[0xA9] | 2 * _cfg[0xAA];
	if (_cfg[0xA])
		*(vu32 *)_cfg[0xA] = _cfg[0xB];
	EMC(0x584) = _cfg[0x7B];
	EMC(0x380) = _cfg[0x7D];
	EMC(0x384) = _cfg[0x7E];
	EMC(0x388) = _cfg[0x7F];
	EMC(0x38C) = _cfg[0x80];
	EMC(0x390) = _cfg[0x81];
	EMC(0x394) = _cfg[0x82];
	EMC(0x398) = _cfg[0x83];
	EMC(0x39C) = _cfg[0x84];
	EMC(0x3A0) = _cfg[0x85];
	EMC(0x3A4) = _cfg[0x86];
	EMC(0x3A8) = _cfg[0x87];
	EMC(0x3AC) = _cfg[0x88];
	EMC(0x3B0) = _cfg[0x89];
	EMC(0xC80) = _cfg[0x14A];
	EMC(0xC84) = _cfg[0x14B];
	EMC(0xC88) = _cfg[0x14C];
	EMC(0x330) = (_cfg_120[0x16] | 0xFEEDFEED) & 0x1FFF1FFF;
	EMC(0x5F0) = _cfg[0x149];
	EMC(0x5C8) = _cfg[0x7C];
	EMC(0x404) = _cfg_100[0x18];
	EMC(0x408) = _cfg_100[0x19];
	EMC(0x40C) = _cfg_100[0x1A];
	EMC(0x410) = _cfg_100[0x1B];
	EMC(0x418) = _cfg_100[0x1C];
	EMC(0x41C) = _cfg_100[0x1D];
	EMC(0x420) = _cfg_100[0x1E];
	EMC(0x424) = _cfg_100[0x1F];
	if (_cfg[0xE])
		*(vu32 *)_cfg[0xE] = _cfg[0xF];
	EMC(0x30C) = _cfg[0x31];
	EMC(0x578) = _cfg[0x32];
	EMC(0x2F4) = _cfg[0x33];
	EMC(0x458) = _cfg[0x1D];
	EMC(0x45C) = _cfg[0x1E];
	EMC(0x5B0) = _cfg[0x1F];
	EMC(0x5B4) = _cfg[0x20];
	EMC(0x5CC) = _cfg[0x21];
	EMC(0x574) = _cfg[0x22];
	EMC(0x2DC) = _cfg[0x23];
	EMC(0xC48) = _cfg[0x2A];
	EMC(0xC70) = _cfg[0x2B];
	EMC(0xC74) = _cfg[0x2C];
	EMC(0xC4C) = _cfg[0x2D];
	EMC(0xC78) = _cfg[0x2E];
	EMC(0x464) = _cfg[0x26];
	EMC(0xC44) = _cfg[0x2F];
	EMC(0x5E4) = _cfg_120[0xD];
	EMC(0x5E8) = _cfg_120[0xE];
	EMC(0x2C8) = _cfg[0xB0];
	EMC(0x588) = _cfg_120[1];
	EMC(0x58C) = _cfg_120[2];
	EMC(0x594) = _cfg_120[3];
	EMC(0x598) = _cfg_120[4];
	EMC(0x59C) = _cfg_120[5];
	EMC(0x5A0) = _cfg_120[6];
	EMC(0x5A4) = _cfg_120[7];
	EMC(0x5A8) = _cfg_120[8];
	EMC(0x5AC) = _cfg_120[9];
	EMC(0x5B8) = _cfg_120[0xA];
	EMC(0x5BC) = _cfg_120[0xB];
	EMC(0x5C4) = _cfg_120[0xC];
	EMC(0x330) = (_cfg_120[0x16] | 0xFE40FE40) & 0x1FFF1FFF;
	EMC(0xC40) = _cfg_120[0x12];
	EMC(0x318) = _cfg_120[0x17];
	EMC(0x334) = _cfg_120[0x18] & 0xFF7FFF7F;
	EMC(0x31C) = _cfg_120[0x19];
	EMC(0xC3C) = _cfg_120[0x1A];
	EMC(0xC54) = _cfg_120[0x1B];
	EMC(0xC50) = _cfg_120[0x1C];
	EMC(0xC64) = _cfg_120[0x1F];
	EMC(0xC5C) = _cfg_120[0x1D];
	EMC(0xC58) = _cfg_120[0x1E];
	EMC(0xC60) = _cfg[0x141];
	EMC(0x49C) = _cfg[0x142];
	EMC(0x720) = _cfg[0x143];
	EMC(0x724) = _cfg[0x144];
	EMC(0x728) = _cfg[0x145];
	EMC(0x72C) = _cfg[0x146];
	EMC(0x730) = _cfg[0x147];
	EMC(0x734) = _cfg[0x148];
	EMC(0x740) = _cfg[0x14D];
	EMC(0x744) = _cfg[0x14E];
	EMC(0x748) = _cfg[0x14F];
	EMC(0x74C) = _cfg[0x150];
	EMC(0x750) = _cfg[0x151];
	EMC(0x754) = _cfg[0x152];
	EMC(0x760) = _cfg[0x153];
	EMC(0x770) = _cfg[0x154];
	EMC(0x774) = _cfg[0x155];
	EMC(0x778) = _cfg[0x156];
	EMC(0x780) = _cfg[0x157];
	EMC(0x784) = _cfg[0x158];
	EMC(0x788) = _cfg[0x159];
	EMC(0xBE0) = _cfg[0xB6];
	EMC(0xBE4) = _cfg[0xB7];
	EMC(0xBF0) = _cfg[0xB8];
	EMC(0xBF4) = _cfg[0xB9];
	EMC(0xCF4) = _cfg[0xBA];
	EMC(0x600) = _cfg[0xBD];
	EMC(0x604) = _cfg[0xBE];
	EMC(0x608) = _cfg[0xBF];
	EMC(0x60C) = _cfg[0xC0];
	EMC(0x610) = _cfg[0xC1];
	EMC(0x614) = _cfg[0xC2];
	EMC(0x620) = _cfg[0xC3];
	EMC(0x624) = _cfg[0xC4];
	EMC(0x628) = _cfg[0xC5];
	EMC(0x62C) = _cfg[0xC6];
	EMC(0x630) = _cfg[0xC7];
	EMC(0x634) = _cfg[0xC8];
	EMC(0x330) = _cfg_120[0x16];
	EMC(0x640) = _cfg[0xC9];
	EMC(0x644) = _cfg[0xCA];
	EMC(0x648) = _cfg[0xCB];
	EMC(0x64C) = _cfg[0xCC];
	EMC(0x650) = _cfg[0xCD];
	EMC(0x654) = _cfg[0xCE];
	EMC(0x660) = _cfg[0xCF];
	EMC(0x664) = _cfg[0xD0];
	EMC(0x668) = _cfg[0xD1];
	EMC(0x66C) = _cfg[0xD2];
	EMC(0x670) = _cfg[0xD3];
	EMC(0x674) = _cfg[0xD4];
	EMC(0x680) = _cfg[0xD5];
	EMC(0x684) = _cfg[0xD6];
	EMC(0x688) = _cfg[0xD7];
	EMC(0x68C) = _cfg[0xD8];
	EMC(0x690) = _cfg[0xD9];
	EMC(0x694) = _cfg[0xDA];
	EMC(0x6A0) = _cfg[0xDB];
	EMC(0x6A4) = _cfg[0xDC];
	EMC(0x6A8) = _cfg[0xDD];
	EMC(0x6AC) = _cfg[0xDE];
	EMC(0x6B0) = _cfg[0xDF];
	EMC(0x6B4) = _cfg[0xE0];
	EMC(0x6C0) = _cfg[0xE1];
	EMC(0x6C4) = _cfg[0xE2];
	EMC(0x6C8) = _cfg[0xE3];
	EMC(0x6CC) = _cfg[0xE4];
	EMC(0x6E0) = _cfg[0xE5];
	EMC(0x6E4) = _cfg[0xE6];
	EMC(0x6E8) = _cfg[0xE7];
	EMC(0x6EC) = _cfg[0xE8];
	EMC(0xC00) = _cfg[0xE9];
	EMC(0xC04) = _cfg[0xEA];
	EMC(0xC08) = _cfg[0xEB];
	EMC(0xC0C) = _cfg[0xEC];
	EMC(0xC10) = _cfg[0xED];
	EMC(0xC20) = _cfg[0xEE];
	EMC(0xC24) = _cfg[0xEF];
	EMC(0xC28) = _cfg[0xF0];
	EMC(0xC68) = (*((u8 *)_cfg + 0x500) | 0xFFFFFFFE) & 0xF;
	if (_cfg[0xC])
		*(vu32 *)_cfg[0xC] = _cfg[0xD];
	EMC(0x28) = 1;
	MC(0x648) = _cfg[0x180];
	MC(0x978) = _cfg[0x181];
	MC(0x64C) = _cfg[0x182];
	MC(0x418) = _cfg[0x183];
	MC(0x590) = _cfg[0x184];
	MC(0x984) = _cfg[0x185];
	MC(0x988) = _cfg[0x186];
	MC(0x54) = _cfg[0x15A];
	MC(0x58) = _cfg[0x15B];
	MC(0x5C) = _cfg[0x15C];
	MC(0x60) = _cfg[0x15D];
	MC(0x64) = _cfg[0x15E];
	MC(0x68) = _cfg[0x15F];
	MC(0x6C) = _cfg[0x160];
	MC(0x50) = _cfg[0x161];
	MC(0x670) = _cfg[0x187];
	MC(0x9D4) = _cfg[0x188];
	MC(0x674) = _cfg[0x189];
	MC(0x9A0) = _cfg[0x1D6];
	MC(0x9A8) = _cfg[0x1D7];
	MC(0x9A4) = _cfg[0x1D8];
	MC(0x90) = _cfg[0x162];
	MC(0x94) = _cfg[0x163];
	MC(0x6F0) = _cfg[0x164];
	MC(0x6F4) = _cfg[0x165];
	MC(0x98) = _cfg[0x166];
	MC(0x9C) = _cfg[0x167];
	MC(0xA0) = _cfg[0x168];
	MC(0xA4) = _cfg[0x169];
	MC(0xA8) = _cfg[0x16A];
	MC(0xAC) = _cfg[0x16B];
	MC(0xB0) = _cfg[0x16C];
	MC(0xB4) = _cfg[0x16D];
	MC(0xB8) = _cfg[0x16E];
	MC(0xBC) = _cfg[0x16F];
	MC(0x6C4) = _cfg[0x17D];
	MC(0xC0) = _cfg[0x170];
	MC(0xC4) = _cfg[0x171];
	MC(0x6C0) = _cfg[0x172];
	MC(0xD0) = _cfg[0x173];
	MC(0xD4) = _cfg[0x174];
	MC(0xD8) = _cfg[0x175];
	MC(0xDC) = _cfg[0x176];
	MC(0xC8) = _cfg[0x177];
	MC(0xE0) = _cfg[0x178];
	MC(0xE8) = _cfg[0x179];
	MC(0x968) = _cfg[0x17A];
	MC(0xEC) = _cfg[0x17B];
	MC(0x9DC) = _cfg[0x17C];
	MC(0xFC) = 1;
	MC(0xF4) = _cfg[0x17E];
	MC(0x100) = _cfg[0x17F];
	EMC(0x10) = _cfg[0x34];
	EMC(0x140) = _cfg_100[7];
	EMC(0x700) = _cfg[0x27];
	EMC(0x704) = _cfg[0x28];
	EMC(0x708) = _cfg[0x29];
	EMC(0x2F8) = _cfg[0x24];
	EMC(0x300) = _cfg[0x25];
	EMC(0x2A8) = _cfg[0x1B];
	EMC(0x2A4) = _cfg[0x1C];
	sleep(_cfg[0x30]);
	if (_cfg[0x10])
		*(vu32 *)_cfg[0x10] = _cfg[0x11];
	EMC(0x2B8) = _cfg[0xA4];
	EMC(0x560) = _cfg[0xA5];
	EMC(0x55C) = _cfg[0xBB];
	EMC(0x554) = _cfg[0xBC];
	EMC(0xF0) = _cfg[0xAB];
	EMC(0xF4) = _cfg[0xAC];
	EMC(0xC8) = _cfg[0xA1];
	EMC(0xC4) = _cfg[0xA2];
	EMC(0x104) = _cfg[0x7A];
	EMC(0x2C) = _cfg[0x3A];
	EMC(0x30) = _cfg[0x3B];
	EMC(0x590) = _cfg[0x3C];
	EMC(0x580) = _cfg[0x3D];
	EMC(0xC0) = _cfg[0x3E];
	EMC(0x34) = _cfg[0x3F];
	EMC(0x38) = _cfg[0x40];
	EMC(0xAC) = _cfg[0x47];
	EMC(0x144) = _cfg[0x41];
	EMC(0x148) = _cfg[0x42];
	EMC(0x3C) = _cfg[0x43];
	EMC(0x40) = _cfg[0x44];
	EMC(0x44) = _cfg[0x45];
	EMC(0x48) = _cfg[0x46];
	EMC(0x5C0) = _cfg[0x48];
	EMC(0x4C) = _cfg[0x49];
	EMC(0x50) = _cfg[0x4A];
	EMC(0x54) = _cfg[0x4B];
	EMC(0x58) = _cfg[0x4C];
	EMC(0xB8) = _cfg[0x4D];
	EMC(0x5C) = _cfg[0x4E];
	EMC(0x4E0) = _cfg[0x4F];
	EMC(0x498) = _cfg[0x50];
	EMC(0x494) = _cfg[0x51];
	EMC(0x2D0) = _cfg[0x52];
	EMC(0x490) = _cfg[0x53];
	EMC(0x48C) = _cfg[0x54];
	EMC(0x60) = _cfg[0x55];
	EMC(0x568) = _cfg[0x56];
	EMC(0x468) = _cfg[0x57];
	EMC(0x46C) = _cfg[0x58];
	EMC(0x14C) = _cfg[0x59];
	EMC(0x150) = _cfg[0x5A];
	EMC(0x154) = _cfg[0x5B];
	EMC(0x56C) = _cfg[0x5C];
	EMC(0xC68) = _cfg[0x140];
	EMC(0x8) = _cfg[0xA9];
	EMC(0x64) = _cfg[0x5D];
	EMC(0x428) = 0;
	EMC(0x68) = _cfg[0x5E];
	EMC(0x6C) = _cfg[0x5F];
	EMC(0x2CC) = _cfg[0x60];
	EMC(0x2D8) = _cfg[0x61];
	EMC(0x2D4) = _cfg[0x62];
	EMC(0x564) = _cfg[0x63];
	EMC(0x70) = _cfg[0x64];
	EMC(0x74) = _cfg[0x65];
	EMC(0x3DC) = _cfg[0x66];
	EMC(0x78) = _cfg[0x67];
	EMC(0x7C) = _cfg[0x68];
	EMC(0x80) = _cfg[0x69];
	EMC(0x84) = _cfg[0x6A];
	EMC(0x88) = _cfg[0x6B];
	EMC(0x8C) = _cfg[0x6C];
	EMC(0x11C) = _cfg[0x6D];
	EMC(0x118) = _cfg[0x6E];
	EMC(0xB4) = _cfg[0x6F];
	EMC(0x90) = _cfg[0x70];
	EMC(0x3E4) = _cfg[0x71];
	EMC(0x94) = _cfg[0x72];
	EMC(0x158) = _cfg[0x73];
	EMC(0x15C) = _cfg[0x74];
	EMC(0x98) = _cfg[0x75];
	EMC(0x9C) = _cfg[0x76];
	EMC(0xA0) = _cfg[0x77];
	EMC(0xA4) = _cfg[0x78];
	EMC(0xA8) = _cfg[0x79];
	EMC(0xB0) = _cfg[0xF2];
	EMC(0x2BC) = _cfg[0xAF];
	EMC(0x2C0) = _cfg[0xB1];
	EMC(0x100) = _cfg[0x8A] & 0xFFFFFFFD;
	EMC(0x120) = _cfg[0x8B];
	EMC(0x440) = _cfg_120[0xF];
	EMC(0x444) = _cfg_120[0x10];
	EMC(0x448) = _cfg_120[0x11];
	EMC(0x124) = _cfg_100[0x17];
	EMC(0x480) = *_cfg_120;
	EMC(0xC) = ((_cfg[0xA3] & 4 | 0x3C00000) & 0xFFFFFFF7 | _cfg[0xA3] & 8) & 0xFFFFFFFD | _cfg[0xA3] & 2;
	if ((_cfg[0x1D4] & 0x80000000) != 0)
	{
		*(vu32 *)(4 * _cfg[0x1D4] + 0x70000000) = _cfg[0x1D5];
		MC(0xFC) = 1;
	}
	PMC(0x45C) = ((4 * _cfg_120[0xF] >> 2) + 0x40000000) & 0xCFFF0000;
	sleep(_cfg_100[0x11]);
	if (!_cfg[0x1B])
		EMC(0x2A4) = _cfg[0x1C] | 0x200;
	EMC(0x334) = _cfg_120[0x18];
	if (_cfg[0xFA] << 31)
	{
		if (*_cfg == 2)
			EMC(0x2E4) = 8 * _cfg[0xF4];
		if (*_cfg == 3)
		{
			EMC(0x2E4) = _cfg[0xF4];
			EMC(0x2E8) = _cfg[0xF5];
		}
	}
	EMC(0x28) = 1;
	sleep(_cfg[0x39]);
	PMC(0x4E4) &= 0xFFF8007F;
	sleep(_cfg_100[0x15]);
	if (*_cfg == 2)
	{
		EMC(0x24) = (_cfg[0x37] << 16) | (_cfg[0x38] << 12);
		sleep(_cfg[0x36] + 200);
		EMC(0x24) = ((_cfg[0x37] << 16) | (_cfg[0x38] << 12)) + 0x100;
		sleep(_cfg[0x36] + 500);
	}
	if (*_cfg == 3)
	{
		EMC(0x24) = (_cfg[0x37] << 16) | (_cfg[0x38] << 12);
		sleep(_cfg[0x36] + 200);
		EMC(0x24) = ((_cfg[0x37] << 16) | (_cfg[0x38] << 12)) + 0x100;
		sleep(_cfg[0x36] + 2000);
	}
	EMC(0x24) = ((_cfg[0x37] << 16) | (_cfg[0x38] << 12)) + 0x101;
	sleep(_cfg[0x35]);
	if (*_cfg != 3)
		EMC(0xDC) = (_cfg[0xB2] << 30) + 1;
	if (*_cfg == 1)
		sleep(_cfg[0x36] + 200);
	if (*_cfg == 3)
	{
		if (_cfg[0x12])
			*(vu32 *)_cfg[0x12] = _cfg[0x13];
		EMC(0x134) = _cfg[0x91];
		EMC(0xE8) = _cfg[0x90];
		EMC(0x138) = _cfg[0x92];
		EMC(0x13C) = _cfg[0x93];
		EMC(0x4A4) = _cfg[0x94];
		EMC(0x4C4) = _cfg[0x9A];
		EMC(0x4AC) = _cfg[0x95];
		EMC(0x4BC) = _cfg[0x98];
		EMC(0x4B0) = _cfg[0x96];
		EMC(0x4C0) = _cfg[0x99];
		if (_cfg[0xFA] << 31)
		{
			EMC(0x2EC) = _cfg[0xF7];
			sleep(_cfg[0xF9]);
			EMC(0x2EC) = _cfg[0xF7] ^ 3;
			if (!(_cfg[0xB2] & 2))
			{
				EMC(0x2EC) = _cfg[0xF8];
				sleep(_cfg[0xF9]);
				EMC(0x2EC) = _cfg[0xF8] ^ 3;
			}
		}
	}
	PMC(0x1D0) = _cfg_100[0xF];
	if (_cfg[0] == 1 || _cfg[0] == 2 || _cfg[0] == 3)
	{
		EMC(0x2E0) = _cfg[0xF3];
		EMC(0x2E4) = _cfg[0xF4];
		EMC(0x2E8) = _cfg[0xF5];
	}
	if (_cfg[0x14])
		*(vu32 *)_cfg[0x14] = _cfg[0x15];
	EMC(0x28) = 1;
	if (_cfg_100[8])
		EMC(0xD4) = ((1 << _cfg_100[8] << 8) - 0xFD) | (_cfg[0x38] << 30);
	EMC(0x20) = _cfg[0xB2] | 0x80000000;
	EMC(0x3E0) = _cfg[0xAD];
	EMC(0x5F4) = _cfg[0xA8];
	EMC(0xC) = _cfg[0xA3];
	EMC(0x310) = _cfg[0xB4];
	EMC(0x314) = _cfg[0xB5];
	EMC(0x3D8) = _cfg[0xB3];
	EMC(0x100) = _cfg[0x8A] | 2;
	EMC(0x28) = 1;
	EMC(0x558) = _cfg[0xA6];
	EMC(0x4D8) = _cfg[0xA7];
	SYSREG(AHB_ARBITRATION_XBAR_CTRL) = SYSREG(AHB_ARBITRATION_XBAR_CTRL) & 0xFFFEFFFF | (*((u16 *)_cfg + 0x15C) << 16);
	MC(0x650) = _cfg[0x18A];
	MC(0x678) = _cfg[0x18B];
	MC(0x9AC) = _cfg[0x1D9];
	MC(MC_EMEM_CFG_ACCESS_CTRL) = 1; //Disable write access to a bunch of MC registers.
}

void sdram_init()
{
	u32 sdram_id = _get_sdram_id();
	const u32 *cfg = _dram_cfgs[sdram_id]; //TODO: sdram_id should be in [0,4].

	i2c_send_byte(I2C_5, 0x3C, 0x22, 0x05);
	i2c_send_byte(I2C_5, 0x3C, 0x17, 40); //40 = (1000 * 1100 - 600000) / 12500

	PMC(APBDEV_PMC_VDDP_SEL) = cfg[0x10C];
	sleep(cfg[0x10D]);
	PMC(APBDEV_PMC_DDR_PWR) = PMC(0xE8);
	PMC(APBDEV_PMC_NO_IOPOWER) = cfg[0x114];
	PMC(APBDEV_PMC_REG_SHORT) = cfg[0x113];
	PMC(APBDEV_PMC_DDR_CNTRL) = cfg[0x116];

	if (cfg[8])
		*(vu32 *)cfg[8] = cfg[9];

	_sdram_config(cfg);
}
