/*
* 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_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/completion.h>
#include <linux/miscdevice.h>
#include <linux/dmaengine.h>
#include <linux/fsldma.h>
#include <linux/interrupt.h>
#include <linux/highmem.h>
#include <linux/vmalloc.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>
/* 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;
void *vaddr;
struct scatterlist *sglist;
int sglen;
int nr_pages;
bool buf_allocated;
/* max size and written bytes */
size_t fw_size;
size_t bytes;
};
static int fpga_dma_init(struct fpga_dev *priv, int nr_pages)
{
struct page *pg;
int i;
priv->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT);
if (NULL == priv->vaddr) {
pr_debug("vmalloc_32(%d pages) failed\n", nr_pages);
return -ENOMEM;
}
pr_debug("vmalloc is at addr 0x%08lx, size=%d\n",
(unsigned long)priv->vaddr,
nr_pages << PAGE_SHIFT);
memset(priv->vaddr, 0, nr_pages << PAGE_SHIFT);
priv->nr_pages = nr_pages;
priv->sglist = vzalloc(priv->nr_pages * sizeof(*priv->sglist));
if (NULL == priv->sglist)
goto vzalloc_err;
sg_init_table(priv->sglist, priv->nr_pages);
for (i = 0; i < priv->nr_pages; i++) {
pg = vmalloc_to_page(priv->vaddr + i * PAGE_SIZE);
if (NULL == pg)
goto vmalloc_to_page_err;
sg_set_page(&priv->sglist[i], pg, PAGE_SIZE, 0);
}
return 0;
vmalloc_to_page_err:
vfree(priv->sglist);
priv->sglist = NULL;
vzalloc_err:
vfree(priv->vaddr);
priv->vaddr = NULL;
return -ENOMEM;
}
static int fpga_dma_map(struct fpga_dev *priv)
{
priv->sglen = dma_map_sg(priv->dev, pr