#include <rtdevice.h>
#include "gtypes.h"
#include "gd_spi.h"
#include "drv_ssi.h"
#include "platform.h"

/*---------------------------------------------------------------------------*/
/* local defines                                                             */
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
/* local data types                                                          */
/*---------------------------------------------------------------------------*/



/*---------------------------------------------------------------------------*/
/* local data                                                                */
/*---------------------------------------------------------------------------*/



/*---------------------------------------------------------------------------*/
/* local  functions                                                          */
/*---------------------------------------------------------------------------*/
static void *gk_get_spi_dev_pri_data(struct rt_spi_device *device)
{
    return device->parent.user_data;
}

static rt_err_t gk_spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *configuration)
{
	struct gk_spi_slave_info *spi_slave;
	struct gk_spi_controller *spi_control;
	GD_SPI_OPEN_PARAMS_S openParamsP;
	rt_uint32_t spi_hz;
	rt_int32_t	ret;
	rt_uint8_t	data_width = 8,polarity;

	if(!device || !configuration)
	{
		rt_kprintf("%s,%d,param wrong!\n",__FUNCTION__,__LINE__);
		return RT_ERROR;
	}

	spi_slave   = (struct gk_spi_slave_info *)gk_get_spi_dev_pri_data(device);
	spi_control = spi_slave->control;

	rt_sem_take(&spi_control->xfer_lock, RT_WAITING_FOREVER);

	if(spi_slave->spihandle > 0)
	{
		GD_SPI_Close(&spi_slave->spihandle);
		spi_slave->spihandle = -1;
	}

	 openParamsP.spi 	    = spi_control->id;
	 openParamsP.slave      = spi_slave->id;
	 openParamsP.csgpio     = spi_slave->cs_pin;
	 openParamsP.used_irq   = 1;

	 if (configuration->max_hz > GD_SPI_FREQ_81M)
		 spi_hz = GD_SPI_FREQ_81M;
	 else
		 spi_hz = configuration->max_hz;

	 openParamsP.baudrate = spi_hz;

	 /* CPOL */
	 if (configuration->mode & RT_SPI_CPOL && configuration->mode & RT_SPI_CPHA)
	 {
	 	polarity = SPI_POLARITY_MODE3;
	 }
	 else if(configuration->mode & RT_SPI_CPOL && !configuration->mode & RT_SPI_CPHA)
	 {
	 	polarity = SPI_POLARITY_MODE2;
	 }
	 else if(!configuration->mode & RT_SPI_CPOL && configuration->mode & RT_SPI_CPHA)
	 {
	 	polarity = SPI_POLARITY_MODE1;
	 }
	 else
		 polarity = SPI_POLARITY_MODE0;

	 openParamsP.polarity = polarity;

	 ret = GD_SPI_Open(&openParamsP,&(spi_slave->spihandle));
	 if(ret != GD_OK)
	 {
	     rt_kprintf("GD_SPI_Open failed!\n");
		 rt_sem_release(&spi_control->xfer_lock);
	     return RT_ERROR;
	 }

	 /* data_width */
	 if (configuration->data_width <= 8)
	 {
	 	data_width = 8;
	 }
	 else if (configuration->data_width <= 16)
	 {

	 	data_width = 16;
	 }

	 ret = GD_SPI_SetDatFormat(spi_slave->spihandle, data_width);;
	 if(ret != GD_OK)
	 {
	     rt_kprintf("GD_SPI_SetDatFormat failed!\n");
		 rt_sem_release(&spi_control->xfer_lock);
	     return RT_ERROR;
	 }

	 rt_sem_release(&spi_control->xfer_lock);

	return RT_EOK;
}

static rt_uint32_t gk_spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message)
{
	struct gk_spi_slave_info *spi_slave;
	struct gk_spi_controller *spi_control;
	GERR ret = GD_OK;

	if(!device || !message)
	{
		rt_kprintf("%s,%d,param wrong!\n",__FUNCTION__,__LINE__);
		return 0;
	}

	spi_slave   = (struct gk_spi_slave_info *)gk_get_spi_dev_pri_data(device);

	if(!spi_slave)
	{
		rt_kprintf("%s,%d,param wrong!\n",__FUNCTION__,__LINE__);
		return 0;
	}

	spi_control = spi_slave->control;

	if(spi_slave->spihandle < 0)
	{
		rt_kprintf("%s,%d,spi channel not configured!\n",__FUNCTION__,__LINE__);
		return 0;
	}

	if(!message)
	{
		rt_kprintf("%s,%d,params wrong!\n",__FUNCTION__,__LINE__);
		return 0;
	}

	rt_sem_take(&spi_control->xfer_lock, RT_WAITING_FOREVER);

	if(message->cs_take)
		GD_SPI_GetDevice(spi_slave->spihandle);

	if(message->recv_buf && message->send_buf)
    	ret = GD_SPI_WriteThenReadBytes(spi_slave->spihandle,(rt_uint8_t *)message->send_buf,message->length,
    	(rt_uint8_t *)message->recv_buf,message->length);
	else if(message->send_buf && !message->recv_buf)
		ret = GD_SPI_WriteBytes(spi_slave->spihandle,(rt_uint8_t *)message->send_buf,message->length);
	else if(!message->send_buf && message->recv_buf)
		ret = GD_SPI_WriteThenReadBytes(spi_slave->spihandle,NULL,0,(rt_uint8_t *)message->recv_buf,message->length);


	if(message->cs_release)
		GD_SPI_ReleaseDevice(spi_slave->spihandle);

	rt_sem_release(&spi_control->xfer_lock);


	if(ret != GD_OK)
	{
		rt_kprintf("%s,%d,transfer error!\n",__FUNCTION__,__LINE__);
		return 0;
	}
	else
	{
		return message->length;
	}

}

static struct rt_spi_ops gk_spi_ops = {

    .configure = gk_spi_configure, .xfer = gk_spi_xfer,

};

int gk_spi_probe(void *priv_data)
{
	int ret,i;
    char spi_dev_name[20]  = {0};
    char spi_bus_name[20]  = {0};
    char spi_lock_name[20] = {0};
    struct gk_spi_controller *spi_control;
	struct gk_spi_controller_data *spi_controller_data = (struct gk_spi_controller_data *)priv_data;
    struct gk_spi_slave_info **control_slave;
    struct gk_spi_slave_info *spi_slave,*next_slave;

	if (!spi_controller_data)
	{
		rt_kprintf("%s,%d input param wrong!\n",__FUNCTION__,__LINE__);
		return -1;
	}

    spi_control = (struct gk_spi_controller *)rt_malloc(sizeof(struct gk_spi_controller));
    if (!spi_control)
    {
        rt_kprintf("%s,%d malloc failed!\n",__FUNCTION__,__LINE__);
        return -1;
    }

    rt_memset(spi_control, 0, sizeof(struct gk_spi_controller));

	spi_control->id = spi_controller_data->id;

    rt_sprintf(spi_lock_name, "%s%d", "spi_lock", spi_control->id);
    rt_sprintf(spi_bus_name, "%s%d", "spi_bus", spi_control->id);

    rt_sem_init(&spi_control->xfer_lock, spi_lock_name, 1, RT_IPC_FLAG_FIFO);

    ret = rt_spi_bus_register(&spi_control->spi_bus, spi_bus_name, &gk_spi_ops);

	control_slave = &spi_control->spi_slave;

    for (i = 0; i < spi_controller_data->total_slave; i++)
    {
        spi_slave = (struct gk_spi_slave_info *)rt_malloc(sizeof(struct gk_spi_slave_info));
        if (!spi_slave)
        {
			rt_kprintf("%s,%d malloc failed!\n",__FUNCTION__,__LINE__);
            goto exit;
        }
        rt_memset(spi_slave, 0, sizeof(struct gk_spi_slave_info));

        spi_slave->id      = i;
        spi_slave->control = spi_control;
        spi_slave->cs_pin  = spi_controller_data->slave_cs_pin[i];
		spi_slave->spihandle = -1;

        rt_sprintf(spi_dev_name, "%s%d%s%d", "ssi", spi_control->id, "_", spi_slave->id);

        *control_slave = spi_slave;
        control_slave  = &spi_slave->next;

        ret = rt_spi_bus_attach_device(&spi_slave->spi_device, spi_dev_name,spi_bus_name, spi_slave);
        if (ret != RT_EOK)
        {
            rt_kprintf("register dev to bus failed...\n");
            goto exit;
        }
    }

	spi_controller_data->control = spi_control;

	GD_SPI_Init();

    return RT_EOK;

exit:

	spi_slave = spi_control->spi_slave;
	while (spi_slave != RT_NULL)
	{
		next_slave = spi_slave->next;
		rt_free(spi_slave);
		spi_slave = next_slave;
	}

    rt_sem_detach(&spi_control->xfer_lock);

    rt_free(spi_control);

	return RT_ERROR;
}

int gk_spi_exit(void *priv_data)
{

    struct gk_spi_controller *spi_control;
    struct gk_spi_controller_data *plat_data;
    struct gk_spi_slave_info *spi_slave;
    struct gk_spi_slave_info *next_slave;

    plat_data   = (struct gk_spi_controller_data *)priv_data;
    spi_control = plat_data->control;
    spi_slave   = spi_control->spi_slave;

    while (spi_slave != RT_NULL)
    {
        next_slave = spi_slave->next;
        rt_free(spi_slave);
        spi_slave = next_slave;
    }

    rt_sem_detach(&spi_control->xfer_lock);

    rt_free(spi_control);

    return RT_EOK;
}

struct gk_platform_driver gk_spi_driver_ops = {

    .name = "spi", .probe = gk_spi_probe, .remove = gk_spi_exit,

};

void rt_hw_spi_init(void)
{

	gk_platform_driver_init(&gk_spi_driver_ops);
}