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