/*
* 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");