;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;; Copyright (C) 2010 KolibriOS team.     All rights reserved.  ;;
;; Distributed under terms of the GNU General Public License    ;;
;;                                                              ;;
;;  HT.inc                                                      ;;                                                    ;;
;;                                                              ;;
;;  AMD HyperTransport bus control                              ;;
;;                                                              ;;
;;                  art_zh  <kolibri@jerdev.co.uk>              ;;
;;                                                              ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

$Revision: 1554 $

NB_MISC_INDEX	equ	0xF0000060	; NB Misc indirect access
NB_MISC_DATA	equ	0xF0000064
PCIEIND_INDEX	equ	0xF00000E0	; PCIe Core indirect config space access
HTIU_NB_INDEX	equ	0xF0000094	; HyperTransport indirect config space access

;=============================================================================
;
; This code is a part of Kolibri-A and will only work with AMD RS760+ chipsets
;
;=============================================================================

org $-OS_BASE	; physical addresses needed at initial stage

align 4

;------------------------------------------
;       params:   al = nbconfig register#
;       returns: eax = register content
;
rs7xx_nbconfig_read_pci:
	and	eax, 0x0FC		 ; leave register# only
	or	eax, 0x80000000 	 ; bdf = 0:0.0
	mov	dx,  0x0CF8		 ; write to index reg
	out	dx, eax
	add	dl, 4
	in	eax, dx
	ret
align 4

rs7xx_nbconfig_flush_pci:
	mov	eax, 0x0B0		; a scratch reg
	mov	dx,  0xCF8
	out	dx,  eax
	ret

align 4

;------------------------------------------
;       params:   al = nbconfig register#
;                ebx = register content
;
rs7xx_nbconfig_write_pci:
	and	eax, 0x0FC		 ; leave register# only
	or	eax, 0x80000000 	 ; bdf = 0:0.0
	mov	dx,  0x0CF8		 ; write to index reg
	out	dx, eax
	add	dl, 4
	mov	eax, ebx
	out	dx, eax
	ret

;***************************************************************************
;   Function
;      rs7xx_unlock_bar3:     unlocks the BAR3 register of nbconfig that
;                             makes pcie config address space visible
;   -----------------------
;   in: nothing      out: nothing      destroys:   eax ebx edx
;
;***************************************************************************
align 4
rs7xx_unlock_bar3:
	mov	eax, NB_MISC_INDEX
	mov	ebx, 0x080			; NBMISCIND:0x0; write-enable
	call	rs7xx_nbconfig_write_pci	; set index
	mov	eax, NB_MISC_DATA
	call	rs7xx_nbconfig_read_pci 	; read data
	mov	ebx, eax
	and	ebx, 0xFFFFFFF7 		; clear bit3
	mov	eax, NB_MISC_DATA
	call	rs7xx_nbconfig_write_pci	; write it back
	mov	eax, NB_MISC_INDEX
	xor	ebx, ebx			; reg#0; write-locked
	call	rs7xx_nbconfig_write_pci	; set index
	ret



;***************************************************************************
;   Function
;      fusion_pcie_init:
;
;   Description
;       PCIe extended  config space detection and mapping
;
;***************************************************************************

align 4



; ---- stepping 10h CPUs and Fusion APUs: the configspace is stored in MSR_C001_0058 ----
align 4
fusion_pcie_init:
	mov	ecx, 0xC0010058
	rdmsr
	or	edx, edx
	jnz	$				; PCIe is in the upper memory. Stop.
	xchg	dl, al
	mov	dword[mmio_pcie_cfg_addr-OS_BASE], eax	; store the physical address
	mov	ecx, edx
	and	dl, 1
	jz	$				; bit[0] = 1 means no PCIe mapping allowed. Stop.
	shr	cl, 2				; ecx = log2(number of buses)
	mov	word[PCIe_bus_range-OS_BASE], cx
	sub	cl, 2
	jae	@f
	xor	cl, cl
@@:
	shl	edx, cl 			; edx = number of 4M pages to map
	mov	word[mmio_pcie_cfg_pdes-OS_BASE], dx
	shl	edx, 22
	dec	edx
	add	edx, eax			; the upper configspace limit
	mov	dword[mmio_pcie_cfg_lim-OS_BASE], edx

; ---- large pages mapping  ----
; (eax = phys. address of PCIe conf.space)
;
.map_pcie_pages:
	or	eax, (PG_NOCACHE + PG_SHARED + PG_LARGE + PG_UW)  ;  UW is unsafe!
	mov	ecx, PCIe_CONFIG_SPACE			; linear address
	mov	ebx, ecx
	shr	ebx, 20
	add	ebx, (sys_pgdir - OS_BASE)		; PgDir entry @
	mov	dl, byte[mmio_pcie_cfg_pdes-OS_BASE]	; 1 page = 4M in address space
	cmp	dl, 0x34	; =(USER_DMA_BUFFER - PCIe_CONFIG_SPACE) / 4M
	jb	@f
	mov	dl, 0x33
	mov	byte[mmio_pcie_cfg_pdes-OS_BASE], dl
@@:
	xor	dx,  dx 			; PDEs counter
.write_pde:
	mov	dword[ebx], eax 		; map 4 buses
	add	bx,  4				; new PDE
	add	eax, 0x400000			; +4M phys.
	add	ecx, 0x400000			; +4M lin.
	cmp	dl, byte[mmio_pcie_cfg_pdes-OS_BASE]
	jae	.pcie_cfg_mapped
	inc	dl
	jmp	.write_pde

.pcie_cfg_mapped:

create_mmio_pte:
	mov	ecx, mmio_pte				; physical address
	or	ecx, (PG_NOCACHE + PG_SHARED + PG_SW)
	mov	ebx, FUSION_MMIO			; linear address
	shr	ebx, 20
	add	ebx, (sys_pgdir - OS_BASE)		; PgDir entry @
	mov	dword[ebx], ecx 			; Fusion MMIO tables

; ---- short page mapping  ----
.map_apic_mmio:
	mov	ecx, 0x01B	; APIC BAR
	rdmsr
	and	eax, 0xFFFFF000 			; physical address
	or	eax, (PG_NOCACHE + PG_SHARED + PG_SW)
	mov	dword[mmio_pte + 0], eax

	ret	; <<< OK >>>

; ================================================================================

org OS_BASE+$	; back to the linear address space

;--------------------------------------------------------------
align 4
rs780_read_misc:
;  in: eax(al) - reg#           out: eax = NBMISCIND data
	push	edx
	mov	edx, NB_MISC_INDEX
	and	eax, 0x07F
	mov	[edx], eax
	add	dl, 4
	mov	eax, [edx]
	pop	edx
	ret

;-------------------------------------------
align 4
rs780_write_misc:
;  in: eax(al) - reg#     ebx = NBMISCIND data
	push	edx
	mov	edx, NB_MISC_INDEX
	and	eax, 0x07F
	or	eax, 0x080		; set WE
	mov	[edx], eax
	add	dl, 4
	mov	[edx], ebx
	sub	dl, 4
	xor	eax,   eax
	mov	[edx], eax		; safety last
	pop	edx
	ret

;-------------------------------------------------------------
align 4
rs780_read_pcieind:
;  in: ah = bridge#, al = reg#           out: eax = PCIEIND data
	push	edx
	xor	edx, edx
	mov	ah,  dl 		; bridge# :     0 = Core+GFX;   0x10 = Core+SB
	and	dl,  15 		;            0x20 = Core+GPP;  2..12 = a PortBridge
	shl	edx, 15 		; device#
	add	edx, PCIEIND_INDEX	; full bdf-address
	and	eax, 0x30FF
	or	al,  al
	jnz	@f
	shl	eax, 4			; set bits 17..16 for a Core bridge
@@:
	mov	[edx], eax
	add	dl,  4
	mov	eax, [edx]
	pop	edx
	ret

;-------------------------------------------
align 4
rs780_write_pcieind:
;  in: ah = bridge#, al = reg#,  ebx = PCIEIND data
	push	edx
	xor	edx, edx
	mov	ah,  dl 		; bridge# :     0 = Core+GFX;   0x10 = Core+SB
	and	dl,  15 		;            0x20 = Core+GPP;  2..12 = a PortBridge
	shl	edx, 15 		; device#
	add	edx, PCIEIND_INDEX	; full bdf-address
	and	eax, 0x30FF
	or	al,  al
	jnz	@f
	shl	eax, 4			; set bits 17..16 for a Core bridge
@@:
	mov	[edx], eax
	add	dl,  4
	mov	[edx], ebx
	sub	dl,  4
	xor	eax,   eax
	mov	[edx], eax		; safety last
	pop	edx
	ret

;------------------------------------------------
align 4
rs780_read_htiu:
; in:  al = reg#  |  out: eax = HTIU data
;------------------------------------------------
	push	edx
	mov	edx,  HTIU_NB_INDEX
	and	eax, 0x07F
	mov	[edx], eax
	add	dl,  4
	mov	eax, [edx]
	pop	edx
	ret
;------------------------------------------------
align 4
rs780_write_htiu:
; in:  al = reg#; ebx = data
;------------------------------------------------
	push	edx
	mov	edx,  HTIU_NB_INDEX
	and	eax, 0x07F
	or	eax, 0x100
	mov	[edx], eax
	add	dl,  4
	mov	[edx], ebx
	sub	dl,  4
	xor	eax,   eax
	mov	[edx], eax
	pop	edx
	ret

;------------------------------------------------
align 4
sys_rdmsr:
;  in: [esp+8] = MSR#
; out: [esp+8] = MSR[63:32]
;        [eax] = MSR[31: 0]
;------------------------------------------------
	push	ecx edx
	mov	ecx, [esp+16]
	rdmsr
	mov	[esp+16], edx
	pop	edx ecx
	ret

;------------------------------------------------
uglobal

align 4
diff16 "apic_data : ", 0, $
apic_data:

    .counter dd ?
    .ticks   dd ?
    .t_freq  dd ?
endg

apic_timer_reset:
	mov	eax, [pll_frequency.osc]
	shr	eax, 1					; default prescaler - fix it !!
	mov	[apic_data.t_freq], eax
	shr	eax, 4					; 16 per second
	mov	[apic_data.ticks], eax

	mov	ebx, LAPIC_BAR+ 0x320
	mov	edx, [ebx]
	and	edx, 0xFFFEFF00
	or	edx, 0x0002003F 			; int vector + restart
	mov	[ebx], edx
	mov	dword [LAPIC_BAR + 0x380], eax		; load APICTIC
	ret


apic_timer_int:
	push	eax
	inc	dword [apic_data.counter]
;        mov     eax,  [apic_data.ticks]
;        mov     dword [LAPIC_BAR + 0x380], eax          ; reload APICTIC
	mov	dword [LAPIC_BAR + 0x0B0], 0		; end of interrupt
;        mov     dword [LAPIC_BAR + 0x420], 0x3F            ; end of interrupt
	pop	eax
	iretd