Commit 99232716 authored by Miguel Jimenez Lopez's avatar Miguel Jimenez Lopez

Update FMC DIO driver for new IRQ scheme.

parent 3b614a4b
......@@ -26,30 +26,26 @@ static int fmc_dio_mdev_open(struct inode *inode, struct file *file)
static int fmc_dio_mdev_release(struct inode *inode, struct file *file)
{
// FIXME: Nothing to do here
return 0;
}
static ssize_t fmc_dio_mdev_write(struct file *file, const char __user *buf,
size_t count, loff_t *offp)
{
// FIXME: Nothing to do here
return 0;
}
static ssize_t fmc_dio_mdev_read (struct file *file, char __user *buf,
size_t count, loff_t *offp)
{
// FIXME: Nothing to do here
return 0;
}
//long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
//long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
static long fmc_dio_mdev_unlocked_ioctl (struct file *file, unsigned int cmd,
unsigned long arg)
{
struct fmc_dio *dio = file->private_data;
return fmc_dio_int_ioctl(dio,cmd,arg);
}
......
/*
* Copyright (C) 2019 CERN (www.sevensols.com)
* Copyright (C) 2019 Seven Solutions (www.sevensols.com)
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released according to the GNU GPL, version 2 or any later version.
......@@ -18,8 +18,8 @@
#include "fmc-dio.h"
// IRQ domain for SPEC board
static const char *spec_irqdomain_name = "htvic-spec.0";
/* Temporary pointer to the last FMC DIO */
static struct fmc_dio *last_dio;
/**
* FMC DIO Interrupt Service Routing (ISR)
......@@ -31,7 +31,11 @@ static const char *spec_irqdomain_name = "htvic-spec.0";
*/
irqreturn_t fmc_dio_interrupt(int irq, void *dev_id)
{
return fmc_dio_int_interrupt(dev_id);
irqreturn_t ret;
ret = fmc_dio_int_interrupt(dev_id);
return ret;
}
// Macros for the memory-mapped hardware resources
......@@ -51,17 +55,20 @@ static int fmc_dio_resource_map(struct fmc_dio *dio)
struct platform_device *dev = dio->pdev;
struct fmc_device *fmc = dio->fmc;
struct resource *r;
int irq;
void __iomem *mem;
struct irq_domain *irqdomain;
// Map the resources
for(i = 0 ; i < dev->num_resources ; i++) {
r = platform_get_resource(dev, IORESOURCE_MEM, i);
if(!r)
if (!r || !r->start)
continue;
mem = fmc->fpga_base + r->start;
mem = ioremap(r->start, r->end + 1 - r->start);
if (!mem) {
dev_err(&dev->dev, "Remap for res %i %pr failed\n",
i, r);
return -ENOMEM;
}
switch(i)
{
......@@ -81,25 +88,24 @@ static int fmc_dio_resource_map(struct fmc_dio *dio)
}
// If some hardware resource is not detected, fail
if(!dio->dio || !dio->gpio || !dio->ppsg)
return -EINVAL;
// Search the IRQ domain
irqdomain = irq_find_host((struct device_node *)dio->irqdomain_name);
if (!irqdomain)
if(!dio->dio || !dio->gpio || !dio->ppsg) {
printk("%s: ERROR in FPGA base pointers initialization. \n",__func__);
return -EINVAL;
}
// Map the IRQ
irq = platform_get_irq(dev,0);
if(irq < 0)
dio->irq = platform_get_irq(dev,0);
if(dio->irq < 0) {
printk("%s: ERROR in FMC DIO IRQ extraction. \n",__func__);
return -EINVAL;
// Register the IRQ handler
dio->irq = irq_find_mapping(irqdomain, irq);
err = request_irq(irq, fmc_dio_interrupt,
IRQF_TRIGGER_LOW, dio->name, dio);
if(err)
}
err = request_any_context_irq(dio->irq, fmc_dio_interrupt,
IRQF_TRIGGER_HIGH, dio->name, dio);
if(err < 0) {
printk("%s: ERROR in request IRQ (%d). \n",__func__,err);
return err;
}
return 0;
}
......@@ -119,70 +125,6 @@ static int fmc_dio_resource_release(struct fmc_dio *dio)
return 0;
}
/**
* FMC DIO probe function
*
* This function initializes the FMC DIO data structures.
*
* @param fmc FMC DIO device
*
* @return 0 if success and a negative error code otherwise
*/
int fmc_dio_probe(struct fmc_device *fmc)
{
struct fmc_dio *dio;
/* Driver data */
dio = devm_kzalloc(&fmc->dev, sizeof(*dio), GFP_KERNEL);
if (!dio)
return -ENOMEM;
fmc_set_drvdata(fmc, dio);
/* Set up the FMC DIO name for the Linux Miscdevice */
snprintf(dio->name,FMC_DIO_NAME_MAX,FMC_DIO_NAME_PATTERN,fmc->device_id);
dio->fmc = fmc;
return 0;
}
/**
* FMC DIO remove function
*
* This function releases the FMC DIO data structures.
*
* @param fmc FMC DIO device
*
* @return 0 if success and a negative error code otherwise
*/
int fmc_dio_remove(struct fmc_device *fmc)
{
struct fmc_dio *dio = fmc_get_drvdata(fmc);
/* Free FMC DIO internal structure */
kfree(dio);
return 0;
}
// FMC DIO FRU identifier
static struct fmc_fru_id fd_fru_id[] = {
{
.product_name = "FmcDio5cha",
},
};
// FMC DIO driver structure (FMC)
static struct fmc_driver fmc_dio_drv = {
.version = FMC_VERSION,
.driver.name = KBUILD_MODNAME,
.probe = fmc_dio_probe,
.remove = fmc_dio_remove,
.id_table = {
.fru_id = fd_fru_id,
.fru_id_nr = ARRAY_SIZE(fd_fru_id),
},
};
/**
* FMC DIO probe function for the platform_device
*
......@@ -196,31 +138,57 @@ static int fmc_dio_pl_probe(struct platform_device *dev)
* and it had filled the private data structure pointer of the
* platform_device before registering it.
*/
struct fmc_device *fmc = platform_get_drvdata(dev);
struct fmc_dio *dio = fmc_get_drvdata(fmc);
struct fmc_device *fmc;
struct fmc_dio *dio;
int ret = 0;
unsigned int devfn;
unsigned char busn;
dio = last_dio;
if(!dio) {
printk("%s: DIO internal structure is NULL \n",__func__);
ret = -EINVAL;
goto exit;
}
fmc = dio->fmc;
if(!fmc) {
printk("%s: FMC device is NULL \n",__func__);
ret = -EINVAL;
goto exit;
}
/* Set up the FMC DIO name for the Linux Miscdevice */
busn = (unsigned char) (fmc->device_id >> 8);
devfn = (unsigned int) (fmc->device_id & 0xFF);
snprintf(dio->name,FMC_DIO_NAME_MAX,
FMC_DIO_NAME_PATTERN,
busn,devfn);
// Specific board info
dio->board = dev->id_entry->driver_data;
// Assign IRQ domain depending on the specific board
if(dio->board == FMC_DIO_BOARD_SPEC)
dio->irqdomain_name = spec_irqdomain_name;
// FIXME: Implement for other boards
dio->pdev = dev;
/* Map the resources for the FMC DIO */
ret = fmc_dio_resource_map(dio);
if(ret)
return ret;
goto exit;
ret = fmc_dio_internal_create(dio);
if(ret)
goto exit;
/* Create a Linux Miscdevice for the FMC DIO */
ret = fmc_dio_mdev_create(dio);
if(ret)
return ret;
goto exit;
/* Create Linux GPIO for the FMC DIO */
ret = fmc_dio_gpio_init(dio);
if(ret)
goto exit;
exit:
return ret;
}
......@@ -233,30 +201,40 @@ static int fmc_dio_pl_probe(struct platform_device *dev)
*/
static int fmc_dio_pl_remove(struct platform_device *dev)
{
struct fmc_device *fmc = platform_get_drvdata(dev);
struct fmc_dio *dio = fmc_get_drvdata(fmc);
struct fmc_device *fmc;
struct fmc_dio *dio;
int ret = 0;
dio = last_dio;
fmc = dio->fmc;
/* Release Linux GPIO for the FMC DIO */
fmc_dio_gpio_exit(dio);
/* Destroy the Linux Miscdevice for the FMC DIO */
fmc_dio_mdev_destroy(dio);
/* Destroy internal DIO structure */
fmc_dio_internal_destroy(dio);
/* Release the resources for the FMC DIO */
ret = fmc_dio_resource_release(dio);
if(ret)
return ret;
/* Destroy the Linux Miscdevice for the FMC DIO */
fmc_dio_mdev_destroy(dio);
return ret;
}
/* Forward declarations */
int fmc_dio_probe(struct fmc_device *fmc);
int fmc_dio_remove(struct fmc_device *fmc);
/**
* List of supported platform
*/
static const struct platform_device_id fmc_dio_id_table[] = {
{ /* SPEC compatible */
.name = "spec-fmc-dio",
.name = "fmc-dio-spec",
.driver_data = FMC_DIO_BOARD_SPEC,
},
{},
......@@ -274,6 +252,82 @@ static struct platform_driver fmc_dio_pl_drv = {
},
};
// FMC DIO FRU identifier
static struct fmc_fru_id fd_fru_id[] = {
{
.product_name = "FmcDio5cha",
},
};
// FMC DIO driver structure (FMC)
static struct fmc_driver fmc_dio_drv = {
.version = FMC_VERSION,
.driver.name = KBUILD_MODNAME,
.probe = fmc_dio_probe,
.remove = fmc_dio_remove,
.id_table = {
.fru_id = fd_fru_id,
.fru_id_nr = ARRAY_SIZE(fd_fru_id),
},
};
/**
* FMC DIO probe function
*
* This function initializes the FMC DIO data structures.
*
* @param fmc FMC DIO device
*
* @return 0 if success and a negative error code otherwise
*/
int fmc_dio_probe(struct fmc_device *fmc)
{
struct fmc_dio *dio;
int ret = 0;
/* Driver data */
dio = devm_kzalloc(&fmc->dev, sizeof(*dio), GFP_KERNEL);
if (!dio)
return -ENOMEM;
fmc_set_drvdata(fmc, dio);
dio->fmc = fmc;
/* FIXME: Recover the DIO internal structure pointer for a global temporary var */
last_dio = dio;
/* Now register the platform_driver */
ret = platform_driver_register(&fmc_dio_pl_drv);
if (ret < 0) {
fmc_driver_unregister(&fmc_dio_drv);
}
return ret;
}
/**
* FMC DIO remove function
*
* This function releases the FMC DIO data structures.
*
* @param fmc FMC DIO device
*
* @return 0 if success and a negative error code otherwise
*/
int fmc_dio_remove(struct fmc_device *fmc)
{
//struct fmc_dio *dio = fmc_get_drvdata(fmc);
/* Free FMC DIO internal structure */
/* NOTE: Here is not necessary to free the memory because
* we use devm_kmalloc in the probe function
*/
platform_driver_unregister(&fmc_dio_pl_drv);
return 0;
}
/**
* FMC DIO init function
*
......@@ -286,11 +340,7 @@ static int fmc_dio_init(void)
int ret;
ret = fmc_driver_register(&fmc_dio_drv);
if (ret < 0)
return ret;
platform_driver_register(&fmc_dio_pl_drv);
if (ret < 0)
fmc_driver_unregister(&fmc_dio_drv);
return ret;
}
......@@ -302,7 +352,6 @@ static int fmc_dio_init(void)
*/
static void fmc_dio_exit(void)
{
platform_driver_unregister(&fmc_dio_pl_drv);
fmc_driver_unregister(&fmc_dio_drv);
}
......
......@@ -46,7 +46,7 @@ struct fmc_dio_gpio_block {
#define FMC_DIO_GPIO_VALUE(bit) (1 << ((4 * (bit)) + 0))
/* FMC DIO name string pattern */
#define FMC_DIO_NAME_PATTERN "fmc-dio-%x"
#define FMC_DIO_NAME_PATTERN "fmc-dio-%d:%d"
/* Size of the name field of the fmc_dio structure */
#define FMC_DIO_NAME_MAX 20
......@@ -81,6 +81,8 @@ struct fmc_dio {
extern irqreturn_t fmc_dio_int_interrupt(struct fmc_dio *dev);
extern int fmc_dio_int_ioctl(struct fmc_dio *dev, unsigned int ioctlcmd,
unsigned long arg);
extern int fmc_dio_internal_create(struct fmc_dio *dev);
extern void fmc_dio_internal_destroy(struct fmc_dio *dev);
// From fmc-dio-mdev.c
extern int fmc_dio_mdev_create(struct fmc_dio *dio);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment