From 56a2e5e0ace395e94f176a90a12d3cfcd8b7f68f Mon Sep 17 00:00:00 2001 From: Philip Balister Date: Wed, 13 Jan 2010 14:35:39 -0500 Subject: [PATCH 3/3] usrp-embedded : Add driver for USRP Embedded FPGA interface. --- arch/arm/mach-omap2/board-overo.c | 176 +++++- drivers/misc/Kconfig | 9 + drivers/misc/Makefile | 1 + drivers/misc/usrp_e.c | 1481 +++++++++++++++++++++++++++++++++++++ include/linux/usrp_e.h | 87 +++ 5 files changed, 1750 insertions(+), 4 deletions(-) create mode 100644 drivers/misc/usrp_e.c create mode 100644 include/linux/usrp_e.h diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c index 8a44c17..8686015 100644 --- a/arch/arm/mach-omap2/board-overo.c +++ b/arch/arm/mach-omap2/board-overo.c @@ -35,6 +35,8 @@ #include #include +#include + #include #include #include @@ -63,6 +65,8 @@ #define OVERO_GPIO_USBH_NRESET 183 #define NAND_BLOCK_SIZE SZ_128K +#define GPMC_CS0_BASE 0x60 +#define GPMC_CS_SIZE 0x30 #define OVERO_SMSC911X_CS 5 #define OVERO_SMSC911X_GPIO 176 @@ -164,7 +168,9 @@ static struct platform_device overo_smsc911x2_device = { static struct platform_device *smsc911x_devices[] = { &overo_smsc911x_device, +#if 0 &overo_smsc911x2_device, +#endif }; static inline void __init overo_init_smsc911x(void) @@ -194,6 +200,7 @@ static inline void __init overo_init_smsc911x(void) /* set up second smsc911x chip */ +#if 0 if (gpmc_cs_request(OVERO_SMSC911X2_CS, SZ_16M, &cs_mem_base2) < 0) { printk(KERN_ERR "Failed request for GPMC mem for smsc911x2\n"); return; @@ -213,6 +220,7 @@ static inline void __init overo_init_smsc911x(void) overo_smsc911x2_resources[1].start = OMAP_GPIO_IRQ(OVERO_SMSC911X2_GPIO); overo_smsc911x2_resources[1].end = 0; +#endif platform_add_devices(smsc911x_devices, ARRAY_SIZE(smsc911x_devices)); } @@ -353,6 +361,16 @@ static struct regulator_consumer_supply overo_vdda_dac_supply = static struct regulator_consumer_supply overo_vdds_dsi_supply = REGULATOR_SUPPLY("vdds_dsi", "omapdss"); +static struct spi_board_info overo_mcspi_board_info[] = { + { + .modalias = "spidev", + .max_speed_hz = 12000000, // 12 MHz + .bus_num = 1, + .chip_select = 0, + .mode = SPI_MODE_1, + }, +}; + static struct mtd_partition overo_nand_partitions[] = { { .name = "xloader", @@ -432,8 +450,8 @@ static struct omap2_hsmmc_info mmc[] = { .mmc = 2, .caps = MMC_CAP_4_BIT_DATA, .gpio_cd = -EINVAL, - .gpio_wp = -EINVAL, - .transceiver = true, + .transceiver = true, + .gpio_wp = 150, .ocr_mask = 0x00100000, /* 3.3V */ }, {} /* Terminator */ @@ -612,6 +630,8 @@ static const struct ehci_hcd_omap_platform_data ehci_pdata __initconst = { static struct omap_board_mux board_mux[] __initdata = { { .reg_offset = OMAP_MUX_TERMINATOR }, }; +#else +#define board_mux NULL #endif static struct omap_musb_board_data musb_board_data = { @@ -626,6 +646,148 @@ static struct omap_musb_board_data musb_board_data = { .power = 100, }; +static void __init usrp1_e_init(void) +{ + unsigned int tmp; + + printk("Setup up gpmc timing.\n"); + +// Set up CS4, data read/write + +#if 1 + // Signal control parameters per chip select + tmp = gpmc_cs_read_reg(4, GPMC_CS_CONFIG1); +// tmp |= (GPMC_CONFIG1_MUXADDDATA); +// tmp |= (GPMC_CONFIG1_WRITETYPE_SYNC); +// tmp |= (GPMC_CONFIG1_READTYPE_SYNC); + tmp |= (GPMC_CONFIG1_FCLK_DIV(0)); + gpmc_cs_write_reg(4, GPMC_CS_CONFIG1, tmp); + printk("GPMC_CONFIG1 reg: %x\n", tmp); +#endif + +#if 1 + // CS signal timing parameter configuration + tmp = 0; + tmp |= GPMC_CONFIG2_CSONTIME(1); /* 1 */ + tmp |= GPMC_CONFIG2_CSWROFFTIME(16); /* 16 */ + tmp |= GPMC_CONFIG2_CSRDOFFTIME(16); /* 16 */ + printk("GPMC_CONFIG2 reg: %x\n", tmp); + gpmc_cs_write_reg(4, GPMC_CS_CONFIG2, tmp); +#endif + +#if 0 + // nADV signal timing parameter configuration + tmp = 0; + tmp |= GPMC_CONFIG3_ADVONTIME(1); + tmp |= GPMC_CONFIG3_ADVRDOFFTIME(2); + tmp |= GPMC_CONFIG3_ADVWROFFTIME(2); + printk("GPMC_CONFIG3 reg: %x\n", tmp); + gpmc_cs_write_reg(4, GPMC_CS_CONFIG3, tmp); +#endif + +#if 1 + // nWE and nOE signals timing parameter configuration + tmp = 0; + tmp |= GPMC_CONFIG4_WEONTIME(3); /* 3 */ + tmp |= GPMC_CONFIG4_WEOFFTIME(16); /* 16 */ + tmp |= GPMC_CONFIG4_OEONTIME(3); /* 3 */ + tmp |= GPMC_CONFIG4_OEOFFTIME(16); /* 16 */ + printk("GPMC_CONFIG4 reg: %x\n", tmp); + gpmc_cs_write_reg(4, GPMC_CS_CONFIG4, tmp); +#endif + +#if 1 + // RdAccess time and Cycle time timing parameters configuration + tmp = 0; + tmp |= GPMC_CONFIG5_PAGEBURSTACCESSTIME(1); + tmp |= GPMC_CONFIG5_RDACCESSTIME(15); /* 15 */ + tmp |= GPMC_CONFIG5_WRCYCLETIME(17); /* 17 */ + tmp |= GPMC_CONFIG5_RDCYCLETIME(17); /* 17 */ + printk("GPMC_CONFIG5 reg: %x\n", tmp); + + gpmc_cs_write_reg(4, GPMC_CS_CONFIG5, tmp); +#endif + +#if 1 + // WrAccessTime WrDataOnADmuxBus, Cycle2Cycle, and BusTurnAround params + tmp = (1<<31); + tmp |= GPMC_CONFIG6_WRACCESSTIME(15); /* 15 */ + tmp |= GPMC_CONFIG6_WRDATAONADMUXBUS(3); + tmp |= GPMC_CONFIG6_CYCLE2CYCLEDELAY(0); + tmp |= GPMC_CONFIG6_BUSTURNAROUND(0); + printk("GPMC_CONFIG6 reg: %x\n", tmp); + gpmc_cs_write_reg(4, GPMC_CS_CONFIG6, tmp); +#endif + +// Configure timing for CS6, wishbone access + +#if 1 + // Signal control parameters per chip select + tmp = gpmc_cs_read_reg(6, GPMC_CS_CONFIG1); +// tmp |= (GPMC_CONFIG1_MUXADDDATA); + tmp |= (GPMC_CONFIG1_WRITETYPE_SYNC); + tmp |= (GPMC_CONFIG1_READTYPE_SYNC); + tmp |= (GPMC_CONFIG1_FCLK_DIV(0)); + gpmc_cs_write_reg(6, GPMC_CS_CONFIG1, tmp); + printk("GPMC_CONFIG1 reg: %x\n", tmp); +#endif + +#if 1 + // CS signal timing parameter configuration + tmp = 0; + tmp |= GPMC_CONFIG2_CSONTIME(1); + tmp |= GPMC_CONFIG2_CSWROFFTIME(17); + tmp |= GPMC_CONFIG2_CSRDOFFTIME(17); + printk("GPMC_CONFIG2 reg: %x\n", tmp); + gpmc_cs_write_reg(6, GPMC_CS_CONFIG2, tmp); +#endif + +#if 0 + // nADV signal timing parameter configuration + tmp = 0; + tmp |= GPMC_CONFIG3_ADVONTIME(1); + tmp |= GPMC_CONFIG3_ADVRDOFFTIME(2); + tmp |= GPMC_CONFIG3_ADVWROFFTIME(2); + printk("GPMC_CONFIG3 reg: %x\n", tmp); + gpmc_cs_write_reg(6, GPMC_CS_CONFIG3, tmp); +#endif + +#if 0 + // nWE and nOE signals timing parameter configuration + tmp = 0; + tmp |= GPMC_CONFIG4_WEONTIME(3); + tmp |= GPMC_CONFIG4_WEOFFTIME(4); + tmp |= GPMC_CONFIG4_OEONTIME(3); + tmp |= GPMC_CONFIG4_OEOFFTIME(4); + printk("GPMC_CONFIG4 reg: %x\n", tmp); + gpmc_cs_write_reg(6, GPMC_CS_CONFIG4, tmp); +#endif + +#if 0 + // RdAccess time and Cycle time timing paraters configuration + tmp = 0; + tmp |= GPMC_CONFIG5_PAGEBURSTACCESSTIME(1); + tmp |= GPMC_CONFIG5_RDACCESSTIME(4); + tmp |= GPMC_CONFIG5_WRCYCLETIME(5); + tmp |= GPMC_CONFIG5_RDCYCLETIME(5); + printk("GPMC_CONFIG5 reg: %x\n", tmp); + gpmc_cs_write_reg(6, GPMC_CS_CONFIG5, tmp); +#endif + +#if 1 + // WrAccessTime WrDataOnADmuxBus, Cycle2Cycle, and BusTurnAround params + tmp = 0; + tmp |= GPMC_CONFIG6_WRACCESSTIME(15); + tmp |= GPMC_CONFIG6_WRDATAONADMUXBUS(3); + tmp |= GPMC_CONFIG6_CYCLE2CYCLEDELAY(3); + tmp |= GPMC_CONFIG6_CYCLE2CYCLESAMECSEN; + tmp |= GPMC_CONFIG6_BUSTURNAROUND(0); + printk("GPMC_CONFIG6 reg: %x\n", tmp); + gpmc_cs_write_reg(6, GPMC_CS_CONFIG6, tmp); +#endif + +} + static void __init overo_init(void) { omap3_mux_init(board_mux, OMAP_PACKAGE_CBB); @@ -637,8 +799,14 @@ static void __init overo_init(void) usb_ehci_init(&ehci_pdata); overo_spi_init(); overo_init_smsc911x(); +#if 0 overo_display_init(); - +#endif + usrp1_e_init(); +#if 1 + spi_register_board_info(overo_mcspi_board_info, + ARRAY_SIZE(overo_mcspi_board_info)); +#endif /* Ensure SDRC pins are mux'd for self-refresh */ omap_mux_init_signal("sdrc_cke0", OMAP_PIN_OUTPUT); omap_mux_init_signal("sdrc_cke1", OMAP_PIN_OUTPUT); @@ -680,7 +848,7 @@ static void __init overo_init(void) "OVERO_GPIO_USBH_CPEN\n"); } -MACHINE_START(OVERO, "Gumstsix Overo") +MACHINE_START(OVERO, "Gumstix Overo") .boot_params = 0x80000100, .map_io = omap3_map_io, .reserve = omap_reserve, diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 4d073f1..8bd6dfb 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -452,6 +452,15 @@ config PCH_PHUB To compile this driver as a module, choose M here: the module will be called pch_phub. +config USRP_E + tristate "USRP-E FPGA interface driver" + default n + help + This driver is for the Ettus Research USRP Embedded Software + Defined Radio platform. + + If you do not plan to run this kernel on that hardware choose N. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 98009cc..f43483e 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o obj-$(CONFIG_C2PORT) += c2port/ obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/ obj-$(CONFIG_HMC6352) += hmc6352.o +obj-$(CONFIG_USRP_E) += usrp_e.o obj-y += eeprom/ obj-y += cb710/ obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o diff --git a/drivers/misc/usrp_e.c b/drivers/misc/usrp_e.c new file mode 100644 index 0000000..2923b14 --- /dev/null +++ b/drivers/misc/usrp_e.c @@ -0,0 +1,1481 @@ +/* + * -*- linux-c -*- + * Interface for USRP Embedded from Ettus Research, LLC. + * This driver uses the GPMC interface on the OMAP3 to pass data + * to/from a Spartan 3 FPGA. + * + * Copyright (C) Ettus Research, LLC + * + * Written by Philip Balister + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "linux/fs.h" +#include "linux/module.h" +#include "linux/cdev.h" +#include "linux/device.h" +#include "linux/spinlock.h" +#include "linux/errno.h" +#include "linux/irq.h" +#include "linux/interrupt.h" +#include "linux/wait.h" +#include "linux/sched.h" +#include "linux/dma-mapping.h" +#include "linux/semaphore.h" +#include "linux/kthread.h" +#include "linux/poll.h" +#include "linux/slab.h" + +#include "plat/gpmc.h" +#include "plat/gpio.h" +#include "plat/dma.h" + +#include "asm/uaccess.h" +#include "asm/io.h" +#include "asm/atomic.h" + +#include "linux/usrp_e.h" + +#define TX_SPACE_AVAILABLE_GPIO 144 +#define RX_DATA_READY_GPIO 146 + +static atomic_t use_count = ATOMIC_INIT(0); +static atomic_t mapped = ATOMIC_INIT(0); +static int shutting_down; + +struct spi_regs_wb; +struct i2c_regs_wb; + +struct usrp_e_dev { + struct cdev cdev; + unsigned long mem_base; + unsigned long control_mem_base; + u32 *ioaddr; + u8 *ctl_addr; + struct spi_regs_wb *ctl_spi; + struct i2c_regs_wb *ctl_i2c; + spinlock_t fpga_lock; + + atomic_t n_overruns; + atomic_t n_underruns; + +} *usrp_e_devp; + +struct dma_data { + int ch; + struct omap_dma_channel_params params; + + unsigned long virt_from; + unsigned long virt_to; + unsigned long phys_from; + unsigned long phys_to; +}; + +#define MISC_REGS_BASE 0x0 + +#define UE_REG_MISC_LED (MISC_REGS_BASE + 0) + +#define UE_REG_MISC_RX_LEN (MISC_REGS_BASE + 10) +#define UE_REG_MISC_TX_LEN (MISC_REGS_BASE + 12) + +#define UE_REG_SLAVE(n) ((n)<<7) +#define UE_REG_SR_ADDR(n) ((UE_REG_SLAVE(5)) + (4*(n))) + +#define UE_REG_CTRL_TX_CLEAR_UNDERRUN UE_REG_SR_ADDR(25) +#define UE_SR_CLEAR_FIFO UE_REG_SR_ADDR(6) + +#define CTL_SPI_BASE 0x100 + +struct spi_regs_wb { + u32 txrx0; + u32 txrx1; + u32 txrx2; + u32 txrx3; + u32 ctrl; + u32 div; + u32 ss; +}; + +/* Defines for spi ctrl register */ +#define UE_SPI_CTRL_ASS (BIT(13)) +#define UE_SPI_CTRL_IE (BIT(12)) +#define UE_SPI_CTRL_LSB (BIT(11)) +/* defines for TXNEG and RXNEG in usrp_e.h so user can pass them to driver. */ +#define UE_SPI_CTRL_GO_BSY (BIT(8)) +#define UE_SPI_CTRL_CHAR_LEN_MASK 0x7f + + +#define CTL_I2C_BASE 0x180 +#if 1 +struct i2c_regs_wb { + u8 prescalar_lo; + u8 dummy; + u8 dummy1; + u8 dummy2; + u8 prescalar_hi; + u8 dummy3; + u8 dummy4; + u8 dummy5; + u8 ctrl; + u8 dummy6; + u8 dummy7; + u8 dummy8; + u8 data; + u8 dummy9; + u8 dummy10; + u8 dummy11; + u8 cmd_status; +}; +#else +struct i2c_regs_wb { + u16 prescalar_lo; + u16 dummy2; + u16 prescalar_hi; + u16 dummy3; + u16 ctrl; + u16 dummy6; + u16 data; + u16 dummy9; + u16 cmd_status; +}; +#endif + +#define I2C_CTRL_EN (BIT(7)) /* core enable */ +#define I2C_CTRL_IE (BIT(6)) /* interrupt enable */ + +/* STA, STO, RD, WR, and IACK bits are cleared automatically */ + +#define I2C_CMD_START (BIT(7)) +#define I2C_CMD_STOP (BIT(6)) +#define I2C_CMD_RD (BIT(5)) +#define I2C_CMD_WR (BIT(4)) +#define I2C_CMD_NACK (BIT(3)) +#define I2C_CMD_RSVD_2 (BIT(2)) +#define I2C_CMD_RSVD_1 (BIT(1)) +#define I2C_CMD_IACK (BIT(0)) + +#define I2C_ST_RXACK (BIT(7)) +#define I2C_ST_BUSY (BIT(6)) +#define I2C_ST_AL (BIT(5)) +#define I2C_RSVD_4 (BIT(4)) +#define I2C_RSVD_3 (BIT(3)) +#define I2C_RSVD_2 (BIT(2)) +#define I2C_ST_TIP (BIT(1)) +#define I2C_ST_IP (BIT(0)) + +#define MAX_WB_DIV 4 +#define MASTER_CLK_RATE 64000000 +#define PRESCALAR(wb_div) (((MASTER_CLK_RATE/(wb_div)) / (5 * 100000)) - 1) + +static __u16 prescalar_values[MAX_WB_DIV+1] = { + 0xffff, + PRESCALAR(1), + PRESCALAR(2), + PRESCALAR(3), + PRESCALAR(4), +}; + +static struct dma_data *rx_dma; +static struct dma_data *tx_dma; + +struct ring_buffer_entry { + unsigned long dma_addr; + __u8 *frame_addr; +}; + +struct ring_buffer { + struct ring_buffer_info (*rbi)[]; + struct ring_buffer_entry (*rbe)[]; + int num_pages; + unsigned long (*pages)[]; +}; + +static struct ring_buffer tx_rb; +static struct ring_buffer rx_rb; + +static struct usrp_e_ring_buffer_size_t rb_size; + +#define NUM_PAGES_RX_FLAGS 1 +#define NUM_RX_FRAMES 100 +#define NUM_PAGES_TX_FLAGS 1 +#define NUM_TX_FRAMES 100 + +static int tx_rb_write; +static int tx_rb_read; +static int rx_rb_write; +static int rx_rb_read; + +static int alloc_ring_buffer(struct ring_buffer *rb, + unsigned int num_bufs, enum dma_data_direction direction); +static void delete_ring_buffer(struct ring_buffer *rb, + unsigned int num_bufs, enum dma_data_direction direction); +static int alloc_ring_buffers(void); +static void init_ring_buffer(struct ring_buffer *rb, int num_bufs, + int init_flags, enum dma_data_direction direction); + +static dev_t usrp_e_dev_number; +static struct class *usrp_e_class; + +#define DEVICE_NAME "usrp_e" + +static const struct file_operations usrp_e_fops; + +static irqreturn_t space_available_irqhandler(int irq, void *dev_id); +static irqreturn_t data_ready_irqhandler(int irq, void *dev_id); +static void usrp_rx_dma_irq(int ch, u16 stat, void *data); +static void usrp_tx_dma_irq(int ch, u16 stat, void *data); + +static DECLARE_WAIT_QUEUE_HEAD(data_received_queue); +static DECLARE_WAIT_QUEUE_HEAD(space_available_queue); +static DECLARE_WAIT_QUEUE_HEAD(received_data_from_user); +static DECLARE_WAIT_QUEUE_HEAD(tx_rb_space_available); + +static int tx_dma_waiting_for_data; +static int waiting_for_space_in_tx_rb; + +#define DEBUG_RX 1 + +static DEFINE_SEMAPHORE(dma_lock); + +static void usrp_e_spi_init(void); +static void usrp_e_i2c_init(void); + +static int init_dma_controller(void); +static void release_dma_controller(void); +static int get_frame_from_fpga_start(void); +static int get_frame_from_fpga_finish(void); +static int send_frame_to_fpga_start(void); +static int send_frame_to_fpga_finish(void); + +static int rx_dma_active; +static int tx_dma_active; + +static int __init +usrp_e_init(void) +{ + int ret; + struct usrp_e_dev *p; + + printk(KERN_DEBUG "usrp_e entering driver initialization\n"); + + if (alloc_chrdev_region(&usrp_e_dev_number, 0, 1, DEVICE_NAME) < 0) { + printk(KERN_DEBUG "Can't register device\n"); + return -1; + } + + usrp_e_class = class_create(THIS_MODULE, DEVICE_NAME); + + usrp_e_devp = kzalloc(sizeof(struct usrp_e_dev), GFP_KERNEL); + if (!usrp_e_devp) { + printk(KERN_ERR "Bad kmalloc\n"); + return -ENOMEM; + } + + p = usrp_e_devp; /* Shorten var name so I stay sane. */ + + printk(KERN_DEBUG "usrp_e data struct malloc'd.\n"); + + atomic_set(&p->n_underruns, 0); + atomic_set(&p->n_overruns, 0); + + printk(KERN_DEBUG "usrp_e Data initialized..\n"); + + cdev_init(&p->cdev, &usrp_e_fops); + p->cdev.owner = THIS_MODULE; + + ret = cdev_add(&p->cdev, MKDEV(MAJOR(usrp_e_dev_number), 0), 1); + if (ret) { + printk(KERN_ERR "Bad cdev\n"); + return ret; + } + + printk(KERN_DEBUG "usrp_e major number : %d\n", + MAJOR(usrp_e_dev_number)); + device_create(usrp_e_class, NULL, MKDEV(MAJOR(usrp_e_dev_number), 0), + NULL, "usrp_e%d", 0); + + printk(KERN_DEBUG "Getting Chip Select\n"); + + if (gpmc_cs_request(4, SZ_2K, &p->mem_base) < 0) { + printk(KERN_ERR "Failed request for GPMC mem for usrp_e\n"); + return -1; + } + printk(KERN_DEBUG "Got CS4, address = %lx\n", p->mem_base); + + if (!request_mem_region(p->mem_base, SZ_2K, "usrp_e")) { + printk(KERN_ERR "Request_mem_region failed.\n"); + gpmc_cs_free(4); + return -1; + } + + p->ioaddr = ioremap(p->mem_base, SZ_2K); + spin_lock_init(&p->fpga_lock); + + if (gpmc_cs_request(6, SZ_2K, &p->control_mem_base) < 0) { + printk(KERN_ERR "Failed request for GPMC control mem for usrp_e\n"); + return -1; + } + printk(KERN_DEBUG "Got CS6, address = %lx\n", p->control_mem_base); + + if (!request_mem_region(p->control_mem_base, SZ_2K, "usrp_e_c")) { + printk(KERN_ERR "Request_mem_region failed.\n"); + gpmc_cs_free(6); + return -1; + } + + p->ctl_addr = ioremap_nocache(p->control_mem_base, SZ_2K); + + /* Initialize wishbone SPI and I2C interfaces */ + + usrp_e_spi_init(); + usrp_e_i2c_init(); + + /* Configure GPIO's */ + + if (!(((gpio_request(TX_SPACE_AVAILABLE_GPIO, + "TX_SPACE_AVAILABLE_GPIO") == 0) && + (gpio_direction_input(TX_SPACE_AVAILABLE_GPIO) == 0)))) { + printk(KERN_ERR "Could not claim GPIO for TX_SPACE_AVAILABLE_GPIO\n"); + return -1; + } + + if (!(((gpio_request(RX_DATA_READY_GPIO, "RX_DATA_READY_GPIO") == 0) && + (gpio_direction_input(RX_DATA_READY_GPIO) == 0)))) { + printk(KERN_ERR "Could not claim GPIO for RX_DATA_READY_GPIO\n"); + return -1; + } + + /* Debug gpios */ + if (!(((gpio_request(14, "Debug0") == 0) && + (gpio_direction_output(14, 0) == 0)))) { + printk(KERN_ERR "Could not claim GPIO for Debug0\n"); + return -1; + } + + if (!(((gpio_request(21, "Debug1") == 0) && + (gpio_direction_output(21, 0) == 0)))) { + printk(KERN_ERR "Could not claim GPIO for Debug1\n"); + return -1; + } + + if (!(((gpio_request(22, "Debug2") == 0) && + (gpio_direction_output(22, 0) == 0)))) { + printk(KERN_ERR "Could not claim GPIO for Debug2\n"); + return -1; + } + + if (!(((gpio_request(23, "Debug3") == 0) && + (gpio_direction_output(23, 0) == 0)))) { + printk(KERN_ERR "Could not claim GPIO for Debug3\n"); + return -1; + } + + + rb_size.num_pages_rx_flags = NUM_PAGES_RX_FLAGS; + rb_size.num_rx_frames = NUM_RX_FRAMES; + rb_size.num_pages_tx_flags = NUM_PAGES_TX_FLAGS; + rb_size.num_tx_frames = NUM_TX_FRAMES; + + ret = alloc_ring_buffers(); + if (ret < 0) + return ret; + + /* Initialize various DMA related flags */ + rx_dma_active = 0; + tx_dma_active = 0; + shutting_down = 0; + + printk(KERN_DEBUG "usrp_e Driver Initialized.\n"); + + return 0; +} + +static void __exit +usrp_e_cleanup(void) +{ + struct usrp_e_dev *p = usrp_e_devp; + + unregister_chrdev_region(usrp_e_dev_number, 1); + + release_mem_region(p->mem_base, SZ_2K); + release_mem_region(p->control_mem_base, SZ_2K); + + device_destroy(usrp_e_class, MKDEV(MAJOR(usrp_e_dev_number), 0)); + cdev_del(&p->cdev); + + class_destroy(usrp_e_class); + + iounmap(p->ioaddr); + iounmap(p->ctl_addr); + + gpmc_cs_free(4); + gpmc_cs_free(6); + + printk(KERN_DEBUG "Freeing gpios\n"); + + gpio_free(TX_SPACE_AVAILABLE_GPIO); + gpio_free(RX_DATA_READY_GPIO); + + /* debug */ + gpio_free(14); + gpio_free(21); + gpio_free(22); + gpio_free(23); + + delete_ring_buffer(&tx_rb, rb_size.num_tx_frames, DMA_TO_DEVICE); + delete_ring_buffer(&rx_rb, rb_size.num_rx_frames, DMA_FROM_DEVICE); + + kfree(p); + + printk(KERN_DEBUG "Leaving cleanup\n"); +} + +static int +usrp_e_open(struct inode *inode, struct file *file) +{ + struct usrp_e_dev *p = usrp_e_devp; + int ret; + + printk(KERN_DEBUG "usrp_e open called, use_count = %d\n", + atomic_read(&use_count)); + if (atomic_add_return(1, &use_count) != 1) { + printk(KERN_ERR "use_count = %d\n", atomic_read(&use_count)); + atomic_dec(&use_count); + return -EBUSY; + } + + /* Clear rx and tx fifos in the fpga */ + writel(1, p->ctl_addr + UE_SR_CLEAR_FIFO); + writel(2, p->ctl_addr + UE_SR_CLEAR_FIFO); + +#if 0 + usrp_e_devp = container_of(inode->i_cdev, struct usrp_e_dev, cdev); + + file->private_data = usrp_e_devp; +#endif + + ret = init_dma_controller(); + if (ret < 0) + return ret; + + tx_rb_write = 0; + tx_rb_read = 0; + + rx_rb_write = 0; + rx_rb_read = 0; + + tx_dma_active = 0; + rx_dma_active = 0; + shutting_down = 0; + + init_ring_buffer(&rx_rb, rb_size.num_rx_frames, RB_KERNEL, DMA_FROM_DEVICE); + init_ring_buffer(&tx_rb, rb_size.num_tx_frames, RB_KERNEL, DMA_TO_DEVICE); + + /* Configure interrupts for GPIO pins */ + + ret = request_irq(gpio_to_irq(TX_SPACE_AVAILABLE_GPIO), + space_available_irqhandler, + IRQF_TRIGGER_RISING, "usrp_e_space_available", NULL); + + ret = request_irq(gpio_to_irq(RX_DATA_READY_GPIO), + data_ready_irqhandler, + IRQF_TRIGGER_RISING, "usrp_e_data_ready", NULL); + + printk(KERN_DEBUG "usrp: leaving open\n"); + return 0; +} + +static int +usrp_e_release(struct inode *inode, struct file *file) +{ + struct usrp_e_dev *usrp_e_devp = file->private_data; + + printk(KERN_DEBUG "usrp_e release called\n"); + + if (atomic_read(&use_count) != 1) { + printk(KERN_ERR "Attempt to close usrp_e driver that is not open"); + return -ENOENT; + } + + printk(KERN_DEBUG "Waiting for DMA to become inactive\n"); + shutting_down = 0; + while (tx_dma_active || rx_dma_active) + cpu_relax(); + + /* Freeing gpio irq's */ + printk(KERN_DEBUG "Freeing gpio irq's\n"); + + free_irq(gpio_to_irq(TX_SPACE_AVAILABLE_GPIO), NULL); + free_irq(gpio_to_irq(RX_DATA_READY_GPIO), NULL); + + printk(KERN_DEBUG "Freeing DMA channels\n"); + + release_dma_controller(); + + usrp_e_devp = 0; + + atomic_dec(&use_count); + + return 0; +} + +static ssize_t +usrp_e_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + size_t ret; + int rx_pkt_len; + + if (count > SZ_2K) + return -EMSGSIZE; + + if (!((*rx_rb.rbi)[rx_rb_read].flags & RB_USER)) { + if (wait_event_interruptible(data_received_queue, + (((*rx_rb.rbi)[rx_rb_read].flags & RB_USER)))) + return -EINTR; + } + + rx_pkt_len = (*rx_rb.rbi)[rx_rb_read].len; + ret = copy_to_user(buf, (*rx_rb.rbe)[rx_rb_read].frame_addr, rx_pkt_len); + + (*rx_rb.rbi)[rx_rb_read].flags = RB_KERNEL; + + rx_rb_read++; + if (rx_rb_read == rb_size.num_rx_frames) + rx_rb_read = 0; + + get_frame_from_fpga_start(); + + return ((ret == 0) ? rx_pkt_len : -ret); +} + +static ssize_t +usrp_e_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + size_t ret; + +#if 0 +// printk("In write.\n"); + + /* Trigger a DMA transfer. Used to start transmit after writing */ + /* data into the transmit ring buffer from user space */ + if (count < 0) { + send_frame_to_fpga_start(); + return 0; + } + + if (count > SZ_2K) + return -EMSGSIZE; + +// printk("Write flags: %d\n", (*tx_rb.rbe)[tx_rb_write].flags); + if (!((*tx_rb.rbi)[tx_rb_write].flags & RB_KERNEL)) { + waiting_for_space_in_tx_rb = 1; +// printk("Sleeping\n"); + if (wait_event_interruptible(tx_rb_space_available, + ((*tx_rb.rbi)[tx_rb_write].flags & RB_KERNEL))) + return -EINTR; +// printk("Waking\n"); + } + + ret = copy_from_user((*tx_rb.rbe)[tx_rb_write].frame_addr, buf, count); + if (ret) + return -ret; + + (*tx_rb.rbi)[tx_rb_write].len = count; + (*tx_rb.rbi)[tx_rb_write].flags = RB_USER; + + tx_rb_write++; + if (tx_rb_write == rb_size.num_tx_frames) + tx_rb_write = 0; + +// printk("Calling send_to_fpga_start from write\n"); +#endif + send_frame_to_fpga_start(); + + return count; +} + +static loff_t +usrp_e_llseek(struct file *file, loff_t offest, int orig) +{ + printk(KERN_DEBUG "usrp_e llseek called\n"); + + return 0; +} + +static int usrp_e_ctl16(unsigned long arg, int direction) +{ + struct usrp_e_ctl16 __user *argp = (struct usrp_e_ctl16 __user *) arg; + int i; + struct usrp_e_ctl16 ctl; + + if (copy_from_user(&ctl, argp, sizeof(struct usrp_e_ctl16))) + return -EFAULT; + + if (ctl.count > 10) + return -EINVAL; + + if (direction == 0) { + for (i = 0; i < ctl.count; i++) + writew(ctl.buf[i], &usrp_e_devp->ctl_addr \ + [i + ctl.offset]); + } else if (direction == 1) { + for (i = 0; i < ctl.count; i++) + ctl.buf[i] = readw(&usrp_e_devp->ctl_addr \ + [i + ctl.offset]); + + if (copy_to_user(argp, &ctl, sizeof(struct usrp_e_ctl16))) + return -EFAULT; + } else + return -EFAULT; + + return 0; +} + +static int usrp_e_ctl32(unsigned long arg, int direction) +{ + struct usrp_e_ctl32 __user *argp = (struct usrp_e_ctl32 __user *) arg; + int i; + struct usrp_e_ctl32 ctl; + + if (copy_from_user(&ctl, argp, sizeof(struct usrp_e_ctl32))) + return -EFAULT; + + if (ctl.count > 20) + return -EINVAL; + + if (direction == 0) { + for (i = 0; i < ctl.count; i++) + writel(ctl.buf[i], &usrp_e_devp->ctl_addr \ + [i + ctl.offset]); + } else if (direction == 1) { + for (i = 0; i < ctl.count; i++) + ctl.buf[i] = readl(&usrp_e_devp->ctl_addr \ + [i + ctl.offset]); + + if (copy_to_user(argp, &ctl, sizeof(struct usrp_e_ctl16))) + return -EFAULT; + + } else + return -EFAULT; + + return 0; +} + +static int usrp_e_get_rb_info(unsigned long arg) +{ + struct usrp_e_ring_buffer_size_t __user *argp = (struct usrp_e_ring_buffer_size_t __user *) arg; + int i; + + if (copy_to_user(argp, &rb_size, sizeof(rb_size))) + return -EFAULT; + + return 0; +} + +static void usrp_e_spi_init() +{ + struct usrp_e_dev *p = usrp_e_devp; + + p->ctl_spi = (struct spi_regs_wb *)(p->ctl_addr + CTL_SPI_BASE); + p->ctl_spi->div = 64; /* 1 = Div by 4 (12.5 MHz) */ +} + +static int usrp_e_spi_wait(void) +{ + struct usrp_e_dev *p = usrp_e_devp; + + while (p->ctl_spi->ctrl & UE_SPI_CTRL_GO_BSY) { + if (signal_pending(current)) { + printk(KERN_DEBUG "Signal received.\n"); + set_current_state(TASK_RUNNING); + return -EINTR; + } + schedule(); + } + + return 0; +} + +static int usrp_e_spi(unsigned long __user arg) +{ + struct usrp_e_dev *p = usrp_e_devp; + struct usrp_e_spi __user *argp = (struct usrp_e_spi __user *) arg; + struct usrp_e_spi spi_cmd; + int ctrl, ret; + + if (copy_from_user(&spi_cmd, argp, sizeof(struct usrp_e_spi))) + return -EFAULT; + + spi_cmd.flags &= (UE_SPI_CTRL_TXNEG | UE_SPI_CTRL_RXNEG); + ctrl = UE_SPI_CTRL_ASS | (UE_SPI_CTRL_CHAR_LEN_MASK & spi_cmd.length) \ + | spi_cmd.flags; + + ret = usrp_e_spi_wait(); + if (ret < 0) + return ret; + + p->ctl_spi->ss = spi_cmd.slave & 0xff; + + p->ctl_spi->txrx0 = spi_cmd.data; + + p->ctl_spi->ctrl = ctrl; + p->ctl_spi->ctrl = ctrl | UE_SPI_CTRL_GO_BSY; + + if (spi_cmd.readback) { + usrp_e_spi_wait(); + if (copy_to_user(&argp->data, &p->ctl_spi->txrx0, + sizeof(__u32))) + return -EFAULT; + else + return 0; + } else + return 0; + +} + +static void usrp_e_i2c_init() +{ + struct usrp_e_dev *p = usrp_e_devp; + int wb_div; + + p->ctl_i2c = (struct i2c_regs_wb *)(p->ctl_addr + CTL_I2C_BASE); + + writeb(0, &p->ctl_i2c->ctrl); /* disable core */ + + /* Assume wb_div is 4, deal with this later */ + wb_div = 4; + if (wb_div > MAX_WB_DIV) + wb_div = MAX_WB_DIV; + + writeb((prescalar_values[wb_div] & 0xff), &p->ctl_i2c->prescalar_lo); + writeb(((prescalar_values[wb_div] >> 8) & 0xff), + &p->ctl_i2c->prescalar_hi); + writeb(I2C_CTRL_EN, &p->ctl_i2c->ctrl); /* enable core */ +} + +static int usrp_e_i2c_wait(__u32 mask, int chk_ack) +{ + struct usrp_e_dev *p = usrp_e_devp; + + while (readb(&p->ctl_i2c->cmd_status) & mask) { + if (signal_pending(current)) { + printk(KERN_DEBUG "Signal received.\n"); + set_current_state(TASK_RUNNING); + return -EINTR; + } + schedule(); + } + + if (chk_ack) { + if ((readb(&p->ctl_i2c->cmd_status) & I2C_ST_RXACK) == 0) + return 1; + else + return 0; + } + + return 0; +} + +static int usrp_e_i2c(unsigned long arg, int direction) +{ + struct usrp_e_dev *p = usrp_e_devp; + struct usrp_e_i2c __user *argp = (struct usrp_e_i2c __user *) arg; + struct usrp_e_i2c tmp; + struct usrp_e_i2c *i2c_msg; + int ret, len, i; + + if (copy_from_user(&tmp, argp, sizeof(struct usrp_e_i2c))) + return -EFAULT; + + i2c_msg = kmalloc(sizeof(struct usrp_e_i2c) + tmp.len, GFP_KERNEL); + if (!i2c_msg) + return -ENOMEM; + + if (copy_from_user(i2c_msg, argp, + (sizeof(struct usrp_e_i2c) + tmp.len))) + return -EFAULT; + + if (direction) { + /* read */ + if (i2c_msg->len == 0) + return 1; + + usrp_e_i2c_wait(I2C_ST_BUSY, 0); + + writeb(((i2c_msg->addr << 1) | 1), &p->ctl_i2c->data); + writeb((I2C_CMD_WR | I2C_CMD_START), &p->ctl_i2c->cmd_status); + ret = usrp_e_i2c_wait(I2C_ST_TIP, 1); + if (ret < 0) { + return ret; + } else if (ret == 0) { + writeb(I2C_CMD_STOP, &p->ctl_i2c->cmd_status); + return 2; + } + + for (len = i2c_msg->len, i = 0; len > 0; i++, len--) { + writeb((I2C_CMD_RD | ((len == 1) ? + (I2C_CMD_NACK | I2C_CMD_STOP) : 0)), + &p->ctl_i2c->cmd_status); + usrp_e_i2c_wait(I2C_ST_TIP, 0); + i2c_msg->data[i] = readb(&p->ctl_i2c->data); + } + if (copy_to_user(argp, i2c_msg, (sizeof(struct usrp_e_i2c) + + tmp.len))) + return -EFAULT; + } else { + /* write */ + usrp_e_i2c_wait(I2C_ST_BUSY, 0); + writeb(((i2c_msg->addr << 1) | 0), &p->ctl_i2c->data); + writeb((I2C_CMD_WR | I2C_CMD_START | + (i2c_msg->len == 0 ? I2C_CMD_STOP : 0)), + &p->ctl_i2c->cmd_status); + ret = usrp_e_i2c_wait(I2C_ST_TIP, 1); + if (ret < 0) { + return ret; + } else if (ret == 0) { + writeb(I2C_CMD_STOP, &p->ctl_i2c->cmd_status); + return 2; + } + for (len = i2c_msg->len, i = 0; len > 0; i++, len--) { + writeb(i2c_msg->data[i], &p->ctl_i2c->data); + writeb((I2C_CMD_WR | (len == 1 ? I2C_CMD_STOP : 0)), + &p->ctl_i2c->cmd_status); + ret = usrp_e_i2c_wait(I2C_ST_TIP, 1); + if (ret < 0) { + return ret; + } else if (ret == 0) { + writeb(I2C_CMD_STOP, &p->ctl_i2c->cmd_status); + return 2; + } + } + + } + + + return 1; +} + +static int usrp_e_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + + switch (cmd) { + case USRP_E_WRITE_CTL16: + return usrp_e_ctl16(arg, 0); + + case USRP_E_READ_CTL16: + return usrp_e_ctl16(arg, 1); + + case USRP_E_WRITE_CTL32: + return usrp_e_ctl32(arg, 0); + + case USRP_E_READ_CTL32: + return usrp_e_ctl32(arg, 1); + + case USRP_E_SPI: + return usrp_e_spi(arg); + + case USRP_E_I2C_WRITE: + return usrp_e_i2c(arg, 0); + + case USRP_E_I2C_READ: + return usrp_e_i2c(arg, 1); + + case USRP_E_GET_RB_INFO: + return usrp_e_get_rb_info(arg); + + default: + return -ENOTTY; + } + + return 0; +} + +static unsigned int usrp_e_poll(struct file *filp, poll_table *wait) +{ + unsigned int mask = 0; + + poll_wait(filp, &data_received_queue, wait); + poll_wait(filp, &tx_rb_space_available, wait); + + // Make sure write is active before sleeping + send_frame_to_fpga_start(); + + /* Make sure to read in case the rx ring buffer is full */ + get_frame_from_fpga_start(); + + // This likely needs some locking. The pointer is incremented + // before the flag state is updated. + + if (rx_rb_write == 0) { + if ((*rx_rb.rbi)[rb_size.num_rx_frames - 1].flags & RB_USER) + mask |= POLLIN | POLLRDNORM; + } else { + if ((*rx_rb.rbi)[rx_rb_write - 1].flags & RB_USER) + mask |= POLLIN | POLLRDNORM; + } + + if (tx_rb_read == 0) { + if ((*tx_rb.rbi)[rb_size.num_tx_frames - 1].flags & RB_KERNEL) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((*tx_rb.rbi)[tx_rb_read - 1].flags & RB_KERNEL) + mask |= POLLOUT | POLLWRNORM; + } + + return mask; + +} + +/* The mmap code is based on code in af_packet.c */ + +static void usrp_e_mm_open(struct vm_area_struct *vma) +{ + + atomic_inc(&mapped); +} + +static void usrp_e_mm_close(struct vm_area_struct *vma) +{ + + atomic_dec(&mapped); +} + +static const struct vm_operations_struct usrp_e_mmap_ops = { + .open = usrp_e_mm_open, + .close = usrp_e_mm_close, +}; + +static int usrp_e_mmap(struct file *filp, struct vm_area_struct *vma) +{ + unsigned long size, expected_size; + unsigned int i; + unsigned long start; + int err; + struct page *page; + + printk("In mmap\n"); + + if (vma->vm_pgoff) + return -EINVAL; + + /* Verify the user will map the entire tx and rx ring buffer space */ + expected_size = (rb_size.num_rx_frames + rb_size.num_tx_frames) * (PAGE_SIZE >> 1) + + (rb_size.num_pages_rx_flags + rb_size.num_pages_tx_flags) * PAGE_SIZE; + + size = vma->vm_end - vma->vm_start; + printk(KERN_DEBUG "Size = %d, expected sixe = %d\n", size, expected_size); + + if (size != expected_size) + return -EINVAL; + + start = vma->vm_start; + + page = virt_to_page(rx_rb.rbi); + err = vm_insert_page(vma, start, page); + if (err) + return -EINVAL; + + start += PAGE_SIZE; + + for (i = 0; i < rx_rb.num_pages; ++i) { + struct page *page = virt_to_page((*rx_rb.pages)[i]); + err = vm_insert_page(vma, start, page); + if (err) + return -EINVAL; + + start += PAGE_SIZE; + } + + page = virt_to_page(tx_rb.rbi); + err = vm_insert_page(vma, start, page); + if (err) + return -EINVAL; + + start += PAGE_SIZE; + + for (i = 0; i < tx_rb.num_pages; ++i) { + struct page *page = virt_to_page((*tx_rb.pages)[i]); + + err = vm_insert_page(vma, start, page); + if (err) + return err; + + start += PAGE_SIZE; + } + + vma->vm_ops = &usrp_e_mmap_ops; + + return 0; +} + +static const struct file_operations usrp_e_fops = { + .owner = THIS_MODULE, + .open = usrp_e_open, + .release = usrp_e_release, + .read = usrp_e_read, + .write = usrp_e_write, + .llseek = usrp_e_llseek, + .unlocked_ioctl = usrp_e_ioctl, + .poll = usrp_e_poll, + .mmap = usrp_e_mmap, +}; + +MODULE_VERSION("0.1"); +MODULE_ALIAS(DEVICE_NAME); +MODULE_DESCRIPTION(DEVICE_NAME); +MODULE_AUTHOR("Philip Balister "); +MODULE_LICENSE("GPL v2"); + +module_init(usrp_e_init); +module_exit(usrp_e_cleanup); + +static irqreturn_t space_available_irqhandler(int irq, void *dev_id) +{ + int serviced = IRQ_NONE; + +#ifdef DEBUG_TX + gpio_set_value(22, 1); +#endif + +// printk("Calling send_to_fpga_start from space_available irq\n"); + send_frame_to_fpga_start(); + + serviced = IRQ_HANDLED; + +#ifdef DEBUG_TX + gpio_set_value(22, 0); +#endif + + return serviced; +} + +static void usrp_rx_dma_irq(int ch, u16 stat, void *data) +{ + +#ifdef DEBUG_RX + gpio_set_value(23, 1); +#endif + + rx_dma_active = 0; + + get_frame_from_fpga_finish(); + +#ifdef DEBUG_RX + gpio_set_value(23, 0); +#endif +} + +static void usrp_tx_dma_irq(int ch, u16 stat, void *data) +{ + +#ifdef DEBUG_TX + gpio_set_value(23, 1); +#endif + tx_dma_active = 0; + + send_frame_to_fpga_finish(); + +#ifdef DEBUG_TX + gpio_set_value(23, 0); +#endif + +/* Save + gpio_set_value(21, 1); + gpio_set_value(21, 0); +*/ + +} + +static irqreturn_t data_ready_irqhandler(int irq, void *dev_id) +{ + int serviced = IRQ_NONE; + +#ifdef DEBUG_RX + gpio_set_value(22, 1); +#endif + + get_frame_from_fpga_start(); + + serviced = IRQ_HANDLED; + +#ifdef DEBUG_RX + gpio_set_value(22, 0); +#endif + return serviced; +} + +static int init_dma_controller() +{ + struct usrp_e_dev *p = usrp_e_devp; + + rx_dma = kzalloc(sizeof(struct dma_data), GFP_KERNEL); + if (!rx_dma) { + printk(KERN_ERR "Failed to allocate memory for rx_dma struct."); + return -ENOMEM; + } + + if (omap_request_dma(OMAP_DMA_NO_DEVICE, "usrp-e-rx", + usrp_rx_dma_irq, (void *) rx_dma, &rx_dma->ch)) { + printk(KERN_ERR "Could not get rx DMA channel for usrp_e\n"); + return -ENOMEM; + } + printk(KERN_DEBUG "rx_dma->ch %d\n", rx_dma->ch); + + rx_dma->phys_from = p->mem_base; + + memset(&rx_dma->params, 0, sizeof(rx_dma->params)); + rx_dma->params.data_type = OMAP_DMA_DATA_TYPE_S16; + + rx_dma->params.src_amode = OMAP_DMA_AMODE_CONSTANT; + rx_dma->params.dst_amode = OMAP_DMA_AMODE_POST_INC; + + rx_dma->params.src_start = p->mem_base; + rx_dma->params.dst_start = rx_dma->phys_to; + + rx_dma->params.src_ei = 1; + rx_dma->params.src_fi = 1; + rx_dma->params.dst_ei = 1; + rx_dma->params.dst_fi = 1; + + rx_dma->params.elem_count = 1024; + rx_dma->params.frame_count = 1; + + rx_dma->params.read_prio = DMA_CH_PRIO_HIGH; + rx_dma->params.write_prio = DMA_CH_PRIO_LOW; + + omap_set_dma_params(rx_dma->ch, &rx_dma->params); + +// Play with these with a real application +//G omap_set_dma_src_burst_mode(rx_dma->ch, OMAP_DMA_DATA_BURST_16); +// omap_set_dma_dest_burst_mode(rx_dma->ch, OMAP_DMA_DATA_BURST_16); + +#if 0 // Need to find implentations of the endian calls + omap_set_dma_src_endian_type(rx_dma->ch, OMAP_DMA_BIG_ENDIAN); + omap_set_dma_dst_endian_type(rx_dma->ch, OMAP_DMA_LITTLE_ENDIAN); +#endif + + tx_dma = kzalloc(sizeof(struct dma_data), GFP_KERNEL); + if (!tx_dma) { + printk(KERN_ERR "Failed to allocate memory for tx_dma struct."); + return -ENOMEM; + } + + if (omap_request_dma(OMAP_DMA_NO_DEVICE, "usrp-e-tx", + usrp_tx_dma_irq, (void *) tx_dma, &tx_dma->ch)) { + printk(KERN_ERR "Could not get tx DMA channel for usrp_e\n"); + return -ENOMEM; + } + + printk(KERN_DEBUG "tx_dma->ch %d\n", tx_dma->ch); + + tx_dma->phys_from = p->mem_base; + + memset(&tx_dma->params, 0, sizeof(tx_dma->params)); + tx_dma->params.data_type = OMAP_DMA_DATA_TYPE_S16; + + tx_dma->params.src_amode = OMAP_DMA_AMODE_POST_INC; + tx_dma->params.dst_amode = OMAP_DMA_AMODE_CONSTANT; + + tx_dma->params.src_start = tx_dma->phys_from; + tx_dma->params.dst_start = p->mem_base; + + tx_dma->params.src_ei = 1; + tx_dma->params.src_fi = 1; + tx_dma->params.dst_ei = 1; + tx_dma->params.dst_fi = 1; + + tx_dma->params.elem_count = 1024; + tx_dma->params.frame_count = 1; + + tx_dma->params.read_prio = DMA_CH_PRIO_LOW; + tx_dma->params.write_prio = DMA_CH_PRIO_HIGH; + + omap_set_dma_params(tx_dma->ch, &tx_dma->params); + +// Play with these with a real application +//G omap_set_dma_src_burst_mode(tx_dma->ch, OMAP_DMA_DATA_BURST_16); +// omap_set_dma_dest_burst_mode(tx_dma->ch, OMAP_DMA_DATA_BURST_16); + + return 0; +} + +static void release_dma_controller() +{ + + omap_free_dma(rx_dma->ch); + omap_free_dma(tx_dma->ch); + + kfree(rx_dma); + kfree(tx_dma); +} + +static int get_frame_from_fpga_start() +{ + struct usrp_e_dev *p = usrp_e_devp; + struct ring_buffer_info *rbi = &(*rx_rb.rbi)[rx_rb_write]; + struct ring_buffer_entry *rbe = &(*rx_rb.rbe)[rx_rb_write]; + u16 elements_to_read; + + /* Check for space available in the ring buffer */ + /* If no space, drop data. A read call will restart dma transfers. */ + if (((*rx_rb.rbi)[rx_rb_write].flags & RB_KERNEL) && (gpio_get_value(RX_DATA_READY_GPIO)) && !rx_dma_active && !shutting_down) { + + rx_dma_active = 1; +#ifdef DEBUG_RX + gpio_set_value(14, 1); +#endif + + elements_to_read = readw(p->ctl_addr + UE_REG_MISC_RX_LEN); + + rbi->flags = RB_DMA_ACTIVE; + + rbi->len = elements_to_read << 1; + + omap_set_dma_dest_addr_size(rx_dma->ch, rbe->dma_addr, + elements_to_read); + + dma_sync_single_for_device(NULL, rbe->dma_addr, SZ_2K, DMA_FROM_DEVICE); + + omap_start_dma(rx_dma->ch); + } + + return 0; +} + + +static int get_frame_from_fpga_finish() +{ + dma_sync_single_for_cpu(NULL, (*rx_rb.rbe)[rx_rb_write].dma_addr, SZ_2K, DMA_FROM_DEVICE); + + (*rx_rb.rbi)[rx_rb_write].flags = RB_USER; + rx_rb_write++; + if (rx_rb_write == rb_size.num_rx_frames) + rx_rb_write = 0; + + wake_up_interruptible(&data_received_queue); + + rx_dma_active = 0; + + get_frame_from_fpga_start(); + +#ifdef DEBUG_RX + gpio_set_value(14, 0); +#endif + + return 0; +} + +static int send_frame_to_fpga_start() +{ + struct usrp_e_dev *p = usrp_e_devp; + struct ring_buffer_info *rbi = &(*tx_rb.rbi)[tx_rb_read]; + struct ring_buffer_entry *rbe = &(*tx_rb.rbe)[tx_rb_read]; + u16 elements_to_write; + +// printk("In send_frame_to_fpga_start.\n"); + + /* Check if there is data to write to the FPGA, if so send it */ + /* Otherwise, do nothing. Process is restarted by calls to write */ + if (((*tx_rb.rbi)[tx_rb_read].flags & RB_USER) && !tx_dma_active && (gpio_get_value(TX_SPACE_AVAILABLE_GPIO)) && !shutting_down) { +// printk("In send_frame_to_fpga_start, past if.\n"); + tx_dma_active = 1; +#ifdef DEBUG_TX + gpio_set_value(14, 1); +#endif + + elements_to_write = ((rbi->len) >> 1); + + writew(elements_to_write, p->ctl_addr + UE_REG_MISC_TX_LEN); + + rbi->flags = RB_DMA_ACTIVE; + + omap_set_dma_src_addr_size(tx_dma->ch, rbe->dma_addr, + elements_to_write); + + dma_sync_single_for_device(NULL, rbe->dma_addr, SZ_2K, DMA_TO_DEVICE); + + omap_start_dma(tx_dma->ch); + } + + return 0; +} + +static int send_frame_to_fpga_finish() +{ + + dma_sync_single_for_cpu(NULL, (*tx_rb.rbe)[tx_rb_read].dma_addr, SZ_2K, DMA_TO_DEVICE); + + (*tx_rb.rbi)[tx_rb_read].flags = RB_KERNEL; + + tx_rb_read++; + if (tx_rb_read == rb_size.num_tx_frames) + tx_rb_read = 0; + + wake_up_interruptible(&tx_rb_space_available); + + tx_dma_active = 0; + + send_frame_to_fpga_start(); + +#ifdef DEBUG_TX + gpio_set_value(14, 0); +#endif + + return 0; +} + +static int alloc_ring_buffer(struct ring_buffer *rb, + unsigned int num_bufs, enum dma_data_direction direction) +{ + int i; + + rb->rbi = __get_free_page(GFP_KERNEL | __GFP_COMP | __GFP_ZERO | __GFP_NOWARN); + + rb->rbe = kzalloc(sizeof(struct ring_buffer_entry) * num_bufs, GFP_KERNEL); + if (!rb) { + printk(KERN_ERR "Failed to allocate memory for rb entries\n"); + return -ENOMEM; + } + + rb->num_pages = (num_bufs & 1) ? ((num_bufs + 1) / 2) : (num_bufs / 2); + + rb->pages = kzalloc(sizeof(unsigned long) * rb->num_pages, GFP_KERNEL); + if (!(rb->pages)) { + printk(KERN_ERR "Failed to allocate memory for rb page entries\n"); + return -ENOMEM; + } + + for (i = 0; i < rb->num_pages; i++) { + (*rb->pages)[i] = __get_free_page(GFP_KERNEL | __GFP_DMA | __GFP_COMP | __GFP_ZERO | __GFP_NOWARN); + + (*(rb->rbe))[i*2].frame_addr = + (*(rb->pages))[i]; + (*(rb->rbe))[i*2 + 1].frame_addr = + ((*(rb->pages))[i] + SZ_2K); + if (!(*(rb->rbe))[i*2].frame_addr || !(*(rb->rbe))[i*2 + 1].frame_addr) { + printk(KERN_ERR "Failed to allocate memory dma buf\n"); + return -ENOMEM; + } + + (*(rb->rbe))[i*2].dma_addr = dma_map_single(NULL, (*(rb->rbe))[i*2].frame_addr, SZ_2K, direction); + (*(rb->rbe))[i*2 + 1].dma_addr = dma_map_single(NULL, (*(rb->rbe))[i*2 + 1].frame_addr, SZ_2K, direction); + if (!(*(rb->rbe))[i*2].dma_addr || !(*(rb->rbe))[i*2 + 1].dma_addr) { + printk(KERN_ERR "Failed to get physical address for dma buf\n"); + return -ENOMEM; + } + } + + return 0; +} + +static void delete_ring_buffer(struct ring_buffer *rb, + unsigned int num_bufs, enum dma_data_direction direction) +{ + unsigned int i; + unsigned int num_pages; + + printk(KERN_DEBUG "Entering delete_ring_buffer\n"); + + num_pages = (num_bufs & 1) ? ((num_bufs + 1) / 2) : (num_bufs / 2); + + for (i = 0; i < num_pages; i++) { + dma_unmap_single(NULL, (*rb->rbe)[i*2].dma_addr, SZ_2K, direction); + dma_unmap_single(NULL, (*rb->rbe)[i*2 + 1].dma_addr, SZ_2K, direction); + free_page((*rb->pages)[i]); + } + + free_page(rb->rbi); + + kfree(rb->pages); + kfree(rb->rbe); + + printk(KERN_DEBUG "Leaving delete_ring_buffer\n"); +} + +static int alloc_ring_buffers() +{ + + if (alloc_ring_buffer(&tx_rb, rb_size.num_rx_frames, DMA_TO_DEVICE) < 0) + return -ENOMEM; + if (alloc_ring_buffer(&rx_rb, rb_size.num_tx_frames, DMA_FROM_DEVICE) < 0) + return -ENOMEM; + + return 0; +} + +static void init_ring_buffer(struct ring_buffer *rb, int num_bufs, + int initial_flags, enum dma_data_direction direction) +{ + int i; + + for (i = 0; i < num_bufs; i++) { + dma_sync_single_for_device(NULL, (*rb->rbe)[i].dma_addr, + SZ_2K, direction); + dma_sync_single_for_cpu(NULL, (*rb->rbe)[i].dma_addr, + SZ_2K, direction); + (*rb->rbi)[i].flags = initial_flags; + } + +} + +#if 0 +static int tx_dma_func(void *data) +{ + int ret; + struct sched_param s = { .sched_priority = 1}; + + printk(KERN_DEBUG "In tx_dma_func\n"); + + allow_signal(SIGSTOP); + + sched_setscheduler(current, SCHED_FIFO, &s); + + while (!kthread_should_stop() && !closing_driver) { + + if (!((*tx_rb.rbe)[tx_rb_read].flags & RB_USER)) { + tx_dma_waiting_for_data = 1; + ret = wait_event_interruptible(received_data_from_user, + (*tx_rb.rbe)[tx_rb_read].flags & RB_USER); + if (ret) { + printk(KERN_DEBUG + "tx_dma_func received signal %d\n", + ret); + if (closing_driver) + break; + + } + tx_dma_waiting_for_data = 0; + } + + if (wait_event_interruptible(space_available_queue, + gpio_get_value(TX_SPACE_AVAILABLE_GPIO))) { + printk(KERN_DEBUG "tx_dma received signal waiting for space.\n"); + if (closing_driver) + break; + } + + if (send_frame_to_fpga(&(*tx_rb.rbe)[tx_rb_read]) != 0) { + printk(KERN_DEBUG "send_frame received signal.\n"); + if (closing_driver) + break; + } + + (*tx_rb.rbe)[tx_rb_read].flags = RB_KERNEL; + + tx_rb_read++; + if (tx_rb_read == RB_SIZE) + tx_rb_read = 0; + +#if 0 + if (waiting_for_space_in_tx_rb) +#endif + wake_up_interruptible(&tx_rb_space_queue); + + } + return 0; +} +#endif diff --git a/include/linux/usrp_e.h b/include/linux/usrp_e.h new file mode 100644 index 0000000..e52f709 --- /dev/null +++ b/include/linux/usrp_e.h @@ -0,0 +1,87 @@ + +/* + * Copyright (C) 2010 Ettus Research, LLC + * + * Written by Philip Balister + * + * 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 of the License, or + * (at your option) any later version. + */ + +#ifndef __USRP_E_H +#define __USRP_E_H + +#include +#include + +struct usrp_e_ctl16 { + __u32 offset; + __u32 count; + __u16 buf[20]; +}; + +struct usrp_e_ctl32 { + __u32 offset; + __u32 count; + __u32 buf[10]; +}; + +/* SPI interface */ + +#define UE_SPI_TXONLY 0 +#define UE_SPI_TXRX 1 + +/* Defines for spi ctrl register */ +#define UE_SPI_CTRL_TXNEG (BIT(10)) +#define UE_SPI_CTRL_RXNEG (BIT(9)) + +#define UE_SPI_PUSH_RISE 0 +#define UE_SPI_PUSH_FALL UE_SPI_CTRL_TXNEG +#define UE_SPI_LATCH_RISE 0 +#define UE_SPI_LATCH_FALL UE_SPI_CTRL_RXNEG + +struct usrp_e_spi { + __u8 readback; + __u32 slave; + __u32 data; + __u32 length; + __u32 flags; +}; + +struct usrp_e_i2c { + __u8 addr; + __u32 len; + __u8 data[]; +}; + +#define USRP_E_IOC_MAGIC 'u' +#define USRP_E_WRITE_CTL16 _IOW(USRP_E_IOC_MAGIC, 0x20, struct usrp_e_ctl16) +#define USRP_E_READ_CTL16 _IOWR(USRP_E_IOC_MAGIC, 0x21, struct usrp_e_ctl16) +#define USRP_E_WRITE_CTL32 _IOW(USRP_E_IOC_MAGIC, 0x22, struct usrp_e_ctl32) +#define USRP_E_READ_CTL32 _IOWR(USRP_E_IOC_MAGIC, 0x23, struct usrp_e_ctl32) +#define USRP_E_SPI _IOWR(USRP_E_IOC_MAGIC, 0x24, struct usrp_e_spi) +#define USRP_E_I2C_READ _IOWR(USRP_E_IOC_MAGIC, 0x25, struct usrp_e_i2c) +#define USRP_E_I2C_WRITE _IOW(USRP_E_IOC_MAGIC, 0x26, struct usrp_e_i2c) +#define USRP_E_GET_RB_INFO _IOR(USRP_E_IOC_MAGIC, 0x27, struct usrp_e_ring_buffer_size_t) + +/* Flag defines */ +#define RB_USER (BIT(0)) +#define RB_KERNEL (BIT(1)) +#define RB_OVERRUN (BIT(2)) +#define RB_DMA_ACTIVE (BIT(3)) + +struct ring_buffer_info { + int flags; + int len; +}; + +struct usrp_e_ring_buffer_size_t { + int num_pages_rx_flags; + int num_rx_frames; + int num_pages_tx_flags; + int num_tx_frames; +}; + +#endif -- 1.6.6.1