/*
 * \brief  Dump Broadwell MMIO registers
 * \author Josef Soentgen
 * \data   2017-03-15
 */

/*
 * Copyright (C) 2017 Genode Labs GmbH
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU Affero General Public License version 3.
 */

/* local includes */
#include <mmio.h>
#include <context.h>


void Igd::Mmio::dump()
{
	using namespace Genode;

	log("MMIO vaddr:", Hex(base()), " size:", Hex(Igd::Mmio::SIZE));
	log("GFX_MODE: ", Hex(read<GFX_MODE>()));
	log("  Privilege_check_disable:   ", Hex(read<GFX_MODE::Privilege_check_disable>()));
	log("  Execlist_enable:           ", Hex(read<GFX_MODE::Execlist_enable>()));
	log("  Virtual_addressing_enable: ", Hex(read<GFX_MODE::Virtual_addressing_enable>()));
	log("  Ppgtt_enable:              ", Hex(read<GFX_MODE::Ppgtt_enable>()));
	log("HWS_PGA: ", Hex(read<HWS_PGA_RCSUNIT>()));
	log("HWSTAM: ", Hex(read<HWSTAM>()));
}


void Igd::Mmio::power_dump()
{
	using namespace Genode;

	log("PWR_WELL_CTL2");
	log("  Misc_io_power_state:              ", Hex(read<PWR_WELL_CTL2::Misc_io_power_state>()));
	log("  Misc_io_power_request:            ", Hex(read<PWR_WELL_CTL2::Misc_io_power_request>()));
	log("  Ddi_a_and_ddi_e_io_power_state:   ", Hex(read<PWR_WELL_CTL2::Ddi_a_and_ddi_e_io_power_state>()));
	log("  Ddi_a_and_ddi_e_io_power_request: ", Hex(read<PWR_WELL_CTL2::Ddi_a_and_ddi_e_io_power_request>()));
	log("  Ddi_b_io_power_state:             ", Hex(read<PWR_WELL_CTL2::Ddi_b_io_power_state>()));
	log("  Ddi_b_io_power_request:           ", Hex(read<PWR_WELL_CTL2::Ddi_b_io_power_request>()));
	log("  Ddi_c_io_power_state:             ", Hex(read<PWR_WELL_CTL2::Ddi_c_io_power_state>()));
	log("  Ddi_c_io_power_request:           ", Hex(read<PWR_WELL_CTL2::Ddi_c_io_power_request>()));
	log("  Ddi_d_io_power_state:             ", Hex(read<PWR_WELL_CTL2::Ddi_d_io_power_state>()));
	log("  Ddi_d_io_power_request:           ", Hex(read<PWR_WELL_CTL2::Ddi_d_io_power_request>()));
	log("  Power_well_1_state:               ", Hex(read<PWR_WELL_CTL2::Power_well_1_state>()));
	log("  Power_well_1_request:             ", Hex(read<PWR_WELL_CTL2::Power_well_1_request>()));
	log("  Power_well_2_state:               ", Hex(read<PWR_WELL_CTL2::Power_well_2_state>()));
	log("  Power_well_2_request:             ", Hex(read<PWR_WELL_CTL2::Power_well_2_request>()));
}


void Igd::Mmio::error_dump()
{
	using namespace Genode;

	log("ERROR: ", Hex(read<ERROR>()));
	if (read<ERROR>()) {
		log("  Ctx_fault_ctxt_not_prsnt_err:       ", Hex(read<ERROR::Ctx_fault_ctxt_not_prsnt_err>()));
		log("  Ctx_fault_root_not_prsnt_err:       ", Hex(read<ERROR::Ctx_fault_root_not_prsnt_err>()));
		log("  Ctx_fault_pasid_not_prsnt_err:      ", Hex(read<ERROR::Ctx_fault_pasid_not_prsnt_err>()));
		log("  Ctx_fault_pasid_ovflw_err:          ", Hex(read<ERROR::Ctx_fault_pasid_ovflw_err>()));
		log("  Ctx_fault_pasid_dis_err:            ", Hex(read<ERROR::Ctx_fault_pasid_dis_err>()));
		log("  Rstrm_fault_nowb_atomic_err:        ", Hex(read<ERROR::Rstrm_fault_nowb_atomic_err>()));
		log("  Unloaded_pd_error:                  ", Hex(read<ERROR::Unloaded_pd_error>()));
		log("  Hws_page_fault_error:               ", Hex(read<ERROR::Hws_page_fault_error>()));
		log("  Invalid_page_directory_entry_error: ", Hex(read<ERROR::Invalid_page_directory_entry_error>()));
		log("  Ctx_page_fault_error:               ", Hex(read<ERROR::Ctx_page_fault_error>()));
		log("  Tlb_fault_error:                    ", Hex(read<ERROR::Tlb_fault_error>()));
	}

	log("ERROR_2: ", Hex(read<ERROR_2>()));
	if (read<ERROR_2>()) {
		log("  Tlbpend_reg_faultcnt:               ", Hex(read<ERROR_2::Tlbpend_reg_faultcnt>()));
	}

	log("RCS_EIR:        ", Hex(read<RCS_EIR>()));
	log("RCS_ESR:        ", Hex(read<RCS_ESR>()));
	log("RCS_EMR:        ", Hex(read<RCS_EMR>()));
	log("RCS_INSTDONE:   ", Hex(read<RCS_INSTDONE>()));
	if (read<RCS_INSTDONE>() != RCS_INSTDONE::DEFAULT_VALUE ||
	    read<RCS_INSTDONE>() != 0xffffffff) {
			log("  Row_0_eu_0_done : ", Hex(read<RCS_INSTDONE::Row_0_eu_0_done >()));
			log("  Row_0_eu_1_done : ", Hex(read<RCS_INSTDONE::Row_0_eu_1_done >()));
			log("  Row_0_eu_2_done : ", Hex(read<RCS_INSTDONE::Row_0_eu_2_done >()));
			log("  Row_0_eu_3_done : ", Hex(read<RCS_INSTDONE::Row_0_eu_3_done >()));
			log("  Row_1_eu_0_done : ", Hex(read<RCS_INSTDONE::Row_1_eu_0_done >()));
			log("  Row_1_eu_1_done : ", Hex(read<RCS_INSTDONE::Row_1_eu_1_done >()));
			log("  Row_1_eu_2_done : ", Hex(read<RCS_INSTDONE::Row_1_eu_2_done >()));
			log("  Row_1_eu_3_done : ", Hex(read<RCS_INSTDONE::Row_1_eu_3_done >()));
			log("  Sf_done         : ", Hex(read<RCS_INSTDONE::Sf_done         >()));
			log("  Se_done         : ", Hex(read<RCS_INSTDONE::Se_done         >()));
			log("  Windower_done   : ", Hex(read<RCS_INSTDONE::Windower_done   >()));
			log("  Reserved1       : ", Hex(read<RCS_INSTDONE::Reserved1       >()));
			log("  Reserved2       : ", Hex(read<RCS_INSTDONE::Reserved2       >()));
			log("  Dip_done        : ", Hex(read<RCS_INSTDONE::Dip_done        >()));
			log("  Pl_done         : ", Hex(read<RCS_INSTDONE::Pl_done         >()));
			log("  Dg_done         : ", Hex(read<RCS_INSTDONE::Dg_done         >()));
			log("  Qc_done         : ", Hex(read<RCS_INSTDONE::Qc_done         >()));
			log("  Ft_done         : ", Hex(read<RCS_INSTDONE::Ft_done         >()));
			log("  Dm_done         : ", Hex(read<RCS_INSTDONE::Dm_done         >()));
			log("  Sc_done         : ", Hex(read<RCS_INSTDONE::Sc_done         >()));
			log("  Fl_done         : ", Hex(read<RCS_INSTDONE::Fl_done         >()));
			log("  By_done         : ", Hex(read<RCS_INSTDONE::By_done         >()));
			log("  Ps_done         : ", Hex(read<RCS_INSTDONE::Ps_done         >()));
			log("  Cc_done         : ", Hex(read<RCS_INSTDONE::Cc_done         >()));
			log("  Map_fl_done     : ", Hex(read<RCS_INSTDONE::Map_fl_done     >()));
			log("  Map_l2_idle     : ", Hex(read<RCS_INSTDONE::Map_l2_idle     >()));
			log("  Msg_arb_0_done  : ", Hex(read<RCS_INSTDONE::Msg_arb_0_done  >()));
			log("  Msg_arb_1_done  : ", Hex(read<RCS_INSTDONE::Msg_arb_1_done  >()));
			log("  Ic_row_0_done   : ", Hex(read<RCS_INSTDONE::Ic_row_0_done   >()));
			log("  Ic_row_1_done   : ", Hex(read<RCS_INSTDONE::Ic_row_1_done   >()));
			log("  Cp_done         : ", Hex(read<RCS_INSTDONE::Cp_done         >()));
			log("  Ring_0_enable   : ", Hex(read<RCS_INSTDONE::Ring_0_enable   >()));
	}
	log("RCS_INSTDONE_1: ", Hex(read<RCS_INSTDONE_1>()));
	log("RCS_ACTHD:      ", Hex(read<RCS_ACTHD>()));
	log("IPEHR:          ", Hex(read<IPEHR>()));
	log("IPEIR:          ", Hex(read<IPEIR>()));
	log("PGTBL_ER:       ", Hex(read<PGTBL_ER>()));
}


void Igd::Mmio::intr_dump()
{
#define DUMP_GT_1
#define DUMP_GT_2
#define DUMP_GT_3

	using namespace Genode;

	log("MASTER_INT_CTL");
	log("  Master_interrupt_enable:        ", Hex(read<MASTER_INT_CTL::Master_interrupt_enable>()));
	log("  Pcu_interrupts_pending:         ", Hex(read<MASTER_INT_CTL::Pcu_interrupts_pending>()));
	log("  Audio_codec_interrupts_pending: ", Hex(read<MASTER_INT_CTL::Audio_codec_interrupts_pending>()));
	log("  De_pch_interrupts_pending:      ", Hex(read<MASTER_INT_CTL::De_pch_interrupts_pending>()));
	log("  De_misc_interrupts_pending:     ", Hex(read<MASTER_INT_CTL::De_misc_interrupts_pending>()));
	log("  De_port_interrupts_pending:     ", Hex(read<MASTER_INT_CTL::De_port_interrupts_pending>()));
	log("  De_pipe_c_interrupts_pending:   ", Hex(read<MASTER_INT_CTL::De_pipe_c_interrupts_pending>()));
	log("  De_pipe_b_interrupts_pending:   ", Hex(read<MASTER_INT_CTL::De_pipe_b_interrupts_pending>()));
	log("  De_pipe_a_interrupts_pending:   ", Hex(read<MASTER_INT_CTL::De_pipe_a_interrupts_pending>()));
	log("  Vebox_interrupts_pending:       ", Hex(read<MASTER_INT_CTL::Vebox_interrupts_pending>()));
	log("  Gtpm_interrupts_pending:        ", Hex(read<MASTER_INT_CTL::Gtpm_interrupts_pending>()));
	log("  Vcs2_interrupts_pending:        ", Hex(read<MASTER_INT_CTL::Vcs2_interrupts_pending>()));
	log("  Vcs1_interrupts_pending:        ", Hex(read<MASTER_INT_CTL::Vcs1_interrupts_pending>()));
	log("  Blitter_interrupts_pending:     ", Hex(read<MASTER_INT_CTL::Blitter_interrupts_pending>()));
	log("  Render_interrupts_pending:      ", Hex(read<MASTER_INT_CTL::Render_interrupts_pending>()));

#ifndef DUMP_GT_0
#define DUMP_GT_0(reg) \
	log(#reg); \
	log("  Bcs_wait_on_semaphore:       ", Hex(read<reg::Bcs_wait_on_semaphore>()));       \
	log("  Bcs_ctx_switch_interrupt:    ", Hex(read<reg::Bcs_ctx_switch_interrupt>()));    \
	log("  Bcs_mi_flush_dw_notify:      ", Hex(read<reg::Bcs_mi_flush_dw_notify>()));      \
	log("  Bcs_error_interrupt:         ", Hex(read<reg::Bcs_error_interrupt>()));         \
	log("  Bcs_mi_user_interrupt:       ", Hex(read<reg::Bcs_mi_user_interrupt>()));       \
	log("  Cs_wait_on_semaphore:        ", Hex(read<reg::Cs_wait_on_semaphore>()));        \
	log("  Cs_l3_counter_slave:         ", Hex(read<reg::Cs_l3_counter_slave>()));         \
	log("  Cs_ctx_switch_interrupt:     ", Hex(read<reg::Cs_ctx_switch_interrupt>()));     \
	log("  Page_fault_error:            ", Hex(read<reg::Page_fault_error>()));            \
	log("  Cs_watchdog_counter_expired: ", Hex(read<reg::Cs_watchdog_counter_expired>())); \
	log("  L3_parity_error:             ", Hex(read<reg::L3_parity_error>()));             \
	log("  Cs_pipe_control_notify:      ", Hex(read<reg::Cs_pipe_control_notify>()));      \
	log("  Cs_error_interrupt:          ", Hex(read<reg::Cs_error_interrupt>()));          \
	log("  Cs_mi_user_interrupt:        ", Hex(read<reg::Cs_mi_user_interrupt>()));

	DUMP_GT_0(GT_0_INTERRUPT_ISR);
	DUMP_GT_0(GT_0_INTERRUPT_IIR);
	DUMP_GT_0(GT_0_INTERRUPT_IER);
	DUMP_GT_0(GT_0_INTERRUPT_IMR);
#undef DUMP_GT_0
#endif

#ifndef DUMP_GT_1
#define DUMP_GT_1(reg) \
	log(#reg); \
	log("  Vcs2_wait_on_semaphore:        ", Hex(read<reg::Vcs2_wait_on_semaphore>()));        \
	log("  Vcs2_ctx_switch_interrupt:     ", Hex(read<reg::Vcs2_ctx_switch_interrupt>()));     \
	log("  Vcs2_watchdog_counter_expired: ", Hex(read<reg::Vcs2_watchdog_counter_expired>())); \
	log("  Vcs2_mi_flush_dw_notify:       ", Hex(read<reg::Vcs2_mi_flush_dw_notify>()));       \
	log("  Vcs2_error_interrupt:          ", Hex(read<reg::Vcs2_error_interrupt>()));          \
	log("  Vcs2_mi_user_interrupt:        ", Hex(read<reg::Vcs2_mi_user_interrupt>()));        \
	log("  Vcs1_wait_on_semaphore:        ", Hex(read<reg::Vcs1_wait_on_semaphore>()));        \
	log("  Vcs1_ctx_switch_interrupt:     ", Hex(read<reg::Vcs1_ctx_switch_interrupt>()));     \
	log("  Vcs1_watchdog_counter_expired: ", Hex(read<reg::Vcs1_watchdog_counter_expired>())); \
	log("  Vcs1_pipe_control_notify:      ", Hex(read<reg::Vcs1_pipe_control_notify>()));      \
	log("  Vcs1_error_interrupt:          ", Hex(read<reg::Vcs1_error_interrupt>()));          \
	log("  Vcs1_mi_user_interrupt:        ", Hex(read<reg::Vcs1_mi_user_interrupt>()));        \

	DUMP_GT_1(GT_1_INTERRUPT_ISR);
	DUMP_GT_1(GT_1_INTERRUPT_IIR);
	DUMP_GT_1(GT_1_INTERRUPT_IER);
	DUMP_GT_1(GT_1_INTERRUPT_IMR);
#undef DUMP_GT_1
#endif

#ifndef DUMP_GT_2
#define DUMP_GT_2(reg) \
	log(#reg); \
	log("  Unslice_frequency_control_up_interrupt:                       ", Hex(read<reg::Unslice_frequency_control_up_interrupt>()));   \
	log("  Unslice_frequency_control_down_interrupt:                     ", Hex(read<reg::Unslice_frequency_control_down_interrupt>())); \
	log("  Nfafdl_frequency_up_interrupt:                                ", Hex(read<reg::Nfafdl_frequency_up_interrupt>()));      \
	log("  Nfafdl_frequency_down_interrupt:                              ", Hex(read<reg::Nfafdl_frequency_down_interrupt>()));    \
	log("  Gtpm_engines_idle_interrupt:                                  ", Hex(read<reg::Gtpm_engines_idle_interrupt>()));        \
	log("  Gtpm_uncore_to_core_trap_interrupt:                           ", Hex(read<reg::Gtpm_uncore_to_core_trap_interrupt>())); \
	log("  Gtpm_render_frequency_downwards_timeout_during_rc6_interrupt: ", Hex(read<reg::Gtpm_render_frequency_downwards_timeout_during_rc6_interrupt>())); \
	log("  Gtpm_render_p_state_up_threshold_interrupt:                   ", Hex(read<reg::Gtpm_render_p_state_up_threshold_interrupt>()));                   \
	log("  Gtpm_render_p_state_down_threshold_interrupt:                 ", Hex(read<reg::Gtpm_render_p_state_down_threshold_interrupt>()));                 \
	log("  Gtpm_render_geyserville_up_evaluation_interval_interrupt:     ", Hex(read<reg::Gtpm_render_geyserville_up_evaluation_interval_interrupt>()));     \
	log("  Gtpm_render_geyserville_down_evaluation_interval_interrupt:   ", Hex(read<reg::Gtpm_render_geyserville_down_evaluation_interval_interrupt>()));

	DUMP_GT_2(GT_2_INTERRUPT_ISR);
	DUMP_GT_2(GT_2_INTERRUPT_IIR);
	DUMP_GT_2(GT_2_INTERRUPT_IER);
	DUMP_GT_2(GT_2_INTERRUPT_IMR);
#undef DUMP_GT_2
#endif

#ifndef DUMP_GT_3
#define DUMP_GT_3(reg) \
	log(#reg); \
	log("  Performance_monitoring_buffer_half_full_interrupt: ", Hex(read<reg::Performance_monitoring_buffer_half_full_interrupt>())); \
	log("  Vecs_wait_on_semaphore:                            ", Hex(read<reg::Vecs_wait_on_semaphore>()));    \
	log("  Vecs_ctx_switch_interrupt:                         ", Hex(read<reg::Vecs_ctx_switch_interrupt>())); \
	log("  Vecs_mi_flush_dw_notify:                           ", Hex(read<reg::Vecs_mi_flush_dw_notify>()));   \
	log("  Vecs_error_interrupt:                              ", Hex(read<reg::Vecs_error_interrupt>()));      \
	log("  Vecs_mi_user_interrupt:                            ", Hex(read<reg::Vecs_mi_user_interrupt>()));    \

	DUMP_GT_3(GT_3_INTERRUPT_ISR);
	DUMP_GT_3(GT_3_INTERRUPT_IIR);
	DUMP_GT_3(GT_3_INTERRUPT_IER);
	DUMP_GT_3(GT_3_INTERRUPT_IMR);
#undef DUMP_GT_3
#endif
}


void Igd::Mmio::fault_dump()
{
	Genode::log("FAULT_TLB_RB_DATA0: ", Genode::Hex(read<FAULT_TLB_RB_DATA0>()));
	Genode::log("FAULT_TLB_RB_DATA1: ", Genode::Hex(read<FAULT_TLB_RB_DATA1>()));

	using namespace Genode;

	uint64_t const addr = ((uint64_t)(read<FAULT_TLB_RB_DATA1::Fault_cycle_va>() & 0xf) << 44) |
                          ((uint64_t) read<FAULT_TLB_RB_DATA0>() << 12);
	Genode::log("  ggtt: ", read<FAULT_TLB_RB_DATA1::Cycle_gtt_sel>(), " "
	            "addr: ", Genode::Hex(addr));
}


void Igd::Mmio::execlist_status_dump()
{
	using namespace Genode;

#define DUMP_EXECLIST_STATUS(reg) \
	log(#reg); \
	log("  Current_context_id: ", Hex(read<reg::Current_context_id>())); \
	log("  Arbitration_enable: ", Hex(read<reg::Arbitration_enable>())); \
	log("  Current_active_element_status: ", Hex(read<reg::Current_active_element_status>())); \
	log("  Last_context_switch_reason: ", Hex(read<reg::Last_context_switch_reason>())); \
	if (read<reg::Last_context_switch_reason>()) { \
		Igd::Context_status_qword::access_t v = read<reg::Last_context_switch_reason>(); \
		log("   Wait_on_scanline:  ", Igd::Context_status_qword::Wait_on_scanline::get(v)); \
		log("   Wait_on_semaphore: ", Igd::Context_status_qword::Wait_on_semaphore::get(v)); \
		log("   Wait_on_v_blank:   ", Igd::Context_status_qword::Wait_on_v_blank::get(v)); \
		log("   Wait_on_sync_flip: ", Igd::Context_status_qword::Wait_on_sync_flip::get(v)); \
		log("   Context_complete:  ", Igd::Context_status_qword::Context_complete::get(v)); \
		log("   Active_to_idle:    ", Igd::Context_status_qword::Active_to_idle::get(v)); \
		log("   Element_switch:    ", Igd::Context_status_qword::Element_switch::get(v)); \
		log("   Preempted:         ", Igd::Context_status_qword::Preempted::get(v)); \
		log("   Idle_to_active:    ", Igd::Context_status_qword::Idle_to_active::get(v)); \
	} \
	log("  Execlist_0_valid: ", Hex(read<reg::Execlist_0_valid>())); \
	log("  Execlist_1_valid: ", Hex(read<reg::Execlist_1_valid>())); \
	log("  Execlist_queue_full: ", Hex(read<reg::Execlist_queue_full>())); \
	log("  Execlist_write_pointer: ", Hex(read<reg::Execlist_write_pointer>())); \
	log("  Current_execlist_pointer: ", Hex(read<reg::Current_execlist_pointer>()));

	DUMP_EXECLIST_STATUS(EXECLIST_STATUS_RSCUNIT);

#undef DUMP_EXECLIST_STATUS
}


void Igd::Mmio::context_status_pointer_dump()
{
	using namespace Genode;

	uint32_t const v  = read<RCS_RING_CONTEXT_STATUS_PTR>();
	uint32_t const wp = read<RCS_RING_CONTEXT_STATUS_PTR::Write_pointer>();
	uint32_t const rp = read<RCS_RING_CONTEXT_STATUS_PTR::Read_pointer>();

	log("RCS_RING_CONTEXT_STATUS_PTR: ", Hex(v, Hex::PREFIX, Hex::PAD));
	log("  Read pointer:   ", Hex(rp));
	log("  Write pointer:  ", Hex(wp));

	if (wp == 0x7) {
		warning("RCS seems to be idle");
		return;
	}

	uint32_t r = rp;
	uint32_t w = wp;
	if (r > w) { w += CTXT_ST_BUF_NUM; }
	while (r < w) {
		uint32_t const i = ++r % CTXT_ST_BUF_NUM;

		uint64_t const cs  = read<CTXT_ST_BUF_RCSUNIT>(i);
		uint32_t const csu = read<CTXT_ST_BUF_RCSUNIT::Context_status_udw>(i);
		uint32_t const csl = read<CTXT_ST_BUF_RCSUNIT::Context_status_ldw>(i);
		log(i, "  Context_status:     ", Hex(cs));

		Igd::Context_status_qword::access_t const v = csl;
		log(i, "    Context_complete:  ", Igd::Context_status_qword::Context_complete::get(v));
		log(i, "    Active_to_idle:    ", Igd::Context_status_qword::Active_to_idle::get(v));
		log(i, "    Element_switch:    ", Igd::Context_status_qword::Element_switch::get(v));
		log(i, "    Preempted:         ", Igd::Context_status_qword::Preempted::get(v));
		log(i, "    Idle_to_active:    ", Igd::Context_status_qword::Idle_to_active::get(v));

		log(i, "  Context_status_udw: ", Hex(csu));
		log(i, "  Context_status_ldw: ", Hex(csl));
	}
}
