ad9960

/*
 * File:         drivers/char/ad9960.c
 * Based on:
 * Author:       Aubrey.Li <aubrey.Li@analog.com>
 *
 * Created:
 * Description:
 *
 * Prototype:
 *
 * Rev:          $Id: ad9960.c,v 1.9 2006/06/28 05:52:02 magicyang Exp $
 *
 * Modified:
 *               Copyright 2004-2005 Analog Devices Inc.
 *
 * Bugs:         Enter bugs at http://blackfin.uclinux.org/
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.
 * If not, write to the Free Software Foundation,
 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/device.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>

#include <asm/io.h>
#include <asm/irq.h>
#include <asm/blackfin.h>
#include <asm/dma.h>
#include <asm/cacheflush.h>
#include <asm/bfin5xx_spi.h>

#include <rtdm/rtdm_driver.h>

/************************************************************/

/* definitions */

#undef  DEBUG
//#define DEBUG

#ifdef DEBUG
#define DPRINTK(x...)   printk(x)
#else
#define DPRINTK(x...)   do { } while (0)
#endif

#define CMD_SPI_WRITE		0x1
#define CMD_GET_SCLK		0x2
#define CMD_TEST_READ		0x3
#define CMD_TEST_WRITE		0x4

/************************************************************/
struct ad9960_spi{
	struct spi_device *spi;
};

struct ad9960_ppi{
	unsigned short irqnum;
	unsigned short dma_config;
	unsigned short ppi_control;
	unsigned short ppi_status;
	unsigned short ppi_delay;
};

struct ad9960_device_t{
	int opened;
	int nonblock;
	unsigned short done;
	struct ad9960_spi *spi_dev;
	struct ad9960_ppi ppi_dev;
	struct fasync_struct *fasyc;
	unsigned short *gpio;
};

/* Instance Context */
struct ad9960_ctx {
	struct ad9960_device_t * ad9960_info; 
		
	/* Real Time */
	rtdm_irq_t irq_handle;
	rtdm_event_t irq_event;
	int64_t timeout;

	/* For RT task running in kernel - Test Only */	
	rtdm_task_t ad9960_task;
	struct semaphore test_done_sem;
	int count;
	char *buf;	
};
	
/************************************************************/

/* Globals */
struct ad9960_device_t ad9960_info;
int ad9960_spi_read(struct ad9960_spi *spi, unsigned short data,
                                        unsigned short *buf);
int ad9960_spi_write(struct ad9960_spi *spi, unsigned short data);

void ad9960_read_test_proc(void *arg);
void ad9960_write_test_proc(void *arg);

extern unsigned long l1_data_A_sram_alloc(unsigned long size);
extern int l1_data_A_sram_free(unsigned long addr);

extern dma_channel_t dma_ch[];

static void dump_regs(void)
{
	printk("--------------------\n");
	printk("PPI_CONTROL: 0x%hx\n", *pPPI_CONTROL);
	printk("PPI_STATUS: 0x%hx\n", *pPPI_STATUS);
	printk("PPI_DELAY: 0x%hx\n", *pPPI_DELAY);
	printk("PPI_COUNT: 0x%hx\n", *pPPI_COUNT);
	printk("PPI_FRAME: 0x%hx\n", *pPPI_FRAME);
	printk("PORTF_FER: 0x%hx\n", *pPORTF_FER); 
	printk("TIMER0_CONFIG: 0x%hx\n", *pTIMER0_CONFIG);
	printk("TIMER1_CONFIG: 0x%hx\n", *pTIMER1_CONFIG);
	printk("---------------------\n");
}

static int rt_ad9960_ppi_irq(rtdm_irq_t *irq_context)
{
    struct ad9960_ctx *ctx;
    ctx = rtdm_irq_get_arg(irq_context, struct ad9960_ctx); 

    DPRINTK("ad9960_ppi_irq: begin\n");

    /* Acknowledge DMA Interrupt*/
    clear_dma_irqstat(CH_PPI);

    /* disable ppi */
    //bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() &  ~PORT_EN);
    *pPPI_CONTROL &= ~PORT_EN;    

    ctx->ad9960_info->done = 1;

    DPRINTK("ad9960_ppi_irq: wake_up_interruptible pdev->done=%d\n",
			ctx->ad9960_info->done);

    /* wake up read*/
    rtdm_event_signal(&ctx->irq_event);

    DPRINTK("ad9960_ppi_irq: return \n");

    return RTDM_IRQ_HANDLED;
}




/* Safe for RT task */
static ssize_t rt_ad9960_read_rt (struct rtdm_dev_context *context, 
		rtdm_user_info_t *user_info, void *buf, size_t count)
{
    int ierr;
    struct ad9960_ctx * ctx = (struct ad9960_ctx *)context->dev_private;
    struct ad9960_device_t *pdev = ctx->ad9960_info;
    char *dma_buf;
    int i = 0;
    short * buf_short = NULL;

    /* FIXME l1_data_A_sram_alloc() uses spin_lock_irqsave(). 
     * Change to spin_lock_irqsave_hw */
    dma_buf = (char *)l1_data_A_sram_alloc((u_long)(count*2));
 
    DPRINTK("ad9960_read: count = %d\n", count);

    if(count <= 0)
        return 0;

    pdev->done=0;

    /* Disable PPI */
    //bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() & ~ PORT_EN);
    *pPPI_CONTROL &= ~ PORT_EN;
    /* Disable dma */
    disable_dma(CH_PPI);
    //bfin_write_PORTFIO_SET(bfin_read_PORTFIO_SET() | 0x0100);
    *pPORTFIO_SET |= 0x0100;
    __builtin_bfin_ssync();
    /* setup PPI */
    //bfin_write_PPI_CONTROL(0x783C);
    *pPPI_CONTROL = 0x783C;
    //bfin_write_PPI_DELAY(1);
    *pPPI_DELAY = 1;
	

    /* configure ppi port for DMA write */
    set_dma_config(CH_PPI, 0x0086);
    set_dma_start_addr(CH_PPI, (u_long)dma_buf);
    set_dma_x_count(CH_PPI, count);
    set_dma_x_modify(CH_PPI, 2);

    DPRINTK("ad9960_read: SETUP DMA : DONE \n");

     dump_regs();
    
    enable_dma(CH_PPI);
    /* Enable PPI */
    //bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN);
    *pPPI_CONTROL |= PORT_EN;
    __builtin_bfin_ssync();

    //bfin_write_PORTFIO_CLEAR(bfin_read_PORTFIO_CLEAR() | 0x0100);
    *pPORTFIO_CLEAR |= 0x0100;
    __builtin_bfin_ssync();

    DPRINTK("ad9960_read: PPI ENABLED : DONE \n");

    /* Wait for data available */
    if(1)
    {
        if(pdev->nonblock)
            return -EAGAIN;
        else
        {
            DPRINTK("ad9960_read: PPI rtdm_event_timedwait\n");
            ierr = rtdm_event_timedwait(&ctx->irq_event, ctx->timeout, NULL);
            if(ierr)
            {
                /* waiting is broken by a signal */
                printk("PPI rtdm_event_timedwait ieer\n");
                return ierr;
            }
        }
    }

	dump_regs();
    //buf_short = (short *)dma_buf;
    //for (i = 0; i < count; i++)
	//printk("k- buf[%d]: %hx\n", i, buf_short[i]);
    memcpy(buf,dma_buf,count*2);

    DPRINTK("ad9960_read: PPI wait_event_interruptible done\n");

    l1_data_A_sram_free((u_long)dma_buf);
    disable_dma(CH_PPI);
    //bfin_write_PORTFIO_SET(bfin_read_PORTFIO_SET() | 0x0100);
    *pPORTFIO_SET |= 0x0100;
    __builtin_bfin_ssync();

    DPRINTK("ppi_read: return \n");

    return count;
}

/* Safe for RT task */
static ssize_t rt_ad9960_write_rt (struct rtdm_dev_context *context,
                   rtdm_user_info_t *user_info, const void *buf, size_t count)
{
    int ierr;
    struct ad9960_ctx * ctx = (struct ad9960_ctx *)context->dev_private;
    struct ad9960_device_t *pdev = ctx->ad9960_info;
    char *dma_buf;

    dma_buf = (char *)l1_data_A_sram_alloc(count*2);
    memcpy(dma_buf,buf,count*2);

    DPRINTK("ad9960_write: %d\n", count);

    if(count <= 0)
        return 0;

    pdev->done=0;

    /* Disable PPI */
    //bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() & ~PORT_EN);
    *pPPI_CONTROL &= ~PORT_EN;
    /* Disable dma */
    disable_dma(CH_PPI);
    //bfin_write_PORTFIO_CLEAR(bfin_read_PORTFIO_CLEAR() | 0x0100);
    *pPORTFIO_CLEAR |= 0x0100;
    __builtin_bfin_ssync();

    /* setup PPI */
    //bfin_write_PPI_CONTROL(0x780E);
    *pPPI_CONTROL = 0x780E;
    //bfin_write_PPI_COUNT(2*count -1);
    *pPPI_COUNT = (2*count -1);
    //bfin_write_PPI_DELAY(0);
    *pPPI_DELAY = 0;
    /* configure ppi port for DMA read*/
    set_dma_config(CH_PPI, 0x0084);
    set_dma_start_addr(CH_PPI, (u_long)dma_buf);
    set_dma_x_count(CH_PPI, 2*count);
    set_dma_x_modify(CH_PPI, 2);

    DPRINTK("ad9960_write: SETUP DMA : DONE \n");

    enable_dma(CH_PPI);

    /* Enable PPI */
    //bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN);
    *pPPI_CONTROL |= PORT_EN;
    __builtin_bfin_ssync();
    
    //bfin_write_PORTFIO_SET(bfin_read_PORTFIO_SET() | 0x0100);
    *pPORTFIO_SET |= 0x0100;
    __builtin_bfin_ssync();

    DPRINTK("ad9960_write: PPI ENABLED : DONE \n");
    /* Wait for data available */
    if(1)
    {
        if(pdev->nonblock)
            return -EAGAIN;
        else
        {
            DPRINTK("ad9960_write: PPI rtdm_event_timedwait\n");
            //ierr = wait_event_interruptible(*(pdev->rx_avail),pdev->done);
            ierr = rtdm_event_timedwait(&ctx->irq_event, ctx->timeout, NULL);
            if(ierr)
            {
                /* waiting is broken by a signal */
                printk("PPI rtdm_event_timedwait ierr\n");
                return ierr;
            }
        }
    }

    DPRINTK("ad9960_write: PPI wait_event_interruptible done\n");

    l1_data_A_sram_free((u_long)dma_buf);
    disable_dma(CH_PPI);
    //bfin_write_PORTFIO_CLEAR(bfin_read_PORTFIO_CLEAR() | 0x0100);
    *pPORTFIO_CLEAR |= 0x0100;
    __builtin_bfin_ssync();

    DPRINTK("ppi_write: return \n");

    return count;
}

static int rt_ad9960_ioctl_rt(struct rtdm_dev_context *context,
		 rtdm_user_info_t *user_info, int request, void *arg)
{
	printk("ioctl_rt - please use ioctl_nrt\n");
	return 0;
}

static int rt_ad9960_ioctl_nrt(struct rtdm_dev_context *context,
		 rtdm_user_info_t *user_info, int request, void *arg)
{
    struct ad9960_ctx * ctx = (struct ad9960_ctx *)context->dev_private;
    
    unsigned short value = 0; 
    unsigned long  sclk;
    int ret = 0;

    switch (request)
    {
	case CMD_SPI_WRITE:
	{
		if (user_info) {
                	if (!rtdm_read_user_ok(user_info, arg, sizeof(unsigned short)) ||
                    		rtdm_copy_from_user(user_info, &value, arg,
                                        sizeof(unsigned short)))
                    	return -EFAULT;
            	}
		DPRINTK("CMD_SPI_WRITE: value: %x\n", value);
		ad9960_spi_write(ctx->ad9960_info->spi_dev, value);    
		break;
	}
	case CMD_GET_SCLK:
	{
		if (user_info)
		{
			DPRINTK("ad9960_ioctl: CMD_GET_SCLK\n");
			sclk = get_sclk();
		        if (!rtdm_rw_user_ok(user_info, arg, sizeof(unsigned long)) ||
                    	rtdm_copy_to_user(user_info, arg, &sclk, sizeof(unsigned long)))
				return -EFAULT;
		}
		break;
	}
	case CMD_TEST_READ:
	{
		if (user_info) {
			if (!rtdm_read_user_ok(user_info, arg, sizeof(unsigned short)) ||
                                rtdm_copy_from_user(user_info, &value, arg,
                                        sizeof(unsigned short)))
                        return -EFAULT;
                }
                printk("CMD_TEST_READ: count: %d\n", value);
		ad9960_spi_write(ctx->ad9960_info->spi_dev, 0x6003);
		ctx->count = value;
		ctx->buf = kmalloc(ctx->count * 2, GFP_KERNEL);
		ret = rtdm_task_init(&ctx->ad9960_task, "ad9960_task",
				ad9960_read_test_proc, context, 50, 0);	    
		down(&ctx->test_done_sem);
		rtdm_task_destroy(&ctx->ad9960_task);
		kfree(ctx->buf);
                printk("CMD_TEST_READ: finish\n");
		break;
	}
	case CMD_TEST_WRITE:
	{
                if (user_info) {
                        if (!rtdm_read_user_ok(user_info, arg, sizeof(unsigned short)) ||
                                rtdm_copy_from_user(user_info, &value, arg,
                                        sizeof(unsigned short)))
                        return -EFAULT;
                }
                printk("CMD_TEST_WRITE: count: %d\n", value);
		ctx->count = value;
		ctx->buf = kmalloc(ctx->count * 2, GFP_KERNEL);
                ret = rtdm_task_init(&ctx->ad9960_task, "ad9960_task",
                                ad9960_write_test_proc, context, 50, 0);
		down(&ctx->test_done_sem);
		rtdm_task_destroy(&ctx->ad9960_task);
		kfree(ctx->buf);
                printk("CMD_TEST_WRITE: finish\n");
		break;
	}
	default:
            return -EINVAL;
    }
    return 0;
}

static int set_dma_callback_rt(struct rtdm_dev_context *context, 
		unsigned int channel, dma_interrupt_t callback)
{
	int ret_irq = 0;
	struct ad9960_ctx * ctx = 
		(struct ad9960_ctx *)context->dev_private;

	assert(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
	       && channel < MAX_BLACKFIN_DMA_CHANNEL);

	if (callback != NULL) {
		int ret_val;
		ret_irq = bf533_channel2irq(channel);
		
		dma_ch[channel].data = ctx;

		/* IRQ num is the HW irq */	
		ret_val = rtdm_irq_request(&ctx->irq_handle, ret_irq, 
		  (rtdm_irq_handler_t)callback, 0, context->device->proc_name, ctx);
		if (ret_val < 0) {
			printk("Request irq in DMA engine failed.\n");
			return -EPERM;
		}
		DPRINTK("rtdm_irq_request ok: %d\n", ret_irq);
		dma_ch[channel].irq_callback = callback;
		rtdm_irq_enable(&ctx->irq_handle);
	}
	return 0;
}

static void clear_dma_buffer(unsigned int channel)
{
	dma_ch[channel].regs->cfg |= RESTART;
        __builtin_bfin_ssync();
	dma_ch[channel].regs->cfg &= ~RESTART;
        __builtin_bfin_ssync();
}

static void free_dma_rt(struct rtdm_dev_context *context, unsigned int channel)
{
        struct ad9960_ctx * ctx =
                (struct ad9960_ctx *)context->dev_private;

	DPRINTK("freedma_rt() : BEGIN \n");
	DPRINTK("free channel %d, chan_free is %d, status is %d, MAX is %d\n",
		DMA_CHANNEL_FREE, channel,dma_ch[channel].chan_status,
		MAX_BLACKFIN_DMA_CHANNEL);
	assert(dma_ch[channel].chan_status != DMA_CHANNEL_FREE
	       && channel < MAX_BLACKFIN_DMA_CHANNEL);

	/* Halt the DMA */
	disable_dma(channel);
	clear_dma_buffer(channel);

	if (dma_ch[channel].irq_callback != NULL) {
		rtdm_irq_free(&ctx->irq_handle);
	}

	/* Clear the DMA Variable in the Channel */
	dma_ch[channel].chan_status = DMA_CHANNEL_FREE;

	DPRINTK("freedma_rt() : END \n");
}

static int rt_ad9960_open (struct rtdm_dev_context *context,
		rtdm_user_info_t *user_info, int oflags)
{
    struct ad9960_ctx *ctx = (struct ad9960_ctx *)context->dev_private;

    if(ad9960_info.opened)
        return -EMFILE;

    ad9960_info.opened = 1;
    ad9960_info.done = 0;

    rtdm_event_init(&ctx->irq_event, 0);    
    ctx->timeout = 0;

    ad9960_info.ppi_dev.irqnum = IRQ_PPI;
    
    ctx->ad9960_info = &ad9960_info;
    init_MUTEX_LOCKED(&ctx->test_done_sem);

    /* Request DMA channel, and pass the interrupt handler */
    if(request_dma(CH_PPI, "AD9960_PPI_DMA") < 0)
    {
        panic("Unable to attach BlackFin PPI DMA channel\n");
        return -EFAULT;
    }

    if (set_dma_callback_rt(context, CH_PPI, (void*) rt_ad9960_ppi_irq))
    {
	printk(KERN_WARNING "rtad9960: cannot set DMA callback\n");
        return -EFAULT;
    }	
	
    printk("ad9960_open ok\n");

    return 0;
}

static int rt_ad9960_close(struct rtdm_dev_context   *context,
                  rtdm_user_info_t          *user_info)

{
    struct ad9960_ctx *ctx = (struct ad9960_ctx *)context->dev_private;
    struct ad9960_device_t *pdev = ctx->ad9960_info;

    DPRINTK("ad9960_release: close() \n");

    /* After finish DMA, release it. */
    free_dma_rt(context, CH_PPI);
    rtdm_event_destroy(&ctx->irq_event);
    
    pdev->opened = 0;

    printk("ad9960_close return\n");
    return 0;
}

int ad9960_spi_read(struct ad9960_spi *spi, unsigned short data,
                                        unsigned short *buf)
{
        struct spi_transfer t = {
                        .tx_buf = &data,
                        .len = 2,
                };
        struct spi_transfer r = {
                        .rx_buf = buf,
                        .len =2,
                };
        struct spi_message m;
        spi_message_init(&m);
        spi_message_add_tail(&t, &m);
        spi_message_add_tail(&r, &m);

        return spi_sync(spi->spi, &m);
}

int ad9960_spi_write(struct ad9960_spi *spi, unsigned short data)
{
        struct spi_transfer t = {
                        .tx_buf = &data,
                        .len = 2,
                };
        struct spi_message m;
        spi_message_init(&m);
        spi_message_add_tail(&t, &m);

        return spi_sync(spi->spi, &m);
}

static int __devinit ad9960_spi_probe(struct spi_device *spi)
{
        struct ad9960_spi       *chip;
	int i;

        chip = kmalloc(sizeof(struct ad9960_spi), GFP_KERNEL);
        if(!chip) {
                return -ENOMEM;
        }
        dev_set_drvdata(&spi->dev, chip);
        spi->dev.power.power_state = PMSG_ON;

        chip->spi = spi;
        ad9960_info.spi_dev = chip;
	
	 /* Setup AD9960 SPI register */

        ad9960_spi_write(ad9960_info.spi_dev, 0x0100);
        ad9960_spi_write(ad9960_info.spi_dev, 0x05FF);
        ad9960_spi_write(ad9960_info.spi_dev, 0x068E);
        ad9960_spi_write(ad9960_info.spi_dev, 0x078E);
        ad9960_spi_write(ad9960_info.spi_dev, 0x088E);
        ad9960_spi_write(ad9960_info.spi_dev, 0x098E);
        ad9960_spi_write(ad9960_info.spi_dev, 0x0AFF);
        ad9960_spi_write(ad9960_info.spi_dev, 0x0000);
        ad9960_spi_write(ad9960_info.spi_dev, 0x1000);
        ad9960_spi_write(ad9960_info.spi_dev, 0x1100);
        ad9960_spi_write(ad9960_info.spi_dev, 0x1450);
        /* AD9960 Filter clear out */
        ad9960_spi_write(ad9960_info.spi_dev, 0x1E00);

        for(i=0;i<256;i++){
                ad9960_spi_write(ad9960_info.spi_dev, 0x1F00);
        }
	DPRINTK("ad9960_spi_probe OK\n");
        return 0;
}

static int __devexit ad9960_spi_remove(struct spi_device *spi)
{
        struct ad9960_spi *chip = dev_get_drvdata(&spi->dev);
        kfree(chip);
	DPRINTK("ad9960_spi_remove: ok\n");

        return 0;
}

static struct spi_driver ad9960_spi_driver = {
        .driver = {
                .name   = "ad9960-spi",
                .bus    = &spi_bus_type,
                .owner  = THIS_MODULE,
        },
        .probe          = ad9960_spi_probe,
        .remove         = __devexit_p(ad9960_spi_remove),
};



static struct rtdm_device rt_ad9960_dev = {
    struct_version:     RTDM_DEVICE_STRUCT_VER,

    device_flags:       RTDM_NAMED_DEVICE,
    context_size:       sizeof(struct ad9960_ctx),
    device_name:        "rtad9960",
    proc_name:		"rtad9960",	

    open_rt:            NULL, /* Not safe to call in RT context */ 
    open_nrt:           rt_ad9960_open,

    ops: {
        close_rt:       NULL,
        close_nrt:      rt_ad9960_close,

        ioctl_rt:       rt_ad9960_ioctl_rt,
        ioctl_nrt:      rt_ad9960_ioctl_nrt,

        read_rt:        rt_ad9960_read_rt,
        read_nrt:       NULL,

        write_rt:       rt_ad9960_write_rt,
        write_nrt:      NULL,

        recvmsg_rt:     NULL,
        recvmsg_nrt:    NULL,

        sendmsg_rt:     NULL,
        sendmsg_nrt:    NULL,
    },

    device_class:       RTDM_CLASS_EXPERIMENTAL,
    device_sub_class:   222,
    driver_name:        "rt_ad9960",
    driver_version:     RTDM_DRIVER_VER(1, 0, 0),
    peripheral_name:    "AD9960",
    provider_name:      "Analog Devices",
};

static int __init ad9960_init(void)
{
    	int result;

	/* Enable PPI_CLK(PF15) and PPI_FS1(PF9) */
	//bfin_write_PORTF_FER(bfin_read_PORTF_FER() | 0x8200); 
	/* PF8 select AD9960 TX/RX */
	//bfin_write_PORTFIO_DIR(bfin_read_PORTFIO_DIR() | 0x0100);  
	//bfin_write_PORTFIO_SET(bfin_read_PORTFIO_SET() | 0x0100);
	//bfin_write_PORTG_FER(0xFFFF);
	*pPORTF_FER |= 0x8200;    /* Enable PPI_CLK(PF15) and PPI_FS1(PF9) */
	*pPORTFIO_DIR |= 0x0100;  /* PF8 select AD9960 TX/RX */
	*pPORTFIO_SET |= 0x0100;
	*pPORTG_FER = (0xFFFF);

	//bfin_write_TIMER0_CONFIG(bfin_read_TIMER0_CONFIG() | OUT_DIS);
	*pTIMER0_CONFIG |= OUT_DIS;
	__builtin_bfin_ssync();

	/* Configuration information, used as device context */ 
        memset(&ad9960_info, 0, sizeof(struct ad9960_device_t));

	/* The chip is configured using SPI */	
    	result = spi_register_driver(&ad9960_spi_driver);
	if (!ad9960_info.spi_dev)
	{
		printk(KERN_WARNING "rtad9960: cannot register spi driver\n");
		return result;
	}

	result = rtdm_dev_register(&rt_ad9960_dev);
	if (result < 0)
	{
		printk(KERN_WARNING "rtad9960: cannot register rt_ad9960_dev\n");
		return result;
	}

	printk("ad9960: AD9960 driver init OK, irq:%d \n",IRQ_PPI);
	return 0;
}
static void __exit ad9960_exit(void)
{
	spi_unregister_driver(&ad9960_spi_driver);
	rtdm_dev_unregister(&rt_ad9960_dev, 1000); /* Poll delay for open instance */
	printk("ad9960_exit: OK\n");
}

/* Test the driver */

static unsigned short qtoi(unsigned short data)
{
        unsigned short i=0;
        int n;
        /* Bit reversed from 19~6 'q' data to 6~19 'i' data */
        for(n=0;n<14;n++){
                if(data&0x004<<n)
                        i |=  0x8000>>n;
                else
                        i &= ~(0x8000>>n);
        }
        return i;
}

static unsigned int lfsr_state;

static unsigned int lfsr_load(unsigned short value)
{
  // load the lfsr state with the 14 bits 15:2
  lfsr_state = (value>>2)&0x3fff;
}

static unsigned int lfsr_train(unsigned short value){
  //use the current state to calculate the feedback value
  lfsr_state = (~((lfsr_state & 0x0001) ^ ((lfsr_state & 0x0008)>>3))<<20) | lfsr_state;
  //shift and zero out the bit that we are going to get from the input
  lfsr_state = (lfsr_state>>1) & 0xfdfff;

  //now use the input to generate the unknown bit 13
  lfsr_state = lfsr_state | ((value>>2)&0x2000);

}

static unsigned short lfsr_next(void) {
  //use the current state to calculate the feedback value
  lfsr_state = (~((lfsr_state & 0x0001) ^ ((lfsr_state & 0x0008)>>3))<<20) | lfsr_state;
  //shift
  lfsr_state = (lfsr_state>>1) & 0xfffff;
  return ((lfsr_state & 0x3fff) << 2);
}

unsigned short validate_rx_datapath(unsigned short buf_hardware[],
			int num)
{
        int i;
        int step = 1;
        int search = 0;
        unsigned short i_samp=0, q_samp=0;
        unsigned short i_samp_prev=0, q_samp_prev=0;
	int I_error = 0, Q_error = 0;

        for(i=3;i<num-2;i+=step){
          /* construct this loop to work on a sample at a time to aid porting to
             a non-buffered system */
          //find the I/Q sample alignment
          if (step == 1) {
            q_samp_prev = i_samp_prev;
            i_samp_prev = q_samp;
            q_samp = i_samp;
            i_samp = buf_hardware[i];
          } else {
            q_samp_prev = q_samp;
            i_samp_prev = i_samp;
            q_samp = buf_hardware[i-1];
            i_samp = buf_hardware[i];
          }

          if (search == 0) {
            if ((i_samp == qtoi(q_samp)) &&
                ((q_samp&0x7ffc) == ((q_samp_prev>>1)&0x7ffc))){
              step = 2; //change the step size once we have found the I/Q sync
              //printk("Found Q\n");
              search ++;
              lfsr_load(q_samp);
            }
          } else if (search < 8) { //use 8 I/Q samples to train the LFSR
            lfsr_train(q_samp);
            search ++;
          } else {
            //now run and compare the received data with the expacted from the LFSR
            unsigned short tmp = lfsr_next();
            if (i_samp != qtoi(tmp))
	    {
		I_error++;
	    	printk("%6d: I_hw:\t%4x\tI_sw:\t%4x", i, i_samp, qtoi(tmp));
		printk(" <== Error");
                printk("\n");
	    }
            if (q_samp != tmp)
	    {
		Q_error++;
            	printk("%6d: Q_hw:\t%4x\tQ_sw:\t%4x", i+1, q_samp, tmp);
		printk(" <== Error");
            	printk("\n");
	    }
          }
        }
	
	printk("Validate done: %d samples, I_Error: %d, Q_Error: %d\n"
			,num, I_error, Q_error);
        return 0;
}

void ad9960_read_test_proc(void *arg)
{
	struct rtdm_dev_context *context = (struct rtdm_dev_context *)arg;
	struct ad9960_ctx * ctx = (struct ad9960_ctx *)context->dev_private;
	int cnt = 0;
	
	unsigned long long start, end;
	
	ipipe_read_tsc(start);
	for (cnt = 0; cnt < 1; cnt++)
	{	
		rt_ad9960_read_rt (context, NULL, ctx->buf, ctx->count);
	}
	ipipe_read_tsc(end);
	
	printk("read ticks: %lu\n", (unsigned long)(end - start));	
	validate_rx_datapath((unsigned short*)ctx->buf, ctx->count);

        up(&ctx->test_done_sem);
}

void ad9960_write_test_proc(void *arg)
{
	struct rtdm_dev_context *context = (struct rtdm_dev_context *)arg;
	struct ad9960_ctx * ctx = (struct ad9960_ctx *)context->dev_private;
	int i = 0, cnt = 0;
	short *ptr;

	ptr = (short *)ctx->buf;
	for (i = 0; i < ctx->count; i++)
		*ptr++ = i;
	for (cnt = 0; cnt < 1000; cnt++)
	{	
        	rt_ad9960_write_rt (context, NULL, ctx->buf, ctx->count);
	}
        up(&ctx->test_done_sem);
}

module_init(ad9960_init);
module_exit(ad9960_exit);

MODULE_AUTHOR("Aubrey Li");
MODULE_LICENSE("GPL");

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值