/**************************************************************************//**
 * @file     libkpi.c
 * @version  V3.00
 * @brief    N9H20 series keypad driver source file
 *
 * SPDX-License-Identifier: Apache-2.0
 * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include "N9H20_GPIO.h"
#include "N9H20_KPI.h"

// latest key pressed recorded by ISR, might _not_ be the most updated status
static unsigned int _key;
// interrupt number for kpi
static unsigned char _int;
static unsigned char _opened = 0;

int key_map[KEY_COUNT] = {
        KEY_UP, KEY_LEFT, KEY_ESC, KEY_DOWN, KEY_RIGHT, KEY_ENTER
};

static unsigned int readkey(void)
{
	unsigned int i, read0, read1, new_key=0;
	gpio_readport(GPIO_PORTA, (unsigned short *)&read0);
	read0 &= 0x1C;
	if(read0 == 0x1C)
		return(0);
	gpio_setportval(GPIO_PORTA, (1 << 7), (1 << 7));
	gpio_readport(GPIO_PORTA, (unsigned short *)&read1);	
	read1 &= 0x1C;
	if(read1 == 0x1C)
		read1 = (read0 ^ read1)>>2;
	else
		read1 = ((~read0 & 0x1C)<<1);
	
	gpio_setportval(GPIO_PORTA, (1 << 7), 0);

	for(i=0; i<KEY_COUNT; i++)
	{
		if (read1 & (1 << i))
		{
			new_key |= key_map[i];
		}
	}	
	
	return new_key;
}

static void kpi_isr(void)
{	
	_key = readkey();
	//(*(unsigned int volatile *)REG_AIC_SCCR) = (1 << (_int + 2));
	gpio_cleartriggersrc(GPIO_PORTA);
	
	return;

}

void kpi_init(void)
{

	_opened = 1;
	
	// PORTA[2-4]
	gpio_setportdir(GPIO_PORTA, ((1 << 2) | (1 << 3) | (1 << 4)), 0);
	gpio_setportpull(GPIO_PORTA, ((1 << 2) | (1 << 3) | (1 << 4)), 0);	
	gpio_setintmode(GPIO_PORTA, ((1 << 2) | (1 << 3) | (1 << 4)), 
					((1 << 2) | (1 << 3) | (1 << 4)), ((1 << 2) | (1 << 3) | (1 << 4)));
        
	// PORTA[7]	
	gpio_setportval(GPIO_PORTA, (1 << 7), 0);
	gpio_setportpull(GPIO_PORTA, (1 << 7), 0);
	gpio_setportdir(GPIO_PORTA, (1 << 7), (1 << 7));

    return;    
}

int kpi_open(unsigned int src)
{
	if(_opened == 0)
		kpi_init();
	if(_opened != 1)
		return(-1);
	
	_opened = 2;
	if(src > 3)
		return(-1);
		
	_int = src;
	
	gpio_setsrcgrp(GPIO_PORTA, ((1 << 2) | (1 << 3) | (1 << 4)), src);

	gpio_setdebounce(128, 1 << src);
	gpio_setlatchtrigger(1 << src);

	sysInstallISR(IRQ_LEVEL_7, (INT_SOURCE_E)(src + 2), (PVOID)kpi_isr);	
		
	sysSetInterruptType((INT_SOURCE_E)(src + 2), HIGH_LEVEL_SENSITIVE);
	sysSetLocalInterrupt(ENABLE_IRQ);	

    return(0);    
}

void kpi_close(void)
{
	if(_opened != 2)
		return;
	_opened = 1;
	sysDisableInterrupt((INT_SOURCE_E)(_int + 2));  
	return;
}

#ifdef __GNUC__
__attribute__((optimize("O0")))
#endif
int kpi_read(unsigned char mode)
{
	// add this var in case key released right before return.
	int volatile k = 0;
	
	if(_opened != 2)
		return(-1);
	
	if(mode != KPI_NONBLOCK && mode != KPI_BLOCK) {
		//sysDisableInterrupt(_int + 2);  
		return(-1);
	}
	sysEnableInterrupt((INT_SOURCE_E)(_int + 2));
	
	if(_key == 0) {
		// not pressed, non blocking, return immediately
		if(mode == KPI_NONBLOCK) {
			sysDisableInterrupt((INT_SOURCE_E)(_int + 2));  
			return(0);
		}
		// not pressed, non blocking, wait for key pressed
#ifndef __GNUC__		
#pragma O0
#endif
// ARMCC is tooooo smart to compile this line correctly, so ether set O0 or use pulling....
		while((k = _key) == 0);
	} else {
		// read latest key(s) and return.
		sysDisableInterrupt((INT_SOURCE_E)(_int + 2));
		do {		
			k = readkey();		
		} while(k == 0 && mode != KPI_NONBLOCK);
	}
	return(k);
}