Commit 7f523503 authored by Alessandro Rubini's avatar Alessandro Rubini

fmc-fakedev and doc: the module is multi-mezzanine

Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent 14319935
...@@ -592,14 +592,16 @@ using the kernel's GPIO mechanisms. ...@@ -592,14 +592,16 @@ using the kernel's GPIO mechanisms.
@node fmc-fakedev @node fmc-fakedev
@section fmc-fakedev @section fmc-fakedev
This package includes a software-only device, called @t{fmc-fakedev}. This package includes a software-only device, called @t{fmc-fakedev},
which is able to register up to 4 mezzanines (by default it registers one).
Unlike the @sc{spec} driver, which creates an @sc{fmc} device for each Unlike the @sc{spec} driver, which creates an @sc{fmc} device for each
PCI cards it manages, this module creates a single instance of its PCI cards it manages, this module creates a single instance of its
device. set of mezzanines.
It is meant as the simplest possible example of how a driver should be It is meant as the simplest possible example of how a driver should be
written, and it includes a fake @sc{eeprom} image (built using the written, and it includes a fake @sc{eeprom} image (built using the
tools described in @ref{FMC Identification}). tools described in @ref{FMC Identification}), which by default is
replicated for each fake mezzanine.
You can also use this device to verify the match algorithms, by You can also use this device to verify the match algorithms, by
asking it to test your own @sc{eeprom} image. You can provide the image by asking it to test your own @sc{eeprom} image. You can provide the image by
...@@ -624,6 +626,25 @@ overwritten starting at offset 0, the module will unregister and ...@@ -624,6 +626,25 @@ overwritten starting at offset 0, the module will unregister and
register again the @sc{fmc} device. This is shown later, in register again the @sc{fmc} device. This is shown later, in
section @ref{fmc-write-eeprom}. section @ref{fmc-write-eeprom}.
In order to register more than one mezzanine, you can use module parameters:
@table @code
@item ndev=<number>
Ask for a number of devices. By default it registers one
device or as many as @sc{eeprom} file names it receives.
@item eeprom=<name>[,<name>]
Name of the @sc{eeprom} images to be faked in the mezzanine devices.
You can pass up to 4 comma-separated names; empty names or
trailing unspecified names will select the default built-in
@sc{eeprom} image. If you specify more than one @sc{eeprom} name,
the module will automatically register more than one mezzanine.
@end table
@c ========================================================================== @c ==========================================================================
@node FMC Device Incompatibilities @node FMC Device Incompatibilities
@section FMC Device Incompatibilities @section FMC Device Incompatibilities
......
...@@ -13,12 +13,20 @@ ...@@ -13,12 +13,20 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/fmc.h> #include <linux/fmc.h>
static char *ff_eeprom; #define FF_EEPROM_SIZE 8192 /* The standard eeprom size */
module_param_named(eeprom, ff_eeprom, charp, 0444); #define FF_MAX_MEZZANINES 4 /* Fakes a multi-mezzanine carrier */
/* Lazily, don't support the "standard" module parameters */ /* The user can pass up to 4 names of eeprom images to load */
static char *ff_eeprom[FF_MAX_MEZZANINES];
static int ff_nr_eeprom;
module_param_array_named(eeprom, ff_eeprom, charp, &ff_nr_eeprom, 0444);
/* The user can ask for a multi-mezzanine carrier, with the default eeprom */
static int ff_nr_dev = 1;
module_param_named(ndev, ff_nr_dev, int, 0444);
#define FF_EEPROM_SIZE 8192
/* Lazily, don't support the "standard" module parameters */
/* /*
* Eeprom built from these commands: * Eeprom built from these commands:
...@@ -28,7 +36,8 @@ module_param_named(eeprom, ff_eeprom, charp, 0444); ...@@ -28,7 +36,8 @@ module_param_named(eeprom, ff_eeprom, charp, 0444);
gensdbfs . ../fake-eeprom.bin gensdbfs . ../fake-eeprom.bin
*/ */
static char ff_eeimg[FF_EEPROM_SIZE] = { static char ff_eeimg[FF_MAX_MEZZANINES][FF_EEPROM_SIZE] = {
{
0x01, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00, 0xf2, 0x01, 0x0b, 0x00, 0xb2, 0x01, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00, 0xf2, 0x01, 0x0b, 0x00, 0xb2,
0x86, 0x87, 0xcb, 0x66, 0x61, 0x6b, 0x65, 0x2d, 0x76, 0x65, 0x6e, 0x64, 0x86, 0x87, 0xcb, 0x66, 0x61, 0x6b, 0x65, 0x2d, 0x76, 0x65, 0x6e, 0x64,
0x6f, 0x72, 0xd7, 0x66, 0x61, 0x6b, 0x65, 0x2d, 0x64, 0x65, 0x73, 0x69, 0x6f, 0x72, 0xd7, 0x66, 0x61, 0x6b, 0x65, 0x2d, 0x64, 0x65, 0x73, 0x69,
...@@ -67,10 +76,11 @@ static char ff_eeimg[FF_EEPROM_SIZE] = { ...@@ -67,10 +76,11 @@ static char ff_eeimg[FF_EEPROM_SIZE] = {
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x49, 0x50, 0x4d, 0x49, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x49, 0x50, 0x4d, 0x49,
0x2d, 0x46, 0x52, 0x55, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x46, 0x52, 0x55, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x01, 0x66, 0x61, 0x6b, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x01, 0x66, 0x61, 0x6b, 0x65, 0x0a,
},
}; };
struct ff_dev { struct ff_dev {
struct fmc_device fmc; struct fmc_device fmc[FF_MAX_MEZZANINES];
struct device dev; struct device dev;
struct delayed_work work; struct delayed_work work;
}; };
...@@ -127,7 +137,7 @@ static void ff_work_fn(struct work_struct *work) ...@@ -127,7 +137,7 @@ static void ff_work_fn(struct work_struct *work)
struct ff_dev *ff = container_of(dw, struct ff_dev, work); struct ff_dev *ff = container_of(dw, struct ff_dev, work);
int ret; int ret;
fmc_device_unregister(&ff->fmc); fmc_device_unregister_n(ff->fmc, ff_nr_dev);
device_unregister(&ff->dev); device_unregister(&ff->dev);
ff_current_dev = NULL; ff_current_dev = NULL;
...@@ -136,7 +146,7 @@ static void ff_work_fn(struct work_struct *work) ...@@ -136,7 +146,7 @@ static void ff_work_fn(struct work_struct *work)
pr_warning("%s: can't re-create FMC device\n", __func__); pr_warning("%s: can't re-create FMC device\n", __func__);
return; return;
} }
ret = fmc_device_register(&ff->fmc); ret = fmc_device_register_n(ff->fmc, ff_nr_dev);
if (ret < 0) { if (ret < 0) {
device_unregister(&ff->dev); device_unregister(&ff->dev);
pr_warning("%s: can't re-register FMC device\n", __func__); pr_warning("%s: can't re-register FMC device\n", __func__);
...@@ -154,7 +164,7 @@ int ff_eeprom_read(struct fmc_device *fmc, uint32_t offset, ...@@ -154,7 +164,7 @@ int ff_eeprom_read(struct fmc_device *fmc, uint32_t offset,
return -EINVAL; return -EINVAL;
if (offset + size > FF_EEPROM_SIZE) if (offset + size > FF_EEPROM_SIZE)
size = FF_EEPROM_SIZE - offset; size = FF_EEPROM_SIZE - offset;
memcpy(buf, ff_eeimg + offset, size); memcpy(buf, fmc->eeprom + offset, size);
return size; return size;
} }
...@@ -168,7 +178,7 @@ int ff_eeprom_write(struct fmc_device *fmc, uint32_t offset, ...@@ -168,7 +178,7 @@ int ff_eeprom_write(struct fmc_device *fmc, uint32_t offset,
if (offset + size > FF_EEPROM_SIZE) if (offset + size > FF_EEPROM_SIZE)
size = FF_EEPROM_SIZE - offset; size = FF_EEPROM_SIZE - offset;
pr_info("%s: size %zi\n", __func__, size); pr_info("%s: size %zi\n", __func__, size);
memcpy(ff_eeimg + offset, buf, size); memcpy(fmc->eeprom + offset, buf, size);
schedule_delayed_work(&ff->work, HZ * 2); /* remove, replug, in 2s */ schedule_delayed_work(&ff->work, HZ * 2); /* remove, replug, in 2s */
return size; return size;
} }
...@@ -221,10 +231,7 @@ static struct fmc_device ff_template_fmc = { ...@@ -221,10 +231,7 @@ static struct fmc_device ff_template_fmc = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.carrier_name = "fake", .carrier_name = "fake",
.device_id = 0xf001, /* fool */ .device_id = 0xf001, /* fool */
.eeprom = ff_eeimg, .eeprom_len = sizeof(ff_eeimg[0]),
.eeprom_addr = 0x50,
.eeprom_len = sizeof(ff_eeimg),
.nr_slots = 1,
.memlen = 0x1000, /* 4k, to show something */ .memlen = 0x1000, /* 4k, to show something */
.op = &ff_fmc_operations, .op = &ff_fmc_operations,
.hwdev = NULL, /* filled at creation time */ .hwdev = NULL, /* filled at creation time */
...@@ -234,7 +241,7 @@ static struct fmc_device ff_template_fmc = { ...@@ -234,7 +241,7 @@ static struct fmc_device ff_template_fmc = {
static struct ff_dev *ff_dev_create(void) static struct ff_dev *ff_dev_create(void)
{ {
struct ff_dev *ff; struct ff_dev *ff;
int ret; int i, ret;
ff = kzalloc(sizeof(*ff), GFP_KERNEL); ff = kzalloc(sizeof(*ff), GFP_KERNEL);
if (!ff) if (!ff)
...@@ -249,12 +256,19 @@ static struct ff_dev *ff_dev_create(void) ...@@ -249,12 +256,19 @@ static struct ff_dev *ff_dev_create(void)
return ERR_PTR(ret); return ERR_PTR(ret);
} }
/* Create an fmc structure that refers to this new "hw" device */ /* Create fmc structures that refers to this new "hw" device */
ff->fmc = ff_template_fmc; for (i = 0; i < ff_nr_dev; i++) {
ff->fmc.hwdev = &ff->dev; ff->fmc[i] = ff_template_fmc;
ff->fmc.carrier_data = ff; ff->fmc[i].hwdev = &ff->dev;
ff_template_fmc.device_id++; /* for next time */ ff->fmc[i].carrier_data = ff;
ff->fmc[i].nr_slots = ff_nr_dev;
/* the following fields are different for each slot */
ff->fmc[i].eeprom = ff_eeimg[i];
ff->fmc[i].eeprom_addr = 0x50 + 2 * i;
ff->fmc[i].slot_id = i;
/* increment the identifier, each must be different */
ff_template_fmc.device_id++;
}
INIT_DELAYED_WORK(&ff->work, ff_work_fn); INIT_DELAYED_WORK(&ff->work, ff_work_fn);
return ff; return ff;
} }
...@@ -264,27 +278,37 @@ int ff_init(void) ...@@ -264,27 +278,37 @@ int ff_init(void)
{ {
struct ff_dev *ff; struct ff_dev *ff;
const struct firmware *fw; const struct firmware *fw;
int len, ret = 0; int i, len, ret = 0;
/* Replicate the default eeprom for the max number of mezzanines */
for (i = 1; i < FF_MAX_MEZZANINES; i++)
memcpy(ff_eeimg[i], ff_eeimg[0], sizeof(ff_eeimg[0]));
if (ff_nr_eeprom > ff_nr_dev)
ff_nr_dev = ff_nr_eeprom;
ff = ff_dev_create(); ff = ff_dev_create();
if (IS_ERR(ff)) if (IS_ERR(ff))
return PTR_ERR(ff); return PTR_ERR(ff);
/* If the user passed "eeprom=" as a parameter, fetch it */ /* If the user passed "eeprom=" as a parameter, fetch them */
if (ff_eeprom) { for (i = 0;i < ff_nr_eeprom; i++) {
ret = request_firmware(&fw, ff_eeprom, &ff->dev); if (!strlen(ff_eeprom[i]))
continue;
ret = request_firmware(&fw, ff_eeprom[i], &ff->dev);
if (ret < 0) { if (ret < 0) {
dev_err(&ff->dev, "Can't load \"%s\" (error %i)\n", dev_err(&ff->dev, "Mezzanine %i: can't load \"%s\" "
ff_eeprom, -ret); "(error %i)\n", i, ff_eeprom[i], -ret);
} else { } else {
len = min_t(size_t, fw->size, (size_t)FF_EEPROM_SIZE); len = min_t(size_t, fw->size, (size_t)FF_EEPROM_SIZE);
memcpy(ff_eeimg, fw->data, len); memcpy(ff_eeimg[i], fw->data, len);
release_firmware(fw); release_firmware(fw);
dev_info(&ff->dev, "Mezzanine %i: eeprom \"%s\"\n", i,
ff_eeprom[i]);
} }
} }
ret = fmc_device_register(&ff->fmc); ret = fmc_device_register_n(ff->fmc, ff_nr_dev);
if (ret) { if (ret) {
device_unregister(&ff->dev); device_unregister(&ff->dev);
return ret; return ret;
...@@ -297,7 +321,7 @@ void ff_exit(void) ...@@ -297,7 +321,7 @@ void ff_exit(void)
{ {
if (ff_current_dev) { if (ff_current_dev) {
cancel_delayed_work_sync(&ff_current_dev->work); cancel_delayed_work_sync(&ff_current_dev->work);
fmc_device_unregister(&ff_current_dev->fmc); fmc_device_unregister_n(ff_current_dev->fmc, ff_nr_dev);
device_unregister(&ff_current_dev->dev); device_unregister(&ff_current_dev->dev);
} }
} }
......
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