#ifndef HPPA_H
#define HPPA_H
/* this file is included by x86.h */

/* version number of this SeaBIOS firmware */
#define SEABIOS_HPPA_VERSION 20
#define SEABIOS_HPPA_VERSION_STR  "20"

#ifdef __LP64__
#define CONFIG_64BIT
#endif

#include "parisc/hppa_hardware.h"

#ifndef __ASSEMBLY__

#include "types.h" // u32
#include "byteorder.h" // le16_to_cpu

/* Pointer to zero-page of PA-RISC */
#define PAGE0 ((struct zeropage *) 0UL)

#define   PSW_I   0x00000001

static inline unsigned long arch_local_save_flags(void)
{
	unsigned long flags;
	asm volatile("ssm 0, %0" : "=r" (flags) : : "memory");
	return flags;
}

static inline void arch_local_irq_disable(void)
{
	asm volatile("rsm %0,%%r0\n" : : "i" (PSW_I) : "memory");
}

static inline void arch_local_irq_enable(void)
{
	asm volatile("ssm %0,%%r0\n" : : "i" (PSW_I) : "memory");
}

static inline unsigned long arch_local_irq_save(void)
{
	unsigned long flags;
	asm volatile("rsm %1,%0" : "=r" (flags) : "i" (PSW_I) : "memory");
	return flags;
}

static inline void arch_local_irq_restore(unsigned long flags)
{
	asm volatile("mtsm %0" : : "r" (flags) : "memory");
}

static inline void irq_disable(void)
{
   arch_local_irq_disable();
}

static inline void irq_enable(void)
{
    arch_local_irq_enable();
}

static inline u32 save_flags(void)
{
    return arch_local_irq_save();
}

static inline void restore_flags(u32 flags)
{
    arch_local_irq_restore(flags);
}



static inline void cpu_relax(void)
{
    asm volatile("nop": : :"memory");
}

static inline void nop(void)
{
    asm volatile("nop");
}

extern void hlt(void);

static inline void wbinvd(void)
{
    asm volatile("sync": : :"memory");
}

#define mfctl(reg)	({		\
	unsigned long cr;		\
	__asm__ __volatile__(		\
		"mfctl %1,%0" : 	\
		 "=r" (cr) : "i" (reg)	\
	);				\
	cr;				\
})

#define mtctl(gr, cr) \
	__asm__ __volatile__("mtctl %0,%1" \
		: /* no outputs */ \
		: "r" (gr), "i" (cr) : "memory")

/* these are here to de-mystefy the calling code, and to provide hooks */
/* which I needed for debugging EIEM problems -PB */
#define get_eiem() mfctl(15)
static inline void set_eiem(unsigned long val)
{
	mtctl(val, 15);
}

#define mfsp(reg)	({		\
	unsigned long cr;		\
	__asm__ __volatile__(		\
		"mfsp " #reg ",%0" :	\
		 "=r" (cr)		\
	);				\
	cr;				\
})

#define mtsp(val, cr) \
	{ if (__builtin_constant_p(val) && ((val) == 0)) \
	 __asm__ __volatile__("mtsp %%r0,%0" : : "i" (cr) : "memory"); \
	else \
	 __asm__ __volatile__("mtsp %0,%1" \
		: /* no outputs */ \
		: "r" (val), "i" (cr) : "memory"); }

static inline unsigned long rdtscll(void)
{
    return mfctl(16);
}

static inline u32 __ffs(u32 x)
{
	unsigned long ret;

	if (!x)
		return 0;

	__asm__(
#ifdef CONFIG_64BIT
		" ldi       63,%1\n"
		" extrd,u,*<>  %0,63,32,%%r0\n"
		" extrd,u,*TR  %0,31,32,%0\n"	/* move top 32-bits down */
		" addi    -32,%1,%1\n"
#else
		" ldi       31,%1\n"
#endif
		" extru,<>  %0,31,16,%%r0\n"
		" extru,TR  %0,15,16,%0\n"	/* xxxx0000 -> 0000xxxx */
		" addi    -16,%1,%1\n"
		" extru,<>  %0,31,8,%%r0\n"
		" extru,TR  %0,23,8,%0\n"	/* 0000xx00 -> 000000xx */
		" addi    -8,%1,%1\n"
		" extru,<>  %0,31,4,%%r0\n"
		" extru,TR  %0,27,4,%0\n"	/* 000000x0 -> 0000000x */
		" addi    -4,%1,%1\n"
		" extru,<>  %0,31,2,%%r0\n"
		" extru,TR  %0,29,2,%0\n"	/* 0000000y, 1100b -> 0011b */
		" addi    -2,%1,%1\n"
		" extru,=  %0,31,1,%%r0\n"	/* check last bit */
		" addi    -1,%1,%1\n"
			: "+r" (x), "=r" (ret) );
	return ret;
}

static inline u32 __fls(u32 x)
{
	int ret;
	if (!x)
		return 0;

	__asm__(
	"	ldi		1,%1\n"
	"	extru,<>	%0,15,16,%%r0\n"
	"	zdep,TR		%0,15,16,%0\n"		/* xxxx0000 */
	"	addi		16,%1,%1\n"
	"	extru,<>	%0,7,8,%%r0\n"
	"	zdep,TR		%0,23,24,%0\n"		/* xx000000 */
	"	addi		8,%1,%1\n"
	"	extru,<>	%0,3,4,%%r0\n"
	"	zdep,TR		%0,27,28,%0\n"		/* x0000000 */
	"	addi		4,%1,%1\n"
	"	extru,<>	%0,1,2,%%r0\n"
	"	zdep,TR		%0,29,30,%0\n"		/* y0000000 (y&3 = 0) */
	"	addi		2,%1,%1\n"
	"	extru,=		%0,0,1,%%r0\n"
	"	addi		1,%1,%1\n"		/* if y & 8, add 1 */
		: "+r" (x), "=r" (ret) );

	return ret;
}

static inline u32 rol(u32 val, u16 rol) {
    u32 res, resr;
    res  = val << rol;
    resr = val >> (32-rol);
    res |= resr;
    return res;
}

static inline u32 ror(u32 word, unsigned int shift)
{
        return (word >> (shift & 31)) | (word << ((-shift) & 31));
}


/* F_EXTEND() is used to access I/O ports. ROM_EXTEND() to access firmware ROM/RAM */
#if defined(__LP64__)
#define F_EXTEND(x)   ((unsigned long)(0xfffffffful<<32) | (x))
#define ROM_EXTEND(x) ((unsigned long)(0xfffffff0ul<<32) | (x))
#else
#define F_EXTEND(x)   ((unsigned long)(x))
#define ROM_EXTEND(x) ((unsigned long)(x))
#endif

static inline void builtin_console_out(char c)
{
    asm volatile("copy %0,%%r26 ! diag 0x101" : : "r" (c) : "r26");
}


extern char has_astro; /* false for B160L machine with Dino PCI chip */
extern unsigned long hppa_port_pci_cmd;
extern unsigned long hppa_port_pci_data;


#define pci_ioport_addr(port) ((port >= 0x1000)  && (port < FIRMWARE_START))
#define is_astro_ioport(port) (has_astro && (port < IOS_DIST_BASE_SIZE))

#define astro_ioport_addr(port) ((void *)(portaddr_t) F_EXTEND(IOS_DIST_BASE_ADDR + port))

/*  inb()/outb() accesses little-endian memory and does byteswapping to host endianess */

static inline void outl(u32 value, portaddr_t port) {
    if (is_astro_ioport(port))
        *(volatile u32 *)(astro_ioport_addr(port)) = cpu_to_le32(value);
    else
    if (!pci_ioport_addr(port)) {
        *(volatile u32 *)(port) = be32_to_cpu(value);
    } else {
	/* write PCI I/O address to Dino's PCI_CONFIG_ADDR */
	*(volatile u32 *)(DINO_HPA + 0x064) = port;
	/* write value to PCI_IO_DATA */
	*(volatile u32 *)(DINO_HPA + 0x06c) = cpu_to_le32(value);
    }
}

static inline void outw(u16 value, portaddr_t port) {
    if (is_astro_ioport(port))
        *(volatile u16 *)(astro_ioport_addr(port)) = cpu_to_le16(value);
    else
    if (!pci_ioport_addr(port)) {
        *(volatile u16 *)(port) = be16_to_cpu(value);
    } else {
	/* write PCI I/O address to Dino's PCI_CONFIG_ADDR */
	*(volatile u32 *)(DINO_HPA + 0x064) = port;
	/* write value to PCI_IO_DATA */
	*(volatile u16 *)(DINO_HPA + 0x06c) = cpu_to_le16(value);
    }
}

static inline void outb(u8 value, portaddr_t port) {
    if (is_astro_ioport(port))
        *(volatile u8 *)(astro_ioport_addr(port)) = value;
    else
    if (!pci_ioport_addr(port)) {
	*(volatile u8 *)(port) = value;
    } else {
	/* write PCI I/O address to Dino's PCI_CONFIG_ADDR */
	*(volatile u32 *)(DINO_HPA + 0x064) = port & ~3U;
	/* write value to PCI_IO_DATA */
	*(volatile u8 *)(DINO_HPA + 0x06c + (port & 3)) = value;
    }
}

static inline u8 inb(portaddr_t port) {
    if (is_astro_ioport(port))
        return *(volatile u8 *)(astro_ioport_addr(port));
    else
    if (has_astro || !pci_ioport_addr(port)) {
        return *(volatile u8 *)(port);
    } else {
	/* write PCI I/O address to Dino's PCI_CONFIG_ADDR */
	*(volatile u32 *)(DINO_HPA + 0x064) = port & ~3U;
	/* read value to PCI_IO_DATA */
	return *(volatile u8 *)(DINO_HPA + 0x06c + (port & 3));
    }
}

static inline u16 inw(portaddr_t port) {
    if (is_astro_ioport(port))
        return le16_to_cpu(*(volatile u16 *)(astro_ioport_addr(port)));
    else
    if (!pci_ioport_addr(port)) {
        return *(volatile u16 *)(port);
    } else {
	/* write PCI I/O address to Dino's PCI_CONFIG_ADDR */
	*(volatile u32 *)(DINO_HPA + 0x064) = port & ~3U;
	/* read value to PCI_IO_DATA */
	return le16_to_cpu(*(volatile u16 *)(DINO_HPA + 0x06c + (port & 3U)));
    }
}
static inline u32 inl(portaddr_t port) {
    if (is_astro_ioport(port))
        return le32_to_cpu(*(volatile u32 *)(astro_ioport_addr(port)));
    else
    if (!pci_ioport_addr(port)) {
        return *(volatile u32 *)(port);
    } else {
	/* write PCI I/O address to Dino's PCI_CONFIG_ADDR */
	*(volatile u32 *)(DINO_HPA + 0x064) = port;
	/* read value to PCI_IO_DATA */
	return le32_to_cpu(*(volatile u32 *)(DINO_HPA + 0x06c));
    }
}

static inline void insb(portaddr_t port, u8 *data, u32 count) {
    while (count--)
	*data++ = inb(port);
}
static inline void insw(portaddr_t port, u16 *data, u32 count) {
    while (count--)
        *data++ = inw(port);
}
static inline void insl(portaddr_t port, u32 *data, u32 count) {
    while (count--)
        *data++ = inl(port);
}
// XXX - outs not limited to es segment
static inline void outsb(portaddr_t port, u8 *data, u32 count) {
    while (count--)
	outb(*data++, port);
}
static inline void outsw(portaddr_t port, u16 *data, u32 count) {
    while (count--) {
        outw(*data, port);
	data++;
    }
}
static inline void outsl(portaddr_t port, u32 *data, u32 count) {
    while (count--) {
        outl(*data, port);
	data++;
    }
}

/* Compiler barrier is enough as an x86 CPU does not reorder reads or writes */
static inline void smp_rmb(void) {
    barrier();
}
static inline void smp_wmb(void) {
    barrier();
}

/*  readX()/writeX() do byteswapping */

static inline void writel(void *addr, u32 val) {
    barrier();
    *(volatile u32 *)addr = cpu_to_le32(val);
}
static inline void writew(void *addr, u16 val) {
    barrier();
    *(volatile u16 *)addr = cpu_to_le16(val);
}
static inline void writeb(void *addr, u8 val) {
    barrier();
    *(volatile u8 *)addr = val;
}
static inline u64 readq(const void *addr) {
    u64 val = *(volatile const u64 *)addr;
    barrier();
    return le64_to_cpu(val);
}
static inline u32 readl(const void *addr) {
    u32 val = *(volatile const u32 *)addr;
    barrier();
    return le32_to_cpu(val);
}
static inline u16 readw(const void *addr) {
    u16 val = *(volatile const u16 *)addr;
    barrier();
    return le16_to_cpu(val);
}
static inline u8 readb(const void *addr) {
    u8 val = *(volatile const u8 *)addr;
    barrier();
    return val;
}

/*  gsc_readX()/gsc_writeX() do no byteswapping */

static inline void gsc_writel(void *addr, u32 val) {
    barrier();
    *(volatile u32 *)addr = val;
}
static inline void gsc_writeb(void *addr, u8 val) {
    barrier();
    *(volatile u8 *)addr = val;
}
static inline u32 gsc_readl(const void *addr) {
    u32 val = *(volatile const u32 *)addr;
    barrier();
    return val;
}
static inline u8 gsc_readb(const void *addr) {
    u8 val = *(volatile const u8 *)addr;
    barrier();
    return val;
}

// FLASH_FLOPPY not supported
#define GDT_CODE     (0)
#define GDT_DATA     (0)
#define GDT_B        (0)
#define GDT_G        (0)
#define GDT_BASE(v)  ((v) & 0)
#define GDT_LIMIT(v) ((v) & 0)
#define GDT_GRANLIMIT(v) ((v) & 0)

static inline u8 get_a20(void) {
    return 0;
}

static inline u8 set_a20(u8 cond) {
    return 0;
}

static inline void wrmsr(u32 index, u64 val)
{
}

// x86.c
void cpuid(u32 index, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx);

// pci.c
unsigned long elroy_offset(u16 bdf);
void *elroy_port(unsigned long port, unsigned long offs);
// pciinit.c
unsigned long add_lmmio_directed_range(unsigned long size, int rope);

#endif // !__ASSEMBLY__
#endif
