/*
 * NPLL - CPU macros
 *
 * Copyright (C) 2025-2026 Techflash
 */

#ifndef _CPU_H
#define _CPU_H

#ifdef __ASSEMBLY__
#define BIT(nr) (1u << (nr))
#endif

/* SPR Numbers */
#define PVR    287

#define IBAT0U 528
#define IBAT0L 529
#define IBAT1U 530
#define IBAT1L 531
#define IBAT2U 532
#define IBAT2L 533
#define IBAT3U 534
#define IBAT3L 535

#define DBAT0U 536
#define DBAT0L 537
#define DBAT1U 538
#define DBAT1L 539
#define DBAT2U 540
#define DBAT2L 541
#define DBAT3U 542
#define DBAT3L 543

#define IBAT4U 560
#define IBAT4L 561
#define IBAT5U 562
#define IBAT5L 563
#define IBAT6U 564
#define IBAT6L 565
#define IBAT7U 566
#define IBAT7L 567

#define DBAT4U 568
#define DBAT4L 569
#define DBAT5U 570
#define DBAT5L 571
#define DBAT6U 572
#define DBAT6L 573
#define DBAT7U 574
#define DBAT7L 575

#define HID0   1008
#define HID1   1009
#define HID2   920
#define HID4   1011
#define DABR   1013
#define L2CR   1017


/* MSR values */
#define MSR_EE BIT(31 - 16)
#define MSR_PR BIT(31 - 17)
#define MSR_FP BIT(31 - 18)
#define MSR_ME BIT(31 - 19)
#define MSR_IR BIT(31 - 26)
#define MSR_DR BIT(31 - 27)

/* HID0 values */
#define HID0_DPM  BIT(31 - 11)
#define HID0_NHR  BIT(31 - 15)
#define HID0_ICE  BIT(31 - 16)
#define HID0_DCE  BIT(31 - 17)
#define HID0_ICFI BIT(31 - 20)
#define HID0_DCFI BIT(31 - 21)
#define HID0_SPD  BIT(31 - 22)
#define HID0_DCFA BIT(31 - 25)
#define HID0_BTIC BIT(31 - 26)
#define HID0_BHT  BIT(31 - 29)

/* HID2 values */
#define HID2_LSQE BIT(31 - 0)
#define HID2_WPE  BIT(31 - 1)
#define HID2_PSE  BIT(31 - 2)

/* HID4 values */
#define HID4_L2FM_32B  (0u << (31 - 2))
#define HID4_L2FM_64B  (1u << (31 - 2))
#define HID4_L2FM_128B (2u << (31 - 2))
#define HID4_BPD_2     (0u << (31 - 4))
#define HID4_BPD_3     (1u << (31 - 4))
#define HID4_BPD_4     (2u << (31 - 4))
#define HID4_BCO       BIT(31 - 5)
#define HID4_SBE       BIT(31 - 6)
#define HID4_ST0       BIT(31 - 7) /* PS1_CTL on 750CL */
#define HID4_LPE       BIT(31 - 8) /* Reserved on 750CL */
#define HID4_L2MUM     BIT(31 - 10)
#define HID4_L2_CCFI   BIT(31 - 11)
#define HID4_PS2_CTL   BIT(31 - 12) /* Reserved on Broadway, but on 750CL it's PS2_CTL */

/* L2CR values */
#define L2CR_L2E     BIT(31 - 0)
#define L2CR_L2I     BIT(31 - 10)
#define L2CR_L2IP    BIT(31 - 31)

#define SETBAT_TYPE_DATA BIT(0)
#define SETBAT_TYPE_INST BIT(1)
#define SETBAT_TYPE_BOTH (SETBAT_TYPE_DATA | SETBAT_TYPE_INST)

#ifndef __ASSEMBLY__
#include <npll/types.h>
#include <npll/panic.h>
#include <npll/utils.h>

#define mfspr(rn) ({ \
	u32 __tmpSPRVal; \
	asm volatile("mfspr %0, " __stringifyResult(rn) "" : "=r" (__tmpSPRVal)); \
	__tmpSPRVal; \
})

#define mfmsr() ({ \
	u32 __tmpMSRVal; \
	asm volatile("mfmsr %0" : "=r" (__tmpMSRVal)); \
	__tmpMSRVal; \
})
#define mtmsr(v) asm volatile("mtmsr %0" : : "r" (v))

#define mtspr(rn, v) asm volatile("mtspr " __stringifyResult(rn) ", %0" : : "r" (v))

/* Somehow GCC generates garbage */
__attribute__((optimize("no-jump-tables")))
static inline void setbat(uint idx, uint typeMask, u32 batu, u32 batl) {
	switch (idx) {
	case 0:
	case 1: {
		panic("Trying to set important BATs");
		break;
	}
	case 2: {
		if (typeMask & SETBAT_TYPE_DATA) {
			mtspr(DBAT2U, batu);
			mtspr(DBAT2L, batl);
		}
		if (typeMask & SETBAT_TYPE_INST) {
			mtspr(IBAT2U, batu);
			mtspr(IBAT2L, batl);
		}
		break;
	}
	case 3: {
		if (typeMask & SETBAT_TYPE_DATA) {
			mtspr(DBAT3U, batu);
			mtspr(DBAT3L, batl);
		}
		if (typeMask & SETBAT_TYPE_INST) {
			mtspr(IBAT3U, batu);
			mtspr(IBAT3L, batl);
		}
		break;
	}
	case 4: {
		if (typeMask & SETBAT_TYPE_DATA) {
			mtspr(DBAT4U, batu);
			mtspr(DBAT4L, batl);
		}
		if (typeMask & SETBAT_TYPE_INST) {
			mtspr(IBAT4U, batu);
			mtspr(IBAT4L, batl);
		}
		break;
	}
	case 5: {
		if (typeMask & SETBAT_TYPE_DATA) {
			mtspr(DBAT5U, batu);
			mtspr(DBAT5L, batl);
		}
		if (typeMask & SETBAT_TYPE_INST) {
			mtspr(IBAT5U, batu);
			mtspr(IBAT5L, batl);
		}
		break;
	}
	case 6: {
		if (typeMask & SETBAT_TYPE_DATA) {
			mtspr(DBAT6U, batu);
			mtspr(DBAT6L, batl);
		}
		if (typeMask & SETBAT_TYPE_INST) {
			mtspr(IBAT6U, batu);
			mtspr(IBAT6L, batl);
		}
		break;
	}
	case 7: {
		if (typeMask & SETBAT_TYPE_DATA) {
			mtspr(DBAT7U, batu);
			mtspr(DBAT7L, batl);
		}
		if (typeMask & SETBAT_TYPE_INST) {
			mtspr(IBAT7U, batu);
			mtspr(IBAT7L, batl);
		}
		break;
	}

	default: {
		panic("Unknown BAT number");
		break;
	}
	}
	asm volatile ("isync; sync");
}

extern void CPU_Init(void);
extern void CPU_L2Disable(void);
extern void CPU_L2Enable(void);

#endif /* __ASSEMBLY__ */

#endif /* _CPU_H */
