/*
* CARMA Board DATA-FPGA Programmer
*
* Copyright (c) 2009-2011 Ira W. Snyder <iws@ovro.caltech.edu>
*
* 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.
*/
#include <linux/dma-mapping.h>
#include <linux/of_platform.h>
#include <linux/completion.h>
#include <linux/miscdevice.h>
#include <linux/dmaengine.h>
#include <linux/interrupt.h>
#include <linux/highmem.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/leds.h>
#include <linux/slab.h>
#include <linux/kref.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <media/videobuf-dma-sg.h>
/* MPC8349EMDS specific get_immrbase() */
#include <sysdev/fsl_soc.h>
static const char drv_name[] = "carma-fpga-program";
/*
* Firmware images are always this exact size
*
* 12849552 bytes for a CARMA Digitizer Board (EP2S90 FPGAs)
* 18662880 bytes for a CARMA Correlator Board (EP2S130 FPGAs)
*/
#define FW_SIZE_EP2S90 12849552
#define FW_SIZE_EP2S130 18662880
struct fpga_dev {
struct miscdevice miscdev;
/* Reference count */
struct kref ref;
/* Device Registers */
struct device *dev;
void __iomem *regs;
void __iomem *immr;
/* Freescale DMA Device */
struct dma_chan *chan;
/* Interrupts */
int irq, status;
struct completion completion;
/* FPGA Bitfile */
struct mutex lock;
struct videobuf_dmabuf vb;
bool vb_allocated;
/* max size and written bytes */
size_t fw_size;
size_t bytes;
};
/*
* FPGA Bitfile Helpers
*/
/**
* fpga_drop_firmware_data() - drop the bitfile image from memory
* @priv: the driver's private data structure
*
* LOCKING: must hold priv->lock
*/
static void fpga_drop_firmware_data(struct fpga_dev *priv)
{
videobuf_dma_free(&priv->vb);
priv->vb_allocated = false;
priv->bytes = 0;
}
/*
* Private Data Reference Count
*/
static void fpga_dev_remove(struct kref *ref)
{
struct fpga_dev *priv = container_of(ref, struct fpga_dev, ref);
/* free any firmware image that was not programmed */
fpga_drop_firmware_data(priv);
mutex_destroy(&priv->lock);
kfree(priv);
}
/*
* LED Trigger (could be a seperate module)
*/
/*
* NOTE: this whole thing does have the problem that whenever the led's are
* NOTE: first set to use the fpga trigger, they could be in the wrong state
*/
DEFINE_LED_TRIGGER(ledtrig_fpga);
static void ledtrig_fpga_programmed(bool enabled)
{
if (enabled)
led_trigger_event(ledtrig_fpga, LED_FULL);
else
led_trigger_event(ledtrig_fpga, LED_OFF);
}
/*
* FPGA Register Helpers
*/
/* Register Definitions */
#define FPGA_CONFIG_CONTROL 0x40
#define FPGA_CONFIG_STATUS 0x44
#define FPGA_CONFIG_FIFO_SIZE 0x48
#define FPGA_CONFIG_FIFO_USED 0x4C
#define FPGA_CONFIG_TOTAL_BYTE_COUNT 0x50
#define FPGA_CONFIG_CUR_BYTE_COUNT 0x54
#define FPGA_FIFO_ADDRESS 0x3000
static int fpga_fifo_size(void __iomem *regs)
{
return ioread32be(regs + FPGA_CONFIG_FIFO_SIZE);
}
#define CFG_STATUS_ERR_MASK 0xfffe
static int fpga_config_error(void __iomem *regs)
{
return ioread32be(regs + FPGA_CONFIG_STATUS) & CFG_STATUS_ERR_MASK;
}
static int fpga_fifo_empty(void __iomem *regs)
{
return ioread32be(regs + FPGA_CONFIG_FIFO_USED) == 0;
}
static void fpga_fifo_write(void __iomem *regs, u32 val)
{
iowrite32be(val, regs + FPGA_FIFO_ADDRESS);
}
static void fpga_set_byte_count(void __iomem *regs, u32 count)
{
iowrite32be(count, regs + FPGA_CONFIG_TOTAL_BYTE_COUNT);
}
#define CFG_CTL_ENABLE (1 << 0)
#define CFG_CTL_RESET (1 << 1)
#define CFG_CTL_DMA (1 << 2)
static void fpga_programmer_enable(struct fpga_dev *priv, bool dma)
{
u32 val;
val = (dma) ? (CFG_CTL_ENABLE | CFG_CTL_DMA) : CFG_CTL_ENABLE;
iowrite32be(val, priv->regs + FPGA_CONFIG_CONTROL);
}
static void