#include "dagutil.h"
#include "dagapi.h"
#include "../include/attribute.h"
#include "../include/util/set.h"
#include "../include/card.h"
#include "../include/cards/card_initialization.h"
#include "../include/component.h"
#include "../include/util/utility.h"
#include "../include/cards/common_dagx_constants.h"
#include "../include/util/enum_string_table.h"
#include "../include/cards/dag71s_impl.h"
#include "../include/attribute_factory.h"
#include "../include/create_attribute.h"
#include "../include/components/vcxo_component.h"
#include "../include/dag_attribute_codes.h"
#include "../include/attribute_types.h"


static dag_err_t vcxo_update_register_base(ComponentPtr component);
static int vcxo_post_initialize(ComponentPtr component);
static void vcxo_default(ComponentPtr component);
static void vcxo_dispose(ComponentPtr component);
static void vcxo_reset(ComponentPtr component);

static void* frequency_mode_get_value(AttributePtr attribute);
static void frequency_mode_set_value(AttributePtr attribute, void* value, int length);

static AttributePtr vcxo_get_new_frequency_mode_attribute(GenericReadWritePtr grw, uint32_t *masks,int length);

ComponentPtr
vcxo_get_new_component(DagCardPtr card, uint32_t index)
{
	vcxo_state_t* state = NULL;
	ComponentPtr result = component_init(kComponentVcxo, card);
	index = card_get_register_index_by_version(card,DAG_REG_SMB,5,index);
	if (NULL != result)
	{
		component_set_dispose_routine(result, vcxo_dispose);
		component_set_post_initialize_routine(result, vcxo_post_initialize);
		component_set_reset_routine(result, vcxo_reset);
		component_set_default_routine(result, vcxo_default);
		component_set_update_register_base_routine(result, vcxo_update_register_base);
		component_set_name(result, "vcxo");
		component_set_description(result, "The voltage controlled crystal oscillator.");
		state = (vcxo_state_t*)malloc(sizeof(vcxo_state_t));
		state->mIndex = index;
		state->mVcxoBase = (uint32_t*)(card_get_iom_address(card) + card_get_register_address(card, DAG_REG_SMB, state->mIndex));
		component_set_private_state(result, state);
	}
	return result;
}
static
dag_err_t vcxo_update_register_base(ComponentPtr component)
{
	if (1 == valid_component(component))
	{
		vcxo_state_t* state = NULL;
		DagCardPtr card;
		state = component_get_private_state(component);
		card = component_get_card(component);
		NULL_RETURN_WV(state, kDagErrGeneral);
		state->mVcxoBase = (uint32_t*)(card_get_iom_address(card) + card_get_register_address(card, DAG_REG_SMB, state->mIndex)); 
		return kDagErrNone;
	}
	return kDagErrInvalidParameter;
}
static
int vcxo_post_initialize(ComponentPtr component)
{
	if (1 == valid_component(component))
	{
		DagCardPtr card = NULL;
		vcxo_state_t* state = NULL;
		AttributePtr frequency_mode = NULL;  
		GenericReadWritePtr grw = NULL;
		uintptr_t address = 0;
		uint32_t mask = 0;
		/* Get card reference */
		card = component_get_card(component);
		/* Get counter state structure */
		state = component_get_private_state(component);
		/*the base address*/
		state->mVcxoBase = (uint32_t*)(card_get_iom_address(card) + card_get_register_address(card, DAG_REG_SMB, state->mIndex));
		/* Add attribute of counter */
		mask = 0xff;   
		grw = grw_init(card,address,grw_drb_smbus_read,grw_drb_smbus_write);
		frequency_mode = vcxo_get_new_frequency_mode_attribute(grw,&mask,1);
		component_add_attribute(component, frequency_mode);
		return 1;
	}
	return kDagErrInvalidParameter;
}
static  
void vcxo_default(ComponentPtr component)
{
	if (1 == valid_component(component))
	{
		DagCardPtr card = NULL;
		/* Get card reference */
		/*Do the following things*/
		/*1.Freeze the DCO.
		2.Recall the default values into the VCXO ram.
		3.Read out the 622 frequency value.
		4.Write it back into the scratch pad register for future reference.*/
		GenericReadWritePtr grw = NULL;
		ComponentPtr scratch_pad_component = NULL;
		AttributePtr scratch_pad_address = NULL;
		AttributePtr scratch_pad_data = NULL;
		AttributePtr vcxo_frequency = NULL;
		ComponentPtr root = NULL;
		uintptr_t address = 0;
		uint64_t freq_default_val = 0;
		uint64_t temp_frequency = 0;
		uint8_t reg_address = 0;
		uint32_t value = 0;
		float vcxo_frequency_val = 0;
		vcxo_state_t *state = NULL;
		
		state = component_get_private_state(component);
		card = component_get_card(component);
		root = component_get_parent(component);
		grw = grw_init(card, address, grw_drb_smbus_read, grw_drb_smbus_write);
		/*Recall default value into the VCXO ram.*/
		reg_address = 0x87;
		address = (0xAA << 8); 
		address |= reg_address;
		address |= (state->mIndex << 16);
		grw_set_address(grw, address);
		
		grw_write(grw,0x01);
	
		dagutil_microsleep(100000);	
		
		reg_address = 8;
		address = (0xAA << 8); 
		address |= reg_address; /* shift 8 because the grw_read/write functions expect the device address there */
		address |= (state->mIndex << 16);
      
		grw_set_address(grw, address);
		freq_default_val = grw_read(grw);
		freq_default_val = (freq_default_val & 0x3f);
		for (reg_address = 9 ; reg_address < 13 ; reg_address++ )
		{
		    address = (0xAA << 8); 
		    address |= reg_address; /* shift 8 because the grw_read/write functions expect the device address there */
		    address |= (state->mIndex << 16);
			  
		    grw_set_address(grw, address);
		    temp_frequency = grw_read(grw);
		    
		    freq_default_val = (freq_default_val << 8) | temp_frequency;
		}
		scratch_pad_component = component_get_subcomponent(root,kComponentScratchPad,0);
		if(scratch_pad_component == NULL)
		{
			dagutil_verbose_level(2,"Faild to get Scratch Pad component. \n");
		}
		scratch_pad_address = component_get_attribute(scratch_pad_component,kUint32AttributeScratchPadAddress);
		if(scratch_pad_address)
		{
		    value = 0 + (state->mIndex*2);
		    attribute_set_value(scratch_pad_address,&value,1);
		}
		scratch_pad_data = component_get_attribute(scratch_pad_component,kUint32AttributeScratchPadData);
		if(scratch_pad_data)
		{
		    value = (freq_default_val&0xffffffff);
		    attribute_set_value(scratch_pad_data,&value,1);
		}
		scratch_pad_address = component_get_attribute(scratch_pad_component,kUint32AttributeScratchPadAddress);
		if(scratch_pad_address)
		{
		    value = 1 + (state->mIndex*2);
		    attribute_set_value(scratch_pad_address,&value,1);
		}
		scratch_pad_data = component_get_attribute(scratch_pad_component,kUint32AttributeScratchPadData);
		if(scratch_pad_data)
		{
		    value = ((freq_default_val&0x3f00000000ll) >> 32);
		    attribute_set_value(scratch_pad_data,&value,1);
		}
		/*Once the default value has been written into the Scratch Pad.Set the vcxo to 10ge frequency */
		vcxo_frequency_val = 644.53125;
		vcxo_frequency = component_get_attribute(component,kFloatAttributeVcxoFrequency);
		if(vcxo_frequency)
		{
		    attribute_set_value(vcxo_frequency,&vcxo_frequency_val,sizeof(float));
		}
	}
}
static
void vcxo_dispose(ComponentPtr component)
{
}
static
void vcxo_reset(ComponentPtr component)
{
}

static
AttributePtr vcxo_get_new_frequency_mode_attribute(GenericReadWritePtr grw, uint32_t *masks,int length)
{
	AttributePtr result = attribute_init(kFloatAttributeVcxoFrequency);
	if (NULL != result)
	{
		attribute_set_name(result, "vcxo_frequency");
		attribute_set_generic_read_write_object(result, grw);
		attribute_set_getvalue_routine(result, frequency_mode_get_value);
		attribute_set_setvalue_routine(result, frequency_mode_set_value);
		attribute_set_to_string_routine(result,attribute_float_to_string);
		attribute_set_from_string_routine(result,attribute_float_from_string);
		attribute_set_description(result, "Reports and modified the frequency of the VCXO.(in MHz)");
		attribute_set_config_status(result, kDagAttrConfig);
		attribute_set_valuetype(result, kAttributeFloat);
		attribute_set_masks(result,masks,length);
	}
	return result;
}

static 
void* frequency_mode_get_value(AttributePtr attribute)
{
    if (1 == valid_attribute(attribute))
    {
	  DagCardPtr card = NULL;
	  GenericReadWritePtr grw = NULL;
	  ComponentPtr component = NULL;
	  uintptr_t address = 0;
	  uint64_t mode_frequency = 0;
	  uint64_t temp_frequency = 0;
	  uint8_t reg_address = 8;
	  vcxo_state_t *state = NULL;
	  uint32_t value = 0,value_1 = 0,value_2 = 0;
	  ComponentPtr root = NULL;
	  ComponentPtr scratch_pad_component = NULL;
	  AttributePtr scratch_pad_address = NULL;
	  AttributePtr scratch_pad_data = NULL;
	  uint64_t freq_default_val = 0;
	  float frequency_value = 0;
	  double temp_freq_1 = 0;
	  double temp_freq_2 = 0;
	  
	  component = attribute_get_component(attribute);
	  state = component_get_private_state(component);
	  root = component_get_parent(component);
	  
	  card = attribute_get_card(attribute);
	  grw = grw_init(card, address, grw_drb_smbus_read, grw_drb_smbus_write);
	  
	  address = (0xAA << 8); 
	  address |= reg_address; /* shift 8 because the grw_read/write functions expect the device address there */
       	  address |= (state->mIndex << 16);
 
          grw_set_address(grw, address);
	  mode_frequency = grw_read(grw);
	  mode_frequency = (mode_frequency & 0xff);
      	  for (reg_address = 9 ; reg_address < 13 ; reg_address++ )
	  {
	      address = (0xAA << 8); 
	      address |= reg_address; 
 	      address |= (state->mIndex << 16);
		     
	      grw_set_address(grw, address);
	      temp_frequency = grw_read(grw);
	      mode_frequency = (mode_frequency << 8);
	      mode_frequency |= temp_frequency;
	  }
	  /*Get the default value from the scratch pad register.*/
	  scratch_pad_component = component_get_subcomponent(root,kComponentScratchPad,0);
	  if(scratch_pad_component == NULL)
	  {
		  dagutil_verbose_level(2,"Faild to get Scratch Pad component. \n");
	  }
	  scratch_pad_address = component_get_attribute(scratch_pad_component,kUint32AttributeScratchPadAddress);
	  if(scratch_pad_address)
	  {
	      value = 0 + (state->mIndex*2);
	      attribute_set_value(scratch_pad_address,&value,1);
	  }
	  scratch_pad_data = component_get_attribute(scratch_pad_component,kUint32AttributeScratchPadData);
	  if(scratch_pad_data)
	  {
	      value_1 = *(uint32_t*)attribute_get_value(scratch_pad_data);
	  }
	  scratch_pad_address = component_get_attribute(scratch_pad_component,kUint32AttributeScratchPadAddress);
	  if(scratch_pad_address)
	  {
	      value = 1 + (state->mIndex*2);
	      attribute_set_value(scratch_pad_address,&value,1);
	  }
	  scratch_pad_data = component_get_attribute(scratch_pad_component,kUint32AttributeScratchPadData);
	  if(scratch_pad_data)
	  {
	      value_2 = *(uint32_t*)attribute_get_value(scratch_pad_data);
	  }
	  freq_default_val = value_2; 
	  freq_default_val = (freq_default_val << 32);
	  freq_default_val|= value_1;
	  temp_freq_1 = mode_frequency & 0xfffffffffll;
	  temp_freq_2 = freq_default_val & 0xfffffffffll;

	  frequency_value = ((temp_freq_1)/(temp_freq_2));
	  frequency_value = (622.08*frequency_value);
	  attribute_set_value_array(attribute,&frequency_value,sizeof(uint64_t));
	  return (void *)attribute_get_value_array(attribute);
    
    }
    return NULL;
}
static 
void frequency_mode_set_value(AttributePtr attribute, void* value, int length)
{
    if (1 == valid_attribute(attribute))
    {
	  DagCardPtr card = NULL;
	  GenericReadWritePtr grw = NULL;
	  ComponentPtr component = NULL;
	  uintptr_t address = 0;
	  uint64_t mode_frequency = 0;
	  uint64_t temp_frequency_1 = 0;
	  double temp_frequency_2 = 0;
	  uint8_t reg_address = 8;
	  vcxo_state_t *state = NULL;
	  uint32_t value_3 = 0,value_1 = 0,value_2 = 0;
	  ComponentPtr root = NULL;
	  ComponentPtr scratch_pad_component = NULL;
	  AttributePtr scratch_pad_address = NULL;
	  AttributePtr scratch_pad_data = NULL;
	  uint64_t freq_default_val = 0;
	  float frequency_value = 0;
	  uint8_t reg_val = 0;
	  uint64_t freq_2 = 0;
	  uint32_t attempts = 0;	 
 
	  component = attribute_get_component(attribute);
	  state = component_get_private_state(component);
	  root = component_get_parent(component);
 
	  /*Get the default value from the scratch pad register.*/
	  scratch_pad_component = component_get_subcomponent(root,kComponentScratchPad,0);
	  if(scratch_pad_component == NULL)
	  {
		  dagutil_verbose_level(2,"Faild to get Scratch Pad component. \n");
	  }
	  scratch_pad_address = component_get_attribute(scratch_pad_component,kUint32AttributeScratchPadAddress);
	  if(scratch_pad_address)
	  {
	      value_3 = 0 + (state->mIndex*2);
	      attribute_set_value(scratch_pad_address,&value_3,1);
	  }
	  scratch_pad_data = component_get_attribute(scratch_pad_component,kUint32AttributeScratchPadData);
	  if(scratch_pad_data)
	  {
	      value_1 = *(uint32_t*)attribute_get_value(scratch_pad_data);
	  }
	  scratch_pad_address = component_get_attribute(scratch_pad_component,kUint32AttributeScratchPadAddress);
	  if(scratch_pad_address)
	  {
	      value_3 = 1 + (state->mIndex*2);
	      attribute_set_value(scratch_pad_address,&value_3,1);
	  }
	  scratch_pad_data = component_get_attribute(scratch_pad_component,kUint32AttributeScratchPadData);
	  if(scratch_pad_data)
	  {
	      value_2 = *(uint32_t*)attribute_get_value(scratch_pad_data);
	  }
	  freq_default_val = value_2; 
	  freq_default_val = (freq_default_val << 32);
	  freq_default_val|= value_1;

	  card = attribute_get_card(attribute);
	  grw = grw_init(card, address, grw_drb_smbus_read, grw_drb_smbus_write);
	  
	  frequency_value = *(float*)value;
	  temp_frequency_2 = (frequency_value/622.08);
	
	  freq_default_val = freq_default_val & 0xfffffffff; 
	  temp_frequency_2 = temp_frequency_2*freq_default_val;

	  freq_2 = round(temp_frequency_2);		
	  temp_frequency_1 = 4; 

	  mode_frequency = (temp_frequency_1 << 36);
	  mode_frequency |= freq_2;
	  
	  /*Freeze the DCO*/
	  address = (0xAA << 8);
          address |= 0x89;
          address |= (state->mIndex << 16);

          grw_set_address(grw, address);
          grw_write(grw,0x10);

	  /*Program the new frequency*/
      	  for (reg_address = 12 ; reg_address > 7 ; reg_address-- )
	  {
	      address = (0xAA << 8); 
	      address |= reg_address; 
 	      address |= (state->mIndex << 16);
		     
	      grw_set_address(grw, address);
	      reg_val = (mode_frequency & 0xff);
	      grw_write(grw,reg_val);
	      mode_frequency = (mode_frequency >> 8);
	  }
	
	  /*Unfreeze the DCO.*/

          address = (0xAA << 8);
          address |= 0x89;
          address |= (state->mIndex << 16);
          
          grw_set_address(grw, address);
          grw_write(grw,0x0);

		
	  /*Apply the new frequency*/

	  address = (0xAA << 8);
          address |= 0x87; 
          address |= (state->mIndex << 16);

          grw_set_address(grw, address);
          grw_write(grw,0x40);

	  reg_val = grw_read(grw);
	  while((((reg_val & 0x40)>>6) == 1) && (attempts < 50))
	  {
		reg_val = grw_read(grw);
		attempts++;
	  }
	  if(attempts == 50)
	  {
		dagutil_warning("New frequency not applied properly \n");
	  }
   }
}

