#include <err.h>
#include <sys/types.h>
#include <debug.h>
#include <reg.h>
#include <platform/osk5912/omap5912.h>
#include <arch/ops.h>
#include <kernel/Scheduler.h>
#include <kernel/Thread.h>
#include <kernel/AutoSpinlock.h>
#include <dev/DeviceManager.h>
#include <dev/RootDevice.h>
#include <dev/IntController.h>
#include <os/Thread.h>

using namespace OS;
using namespace OS::Kernel;

namespace OS {
namespace Platform {

struct iframe {
	uint32_t spsr;
	uint32_t r0;
	uint32_t r1;
	uint32_t r2;
	uint32_t r3;
	uint32_t r12;
	uint32_t lr;
	uint32_t pc;
};

static const uint32_t icBase[5] = {
	INTCON0_BASE, INTCON1_BASE, INTCON2_BASE, INTCON3_BASE, INTCON4_BASE };

inline volatile uint32_t *ICReg(uint controller, uint reg)
{
	return (volatile uint32_t *)(icBase[controller] + reg);
}

inline uint32_t readICReg(uint controller, uint reg)
{
	return *ICReg(controller, reg);
}
inline void writeICReg(uint controller, uint reg, uint val)
{
	*ICReg(controller, reg) = val;
}

inline uint vectorToController(uint vector)
{
	return vector / 32;
}

class PlatformIC : public Dev::IntController {
public:
	PlatformIC(const char *name);
	virtual ~PlatformIC();

	virtual status_t MaskInterrupt(unsigned int vector, bool *oldstate);
	virtual status_t UnmaskInterrupt(unsigned int vector, bool *oldstate);

	virtual unsigned int VectorCount() { return INT_VECTORS; }

	/* called by the platform_* handlers */
	void IRQ(struct iframe *);
	void FIQ(struct iframe *);

private:
	Spinlock mLock;
};
static PlatformIC *sPIC;

PlatformIC::PlatformIC(const char *name)
	: Dev::IntController(name)
{
	// allocate the handler table
	AllocHandlerTable();

	// mask all interrupts
	*ICReg(0, INTCON_MIR) = 0xffffffff;
	*ICReg(1, INTCON_MIR) = 0xffffffff;
	*ICReg(2, INTCON_MIR) = 0xffffffff;
	*ICReg(3, INTCON_MIR) = 0xffffffff;
	*ICReg(4, INTCON_MIR) = 0xffffffff;

	// clear any pending interrupts
	*ICReg(0, INTCON_ITR) = 0;
	*ICReg(1, INTCON_ITR) = 0;
	*ICReg(2, INTCON_ITR) = 0;
	*ICReg(3, INTCON_ITR) = 0;
	*ICReg(4, INTCON_ITR) = 0;
}

PlatformIC::~PlatformIC()
{
	panic("should not delete PlatformIC\n");
}

status_t PlatformIC::MaskInterrupt(unsigned int vector, bool *oldstate)
{
	if (vector >= INT_VECTORS)
		return ERR_INVALID_ARGS;

//	dprintf("%s: vector %d\n", __PRETTY_FUNCTION__, vector);

	AutoSpinlock alock(mLock);

	if (oldstate)
		*oldstate = false;

	volatile uint32_t *mir = ICReg(vectorToController(vector), INTCON_MIR);
	*mir = *mir | (1<<(vector % 32));

	return NO_ERROR;
}

status_t PlatformIC::UnmaskInterrupt(unsigned int vector, bool *oldstate)
{
	if (vector >= INT_VECTORS)
		return ERR_INVALID_ARGS;

//	dprintf("%s: vector %d\n", __PRETTY_FUNCTION__, vector);

	AutoSpinlock alock(mLock);

	if (oldstate)
		*oldstate = false;
	
	volatile uint32_t *mir = ICReg(vectorToController(vector), INTCON_MIR);
	*mir = *mir & ~(1<<(vector % 32));

	return NO_ERROR;
}

void PlatformIC::IRQ(struct iframe *frame)
{
	IRQPreemptRef irqref; // forcfully increments the preemption count by one and holds until it goes out of scope

	dprintf("platform_irq: spsr 0x%x, pc 0x%x, currthread %p\n", frame->spsr, frame->pc, Thread::GetCurrent());
//	Thread::GetCurrent()->Dump();

	SpinlockRef alock(mLock);

	PANIC_UNIMPLEMENTED;

#if 0

	// nothing pending
	uint32_t current_bit = *REG32(PIC_CURRENT_BIT);
	if (current_bit == 0)
		return;

	// get the current vector
	int vector = *REG32(PIC_CURRENT_NUM);

	// mask it out
//	*REG32(PIC_MASK_LATCH) = current_bit;

	// clear it, let another interrupt latch
	*REG32(PIC_CURRENT_BIT) = current_bit;

	alock.Release();

	// deliver the interrupt
	enum handlerReturn ret = DispatchInterrupt(vector);
	if (ret != INT_NO_RESCHEDULE)
		Scheduler::Preempt();
#endif

//	dprintf("platform_irq: exit\n");
}

void PlatformIC::FIQ(struct iframe *)
{
	panic("unhandled fiq");
}

extern "C" 
void platform_irq(struct iframe *frame)
{
	sPIC->IRQ(frame);
}

extern "C"
void platform_fiq(struct iframe *frame)
{
	sPIC->FIQ(frame);
}

void InitPlatformPIC()
{
	/* create the interrupt controller and add it to the device list */
	PlatformIC *pic = new PlatformIC("pic0");
	Dev::Root()->AddChild(pic);
	Dev::SetPIC(pic);
	sPIC = pic;

#if 0
	arch_enable_ints();
	pic->UnmaskInterrupt(0, NULL);
	*ICReg(0, INTCON_ILR_BASE) = 0;
	*ICReg(0, INTCON_SISR) = 1;

	dprintf("MIR 0x%x\n", *ICReg(0, INTCON_MIR));
	dprintf("ITR 0x%x\n", *ICReg(0, INTCON_ITR));

	for(;;);
#endif
}

}} // namespace OS::Platform
