/* * fpgadl_ser.c - FPGA serial programming driver * * Copyright (C) 2008 Lyrtech * * Based on SH SCI SPI interface * Copyright (c) 2008 Magnus Damm * * 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 #define MODULE_NAME "fpgadl_ser" #define MODULE_VERSION_STR "v1.0" /* Define this to enable verbose debug messages */ #define FPGADL_SER_DEBUG 1 /* Module parameters */ static unsigned int fpgadl_ser_debug; module_param_named(debug, fpgadl_ser_debug, int, 0644); static char *fpgadl_ser_fw_name = NULL; module_param_named(fw_name, fpgadl_ser_fw_name, charp, 0644); #ifdef FPGADL_SER_DEBUG #define INFOMSG(fmt, args...) \ do { \ printk(KERN_INFO "%s: "fmt"\n", MODULE_NAME, ## args); } while (0) #define DBGMSG(fmt, args...) \ do { if (fpgadl_ser_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_ser_dev_t { char devname[32]; enum { FPGADL_SER_DEV_STATE_STRUCT_ALLOCATED, FPGADL_SER_DEV_STATE_SPI_SETUP, FPGADL_SER_DEV_STATE_FPGADL_DEV_REGISTERED, } state; struct spi_transfer t; struct spi_message m; struct spi_device *spi; struct fpgadl_device fpgadl_dev; }; #define MAX_FPGADL_SER_DEV 5 static int fpgadl_ser_dev_count; static int fpgadl_ser_write_byte(struct fpgadl_device *fpgadl_dev, u8 *data, int size) { int status; struct fpgadl_ser_dev_t *fpgadl_ser_dev; fpgadl_ser_dev = (struct fpgadl_ser_dev_t *) fpgadl_dev->devdata; if (!data) { FAILMSG("NULL data pointer"); return -EFAULT; } status = spi_write(fpgadl_ser_dev->spi, data, size); if (status < 0) FAILMSG("spi_write() failed (%d)", status); return status; } static void fpgadl_ser_cleanup(struct fpgadl_ser_dev_t *dev) { DBGMSG_ENTER(); if (!dev) return; switch (dev->state) { case FPGADL_SER_DEV_STATE_FPGADL_DEV_REGISTERED: fpgadl_unregister_device(&dev->fpgadl_dev); case FPGADL_SER_DEV_STATE_SPI_SETUP: case FPGADL_SER_DEV_STATE_STRUCT_ALLOCATED: kfree(dev); break; } } static int __devinit fpgadl_ser_probe(struct spi_device *spi) { int res; struct fpgadl_ser_dev_t *dev = NULL; DBGMSG_ENTER(); if (fpgadl_ser_dev_count == MAX_FPGADL_SER_DEV) { FAILMSG("Maximum number of devices reached (%d)", fpgadl_ser_dev_count); res = -ENODEV; goto error; } DBGMSG(" device %d", fpgadl_ser_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_SER_DEV_STATE_STRUCT_ALLOCATED; DBGMSG(" SPI mode = %d", spi->mode); if (!spi->dev.platform_data) { FAILMSG("Error getting platform data"); res = -ENODEV; goto error; } dev->fpgadl_dev.pdata = spi->dev.platform_data; //spi->dev.driver_data = dev; /* Private driver data */ spi->bits_per_word = 8; /* Size of Tx and Rx transfers. */ res = spi_setup(spi); if (res < 0) { FAILMSG("Error setting-up SPI"); goto error; } dev->spi = spi; dev->state = FPGADL_SER_DEV_STATE_SPI_SETUP; if (fpgadl_ser_fw_name) dev->fpgadl_dev.fw_name = fpgadl_ser_fw_name; dev->fpgadl_dev.write_byte = fpgadl_ser_write_byte; sprintf(dev->devname, "%s%d", MODULE_NAME, fpgadl_ser_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_ser device"); goto error; } dev->state = FPGADL_SER_DEV_STATE_FPGADL_DEV_REGISTERED; fpgadl_ser_dev_count++; return 0; error: fpgadl_ser_cleanup(dev); return res; } static int __devexit fpgadl_ser_remove(struct spi_device *spi) { struct fpgadl_ser_dev_t *dev = spi_get_drvdata(spi); DBGMSG_ENTER(); fpgadl_ser_cleanup(dev); return 0; } static struct spi_driver fpgadl_ser_spi_driver = { .driver = { .name = MODULE_NAME, .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = fpgadl_ser_probe, .remove = fpgadl_ser_remove, }; static struct fpgadl_driver fpgadl_ser_driver = { .version = MODULE_VERSION_STR, .module = THIS_MODULE, .driver = { .name = "fpgadl_ser", }, }; static int __init fpgadl_ser_init(void) { int res; DBGMSG_ENTER(); /* Register with the driver core. */ res = fpgadl_register_driver(&fpgadl_ser_driver); if (res) { FAILMSG("Can't register fpgadl_ser serial driver"); return res; } return spi_register_driver(&fpgadl_ser_spi_driver); } module_init(fpgadl_ser_init); static void __exit fpgadl_ser_exit(void) { DBGMSG_ENTER(); spi_unregister_driver(&fpgadl_ser_spi_driver); fpgadl_unregister_driver(&fpgadl_ser_driver); } module_exit(fpgadl_ser_exit); MODULE_DESCRIPTION("FPGA serial programming driver"); MODULE_AUTHOR("Hugo Villeneuve"); MODULE_LICENSE("GPL");