/* * fpgadl_par.c - FPGA parallel programming driver * * Copyright (C) 2008 Lyrtech * * 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. * * 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; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For ioremap() */ #define MODULE_NAME "fpgadl_par" #define MODULE_VERSION_STR "v1.0" /* Define this to enable verbose debug messages */ #define FPGADL_PAR_DEBUG 1 /* Module parameters */ static unsigned int fpgadl_par_debug; module_param_named(debug, fpgadl_par_debug, int, 0644); static char *fpgadl_par_fw_name = NULL; module_param_named(fw_name, fpgadl_par_fw_name, charp, 0644); #ifdef FPGADL_PAR_DEBUG #define INFOMSG(fmt, args...) \ do { \ printk(KERN_INFO "%s: "fmt"\n", MODULE_NAME, ## args); } while (0) #define DBGMSG(fmt, args...) \ do { if (fpgadl_par_debug > 0) \ printk(KERN_DEBUG "%s: "fmt"\n", MODULE_NAME, ## args); } while (0) #define DBGMSG_ENTER() \ DBGMSG("%s() enter", __func__); #define DBGMSG_LEAVE() \ DBGMSG("%s() leave", __func__); #else #define INFOMSG(fmt, args...) do {} while (0) #define DBGMSG(fmt, args...) do {} while (0) #define DBGMSG_ENTER() do {} while (0) #define DBGMSG_LEAVE() do {} while (0) #endif #define FAILMSG(fmt, args...) \ do { \ printk(KERN_ERR "%s: "fmt"\n", MODULE_NAME, ## args); } while (0) struct fpgadl_par_dev_t { char devname[32]; enum { FPGADL_PAR_DEV_STATE_STRUCT_ALLOCATED, FPGADL_PAR_DEV_STATE_HAVE_IOREMAP, FPGADL_PAR_DEV_STATE_FPGADL_DEV_REGISTERED, } state; u8 *selectmap; struct fpgadl_device fpgadl_dev; }; #define MAX_FPGADL_PAR_DEV 5 static int fpgadl_par_dev_count; /* * Writes a byte of data to the FPGA using the SelectMAP * interface. The FPGA_SELECT_MAP_REG address is within * the FPGA address space (CS3), and when we write a byte * to that address, the CCLK line will be toggled. */ static int selectmap_write_byte(struct fpgadl_device *fpgadl_dev, u8 *data, int size) { int k; struct fpgadl_par_dev_t *fpgadl_par_dev; fpgadl_par_dev = (struct fpgadl_par_dev_t *) fpgadl_dev->devdata; for (k = 0; k < size; k++) fpgadl_par_dev->selectmap[0] = bitrev8(data[k]); return 0; } static void fpgadl_par_cleanup(struct fpgadl_par_dev_t *dev) { DBGMSG("fpgadl_par_cleanup"); if (!dev) return; switch (dev->state) { case FPGADL_PAR_DEV_STATE_FPGADL_DEV_REGISTERED: fpgadl_unregister_device(&dev->fpgadl_dev); case FPGADL_PAR_DEV_STATE_HAVE_IOREMAP: iounmap(dev->selectmap); case FPGADL_PAR_DEV_STATE_STRUCT_ALLOCATED: kfree(dev); break; } } static int __devinit fpgadl_par_probe(struct platform_device *pdev) { int len; int res; struct fpgadl_par_dev_t *dev = NULL; const struct resource *selectmap_res; DBGMSG("fpgadl_par_probe()"); if (fpgadl_par_dev_count == MAX_FPGADL_PAR_DEV) { FAILMSG("Maximum number of devices reached (%d)", fpgadl_par_dev_count); res = -ENODEV; goto error; } DBGMSG(" device %d", fpgadl_par_dev_count); dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { FAILMSG("Failed to allocate device structure"); res = -ENOMEM; goto error; } /* Set some default values. */ dev->state = FPGADL_PAR_DEV_STATE_STRUCT_ALLOCATED; if (!pdev->dev.platform_data) { FAILMSG("Error getting platform data"); res = -ENODEV; goto error; } dev->fpgadl_dev.pdata = pdev->dev.platform_data; //pdev->dev.driver_data = dev; /* Private driver data */ /* Assign virtual addresses to SELECTMAP I/O memory regions. */ selectmap_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "selectmap"); if (!selectmap_res) { FAILMSG("Error getting selectmap ressource"); res = -ENODEV; goto error; } len = selectmap_res->end - selectmap_res->start; dev->selectmap = ioremap(selectmap_res->start, len); if (!dev->selectmap) { FAILMSG("Can't remap selectmap register"); res = -ENXIO; goto error; } dev->state = FPGADL_PAR_DEV_STATE_HAVE_IOREMAP; if (fpgadl_par_fw_name) dev->fpgadl_dev.fw_name = fpgadl_par_fw_name; dev->fpgadl_dev.write_byte = selectmap_write_byte; sprintf(dev->devname, "%s%d", MODULE_NAME, fpgadl_par_dev_count); DBGMSG(" NAME = %s", dev->devname); dev->fpgadl_dev.name = dev->devname; dev->fpgadl_dev.devdata = dev; /* For our write_byte() callback */ res = fpgadl_register_device(&dev->fpgadl_dev); if (res < 0) { FAILMSG("Error registering fpgadl_par device"); goto error; } dev->state = FPGADL_PAR_DEV_STATE_FPGADL_DEV_REGISTERED; fpgadl_par_dev_count++; return 0; error: fpgadl_par_cleanup(dev); return res; } static int __devexit fpgadl_par_remove(struct platform_device *pdev) { struct fpgadl_par_dev_t *dev = platform_get_drvdata(pdev); DBGMSG("fpgadl_par_remove()"); fpgadl_par_cleanup(dev); return 0; } static struct fpgadl_driver fpgadl_par_driver = { .version = MODULE_VERSION_STR, .module = THIS_MODULE, .driver = { .name = "fpgadl_par", }, }; static struct platform_driver fpgadl_platform_driver = { .driver = { .name = MODULE_NAME, .owner = THIS_MODULE, }, .remove = fpgadl_par_remove, }; static int __init fpgadl_par_init(void) { int res; DBGMSG("fpgadl_par_init()"); /* Register with the driver core. */ res = fpgadl_register_driver(&fpgadl_par_driver); if (res) { FAILMSG("Can't register fpgadl parallel driver"); return res; } /* The probe function will be called for each platform device declared * in board setup code. */ res = platform_driver_probe(&fpgadl_platform_driver, fpgadl_par_probe); if (res) { FAILMSG("platform_driver_probe() failed"); return res; } return 0; } module_init(fpgadl_par_init); static void __exit fpgadl_par_exit(void) { DBGMSG("fpgadl_par_exit()"); platform_driver_unregister(&fpgadl_platform_driver); fpgadl_unregister_driver(&fpgadl_par_driver); } module_exit(fpgadl_par_exit); MODULE_AUTHOR("Hugo Villeneuve "); MODULE_DESCRIPTION("FPGA parallel programming driver"); MODULE_LICENSE("GPL");