/*
 * \brief  Programmable interrupt controller for core
 * \author Stefan Kalkowski
 * \date   2012-10-24
 */

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

#ifndef _IMX53__PIC_H_
#define _IMX53__PIC_H_

/* Genode includes */
#include <util/mmio.h>

/* core includes */
#include <board.h>

namespace Imx53
{
	using namespace Genode;

	/**
	 * Programmable interrupt controller for core
	 */
	class Pic : public Mmio
	{
		public:

			enum { MAX_INTERRUPT_ID = 108 };

		protected:

			/**
			 * Interrupt control register
			 */
			struct Intctrl : Register<0, 32>
			{
				struct Enable    : Bitfield<0,1>  { };
				struct Nsen      : Bitfield<16,1> { };
				struct Nsen_mask : Bitfield<31,1> { };
			};

			struct Priomask : Register<0xc, 32>
			{
				struct Mask : Bitfield<0,8> { };
			};

			struct Syncctrl : Register<0x10, 32>
			{
				struct Syncmode : Bitfield<0,2> { };
			};

			struct Dsmint : Register<0x14, 32>
			{
				struct Dsm : Bitfield<0,1> { };
			};

			/**
			 * Interrupt security registers
			 */
			struct Intsec : Register_array<0x80, 32, MAX_INTERRUPT_ID+1, 1>
			{
				struct Nonsecure : Bitfield<0, 1> { };
			};

			/**
			 * Interrupt set enable registers
			 */
			struct Enset : Register_array<0x100, 32, MAX_INTERRUPT_ID+1, 1, true>
			{
				struct Set_enable : Bitfield<0, 1> { };
			};

			/**
			 * Interrupt clear enable registers
			 */
			struct Enclear : Register_array<0x180, 32, MAX_INTERRUPT_ID+1, 1, true>
			{
				struct Clear_enable : Bitfield<0, 1> { };
			};

			/**
			 * Interrupt priority level registers
			 */
			struct Icdipr  : Register_array<0x400, 32, MAX_INTERRUPT_ID+1, 8>
			{
				struct Priority : Bitfield<0, 8>
				{
					enum { GET_MIN_PRIORITY = 0xff };
				};
			};

			/**
			 * Pending registers
			 */
			struct Pndr  : Register_array<0xd00, 32, MAX_INTERRUPT_ID+1, 1>
			{
				struct Pending : Bitfield<0, 1> { };
			};

			/**
			 * Highest interrupt pending registers
			 */
			struct Hipndr  : Register_array<0xd80, 32, MAX_INTERRUPT_ID+1, 1, true>
			{
				struct Pending : Bitfield<0, 1> { };
			};

			/**
			 * Maximum supported interrupt priority
			 */
			unsigned _max_priority() { return 255; }

		public:

			/**
			 * Constructor, all interrupts get masked
			 */
			Pic() : Mmio(Board::TZIC_MMIO_BASE)
			{
				/* configure interrupts as nonsecure, and disable them */
				for (unsigned i = 0; i <= MAX_INTERRUPT_ID; i++) {
					write<Enclear::Clear_enable>(1, i);
					write<Intsec::Nonsecure>(1, i);
				}

				write<Priomask::Mask>(0x1f);
				write<Intctrl>(Intctrl::Enable::bits(1) |
				               Intctrl::Nsen::bits(1)   |
				               Intctrl::Nsen_mask::bits(1));
			}

			/**
			 * Receive a pending request number 'i'
			 */
			bool take_request(unsigned & i)
			{
				for (unsigned j = 0; j <= MAX_INTERRUPT_ID; j++) {
					if (read<Pndr::Pending>(j)) {
						i = j;
						return true;
					}
				}
				return false;
			}

			/**
			 * Finish the last taken request
			 */
			void finish_request() { }

			/**
			 * Validate request number 'i'
			 */
			bool valid(unsigned const i) const {
				return i <= MAX_INTERRUPT_ID; }

			/**
			 * Unmask all interrupts
			 */
			void unmask()
			{
				for (unsigned i=0; i <= MAX_INTERRUPT_ID; i++)
					write<Enset::Set_enable>(1, i);
			}

			/**
			 * Mask all interrupts
			 */
			void mask()
			{
				for (unsigned i=0; i <= MAX_INTERRUPT_ID; i++)
					write<Enclear::Clear_enable>(1, i);
			}

			/**
			 * Unmask interrupt 'i'
			 */
			void unmask(unsigned const i)
			{
				if (i <= MAX_INTERRUPT_ID)
					write<Enset::Set_enable>(1, i);
			}

			/**
			 * Mask interrupt 'i'
			 */
			void mask(unsigned const i)
			{
				if (i <= MAX_INTERRUPT_ID)
					write<Enclear::Clear_enable>(1, i);
			}
	};
}

namespace Kernel { class Pic : public Imx53::Pic { }; }

#endif /* _IMX53__PIC_H_ */
