diff options
author | Alessandro Rubini <rubini@gnudd.com> | 2013-06-18 17:47:46 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-06-18 18:41:04 -0400 |
commit | 056d83f3c30c398f14eb879f1d1707e3a7808f4a (patch) | |
tree | 44c3883a0cd722472b9888b2a50ce2ac761a9373 /drivers/fmc | |
parent | 6c62a895e572145f8aa24f2040d1bb8eff473313 (diff) |
FMC: add a software mezzanine driver
This simple do-nothing mezzanine driver shows how to write a mezzanine
driver, that can also handle interrupts reported by the carrier.
Signed-off-by: Alessandro Rubini <rubini@gnudd.com>
Acked-by: Juan David Gonzalez Cobas <dcobas@cern.ch>
Acked-by: Emilio G. Cota <cota@braap.org>
Acked-by: Samuel Iglesias Gonsalvez <siglesias@igalia.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/fmc')
-rw-r--r-- | drivers/fmc/Kconfig | 7 | ||||
-rw-r--r-- | drivers/fmc/Makefile | 1 | ||||
-rw-r--r-- | drivers/fmc/fmc-trivial.c | 107 |
3 files changed, 115 insertions, 0 deletions
diff --git a/drivers/fmc/Kconfig b/drivers/fmc/Kconfig index 505e96b02a93..7eacef990be8 100644 --- a/drivers/fmc/Kconfig +++ b/drivers/fmc/Kconfig | |||
@@ -25,4 +25,11 @@ config FMC_FAKEDEV | |||
25 | that can be rewritten at run time and usef for matching | 25 | that can be rewritten at run time and usef for matching |
26 | mezzanines. | 26 | mezzanines. |
27 | 27 | ||
28 | config FMC_TRIVIAL | ||
29 | tristate "FMC trivial mezzanine driver (software testing)" | ||
30 | help | ||
31 | This is a fake mezzanine driver, to show how FMC works and test it. | ||
32 | The driver also handles interrupts (we used it with a real carrier | ||
33 | before the mezzanines were produced) | ||
34 | |||
28 | endif # FMC | 35 | endif # FMC |
diff --git a/drivers/fmc/Makefile b/drivers/fmc/Makefile index 9832b79976b1..52624e6c0b1f 100644 --- a/drivers/fmc/Makefile +++ b/drivers/fmc/Makefile | |||
@@ -8,3 +8,4 @@ fmc-y += fru-parse.o | |||
8 | fmc-y += fmc-dump.o | 8 | fmc-y += fmc-dump.o |
9 | 9 | ||
10 | obj-$(CONFIG_FMC_FAKEDEV) += fmc-fakedev.o | 10 | obj-$(CONFIG_FMC_FAKEDEV) += fmc-fakedev.o |
11 | obj-$(CONFIG_FMC_TRIVIAL) += fmc-trivial.o | ||
diff --git a/drivers/fmc/fmc-trivial.c b/drivers/fmc/fmc-trivial.c new file mode 100644 index 000000000000..6c590f54c79d --- /dev/null +++ b/drivers/fmc/fmc-trivial.c | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 CERN (www.cern.ch) | ||
3 | * Author: Alessandro Rubini <rubini@gnudd.com> | ||
4 | * | ||
5 | * Permission to use, copy, modify, and/or distribute this software for any | ||
6 | * purpose with or without fee is hereby granted, provided that the above | ||
7 | * copyright notice and this permission notice appear in all copies. | ||
8 | * | ||
9 | * The software is provided "as is"; the copyright holders disclaim | ||
10 | * all warranties and liabilities, to the extent permitted by | ||
11 | * applicable law. | ||
12 | */ | ||
13 | |||
14 | /* A trivial fmc driver that can load a gateware file and reports interrupts */ | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/gpio.h> | ||
19 | #include <linux/fmc.h> | ||
20 | |||
21 | static struct fmc_driver t_drv; /* initialized later */ | ||
22 | |||
23 | static irqreturn_t t_handler(int irq, void *dev_id) | ||
24 | { | ||
25 | struct fmc_device *fmc = dev_id; | ||
26 | |||
27 | fmc->op->irq_ack(fmc); | ||
28 | dev_info(&fmc->dev, "received irq %i\n", irq); | ||
29 | return IRQ_HANDLED; | ||
30 | } | ||
31 | |||
32 | static struct fmc_gpio t_gpio[] = { | ||
33 | { | ||
34 | .gpio = FMC_GPIO_IRQ(0), | ||
35 | .mode = GPIOF_DIR_IN, | ||
36 | .irqmode = IRQF_TRIGGER_RISING, | ||
37 | }, { | ||
38 | .gpio = FMC_GPIO_IRQ(1), | ||
39 | .mode = GPIOF_DIR_IN, | ||
40 | .irqmode = IRQF_TRIGGER_RISING, | ||
41 | } | ||
42 | }; | ||
43 | |||
44 | static int t_probe(struct fmc_device *fmc) | ||
45 | { | ||
46 | int ret; | ||
47 | int index = 0; | ||
48 | |||
49 | if (fmc->op->validate) | ||
50 | index = fmc->op->validate(fmc, &t_drv); | ||
51 | if (index < 0) | ||
52 | return -EINVAL; /* not our device: invalid */ | ||
53 | |||
54 | ret = fmc->op->irq_request(fmc, t_handler, "fmc-trivial", IRQF_SHARED); | ||
55 | if (ret < 0) | ||
56 | return ret; | ||
57 | /* ignore error code of call below, we really don't care */ | ||
58 | fmc->op->gpio_config(fmc, t_gpio, ARRAY_SIZE(t_gpio)); | ||
59 | |||
60 | /* Reprogram, if asked to. ESRCH == no filename specified */ | ||
61 | ret = -ESRCH; | ||
62 | if (fmc->op->reprogram) | ||
63 | ret = fmc->op->reprogram(fmc, &t_drv, ""); | ||
64 | if (ret == -ESRCH) | ||
65 | ret = 0; | ||
66 | if (ret < 0) | ||
67 | fmc->op->irq_free(fmc); | ||
68 | |||
69 | /* FIXME: reprogram LM32 too */ | ||
70 | return ret; | ||
71 | } | ||
72 | |||
73 | static int t_remove(struct fmc_device *fmc) | ||
74 | { | ||
75 | fmc->op->irq_free(fmc); | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static struct fmc_driver t_drv = { | ||
80 | .version = FMC_VERSION, | ||
81 | .driver.name = KBUILD_MODNAME, | ||
82 | .probe = t_probe, | ||
83 | .remove = t_remove, | ||
84 | /* no table, as the current match just matches everything */ | ||
85 | }; | ||
86 | |||
87 | /* We accept the generic parameters */ | ||
88 | FMC_PARAM_BUSID(t_drv); | ||
89 | FMC_PARAM_GATEWARE(t_drv); | ||
90 | |||
91 | static int t_init(void) | ||
92 | { | ||
93 | int ret; | ||
94 | |||
95 | ret = fmc_driver_register(&t_drv); | ||
96 | return ret; | ||
97 | } | ||
98 | |||
99 | static void t_exit(void) | ||
100 | { | ||
101 | fmc_driver_unregister(&t_drv); | ||
102 | } | ||
103 | |||
104 | module_init(t_init); | ||
105 | module_exit(t_exit); | ||
106 | |||
107 | MODULE_LICENSE("Dual BSD/GPL"); | ||