linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/8] Support for FMC carriers and mezzanines
@ 2013-06-12  7:13 Alessandro Rubini
  2013-06-12  7:13 ` [PATCH 1/8] FMC: create drivers/fmc and toplevel Kconfig question Alessandro Rubini
                   ` (7 more replies)
  0 siblings, 8 replies; 26+ messages in thread
From: Alessandro Rubini @ 2013-06-12  7:13 UTC (permalink / raw)
  To: linux-kernel
  Cc: Juan David Gonzalez Cobas, Emilio G. Cota,
	Samuel Iglesias Gonsalvez, gregkh, Rob Landley, linux-doc

This patch-set includes the Linux bus abstraction for FMC (ANSI/VITA 57).

I posted it on Feb 21 2013 as an RFC, and I only got an Acked-by
about documentation by Rob Landley; I include that Acked-by in this patch
set, even if there are some minor changes in the docs -- typos and
new capabilities for the fmc-fakedev carrier module.

I fixed the device release bug noted by Greg-KH (yes, I deserved being
ridiculed for that). There are no other serious changes, and we are now
running this in production as a backport to 2.6.24-rt.

The whole patch-set is sent to lkml, Greg K H (for drivers/), Rob Landley
and the linux-doc list (both for Documentation/).

The code I'm submitting is currently maintained outside of the kernel
proper. See http://www.ohwr.org/projects/fmc-bus/ . The public
repository is at git://ohwr.org/fmc-projects/fmc-bus.git and the
current manual is at
http://www.ohwr.org/attachments/download/2124/fmc-bus-2013-05.1-release.pdf

The following text is an almost verbatim copy of parts of our
documentation, as included in the fmc-bus package.



FMC (FPGA Mezzanine Card) is the standard we use for our I/O devices,
in the context of White Rabbit and related hardware.

In our I/O environments we need to write drivers for each mezzanine
card, and such drivers must work independent of the carrier being used.
To achieve this, we abstract the FMC interface.

We have a carrier for PCI-E called SPEC and one for VME called SVEC,
but more are planned.  Also, we support stand-alone devices (usually
plugged on a SPEC card), controlled through Etherbone, developed by GSI.

FMC is a standard developed by the VME consortium called VITA (VMEbus
International Trade Association and ratified by ANSI, the American
National Standard Institute.  The official documentation is called
"ANSI-VITA 57.1".

The FMC card is an almost square PCB, around 70x75 millimeters, that
is called mezzanine in code and documentation under drivers/fmc.  It
usually lives plugged into into another PCB for power supply and
control; such bigger circuit board is called carrier from now on, and
a single carrier may host more than one mezzanine.

In the typical application the mezzanine is mostly analog while the
carrier is mostly digital, and hosts an FPGA that must be programmed to
match the specific mezzanine and the desired application. Thus, you may
need to load different FPGA images to drive different instances of the
same mezzanine.

FMC, as such, is not a bus in the usual meaning of the term, because
most carriers have only one connector, and carriers with several
connectors have completely separate electrical connections to them.
This package, however, implements a bus as a software abstraction.


SDB (Self Describing Bus) is a set of data structures that we use for
enumerating the internal structure of an FPGA image. We also use it as
a filesystem inside the FMC EEPROM.

SDB is not mandatory for use of this FMC kernel bus, but if you have SDB
this package can make good use of it.  SDB itself is developed in the
fpga-config-space OHWR project. The link to the repository is
`git://ohwr.org/hdl-core-lib/fpga-config-space.git' and what is used in
this project lives in the sdbfs subdirectory in there.


Alessandro Rubini (8):
  FMC: create drivers/fmc and toplevel Kconfig question
  FMC: add needed headers
  FMC: add core bus driver
  FMC: add documentation for the core
  FMC: add a software carrier driver
  FMC: add a software mezzanine driver
  FMC: add a driver to write mezzanine EEPROM
  FMC: add a char-device mezzanine driver

 Documentation/00-INDEX                 |    2 +
 Documentation/fmc/00-INDEX             |   38 ++++
 Documentation/fmc/API.txt              |   47 +++++
 Documentation/fmc/FMC-and-SDB.txt      |   88 ++++++++
 Documentation/fmc/carrier.txt          |  311 ++++++++++++++++++++++++++++
 Documentation/fmc/fmc-chardev.txt      |   64 ++++++
 Documentation/fmc/fmc-fakedev.txt      |   36 ++++
 Documentation/fmc/fmc-trivial.txt      |   17 ++
 Documentation/fmc/fmc-write-eeprom.txt |  125 ++++++++++++
 Documentation/fmc/identifiers.txt      |  168 +++++++++++++++
 Documentation/fmc/mezzanine.txt        |  123 +++++++++++
 Documentation/fmc/parameters.txt       |   56 +++++
 MAINTAINERS                            |    9 +
 drivers/Kconfig                        |    2 +
 drivers/Makefile                       |    1 +
 drivers/fmc/Kconfig                    |   51 +++++
 drivers/fmc/Makefile                   |   13 ++
 drivers/fmc/fmc-chardev.c              |  197 ++++++++++++++++++
 drivers/fmc/fmc-core.c                 |  296 +++++++++++++++++++++++++++
 drivers/fmc/fmc-dump.c                 |  100 +++++++++
 drivers/fmc/fmc-fakedev.c              |  349 ++++++++++++++++++++++++++++++++
 drivers/fmc/fmc-match.c                |  114 +++++++++++
 drivers/fmc/fmc-sdb.c                  |  265 ++++++++++++++++++++++++
 drivers/fmc/fmc-trivial.c              |  101 +++++++++
 drivers/fmc/fmc-write-eeprom.c         |  176 ++++++++++++++++
 drivers/fmc/fru-parse.c                |   82 ++++++++
 include/linux/fmc-sdb.h                |   36 ++++
 include/linux/fmc.h                    |  237 ++++++++++++++++++++++
 include/linux/ipmi-fru.h               |  135 ++++++++++++
 include/linux/sdb.h                    |  159 +++++++++++++++
 30 files changed, 3398 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/fmc/00-INDEX
 create mode 100644 Documentation/fmc/API.txt
 create mode 100644 Documentation/fmc/FMC-and-SDB.txt
 create mode 100644 Documentation/fmc/carrier.txt
 create mode 100644 Documentation/fmc/fmc-chardev.txt
 create mode 100644 Documentation/fmc/fmc-fakedev.txt
 create mode 100644 Documentation/fmc/fmc-trivial.txt
 create mode 100644 Documentation/fmc/fmc-write-eeprom.txt
 create mode 100644 Documentation/fmc/identifiers.txt
 create mode 100644 Documentation/fmc/mezzanine.txt
 create mode 100644 Documentation/fmc/parameters.txt
 create mode 100644 drivers/fmc/Kconfig
 create mode 100644 drivers/fmc/Makefile
 create mode 100644 drivers/fmc/fmc-chardev.c
 create mode 100644 drivers/fmc/fmc-core.c
 create mode 100644 drivers/fmc/fmc-dump.c
 create mode 100644 drivers/fmc/fmc-fakedev.c
 create mode 100644 drivers/fmc/fmc-match.c
 create mode 100644 drivers/fmc/fmc-sdb.c
 create mode 100644 drivers/fmc/fmc-trivial.c
 create mode 100644 drivers/fmc/fmc-write-eeprom.c
 create mode 100644 drivers/fmc/fru-parse.c
 create mode 100644 include/linux/fmc-sdb.h
 create mode 100644 include/linux/fmc.h
 create mode 100644 include/linux/ipmi-fru.h
 create mode 100644 include/linux/sdb.h

-- 
1.7.7.2

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 1/8] FMC: create drivers/fmc and toplevel Kconfig question
  2013-06-12  7:13 [PATCH 0/8] Support for FMC carriers and mezzanines Alessandro Rubini
@ 2013-06-12  7:13 ` Alessandro Rubini
  2013-06-17 23:17   ` Greg KH
  2013-06-17 23:24   ` Alessandro Rubini
  2013-06-12  7:13 ` [PATCH 2/8] FMC: add needed headers Alessandro Rubini
                   ` (6 subsequent siblings)
  7 siblings, 2 replies; 26+ messages in thread
From: Alessandro Rubini @ 2013-06-12  7:13 UTC (permalink / raw)
  To: linux-kernel
  Cc: Juan David Gonzalez Cobas, Emilio G. Cota,
	Samuel Iglesias Gonsalvez, gregkh, Rob Landley, linux-doc

This commit creates the drivers/fmc directory and puts the necessary
hooks for kbuild and kconfig.  The code is currently a placeholder
that only registers an empty bus.

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>
---
 MAINTAINERS            |    9 +++++++++
 drivers/Kconfig        |    2 ++
 drivers/Makefile       |    1 +
 drivers/fmc/Kconfig    |   17 +++++++++++++++++
 drivers/fmc/Makefile   |    4 ++++
 drivers/fmc/fmc-core.c |   24 ++++++++++++++++++++++++
 6 files changed, 57 insertions(+), 0 deletions(-)
 create mode 100644 drivers/fmc/Kconfig
 create mode 100644 drivers/fmc/Makefile
 create mode 100644 drivers/fmc/fmc-core.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 8d97b3e..10ebdfc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3322,6 +3322,15 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/floppy.git
 S:	Odd fixes
 F:	drivers/block/floppy.c
 
+FMC SUBSYSTEM
+M:	Alessandro Rubini <rubini@gnudd.com>
+W:	http://www.ohwr.org/projects/fmc-bus
+S:	Supported
+F:	drivers/fmc/
+F:	include/linux/fmc*.h
+F:	include/linux/ipmi-fru.h
+K:	fmc_d.*register
+
 FPU EMULATOR
 M:	Bill Metzenthen <billm@melbpc.org.au>
 W:	http://floatingpoint.sourceforge.net/emulator/index.html
diff --git a/drivers/Kconfig b/drivers/Kconfig
index b8ec9cf..aa43b91 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -164,4 +164,6 @@ source "drivers/ipack/Kconfig"
 
 source "drivers/reset/Kconfig"
 
+source "drivers/fmc/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index bbf3810..ab93de8 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -151,3 +151,4 @@ obj-$(CONFIG_IIO)		+= iio/
 obj-$(CONFIG_VME_BUS)		+= vme/
 obj-$(CONFIG_IPACK_BUS)		+= ipack/
 obj-$(CONFIG_NTB)		+= ntb/
+obj-$(CONFIG_FMC)		+= fmc/
diff --git a/drivers/fmc/Kconfig b/drivers/fmc/Kconfig
new file mode 100644
index 0000000..e287902
--- /dev/null
+++ b/drivers/fmc/Kconfig
@@ -0,0 +1,17 @@
+#
+# FMC (ANSI-VITA 57.1) bus support
+#
+
+menuconfig FMC
+	tristate "FMC support"
+	help
+
+	  FMC (FPGA Mezzanine Carrier) is a mechanical and electrical
+	  standard for mezzanine cards that plug into a carrier board.
+	  This kernel subsystem supports the matching between carrier
+	  and mezzanine based on identifiers stored in the internal I2C
+	  EEPROM, as well as having carrier-independent drivers.
+
+	  The framework was born outside of the kernel and at this time
+	  the off-tree code base is more complete.  Code and documentation
+	  is at git://ohwr.org/fmc-projects/fmc-bus.git .
diff --git a/drivers/fmc/Makefile b/drivers/fmc/Makefile
new file mode 100644
index 0000000..a2784d8
--- /dev/null
+++ b/drivers/fmc/Makefile
@@ -0,0 +1,4 @@
+
+obj-$(CONFIG_FMC) += fmc.o
+
+fmc-y = fmc-core.o
diff --git a/drivers/fmc/fmc-core.c b/drivers/fmc/fmc-core.c
new file mode 100644
index 0000000..fc3547f
--- /dev/null
+++ b/drivers/fmc/fmc-core.c
@@ -0,0 +1,24 @@
+/* Temporary placeholder so the empty code can build */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+
+static struct bus_type fmc_bus_type = {
+	.name = "fmc",
+};
+
+static int fmc_init(void)
+{
+	return bus_register(&fmc_bus_type);
+}
+
+static void fmc_exit(void)
+{
+	bus_unregister(&fmc_bus_type);
+}
+
+module_init(fmc_init);
+module_exit(fmc_exit);
+
+MODULE_LICENSE("GPL");
-- 
1.7.7.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 2/8] FMC: add needed headers
  2013-06-12  7:13 [PATCH 0/8] Support for FMC carriers and mezzanines Alessandro Rubini
  2013-06-12  7:13 ` [PATCH 1/8] FMC: create drivers/fmc and toplevel Kconfig question Alessandro Rubini
@ 2013-06-12  7:13 ` Alessandro Rubini
  2013-06-17 23:21   ` Greg KH
                     ` (3 more replies)
  2013-06-12  7:13 ` [PATCH 3/8] FMC: add core bus driver Alessandro Rubini
                   ` (5 subsequent siblings)
  7 siblings, 4 replies; 26+ messages in thread
From: Alessandro Rubini @ 2013-06-12  7:13 UTC (permalink / raw)
  To: linux-kernel
  Cc: Juan David Gonzalez Cobas, Emilio G. Cota,
	Samuel Iglesias Gonsalvez, gregkh, Rob Landley, linux-doc

This set of headers comes from commit ab23167f (current master of the
project on ohwr.org). They define the basic data structures for FMC
and its SDB support.

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>
---
 include/linux/fmc-sdb.h  |   36 +++++++
 include/linux/fmc.h      |  237 ++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/ipmi-fru.h |  135 ++++++++++++++++++++++++++
 include/linux/sdb.h      |  159 +++++++++++++++++++++++++++++++
 4 files changed, 567 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/fmc-sdb.h
 create mode 100644 include/linux/fmc.h
 create mode 100644 include/linux/ipmi-fru.h
 create mode 100644 include/linux/sdb.h

diff --git a/include/linux/fmc-sdb.h b/include/linux/fmc-sdb.h
new file mode 100644
index 0000000..1974317
--- /dev/null
+++ b/include/linux/fmc-sdb.h
@@ -0,0 +1,36 @@
+/*
+ * This file is separate from sdb.h, because I want that one to remain
+ * unchanged (as far as possible) from the official sdb distribution
+ *
+ * This file and associated functionality are a playground for me to
+ * understand stuff which will later be implemented in more generic places.
+ */
+#include <linux/sdb.h>
+
+/* This is the union of all currently defined types */
+union sdb_record {
+	struct sdb_interconnect ic;
+	struct sdb_device dev;
+	struct sdb_bridge bridge;
+	struct sdb_integration integr;
+	struct sdb_empty empty;
+};
+
+struct fmc_device;
+
+/* Every sdb table is turned into this structure */
+struct sdb_array {
+	int len;
+	int level;
+	unsigned long baseaddr;
+	struct fmc_device *fmc;		/* the device that hosts it */
+	struct sdb_array *parent;	/* NULL at root */
+	union sdb_record *record;	/* copies of the struct */
+	struct sdb_array **subtree;	/* only valid for bridge items */
+};
+
+extern int fmc_scan_sdb_tree(struct fmc_device *fmc, unsigned long address);
+extern void fmc_show_sdb_tree(const struct fmc_device *fmc);
+extern signed long fmc_find_sdb_device(struct sdb_array *tree, uint64_t vendor,
+				       uint32_t device, unsigned long *sz);
+extern int fmc_free_sdb_tree(struct fmc_device *fmc);
diff --git a/include/linux/fmc.h b/include/linux/fmc.h
new file mode 100644
index 0000000..a3c4985
--- /dev/null
+++ b/include/linux/fmc.h
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2012 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ *
+ * This work is part of the White Rabbit project, a research effort led
+ * by CERN, the European Institute for Nuclear Research.
+ */
+#ifndef __LINUX_FMC_H__
+#define __LINUX_FMC_H__
+#include <linux/types.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+struct fmc_device;
+struct fmc_driver;
+
+/*
+ * This bus abstraction is developed separately from drivers, so we need
+ * to check the version of the data structures we receive.
+ */
+
+#define FMC_MAJOR	3
+#define FMC_MINOR	0
+#define FMC_VERSION	((FMC_MAJOR << 16) | FMC_MINOR)
+#define __FMC_MAJOR(x)	((x) >> 16)
+#define __FMC_MINOR(x)	((x) & 0xffff)
+
+/*
+ * The device identification, as defined by the IPMI FRU (Field Replaceable
+ * Unit) includes four different strings to describe the device. Here we
+ * only match the "Board Manufacturer" and the "Board Product Name",
+ * ignoring the "Board Serial Number" and "Board Part Number". All 4 are
+ * expected to be strings, so they are treated as zero-terminated C strings.
+ * Unspecified string (NULL) means "any", so if both are unspecified this
+ * is a catch-all driver. So null entries are allowed and we use array
+ * and length. This is unlike pci and usb that use null-terminated arrays
+ */
+struct fmc_fru_id {
+	char *manufacturer;
+	char *product_name;
+};
+
+/*
+ * If the FPGA is already programmed (think Etherbone or the second
+ * SVEC slot), we can match on SDB devices in the memory image. This
+ * match uses an array of devices that must all be present, and the
+ * match is based on vendor and device only. Further checks are expected
+ * to happen in the probe function. Zero means "any" and catch-all is allowed.
+ */
+struct fmc_sdb_one_id {
+	uint64_t vendor;
+	uint32_t device;
+};
+struct fmc_sdb_id {
+	struct fmc_sdb_one_id *cores;
+	int cores_nr;
+};
+
+struct fmc_device_id {
+	struct fmc_fru_id *fru_id;
+	int fru_id_nr;
+	struct fmc_sdb_id *sdb_id;
+	int sdb_id_nr;
+};
+
+/* This sizes the module_param_array used by generic module parameters */
+#define FMC_MAX_CARDS 32
+
+/* The driver is a pretty simple thing */
+struct fmc_driver {
+	unsigned long version;
+	struct device_driver driver;
+	int (*probe)(struct fmc_device *);
+	int (*remove)(struct fmc_device *);
+	const struct fmc_device_id id_table;
+	/* What follows is for generic module parameters */
+	int busid_n;
+	int busid_val[FMC_MAX_CARDS];
+	int gw_n;
+	char *gw_val[FMC_MAX_CARDS];
+};
+#define to_fmc_driver(x) container_of((x), struct fmc_driver, driver)
+
+/* These are the generic parameters, that drivers may instantiate */
+#define FMC_PARAM_BUSID(_d) \
+    module_param_array_named(busid, _d.busid_val, int, &_d.busid_n, 0444)
+#define FMC_PARAM_GATEWARE(_d) \
+    module_param_array_named(gateware, _d.gw_val, charp, &_d.gw_n, 0444)
+
+/*
+ * Drivers may need to configure gpio pins in the carrier. To read input
+ * (a very uncommon operation, and definitely not in the hot paths), just
+ * configure one gpio only and get 0 or 1 as retval of the config method
+ */
+struct fmc_gpio {
+	char *carrier_name; /* name or NULL for virtual pins */
+	int gpio;
+	int _gpio;	/* internal use by the carrier */
+	int mode;	/* GPIOF_DIR_OUT etc, from <linux/gpio.h> */
+	int irqmode;	/* IRQF_TRIGGER_LOW and so on */
+};
+
+/* The numbering of gpio pins allows access to raw pins or virtual roles */
+#define FMC_GPIO_RAW(x)		(x)		/* 4096 of them */
+#define __FMC_GPIO_IS_RAW(x)	((x) < 0x1000)
+#define FMC_GPIO_IRQ(x)		((x) + 0x1000)	/*  256 of them */
+#define FMC_GPIO_LED(x)		((x) + 0x1100)	/*  256 of them */
+#define FMC_GPIO_KEY(x)		((x) + 0x1200)	/*  256 of them */
+#define FMC_GPIO_TP(x)		((x) + 0x1300)	/*  256 of them */
+#define FMC_GPIO_USER(x)	((x) + 0x1400)	/*  256 of them */
+/* We may add SCL and SDA, or other roles if the need arises */
+
+/* GPIOF_DIR_IN etc are missing before 3.0. copy from <linux/gpio.h> */
+#ifndef GPIOF_DIR_IN
+#  define GPIOF_DIR_OUT   (0 << 0)
+#  define GPIOF_DIR_IN    (1 << 0)
+#  define GPIOF_INIT_LOW  (0 << 1)
+#  define GPIOF_INIT_HIGH (1 << 1)
+#endif
+
+/*
+ * The operations are offered by each carrier and should make driver
+ * design completely independent of the carrier. Named GPIO pins may be
+ * the exception.
+ */
+struct fmc_operations {
+	uint32_t (*readl)(struct fmc_device *fmc, int offset);
+	void (*writel)(struct fmc_device *fmc, uint32_t value, int offset);
+	int (*validate)(struct fmc_device *fmc, struct fmc_driver *drv);
+	int (*reprogram)(struct fmc_device *f, struct fmc_driver *d, char *gw);
+	int (*irq_request)(struct fmc_device *fmc, irq_handler_t h,
+			   char *name, int flags);
+	void (*irq_ack)(struct fmc_device *fmc);
+	int (*irq_free)(struct fmc_device *fmc);
+	int (*gpio_config)(struct fmc_device *fmc, struct fmc_gpio *gpio,
+			   int ngpio);
+	int (*read_ee)(struct fmc_device *fmc, int pos, void *d, int l);
+	int (*write_ee)(struct fmc_device *fmc, int pos, const void *d, int l);
+};
+
+/* Prefer this helper rather than calling of fmc->reprogram directly */
+extern int fmc_reprogram(struct fmc_device *f, struct fmc_driver *d, char *gw,
+		     int sdb_entry);
+
+/*
+ * The device reports all information needed to access hw.
+ *
+ * If we have eeprom_len and not contents, the core reads it.
+ * Then, parsing of identifiers is done by the core which fills fmc_fru_id..
+ * Similarly a device that must be matched based on SDB cores must
+ * fill the entry point and the core will scan the bus (FIXME: sdb match)
+ */
+struct fmc_device {
+	unsigned long version;
+	unsigned long flags;
+	struct module *owner;		/* char device must pin it */
+	struct fmc_fru_id id;		/* for EEPROM-based match */
+	struct fmc_operations *op;	/* carrier-provided */
+	int irq;			/* according to host bus. 0 == none */
+	int eeprom_len;			/* Usually 8kB, may be less */
+	int eeprom_addr;		/* 0x50, 0x52 etc */
+	uint8_t *eeprom;		/* Full contents or leading part */
+	char *carrier_name;		/* "SPEC" or similar, for special use */
+	void *carrier_data;		/* "struct spec *" or equivalent */
+	__iomem void *fpga_base;	/* May be NULL (Etherbone) */
+	__iomem void *slot_base;	/* Set by the driver */
+	struct fmc_device **devarray;	/* Allocated by the bus */
+	int slot_id;			/* Index in the slot array */
+	int nr_slots;			/* Number of slots in this carrier */
+	unsigned long memlen;		/* Used for the char device */
+	struct device dev;		/* For Linux use */
+	struct device *hwdev;		/* The underlying hardware device */
+	unsigned long sdbfs_entry;
+	struct sdb_array *sdb;
+	uint32_t device_id;		/* Filled by the device */
+	char *mezzanine_name;		/* Defaults to ``fmc'' */
+	void *mezzanine_data;
+};
+#define to_fmc_device(x) container_of((x), struct fmc_device, dev)
+
+#define FMC_DEVICE_HAS_GOLDEN		1
+#define FMC_DEVICE_HAS_CUSTOM		2
+#define FMC_DEVICE_NO_MEZZANINE		4
+#define FMC_DEVICE_MATCH_SDB		8 /* fmc-core must scan sdb in fpga */
+
+/*
+ * If fpga_base can be used, the carrier offers no readl/writel methods, and
+ * this expands to a single, fast, I/O access.
+ */
+static inline uint32_t fmc_readl(struct fmc_device *fmc, int offset)
+{
+	if (unlikely(fmc->op->readl))
+		return fmc->op->readl(fmc, offset);
+	return readl(fmc->fpga_base + offset);
+}
+static inline void fmc_writel(struct fmc_device *fmc, uint32_t val, int off)
+{
+	if (unlikely(fmc->op->writel))
+		fmc->op->writel(fmc, val, off);
+	else
+		writel(val, fmc->fpga_base + off);
+}
+
+/* pci-like naming */
+static inline void *fmc_get_drvdata(const struct fmc_device *fmc)
+{
+	return dev_get_drvdata(&fmc->dev);
+}
+
+static inline void fmc_set_drvdata(struct fmc_device *fmc, void *data)
+{
+	dev_set_drvdata(&fmc->dev, data);
+}
+
+/* The 4 access points */
+extern int fmc_driver_register(struct fmc_driver *drv);
+extern void fmc_driver_unregister(struct fmc_driver *drv);
+extern int fmc_device_register(struct fmc_device *tdev);
+extern void fmc_device_unregister(struct fmc_device *tdev);
+
+/* Two more for device sets, all driven by the same FPGA */
+extern int fmc_device_register_n(struct fmc_device **devs, int n);
+extern void fmc_device_unregister_n(struct fmc_device **devs, int n);
+
+/* Internal cross-calls between files; not exported to other modules */
+extern int fmc_match(struct device *dev, struct device_driver *drv);
+extern int fmc_fill_id_info(struct fmc_device *fmc);
+extern void fmc_free_id_info(struct fmc_device *fmc);
+extern void fmc_dump_eeprom(const struct fmc_device *fmc);
+extern void fmc_dump_sdb(const struct fmc_device *fmc);
+
+#endif /* __LINUX_FMC_H__ */
diff --git a/include/linux/ipmi-fru.h b/include/linux/ipmi-fru.h
new file mode 100644
index 0000000..4d3a763
--- /dev/null
+++ b/include/linux/ipmi-fru.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2012 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ *
+ * This work is part of the White Rabbit project, a research effort led
+ * by CERN, the European Institute for Nuclear Research.
+ */
+#ifndef __LINUX_IPMI_FRU_H__
+#define __LINUX_IPMI_FRU_H__
+#ifdef __KERNEL__
+#  include <linux/types.h>
+#  include <linux/string.h>
+#else
+#  include <stdint.h>
+#  include <string.h>
+#endif
+
+/*
+ * These structures match the unaligned crap we have in FRU1011.pdf
+ * (http://download.intel.com/design/servers/ipmi/FRU1011.pdf)
+ */
+
+/* chapter 8, page 5 */
+struct fru_common_header {
+	uint8_t format;			/* 0x01 */
+	uint8_t internal_use_off;	/* multiple of 8 bytes */
+	uint8_t chassis_info_off;	/* multiple of 8 bytes */
+	uint8_t board_area_off;		/* multiple of 8 bytes */
+	uint8_t product_area_off;	/* multiple of 8 bytes */
+	uint8_t multirecord_off;	/* multiple of 8 bytes */
+	uint8_t pad;			/* must be 0 */
+	uint8_t checksum;		/* sum modulo 256 must be 0 */
+};
+
+/* chapter 9, page 5 -- internal_use: not used by us */
+
+/* chapter 10, page 6 -- chassis info: not used by us */
+
+/* chapter 13, page 9 -- used by board_info_area below */
+struct fru_type_length {
+	uint8_t type_length;
+	uint8_t data[0];
+};
+
+/* chapter 11, page 7 */
+struct fru_board_info_area {
+	uint8_t format;			/* 0x01 */
+	uint8_t area_len;		/* multiple of 8 bytes */
+	uint8_t language;		/* I hope it's 0 */
+	uint8_t mfg_date[3];		/* LSB, minutes since 1996-01-01 */
+	struct fru_type_length tl[0];	/* type-length stuff follows */
+
+	/*
+	 * the TL there are in order:
+	 * Board Manufacturer
+	 * Board Product Name
+	 * Board Serial Number
+	 * Board Part Number
+	 * FRU File ID (may be null)
+	 * more manufacturer-specific stuff
+	 * 0xc1 as a terminator
+	 * 0x00 pad to a multiple of 8 bytes - 1
+	 * checksum (sum of all stuff module 256 must be zero)
+	 */
+};
+
+enum fru_type {
+	FRU_TYPE_BINARY		= 0x00,
+	FRU_TYPE_BCDPLUS	= 0x40,
+	FRU_TYPE_ASCII6		= 0x80,
+	FRU_TYPE_ASCII		= 0xc0, /* not ascii: depends on language */
+};
+
+/*
+ * some helpers
+ */
+static inline struct fru_board_info_area *fru_get_board_area(
+	const struct fru_common_header *header)
+{
+	/* we know for sure that the header is 8 bytes in size */
+	return (struct fru_board_info_area *)(header + header->board_area_off);
+}
+
+static inline int fru_type(struct fru_type_length *tl)
+{
+	return tl->type_length & 0xc0;
+}
+
+static inline int fru_length(struct fru_type_length *tl)
+{
+	return (tl->type_length & 0x3f) + 1; /* len of whole record */
+}
+
+/* assume ascii-latin1 encoding */
+static inline int fru_strlen(struct fru_type_length *tl)
+{
+	return fru_length(tl) - 1;
+}
+
+static inline char *fru_strcpy(char *dest, struct fru_type_length *tl)
+{
+	int len = fru_strlen(tl);
+	memcpy(dest, tl->data, len);
+	dest[len] = '\0';
+	return dest;
+}
+
+static inline struct fru_type_length *fru_next_tl(struct fru_type_length *tl)
+{
+	return tl + fru_length(tl);
+}
+
+static inline int fru_is_eof(struct fru_type_length *tl)
+{
+	return tl->type_length == 0xc1;
+}
+
+/*
+ * External functions defined in fru-parse.c.
+ */
+extern int fru_header_cksum_ok(struct fru_common_header *header);
+extern int fru_bia_cksum_ok(struct fru_board_info_area *bia);
+
+/* All these 4 return allocated strings by calling fru_alloc() */
+extern char *fru_get_board_manufacturer(struct fru_common_header *header);
+extern char *fru_get_product_name(struct fru_common_header *header);
+extern char *fru_get_serial_number(struct fru_common_header *header);
+extern char *fru_get_part_number(struct fru_common_header *header);
+
+/* This must be defined by the caller of the above functions */
+extern void *fru_alloc(size_t size);
+
+#endif /* __LINUX_IMPI_FRU_H__ */
diff --git a/include/linux/sdb.h b/include/linux/sdb.h
new file mode 100644
index 0000000..fbb76a4
--- /dev/null
+++ b/include/linux/sdb.h
@@ -0,0 +1,159 @@
+/*
+ * This is the official version 1.1 of sdb.h
+ */
+#ifndef __SDB_H__
+#define __SDB_H__
+#ifdef __KERNEL__
+#include <linux/types.h>
+#else
+#include <stdint.h>
+#endif
+
+/*
+ * All structures are 64 bytes long and are expected
+ * to live in an array, one for each interconnect.
+ * Most fields of the structures are shared among the
+ * various types, and most-specific fields are at the
+ * beginning (for alignment reasons, and to keep the
+ * magic number at the head of the interconnect record
+ */
+
+/* Product, 40 bytes at offset 24, 8-byte aligned
+ *
+ * device_id is vendor-assigned; version is device-specific,
+ * date is hex (e.g 0x20120501), name is UTF-8, blank-filled
+ * and not terminated with a 0 byte.
+ */
+struct sdb_product {
+	uint64_t		vendor_id;	/* 0x18..0x1f */
+	uint32_t		device_id;	/* 0x20..0x23 */
+	uint32_t		version;	/* 0x24..0x27 */
+	uint32_t		date;		/* 0x28..0x2b */
+	uint8_t			name[19];	/* 0x2c..0x3e */
+	uint8_t			record_type;	/* 0x3f */
+};
+
+/*
+ * Component, 56 bytes at offset 8, 8-byte aligned
+ *
+ * The address range is first to last, inclusive
+ * (for example 0x100000 - 0x10ffff)
+ */
+struct sdb_component {
+	uint64_t		addr_first;	/* 0x08..0x0f */
+	uint64_t		addr_last;	/* 0x10..0x17 */
+	struct sdb_product	product;	/* 0x18..0x3f */
+};
+
+/* Type of the SDB record */
+enum sdb_record_type {
+	sdb_type_interconnect	= 0x00,
+	sdb_type_device		= 0x01,
+	sdb_type_bridge		= 0x02,
+	sdb_type_integration	= 0x80,
+	sdb_type_repo_url	= 0x81,
+	sdb_type_synthesis	= 0x82,
+	sdb_type_empty		= 0xFF,
+};
+
+/* Type 0: interconnect (first of the array)
+ *
+ * sdb_records is the length of the table including this first
+ * record, version is 1. The bus type is enumerated later.
+ */
+#define				SDB_MAGIC	0x5344422d /* "SDB-" */
+struct sdb_interconnect {
+	uint32_t		sdb_magic;	/* 0x00-0x03 */
+	uint16_t		sdb_records;	/* 0x04-0x05 */
+	uint8_t			sdb_version;	/* 0x06 */
+	uint8_t			sdb_bus_type;	/* 0x07 */
+	struct sdb_component	sdb_component;	/* 0x08-0x3f */
+};
+
+/* Type 1: device
+ *
+ * class is 0 for "custom device", other values are
+ * to be standardized; ABI version is for the driver,
+ * bus-specific bits are defined by each bus (see below)
+ */
+struct sdb_device {
+	uint16_t		abi_class;	/* 0x00-0x01 */
+	uint8_t			abi_ver_major;	/* 0x02 */
+	uint8_t			abi_ver_minor;	/* 0x03 */
+	uint32_t		bus_specific;	/* 0x04-0x07 */
+	struct sdb_component	sdb_component;	/* 0x08-0x3f */
+};
+
+/* Type 2: bridge
+ *
+ * child is the address of the nested SDB table
+ */
+struct sdb_bridge {
+	uint64_t		sdb_child;	/* 0x00-0x07 */
+	struct sdb_component	sdb_component;	/* 0x08-0x3f */
+};
+
+/* Type 0x80: integration
+ *
+ * all types with bit 7 set are meta-information, so
+ * software can ignore the types it doesn't know. Here we
+ * just provide product information for an aggregate device
+ */
+struct sdb_integration {
+	uint8_t			reserved[24];	/* 0x00-0x17 */
+	struct sdb_product	product;	/* 0x08-0x3f */
+};
+
+/* Type 0x81: Top module repository url
+ *
+ * again, an informative field that software can ignore
+ */
+struct sdb_repo_url {
+	uint8_t			repo_url[63];	/* 0x00-0x3e */
+	uint8_t			record_type;	/* 0x3f */
+};
+
+/* Type 0x82: Synthesis tool information
+ *
+ * this informative record
+ */
+struct sdb_synthesis {
+	uint8_t			syn_name[16];	/* 0x00-0x0f */
+	uint8_t			commit_id[16];	/* 0x10-0x1f */
+	uint8_t			tool_name[8];	/* 0x20-0x27 */
+	uint32_t		tool_version;	/* 0x28-0x2b */
+	uint32_t		date;		/* 0x2c-0x2f */
+	uint8_t			user_name[15];	/* 0x30-0x3e */
+	uint8_t			record_type;	/* 0x3f */
+};
+
+/* Type 0xff: empty
+ *
+ * this allows keeping empty slots during development,
+ * so they can be filled later with minimal efforts and
+ * no misleading description is ever shipped -- hopefully.
+ * It can also be used to pad a table to a desired length.
+ */
+struct sdb_empty {
+	uint8_t			reserved[63];	/* 0x00-0x3e */
+	uint8_t			record_type;	/* 0x3f */
+};
+
+/* The type of bus, for bus-specific flags */
+enum sdb_bus_type {
+	sdb_wishbone = 0x00,
+	sdb_data     = 0x01,
+};
+
+#define SDB_WB_WIDTH_MASK	0x0f
+#define SDB_WB_ACCESS8			0x01
+#define SDB_WB_ACCESS16			0x02
+#define SDB_WB_ACCESS32			0x04
+#define SDB_WB_ACCESS64			0x08
+#define SDB_WB_LITTLE_ENDIAN	0x80
+
+#define SDB_DATA_READ		0x04
+#define SDB_DATA_WRITE		0x02
+#define SDB_DATA_EXEC		0x01
+
+#endif /* __SDB_H__ */
-- 
1.7.7.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 3/8] FMC: add core bus driver
  2013-06-12  7:13 [PATCH 0/8] Support for FMC carriers and mezzanines Alessandro Rubini
  2013-06-12  7:13 ` [PATCH 1/8] FMC: create drivers/fmc and toplevel Kconfig question Alessandro Rubini
  2013-06-12  7:13 ` [PATCH 2/8] FMC: add needed headers Alessandro Rubini
@ 2013-06-12  7:13 ` Alessandro Rubini
  2013-06-17 23:15   ` Greg KH
                     ` (2 more replies)
  2013-06-12  7:13 ` [PATCH 4/8] FMC: add documentation for the core Alessandro Rubini
                   ` (4 subsequent siblings)
  7 siblings, 3 replies; 26+ messages in thread
From: Alessandro Rubini @ 2013-06-12  7:13 UTC (permalink / raw)
  To: linux-kernel
  Cc: Juan David Gonzalez Cobas, Emilio G. Cota,
	Samuel Iglesias Gonsalvez, gregkh, Rob Landley, linux-doc

This module offers registration services for both carriers
(i.e. devices) and mezzanines (i.e. drivers). The matching for devices
and drivers is performed according to the IPMI standard for FRU
devices (Field Replaceable Units).

The code includes support for parsing an SDB tree if present in the FPGA,
and dumping it for diagnostics. SDB is not mandatory.

Files in this commit correspond to commit ab23167f in the master branch
of the project hosted on ohwr.org.

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>
---
 drivers/fmc/Makefile    |    4 +
 drivers/fmc/fmc-core.c  |  274 ++++++++++++++++++++++++++++++++++++++++++++++-
 drivers/fmc/fmc-dump.c  |  100 +++++++++++++++++
 drivers/fmc/fmc-match.c |  114 ++++++++++++++++++++
 drivers/fmc/fmc-sdb.c   |  265 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/fmc/fru-parse.c |   82 ++++++++++++++
 6 files changed, 838 insertions(+), 1 deletions(-)
 create mode 100644 drivers/fmc/fmc-dump.c
 create mode 100644 drivers/fmc/fmc-match.c
 create mode 100644 drivers/fmc/fmc-sdb.c
 create mode 100644 drivers/fmc/fru-parse.c

diff --git a/drivers/fmc/Makefile b/drivers/fmc/Makefile
index a2784d8..df98939 100644
--- a/drivers/fmc/Makefile
+++ b/drivers/fmc/Makefile
@@ -2,3 +2,7 @@
 obj-$(CONFIG_FMC) += fmc.o
 
 fmc-y = fmc-core.o
+fmc-y += fmc-match.o
+fmc-y += fmc-sdb.o
+fmc-y += fru-parse.o
+fmc-y += fmc-dump.o
diff --git a/drivers/fmc/fmc-core.c b/drivers/fmc/fmc-core.c
index fc3547f..f4c8801 100644
--- a/drivers/fmc/fmc-core.c
+++ b/drivers/fmc/fmc-core.c
@@ -1,13 +1,285 @@
-/* Temporary placeholder so the empty code can build */
+/*
+ * Copyright (C) 2012 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ *
+ * This work is part of the White Rabbit project, a research effort led
+ * by CERN, the European Institute for Nuclear Research.
+ */
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/device.h>
+#include <linux/fmc.h>
+
+static int fmc_check_version(unsigned long version, const char *name)
+{
+	if (__FMC_MAJOR(version) != FMC_MAJOR) {
+		pr_err("%s: \"%s\" has wrong major (has %li, expected %i)\n",
+		       __func__, name, __FMC_MAJOR(version), FMC_MAJOR);
+		return -EINVAL;
+	}
+
+	if (__FMC_MINOR(version) != FMC_MINOR)
+		pr_info("%s: \"%s\" has wrong minor (has %li, expected %i)\n",
+		       __func__, name, __FMC_MINOR(version), FMC_MINOR);
+	return 0;
+}
+
+static int fmc_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	/* struct fmc_device *fdev = to_fmc_device(dev); */
+
+	/* FIXME: The MODALIAS */
+	add_uevent_var(env, "MODALIAS=%s", "fmc");
+	return 0;
+}
+
+static int fmc_probe(struct device *dev)
+{
+	struct fmc_driver *fdrv = to_fmc_driver(dev->driver);
+	struct fmc_device *fdev = to_fmc_device(dev);
+
+	return fdrv->probe(fdev);
+}
+
+static int fmc_remove(struct device *dev)
+{
+	struct fmc_driver *fdrv = to_fmc_driver(dev->driver);
+	struct fmc_device *fdev = to_fmc_device(dev);
+
+	return fdrv->remove(fdev);
+}
+
+static void fmc_shutdown(struct device *dev)
+{
+	/* not implemented but mandatory */
+}
 
 static struct bus_type fmc_bus_type = {
 	.name = "fmc",
+	.match = fmc_match,
+	.uevent = fmc_uevent,
+	.probe = fmc_probe,
+	.remove = fmc_remove,
+	.shutdown = fmc_shutdown,
 };
 
+static void fmc_release(struct device *dev)
+{
+	struct fmc_device *fmc = container_of(dev, struct fmc_device, dev);
+
+	kfree(fmc);
+}
+
+/*
+ * The eeprom is exported in sysfs, through a binary attribute
+ */
+
+static ssize_t fmc_read_eeprom(struct file *file, struct kobject *kobj,
+			   struct bin_attribute *bin_attr,
+			   char *buf, loff_t off, size_t count)
+{
+	struct device *dev;
+	struct fmc_device *fmc;
+	int eelen;
+
+	dev = container_of(kobj, struct device, kobj);
+	fmc = container_of(dev, struct fmc_device, dev);
+	eelen = fmc->eeprom_len;
+	if (off > eelen)
+		return -ESPIPE;
+	if (off == eelen)
+		return 0; /* EOF */
+	if (off + count > eelen)
+		count = eelen - off;
+	memcpy(buf, fmc->eeprom + off, count);
+	return count;
+}
+
+static struct bin_attribute fmc_eeprom_attr = {
+	.attr = { .name = "eeprom", .mode = S_IRUGO, },
+	.size = 8192, /* more or less standard */
+	.read = fmc_read_eeprom,
+};
+
+/*
+ * Functions for client modules follow
+ */
+
+int fmc_driver_register(struct fmc_driver *drv)
+{
+	if (fmc_check_version(drv->version, drv->driver.name))
+		return -EINVAL;
+	drv->driver.bus = &fmc_bus_type;
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(fmc_driver_register);
+
+void fmc_driver_unregister(struct fmc_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(fmc_driver_unregister);
+
+/*
+ * When a device set is registered, all eeproms must be read
+ * and all FRUs must be parsed
+ */
+int fmc_device_register_n(struct fmc_device **devs, int n)
+{
+	struct fmc_device *fmc, **devarray;
+	uint32_t device_id;
+	int i, ret = 0;
+
+	if (n < 1)
+		return 0;
+
+	/* Check the version of the first data structure (function prints) */
+	if (fmc_check_version(devs[0]->version, devs[0]->carrier_name))
+		return -EINVAL;
+
+	devarray = kmemdup(devs, n * sizeof(*devs), GFP_KERNEL);
+	if (!devarray)
+		return -ENOMEM;
+
+	/* Make all other checks before continuing, for all devices */
+	for (i = 0; i < n; i++) {
+		fmc = devarray[i];
+		if (!fmc->hwdev) {
+			pr_err("%s: device nr. %i has no hwdev pointer\n",
+			       __func__, i);
+			ret = -EINVAL;
+			break;
+		}
+		if (fmc->flags == FMC_DEVICE_NO_MEZZANINE) {
+			dev_info(fmc->hwdev, "absent mezzanine in slot %d\n",
+				 fmc->slot_id);
+			continue;
+		}
+		if (!fmc->eeprom) {
+			dev_err(fmc->hwdev, "no eeprom provided for slot %i\n",
+				fmc->slot_id);
+			ret = -EINVAL;
+		}
+		if (!fmc->eeprom_addr) {
+			dev_err(fmc->hwdev, "no eeprom_addr for slot %i\n",
+				fmc->slot_id);
+			ret = -EINVAL;
+		}
+		if (!fmc->carrier_name || !fmc->carrier_data || \
+		    !fmc->device_id) {
+			dev_err(fmc->hwdev,
+				"deivce nr %i: carrier name, "
+				"data or dev_id not set\n", i);
+			ret = -EINVAL;
+		}
+		if (ret)
+			break;
+
+	}
+	if (ret) {
+		kfree(devarray);
+		return ret;
+	}
+
+	/* Validation is ok. Now init and register the devices */
+	for (i = 0; i < n; i++) {
+		fmc = devarray[i];
+
+		if (fmc->flags == FMC_DEVICE_NO_MEZZANINE)
+			continue; /* dev_info already done above */
+
+		fmc->nr_slots = n; /* each slot must know how many are there */
+		fmc->devarray = devarray;
+
+		device_initialize(&fmc->dev);
+		fmc->dev.release = fmc_release;
+		fmc->dev.parent = fmc->hwdev;
+
+		/* Fill the identification stuff (may fail) */
+		fmc_fill_id_info(fmc);
+
+		fmc->dev.bus = &fmc_bus_type;
+
+		/* Name from mezzanine info or carrier info. Or 0,1,2.. */
+		device_id = fmc->device_id;
+		if (!fmc->mezzanine_name)
+			dev_set_name(&fmc->dev, "fmc-%04x", device_id);
+		else
+			dev_set_name(&fmc->dev, "%s-%04x", fmc->mezzanine_name,
+				     device_id);
+		ret = device_add(&fmc->dev);
+		if (ret < 0) {
+			dev_err(fmc->hwdev, "Slot %i: Failed in registering "
+				"\"%s\"\n", fmc->slot_id, fmc->dev.kobj.name);
+			goto out;
+		}
+		ret = sysfs_create_bin_file(&fmc->dev.kobj, &fmc_eeprom_attr);
+		if (ret < 0) {
+			dev_err(&fmc->dev, "Failed in registering eeprom\n");
+			goto out1;
+		}
+		/* This device went well, give information to the user */
+		fmc_dump_eeprom(fmc);
+		fmc_dump_sdb(fmc);
+	}
+	return 0;
+
+out1:
+	device_del(&fmc->dev);
+out:
+	fmc_free_id_info(fmc);
+	put_device(&fmc->dev);
+
+	kfree(devarray);
+	for (i--; i >= 0; i--) {
+		sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr);
+		device_del(&devs[i]->dev);
+		fmc_free_id_info(devs[i]);
+		put_device(&devs[i]->dev);
+	}
+	return ret;
+
+}
+EXPORT_SYMBOL(fmc_device_register_n);
+
+int fmc_device_register(struct fmc_device *fmc)
+{
+	return fmc_device_register_n(&fmc, 1);
+}
+EXPORT_SYMBOL(fmc_device_register);
+
+void fmc_device_unregister_n(struct fmc_device **devs, int n)
+{
+	int i;
+
+	if (n < 1)
+		return;
+
+	/* Free devarray first, not used by the later loop */
+	kfree(devs[0]->devarray);
+
+	for (i = 0; i < n; i++) {
+		if (devs[i]->flags == FMC_DEVICE_NO_MEZZANINE)
+			continue;
+		sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr);
+		device_del(&devs[i]->dev);
+		fmc_free_id_info(devs[i]);
+		put_device(&devs[i]->dev);
+	}
+}
+EXPORT_SYMBOL(fmc_device_unregister_n);
+
+void fmc_device_unregister(struct fmc_device *fmc)
+{
+	fmc_device_unregister_n(&fmc, 1);
+}
+EXPORT_SYMBOL(fmc_device_unregister);
+
+/* Init and exit are trivial */
 static int fmc_init(void)
 {
 	return bus_register(&fmc_bus_type);
diff --git a/drivers/fmc/fmc-dump.c b/drivers/fmc/fmc-dump.c
new file mode 100644
index 0000000..c91afd6
--- /dev/null
+++ b/drivers/fmc/fmc-dump.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2013 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ *
+ * This work is part of the White Rabbit project, a research effort led
+ * by CERN, the European Institute for Nuclear Research.
+ */
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/fmc.h>
+#include <linux/fmc-sdb.h>
+
+static int fmc_must_dump_eeprom;
+module_param_named(dump_eeprom, fmc_must_dump_eeprom, int, 0644);
+static int fmc_must_dump_sdb;
+module_param_named(dump_sdb, fmc_must_dump_sdb, int, 0644);
+
+#define LINELEN 16
+
+/* Dumping 8k takes oh so much: avoid duplicate lines */
+static const uint8_t *dump_line(int addr, const uint8_t *line,
+				const uint8_t *prev)
+{
+	int i;
+
+	if (!prev || memcmp(line, prev, LINELEN)) {
+		pr_info("%04x: ", addr);
+		for (i = 0; i < LINELEN; ) {
+			printk(KERN_CONT "%02x", line[i]);
+			i++;
+			printk(i & 3 ? " " : i & (LINELEN - 1) ? "  " : "\n");
+		}
+		return line;
+	}
+	/* repeated line */
+	if (line == prev + LINELEN)
+		pr_info("[...]\n");
+	return prev;
+}
+
+void fmc_dump_eeprom(const struct fmc_device *fmc)
+{
+	const uint8_t *line, *prev;
+	int i;
+
+	if (!fmc_must_dump_eeprom)
+		return;
+
+	pr_info("FMC: %s (%s), slot %i, device %s\n", dev_name(fmc->hwdev),
+		fmc->carrier_name, fmc->slot_id, dev_name(&fmc->dev));
+	pr_info("FMC: dumping eeprom 0x%x (%i) bytes\n", fmc->eeprom_len,
+	       fmc->eeprom_len);
+
+	line = fmc->eeprom;
+	prev = NULL;
+	for (i = 0; i < fmc->eeprom_len; i += LINELEN, line += LINELEN)
+		prev = dump_line(i, line, prev);
+}
+
+void fmc_dump_sdb(const struct fmc_device *fmc)
+{
+	const uint8_t *line, *prev;
+	int i, len;
+
+	if (!fmc->sdb)
+		return;
+	if (!fmc_must_dump_sdb)
+		return;
+
+	/* If the argument is not-zero, do simple dump (== show) */
+	if (fmc_must_dump_sdb > 0)
+		fmc_show_sdb_tree(fmc);
+
+	if (fmc_must_dump_sdb == 1)
+		return;
+
+	/* If bigger than 1, dump it seriously, to help debugging */
+
+	/*
+	 * Here we should really use libsdbfs (which is designed to
+	 * work in kernel space as well) , but it doesn't support
+	 * directories yet, and it requires better intergration (it
+	 * should be used instead of fmc-specific code).
+	 *
+	 * So, lazily, just dump the top-level array
+	 */
+	pr_info("FMC: %s (%s), slot %i, device %s\n", dev_name(fmc->hwdev),
+		fmc->carrier_name, fmc->slot_id, dev_name(&fmc->dev));
+	pr_info("FMC: poor dump of sdb first level:\n");
+
+	len = fmc->sdb->len * sizeof(union sdb_record);
+	line = (void *)fmc->sdb->record;
+	prev = NULL;
+	for (i = 0; i < len; i += LINELEN, line += LINELEN)
+		prev = dump_line(i, line, prev);
+	return;
+}
diff --git a/drivers/fmc/fmc-match.c b/drivers/fmc/fmc-match.c
new file mode 100644
index 0000000..104a5ef
--- /dev/null
+++ b/drivers/fmc/fmc-match.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2012 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ *
+ * This work is part of the White Rabbit project, a research effort led
+ * by CERN, the European Institute for Nuclear Research.
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fmc.h>
+#include <linux/ipmi-fru.h>
+
+/* The fru parser is both user and kernel capable: it needs alloc */
+void *fru_alloc(size_t size)
+{
+	return kzalloc(size, GFP_KERNEL);
+}
+
+/* The actual match function */
+int fmc_match(struct device *dev, struct device_driver *drv)
+{
+	struct fmc_driver *fdrv = to_fmc_driver(drv);
+	struct fmc_device *fdev = to_fmc_device(dev);
+	struct fmc_fru_id *fid;
+	int i, matched = 0;
+
+	/* This currently only matches the EEPROM (FRU id) */
+	fid = fdrv->id_table.fru_id;
+	if (!fid) {
+		dev_warn(&fdev->dev, "Driver has no ID: matches all\n");
+		matched = 1;
+	} else {
+		if (!fdev->id.manufacturer || !fdev->id.product_name)
+			return 0; /* the device has no FRU information */
+		for (i = 0; i < fdrv->id_table.fru_id_nr; i++, fid++) {
+			if (fid->manufacturer &&
+			    strcmp(fid->manufacturer, fdev->id.manufacturer))
+				continue;
+			if (fid->product_name &&
+			    strcmp(fid->product_name, fdev->id.product_name))
+				continue;
+			matched = 1;
+			break;
+		}
+	}
+
+	/* FIXME: match SDB contents */
+	return matched;
+}
+
+/* This function creates ID info for a newly registered device */
+int fmc_fill_id_info(struct fmc_device *fmc)
+{
+	struct fru_common_header *h;
+	struct fru_board_info_area *bia;
+	int ret, allocated = 0;
+
+	/* If we know the eeprom length, try to read it off the device */
+	if (fmc->eeprom_len && !fmc->eeprom) {
+		fmc->eeprom = kzalloc(fmc->eeprom_len, GFP_KERNEL);
+		if (!fmc->eeprom)
+			return -ENOMEM;
+		allocated = 1;
+		ret = fmc->op->read_ee(fmc, 0, fmc->eeprom, fmc->eeprom_len);
+		if (ret < 0)
+			goto out;
+	}
+
+	/* If no eeprom, continue with other matches */
+	if (!fmc->eeprom)
+		return 0;
+
+	dev_info(fmc->hwdev, "mezzanine %i\n", fmc->slot_id); /* header */
+
+	/* So we have the eeprom: parse the FRU part (if any) */
+	h = (void *)fmc->eeprom;
+	if (h->format != 1) {
+		pr_info("      EEPROM has no FRU information\n");
+		goto out;
+	}
+	if (!fru_header_cksum_ok(h)) {
+		pr_info("      FRU: wrong header checksum\n");
+		goto out;
+	}
+	bia = fru_get_board_area(h);
+	if (!fru_bia_cksum_ok(bia)) {
+		pr_info("      FRU: wrong board area checksum\n");
+		goto out;
+	}
+	fmc->id.manufacturer = fru_get_board_manufacturer(h);
+	fmc->id.product_name = fru_get_product_name(h);
+	pr_info("      Manufacturer: %s\n", fmc->id.manufacturer);
+	pr_info("      Product name: %s\n", fmc->id.product_name);
+
+	/* Create the short name (FIXME: look in sdb as well) */
+	fmc->mezzanine_name = kstrdup(fmc->id.product_name, GFP_KERNEL);
+
+out:
+	if (allocated) {
+		kfree(fmc->eeprom);
+		fmc->eeprom = NULL;
+	}
+	return 0; /* no error: let other identification work */
+}
+
+/* Some ID data is allocated using fru_alloc() above, so release it */
+void fmc_free_id_info(struct fmc_device *fmc)
+{
+	kfree(fmc->mezzanine_name);
+	kfree(fmc->id.manufacturer);
+	kfree(fmc->id.product_name);
+}
diff --git a/drivers/fmc/fmc-sdb.c b/drivers/fmc/fmc-sdb.c
new file mode 100644
index 0000000..74fb326
--- /dev/null
+++ b/drivers/fmc/fmc-sdb.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2012 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ *
+ * This work is part of the White Rabbit project, a research effort led
+ * by CERN, the European Institute for Nuclear Research.
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/fmc.h>
+#include <linux/sdb.h>
+#include <linux/err.h>
+#include <linux/fmc-sdb.h>
+#include <asm/byteorder.h>
+
+static uint32_t __sdb_rd(struct fmc_device *fmc, unsigned long address,
+			int convert)
+{
+	uint32_t res = fmc_readl(fmc, address);
+	if (convert)
+		return __be32_to_cpu(res);
+	return res;
+}
+
+static struct sdb_array *__fmc_scan_sdb_tree(struct fmc_device *fmc,
+					     unsigned long sdb_addr,
+					     unsigned long reg_base, int level)
+{
+	uint32_t onew;
+	int i, j, n, convert = 0;
+	struct sdb_array *arr, *sub;
+
+	onew = fmc_readl(fmc, sdb_addr);
+	if (onew == SDB_MAGIC) {
+		/* Uh! If we are little-endian, we must convert */
+		if (SDB_MAGIC != __be32_to_cpu(SDB_MAGIC))
+			convert = 1;
+	} else if (onew == __be32_to_cpu(SDB_MAGIC)) {
+		/* ok, don't convert */
+	} else {
+		return ERR_PTR(-ENOENT);
+	}
+	/* So, the magic was there: get the count from offset 4*/
+	onew = __sdb_rd(fmc, sdb_addr + 4, convert);
+	n = __be16_to_cpu(*(uint16_t *)&onew);
+	arr = kzalloc(sizeof(*arr), GFP_KERNEL);
+	if (arr) {
+		arr->record = kzalloc(sizeof(arr->record[0]) * n, GFP_KERNEL);
+		arr->subtree = kzalloc(sizeof(arr->subtree[0]) * n, GFP_KERNEL);
+	}
+	if (!arr || !arr->record || !arr->subtree) {
+		kfree(arr->record);
+		kfree(arr->subtree);
+		kfree(arr);
+		return ERR_PTR(-ENOMEM);
+	}
+	arr->len = n;
+	arr->level = level;
+	arr->fmc = fmc;
+	for (i = 0; i < n; i++) {
+		union  sdb_record *r;
+
+		for (j = 0; j < sizeof(arr->record[0]); j += 4) {
+			*(uint32_t *)((void *)(arr->record + i) + j) =
+				__sdb_rd(fmc, sdb_addr + (i * 64) + j, convert);
+		}
+		r = &arr->record[i];
+		arr->subtree[i] = ERR_PTR(-ENODEV);
+		if (r->empty.record_type == sdb_type_bridge) {
+			struct sdb_component *c = &r->bridge.sdb_component;
+			uint64_t subaddr = __be64_to_cpu(r->bridge.sdb_child);
+			uint64_t newbase = __be64_to_cpu(c->addr_first);
+
+			subaddr += reg_base;
+			newbase += reg_base;
+			sub = __fmc_scan_sdb_tree(fmc, subaddr, newbase,
+						  level + 1);
+			arr->subtree[i] = sub; /* may be error */
+			if (IS_ERR(sub))
+				continue;
+			sub->parent = arr;
+			sub->baseaddr = newbase;
+		}
+	}
+	return arr;
+}
+
+int fmc_scan_sdb_tree(struct fmc_device *fmc, unsigned long address)
+{
+	struct sdb_array *ret;
+	if (fmc->sdb)
+		return -EBUSY;
+	ret = __fmc_scan_sdb_tree(fmc, address, 0 /* regs */, 0);
+	if (IS_ERR(ret))
+		return PTR_ERR(ret);
+	fmc->sdb = ret;
+	return 0;
+}
+EXPORT_SYMBOL(fmc_scan_sdb_tree);
+
+static void __fmc_sdb_free(struct sdb_array *arr)
+{
+	int i, n;
+
+	if (!arr)
+		return;
+	n = arr->len;
+	for (i = 0; i < n; i++) {
+		if (IS_ERR(arr->subtree[i]))
+			continue;
+		__fmc_sdb_free(arr->subtree[i]);
+	}
+	kfree(arr->record);
+	kfree(arr->subtree);
+	kfree(arr);
+}
+
+int fmc_free_sdb_tree(struct fmc_device *fmc)
+{
+	__fmc_sdb_free(fmc->sdb);
+	fmc->sdb = NULL;
+	return 0;
+}
+EXPORT_SYMBOL(fmc_free_sdb_tree);
+
+/* This helper calls reprogram and inizialized sdb as well */
+int fmc_reprogram(struct fmc_device *fmc, struct fmc_driver *d, char *gw,
+			 int sdb_entry)
+{
+	int ret;
+
+	ret = fmc->op->reprogram(fmc, d, gw);
+	if (ret < 0)
+		return ret;
+	if (sdb_entry < 0)
+		return ret;
+
+	/* We are required to find SDB at a given offset */
+	ret = fmc_scan_sdb_tree(fmc, sdb_entry);
+	if (ret < 0) {
+		dev_err(&fmc->dev, "Can't find SDB at address 0x%x\n",
+			sdb_entry);
+		return -ENODEV;
+	}
+	fmc_dump_sdb(fmc);
+	return 0;
+}
+EXPORT_SYMBOL(fmc_reprogram);
+
+static void __fmc_show_sdb_tree(const struct fmc_device *fmc,
+				const struct sdb_array *arr)
+{
+	int i, j, n = arr->len, level = arr->level;
+	const struct sdb_array *ap;
+
+	for (i = 0; i < n; i++) {
+		unsigned long base;
+		union  sdb_record *r;
+		struct sdb_product *p;
+		struct sdb_component *c;
+		r = &arr->record[i];
+		c = &r->dev.sdb_component;
+		p = &c->product;
+		base = 0;
+		for (ap = arr; ap; ap = ap->parent)
+			base += ap->baseaddr;
+		dev_info(&fmc->dev, "SDB: ");
+
+		for (j = 0; j < level; j++)
+			printk(KERN_CONT "   ");
+		switch (r->empty.record_type) {
+		case sdb_type_interconnect:
+			printk(KERN_CONT "%08llx:%08x %.19s\n",
+			       __be64_to_cpu(p->vendor_id),
+			       __be32_to_cpu(p->device_id),
+			       p->name);
+			break;
+		case sdb_type_device:
+			printk(KERN_CONT "%08llx:%08x %.19s (%08llx-%08llx)\n",
+			       __be64_to_cpu(p->vendor_id),
+			       __be32_to_cpu(p->device_id),
+			       p->name,
+			       __be64_to_cpu(c->addr_first) + base,
+			       __be64_to_cpu(c->addr_last) + base);
+			break;
+		case sdb_type_bridge:
+			printk(KERN_CONT "%08llx:%08x %.19s (bridge: %08llx)\n",
+			       __be64_to_cpu(p->vendor_id),
+			       __be32_to_cpu(p->device_id),
+			       p->name,
+			       __be64_to_cpu(c->addr_first) + base);
+			if (IS_ERR(arr->subtree[i])) {
+				printk(KERN_CONT "(bridge error %li)\n",
+				       PTR_ERR(arr->subtree[i]));
+				break;
+			}
+			__fmc_show_sdb_tree(fmc, arr->subtree[i]);
+			break;
+		case sdb_type_integration:
+			printk(KERN_CONT "integration\n");
+			break;
+		case sdb_type_repo_url:
+			printk(KERN_CONT "repo-url\n");
+			break;
+		case sdb_type_synthesis:
+			printk(KERN_CONT "synthesis-info\n");
+			break;
+		case sdb_type_empty:
+			printk(KERN_CONT "empty\n");
+			break;
+		default:
+			printk(KERN_CONT "UNKNOWN TYPE 0x%02x\n",
+			       r->empty.record_type);
+			break;
+		}
+	}
+}
+
+void fmc_show_sdb_tree(const struct fmc_device *fmc)
+{
+	if (!fmc->sdb)
+		return;
+	__fmc_show_sdb_tree(fmc, fmc->sdb);
+}
+EXPORT_SYMBOL(fmc_show_sdb_tree);
+
+signed long fmc_find_sdb_device(struct sdb_array *tree,
+				uint64_t vid, uint32_t did, unsigned long *sz)
+{
+	signed long res = -ENODEV;
+	union  sdb_record *r;
+	struct sdb_product *p;
+	struct sdb_component *c;
+	int i, n = tree->len;
+	uint64_t last, first;
+
+	/* FIXME: what if the first interconnect is not at zero? */
+	for (i = 0; i < n; i++) {
+		r = &tree->record[i];
+		c = &r->dev.sdb_component;
+		p = &c->product;
+
+		if (!IS_ERR(tree->subtree[i]))
+			res = fmc_find_sdb_device(tree->subtree[i],
+						  vid, did, sz);
+		if (res >= 0)
+			return res + tree->baseaddr;
+		if (r->empty.record_type != sdb_type_device)
+			continue;
+		if (__be64_to_cpu(p->vendor_id) != vid)
+			continue;
+		if (__be32_to_cpu(p->device_id) != did)
+			continue;
+		/* found */
+		last = __be64_to_cpu(c->addr_last);
+		first = __be64_to_cpu(c->addr_first);
+		if (sz)
+			*sz = (typeof(*sz))(last + 1 - first);
+		return first + tree->baseaddr;
+	}
+	return res;
+}
+EXPORT_SYMBOL(fmc_find_sdb_device);
diff --git a/drivers/fmc/fru-parse.c b/drivers/fmc/fru-parse.c
new file mode 100644
index 0000000..cb46263
--- /dev/null
+++ b/drivers/fmc/fru-parse.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ *
+ * This work is part of the White Rabbit project, a research effort led
+ * by CERN, the European Institute for Nuclear Research.
+ */
+#include <linux/ipmi-fru.h>
+
+/* Some internal helpers */
+static struct fru_type_length *
+__fru_get_board_tl(struct fru_common_header *header, int nr)
+{
+	struct fru_board_info_area *bia;
+	struct fru_type_length *tl;
+
+	bia = fru_get_board_area(header);
+	tl = bia->tl;
+	while (nr > 0 && !fru_is_eof(tl)) {
+		tl = fru_next_tl(tl);
+		nr--;
+	}
+	if (fru_is_eof(tl))
+		return NULL;
+	return tl;
+}
+
+static char *__fru_alloc_get_tl(struct fru_common_header *header, int nr)
+{
+	struct fru_type_length *tl;
+	char *res;
+	int len;
+
+	tl = __fru_get_board_tl(header, nr);
+	if (!tl)
+		return NULL;
+	len = fru_strlen(tl);
+	res = fru_alloc(fru_strlen(tl) + 1);
+	if (!res)
+		return NULL;
+	return fru_strcpy(res, tl);
+}
+
+/* Public checksum verifiers */
+int fru_header_cksum_ok(struct fru_common_header *header)
+{
+	uint8_t *ptr = (void *)header;
+	int i, sum;
+
+	for (i = sum = 0; i < sizeof(*header); i++)
+		sum += ptr[i];
+	return (sum & 0xff) == 0;
+}
+int fru_bia_cksum_ok(struct fru_board_info_area *bia)
+{
+	uint8_t *ptr = (void *)bia;
+	int i, sum;
+
+	for (i = sum = 0; i < 8 * bia->area_len; i++)
+		sum += ptr[i];
+	return (sum & 0xff) == 0;
+}
+
+/* Get various stuff, trivial */
+char *fru_get_board_manufacturer(struct fru_common_header *header)
+{
+	return __fru_alloc_get_tl(header, 0);
+}
+char *fru_get_product_name(struct fru_common_header *header)
+{
+	return __fru_alloc_get_tl(header, 1);
+}
+char *fru_get_serial_number(struct fru_common_header *header)
+{
+	return __fru_alloc_get_tl(header, 2);
+}
+char *fru_get_part_number(struct fru_common_header *header)
+{
+	return __fru_alloc_get_tl(header, 3);
+}
-- 
1.7.7.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 4/8] FMC: add documentation for the core
  2013-06-12  7:13 [PATCH 0/8] Support for FMC carriers and mezzanines Alessandro Rubini
                   ` (2 preceding siblings ...)
  2013-06-12  7:13 ` [PATCH 3/8] FMC: add core bus driver Alessandro Rubini
@ 2013-06-12  7:13 ` Alessandro Rubini
  2013-06-12  7:14 ` [PATCH 5/8] FMC: add a software carrier driver Alessandro Rubini
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 26+ messages in thread
From: Alessandro Rubini @ 2013-06-12  7:13 UTC (permalink / raw)
  To: linux-kernel
  Cc: Juan David Gonzalez Cobas, Emilio G. Cota,
	Samuel Iglesias Gonsalvez, gregkh, Rob Landley, linux-doc

This is selected sections of the current manual for fmc-bus, as
developed outside of the kernel before submission.

Like the other patches in this set, it corresponds to commit ab23167f of
the repository at ohwr.org

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>
Acked-by: Rob Landley <rob@landley.net>
---
 Documentation/00-INDEX            |    2 +
 Documentation/fmc/00-INDEX        |   26 +++
 Documentation/fmc/API.txt         |   47 ++++++
 Documentation/fmc/FMC-and-SDB.txt |   88 +++++++++++
 Documentation/fmc/carrier.txt     |  311 +++++++++++++++++++++++++++++++++++++
 Documentation/fmc/identifiers.txt |  168 ++++++++++++++++++++
 Documentation/fmc/mezzanine.txt   |  123 +++++++++++++++
 Documentation/fmc/parameters.txt  |   56 +++++++
 8 files changed, 821 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/fmc/00-INDEX
 create mode 100644 Documentation/fmc/API.txt
 create mode 100644 Documentation/fmc/FMC-and-SDB.txt
 create mode 100644 Documentation/fmc/carrier.txt
 create mode 100644 Documentation/fmc/identifiers.txt
 create mode 100644 Documentation/fmc/mezzanine.txt
 create mode 100644 Documentation/fmc/parameters.txt

diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX
index 45b3df9..0c4cc68 100644
--- a/Documentation/00-INDEX
+++ b/Documentation/00-INDEX
@@ -187,6 +187,8 @@ firmware_class/
 	- request_firmware() hotplug interface info.
 flexible-arrays.txt
 	- how to make use of flexible sized arrays in linux
+fmc/
+	- information about the FMC bus abstraction
 frv/
 	- Fujitsu FR-V Linux documentation.
 futex-requeue-pi.txt
diff --git a/Documentation/fmc/00-INDEX b/Documentation/fmc/00-INDEX
new file mode 100644
index 0000000..71304b7
--- /dev/null
+++ b/Documentation/fmc/00-INDEX
@@ -0,0 +1,26 @@
+
+Documentation in this directory comes from sections of the manual we
+wrote for the externally-developed fmc-bus package. The complete
+manual as of today (2013-02) is available in PDF format at
+http://www.ohwr.org/projects/fmc-bus/files
+
+00-INDEX
+	- this file.
+
+FMC-and-SDB.txt
+	- What are FMC and SDB, basic concepts for this framework
+
+API.txt
+	- The functions that are exported by the bus driver
+
+parameters.txt
+	- The module parameters
+
+carrier.txt
+	- writing a carrier (a device)
+
+mezzanine.txt
+	- writing code for your mezzanine (a driver)
+
+identifiers.txt
+	- how identification and matching works
diff --git a/Documentation/fmc/API.txt b/Documentation/fmc/API.txt
new file mode 100644
index 0000000..06b06b92
--- /dev/null
+++ b/Documentation/fmc/API.txt
@@ -0,0 +1,47 @@
+Functions Exported by fmc.ko
+****************************
+
+The FMC core exports the usual 4 functions that are needed for a bus to
+work, and a few more:
+
+        int fmc_driver_register(struct fmc_driver *drv);
+        void fmc_driver_unregister(struct fmc_driver *drv);
+        int fmc_device_register(struct fmc_device *fmc);
+        void fmc_device_unregister(struct fmc_device *fmc);
+
+        int fmc_device_register_n(struct fmc_device **fmc, int n);
+        void fmc_device_unregister_n(struct fmc_device **fmc, int n);
+
+        uint32_t fmc_readl(struct fmc_device *fmc, int offset);
+        void fmc_writel(struct fmc_device *fmc, uint32_t val, int off);
+        void *fmc_get_drvdata(struct fmc_device *fmc);
+        void fmc_set_drvdata(struct fmc_device *fmc, void *data);
+
+        int fmc_reprogram(struct fmc_device *f, struct fmc_driver *d, char *gw,
+                          int sdb_entry);
+
+The data structure that describe a device is detailed in *note FMC
+Device::, the one that describes a driver is detailed in *note FMC
+Driver::.  Please note that structures of type fmc_device must be
+allocated by the caller, but must not be released after unregistering.
+The fmc-bus itself takes care of releasing the structure when their use
+count reaches zero - actually, the device model does that in lieu of us.
+
+The functions to register and unregister n devices are meant to be used
+by carriers that host more than one mezzanine. The devices must all be
+registered at the same time because if the FPGA is reprogrammed, all
+devices in the array are affected. Usually, the driver matching the
+first device will reprogram the FPGA, so other devices must know they
+are already driven by a reprogrammed FPGA.
+
+If a carrier hosts slots that are driven by different FPGA devices, it
+should register as a group only mezzanines that are driven by the same
+FPGA, for the reason outlined above.
+
+Finally, the fmc_reprogram function calls the reprogram method (see
+*note The API Offered by Carriers:: and also scans the memory area for
+an SDB tree. You can pass -1 as sdb_entry to disable such scan.
+Otherwise, the function fails if no tree is found at the specified
+entry point.  The function is meant to factorize common code, and by
+the time you read this it is already used by the spec-sw and fine-delay
+modules.
diff --git a/Documentation/fmc/FMC-and-SDB.txt b/Documentation/fmc/FMC-and-SDB.txt
new file mode 100644
index 0000000..fa14e0b
--- /dev/null
+++ b/Documentation/fmc/FMC-and-SDB.txt
@@ -0,0 +1,88 @@
+
+FMC (FPGA Mezzanine Card) is the standard we use for our I/O devices,
+in the context of White Rabbit and related hardware.
+
+In our I/O environments we need to write drivers for each mezzanine
+card, and such drivers must work regardless of the carrier being used.
+To achieve this, we abstract the FMC interface.
+
+We have a carrier for PCI-E called SPEC and one for VME called SVEC,
+but more are planned.  Also, we support stand-alone devices (usually
+plugged on a SPEC card), controlled through Etherbone, developed by GSI.
+
+Code and documentation for the FMC bus was born as part of the spec-sw
+project, but now it lives in its own project. Other projects, i.e.
+software support for the various carriers, should include this as a
+submodule.
+
+The most up to date version of code and documentation is always
+available from the repository you can clone from:
+
+        git://ohwr.org/fmc-projects/fmc-bus.git (read-only)
+        git@ohwr.org:fmc-projects/fmc-bus.git (read-write for developers)
+
+Selected versions of the documentation, as well as complete tar
+archives for selected revisions are placed to the Files section of the
+project: `http://www.ohwr.org/projects/fmc-bus/files'
+
+
+What is FMC
+***********
+
+FMC, as said, stands for "FPGA Mezzanine Card". It is a standard
+developed by the VME consortium called VITA (VMEbus International Trade
+Association and ratified by ANSI, the American National Standard
+Institute.  The official documentation is called "ANSI-VITA 57.1".
+
+The FMC card is an almost square PCB, around 70x75 millimeters, that is
+called mezzanine in this document.  It usually lives plugged into
+another PCB for power supply and control; such bigger circuit board is
+called carrier from now on, and a single carrier may host more than one
+mezzanine.
+
+In the typical application the mezzanine is mostly analog while the
+carrier is mostly digital, and hosts an FPGA that must be configured to
+match the specific mezzanine and the desired application. Thus, you may
+need to load different FPGA images to drive different instances of the
+same mezzanine.
+
+FMC, as such, is not a bus in the usual meaning of the term, because
+most carriers have only one connector, and carriers with several
+connectors have completely separate electrical connections to them.
+This package, however, implements a bus as a software abstraction.
+
+
+What is SDB
+***********
+
+SDB (Self Describing Bus) is a set of data structures that we use for
+enumerating the internal structure of an FPGA image. We also use it as
+a filesystem inside the FMC EEPROM.
+
+SDB is not mandatory for use of this FMC kernel bus, but if you have SDB
+this package can make good use of it.  SDB itself is developed in the
+fpga-config-space OHWR project. The link to the repository is
+`git://ohwr.org/hdl-core-lib/fpga-config-space.git' and what is used in
+this project lives in the sdbfs subdirectory in there.
+
+SDB support for FMC is described in *note FMC Identification:: and
+*note SDB Support::
+
+
+SDB Support
+***********
+
+The fmc.ko bus driver exports a few functions to help drivers taking
+advantage of the SDB information that may be present in your own FPGA
+memory image.
+
+The module exports the following functions, in the special header
+<linux/fmc-sdb.h>. The linux/ prefix in the name is there because we
+plan to submit it upstream in the future, and don't want to force
+changes on our drivers if that happens.
+
+         int fmc_scan_sdb_tree(struct fmc_device *fmc, unsigned long address);
+         void fmc_show_sdb_tree(struct fmc_device *fmc);
+         signed long fmc_find_sdb_device(struct sdb_array *tree, uint64_t vendor,
+                                         uint32_t device, unsigned long *sz);
+         int fmc_free_sdb_tree(struct fmc_device *fmc);
diff --git a/Documentation/fmc/carrier.txt b/Documentation/fmc/carrier.txt
new file mode 100644
index 0000000..173f6d6
--- /dev/null
+++ b/Documentation/fmc/carrier.txt
@@ -0,0 +1,311 @@
+FMC Device
+**********
+
+Within the Linux bus framework, the FMC device is created and
+registered by the carrier driver. For example, the PCI driver for the
+SPEC card fills a data structure for each SPEC that it drives, and
+registers an associated FMC device for each card.  The SVEC driver can
+do exactly the same for the VME carrier (actually, it should do it
+twice, because the SVEC carries two FMC mezzanines).  Similarly, an
+Etherbone driver will be able to register its own FMC devices, offering
+communication primitives through frame exchange.
+
+The contents of the EEPROM within the FMC are used for identification
+purposes, i.e. for matching the device with its own driver. For this
+reason the device structure includes a complete copy of the EEPROM
+(actually, the carrier driver may choose whether or not to return it -
+for example we most likely won't have the whole EEPROM available for
+Etherbone devices.
+
+The following listing shows the current structure defining a device.
+Please note that all the machinery is in place but some details may
+still change in the future.  For this reason, there is a version field
+at the beginning of the structure.  As usual, the minor number will
+change for compatible changes (like a new flag) and the major number
+will increase when an incompatible change happens (for example, a
+change in layout of some fmc data structures).  Device writers should
+just set it to the value FMC_VERSION, and be ready to get back -EINVAL
+at registration time.
+
+     struct fmc_device {
+             unsigned long version;
+             unsigned long flags;
+             struct module *owner;           /* char device must pin it */
+             struct fmc_fru_id id;           /* for EEPROM-based match */
+             struct fmc_operations *op;      /* carrier-provided */
+             int irq;                        /* according to host bus. 0 == none */
+             int eeprom_len;                 /* Usually 8kB, may be less */
+             int eeprom_addr;                /* 0x50, 0x52 etc */
+             uint8_t *eeprom;                /* Full contents or leading part */
+             char *carrier_name;             /* "SPEC" or similar, for special use */
+             void *carrier_data;             /* "struct spec *" or equivalent */
+             __iomem void *fpga_base;        /* May be NULL (Etherbone) */
+             __iomem void *slot_base;        /* Set by the driver */
+             struct fmc_device **devarray;   /* Allocated by the bus */
+             int slot_id;                    /* Index in the slot array */
+             int nr_slots;                   /* Number of slots in this carrier */
+             unsigned long memlen;           /* Used for the char device */
+             struct device dev;              /* For Linux use */
+             struct device *hwdev;           /* The underlying hardware device */
+             unsigned long sdbfs_entry;
+             struct sdb_array *sdb;
+             uint32_t device_id;             /* Filled by the device */
+             char *mezzanine_name;           /* Defaults to ``fmc'' */
+             void *mezzanine_data;
+     };
+
+The meaning of most fields is summarized in the code comment above.
+
+The following fields must be filled by the carrier driver before
+registration:
+
+   * version: must be set to FMC_VERSION.
+
+   * owner: set to MODULE_OWNER.
+
+   * op: the operations to act on the device.
+
+   * irq: number for the mezzanine; may be zero.
+
+   * eeprom_len: length of the following array.
+
+   * eeprom_addr: 0x50 for first mezzanine and so on.
+
+   * eeprom: the full content of the I2C EEPROM.
+
+   * carrier_name.
+
+   * carrier_data: a unique pointer for the carrier.
+
+   * fpga_base: the I/O memory address (may be NULL).
+
+   * slot_id: the index of this slot (starting from zero).
+
+   * memlen: if fpga_base is valid, the length of I/O memory.
+
+   * hwdev: to be used in some dev_err() calls.
+
+   * device_id: a slot-specific unique integer number.
+
+
+Please note that the carrier should read its own EEPROM memory before
+registering the device, as well as fill all other fields listed above.
+
+The following fields should not be assigned, because they are filled
+later by either the bus or the device driver:
+
+   * flags.
+
+   * fru_id: filled by the bus, parsing the eeprom.
+
+   * slot_base: filled and used by the driver, if useful to it.
+
+   * devarray: an array og all mezzanines driven by a singe FPGA.
+
+   * nr_slots: set by the core at registration time.
+
+   * dev: used by Linux.
+
+   * sdb: FPGA contents, scanned according to driver's directions.
+
+   * sdbfs_entry: SDB entry point in EEPROM: autodetected.
+
+   * mezzanine_data: available for the driver.
+
+   * mezzanine_name: filled by fmc-bus during identification.
+
+
+Note: mezzanine_data may be redundant, because Linux offers the drvdata
+approach, so the field may be removed in later versions of this bus
+implementation.
+
+As I write this, she SPEC carrier is already completely functional in
+the fmc-bus environment, and is a good reference to look at.
+
+
+The API Offered by Carriers
+===========================
+
+The carrier provides a number of methods by means of the
+`fmc_operations' structure, which currently is defined like this
+(again, it is a moving target, please refer to the header rather than
+this document):
+
+     struct fmc_operations {
+             uint32_t (*readl)(struct fmc_device *fmc, int offset);
+             void (*writel)(struct fmc_device *fmc, uint32_t value, int offset);
+             int (*reprogram)(struct fmc_device *f, struct fmc_driver *d, char *gw);
+             int (*validate)(struct fmc_device *fmc, struct fmc_driver *drv);
+             int (*irq_request)(struct fmc_device *fmc, irq_handler_t h,
+                                char *name, int flags);
+             void (*irq_ack)(struct fmc_device *fmc);
+             int (*irq_free)(struct fmc_device *fmc);
+             int (*gpio_config)(struct fmc_device *fmc, struct fmc_gpio *gpio,
+                                int ngpio);
+             int (*read_ee)(struct fmc_device *fmc, int pos, void *d, int l);
+             int (*write_ee)(struct fmc_device *fmc, int pos, const void *d, int l);
+     };
+
+The individual methods perform the following tasks:
+
+`readl'
+`writel'
+     These functions access FPGA registers by whatever means the
+     carrier offers. They are not expected to fail, and most of the time
+     they will just make a memory access to the host bus. If the
+     carrier provides a fpga_base pointer, the driver may use direct
+     access through that pointer. For this reason the header offers the
+     inline functions fmc_readl and fmc_writel that access fpga_base if
+     the respective method is NULL. A driver that wants to be portable
+     and efficient should use fmc_readl and fmc_writel.  For Etherbone,
+     or other non-local carriers, error-management is still to be
+     defined.
+
+`validate'
+     Module parameters are used to manage different applications for
+     two or more boards of the same kind. Validation is based on the
+     busid module parameter, if provided, and returns the matching
+     index in the associated array. See *note Module Parameters:: in in
+     doubt. If no match is found, `-ENOENT' is returned; if the user
+     didn't pass `busid=', all devices will pass validation.  The value
+     returned by the validate method can be used as index into other
+     parameters (for example, some drivers use the `lm32=' parameter in
+     this way). Such "generic parameters" are documented in *note
+     Module Parameters::, below. The validate method is used by
+     `fmc-trivial.ko', described in *note fmc-trivial::.
+
+`reprogram'
+     The carrier enumerates FMC devices by loading a standard (or
+     golden) FPGA binary that allows EEPROM access. Each driver, then,
+     will need to reprogram the FPGA by calling this function.  If the
+     name argument is NULL, the carrier should reprogram the golden
+     binary. If the gateware name has been overridden through module
+     parameters (in a carrier-specific way) the file loaded will match
+     the parameters. Per-device gateware names can be specified using
+     the `gateware=' parameter, see *note Module Parameters::.  Note:
+     Clients should call rhe new helper, fmc_reprogram, which both
+     calls this method and parse the SDB tree of the FPGA.
+
+`irq_request'
+`irq_ack'
+`irq_free'
+     Interrupt management is carrier-specific, so it is abstracted as
+     operations. The interrupt number is listed in the device
+     structure, and for the mezzanine driver the number is only
+     informative.  The handler will receive the fmc pointer as dev_id;
+     the flags argument is passed to the Linux request_irq function,
+     but fmc-specific flags may be added in the future. You'll most
+     likely want to pass the `IRQF_SHARED' flag.
+
+`gpio_config'
+     The method allows to configure a GPIO pin in the carrier, and read
+     its current value if it is configured as input. See *note The GPIO
+     Abstraction:: for details.
+
+`read_ee'
+`write_ee'
+     Read or write the EEPROM. The functions are expected to be only
+     called before reprogramming and the carrier should refuse them
+     with `ENODEV' after reprogramming.  The offset is expected to be
+     within 8kB (the current size), but addresses up to 1MB are
+     reserved to fit bigger I2C devices in the future. Carriers may
+     offer access to other internal flash memories using these same
+     methods: for example the SPEC driver may define that its carrier
+     I2C memory is seen at offset 1M and the internal SPI flash is seen
+     at offset 16M.  This multiplexing of several flash memories in the
+     same address space is is carrier-specific and should only be used
+     by a driver that has verified the `carrier_name' field.
+
+
+
+The GPIO Abstraction
+====================
+
+Support for GPIO pins in the fmc-bus environment is not very
+straightforward and deserves special discussion.
+
+While the general idea of a carrier-independent driver seems to fly,
+configuration of specific signals within the carrier needs at least
+some knowledge of the carrier itself.  For this reason, the specific
+driver can request to configure carrier-specific GPIO pins, numbered
+from 0 to at most 4095.  Configuration is performed by passing a
+pointer to an array of struct fmc_gpio items, as well as the length of
+the array. This is the data structure:
+
+        struct fmc_gpio {
+                char *carrier_name;
+                int gpio;
+                int _gpio;      /* internal use by the carrier */
+                int mode;       /* GPIOF_DIR_OUT etc, from <linux/gpio.h> */
+                int irqmode;    /* IRQF_TRIGGER_LOW and so on */
+        };
+
+By specifying a carrier_name for each pin, the driver may access
+different pins in different carriers.  The gpio_config method is
+expected to return the number of pins successfully configured, ignoring
+requests for other carriers. However, if no pin is configured (because
+no structure at all refers to the current carrier_name), the operation
+returns an error so the caller will know that it is running under a
+yet-unsupported carrier.
+
+So, for example, a driver that has been developed and tested on both
+the SPEC and the SVEC may request configuration of two different GPIO
+pins, and expect one such configuration to succeed - if none succeeds
+it most likely means that the current carrier is a still-unknown one.
+
+If, however, your GPIO pin has a specific known role, you can pass a
+special number in the gpio field, using one of the following macros:
+
+        #define FMC_GPIO_RAW(x)         (x)             /* 4096 of them */
+        #define FMC_GPIO_IRQ(x)         ((x) + 0x1000)  /*  256 of them */
+        #define FMC_GPIO_LED(x)         ((x) + 0x1100)  /*  256 of them */
+        #define FMC_GPIO_KEY(x)         ((x) + 0x1200)  /*  256 of them */
+        #define FMC_GPIO_TP(x)          ((x) + 0x1300)  /*  256 of them */
+        #define FMC_GPIO_USER(x)        ((x) + 0x1400)  /*  256 of them */
+
+Use of virtual GPIO numbers (anything but FMC_GPIO_RAW) is allowed
+provided the carrier_name field in the data structure is left
+unspecified (NULL). Each carrier is responsible for providing a mapping
+between virtual and physical GPIO numbers. The carrier may then use the
+_gpio field to cache the result of this mapping.
+
+All carriers must map their I/O lines to the sets above starting from
+zero.  The SPEC, for example, maps interrupt pins 0 and 1, and test
+points 0 through 3 (even if the test points on the PCB are called
+5,6,7,8).
+
+If, for example, a driver requires a free LED and a test point (for a
+scope probe to be plugged at some point during development) it may ask
+for FMC_GPIO_LED(0) and FMC_GPIO_TP(0). Each carrier will provide
+suitable GPIO pins.  Clearly, the person running the drivers will know
+the order used by the specific carrier driver in assigning leds and
+testpoints, so to make a carrier-dependent use of the diagnostic tools.
+
+In theory, some form of autodetection should be possible: a driver like
+the wr-nic (which uses IRQ(1) on the SPEC card) should configure
+IRQ(0), make a test with software-generated interrupts and configure
+IRQ(1) if the test fails. This probing step should be used because even
+if the wr-nic gateware is known to use IRQ1 on the SPEC, the driver
+should be carrier-independent and thus use IRQ(0) as a first bet -
+actually, the knowledge that IRQ0 may fail is carrier-dependent
+information, but using it doesn't make the driver unsuitable for other
+carriers.
+
+The return value of gpio_config is defined as follows:
+
+   * If no pin in the array can be used by the carrier, `-ENODEV'.
+
+   * If at least one virtual GPIO number cannot be mapped, `-ENOENT'.
+
+   * On success, 0 or positive. The value returned is the number of
+     high input bits (if no input is configured, the value for success
+     is 0).
+
+While I admit the procedure is not completely straightforward, it
+allows configuration, input and output with a single carrier operation.
+Given the typical use case of FMC devices, GPIO operations are not
+expected to ever by in hot paths, and GPIO access so fare has only been
+used to configure the interrupt pin, mode and polarity. Especially
+reading inputs is not expected to be common. If your device has GPIO
+capabilities in the hot path, you should consider using the kernel's
+GPIO mechanisms.
diff --git a/Documentation/fmc/identifiers.txt b/Documentation/fmc/identifiers.txt
new file mode 100644
index 0000000..3bb577f
--- /dev/null
+++ b/Documentation/fmc/identifiers.txt
@@ -0,0 +1,168 @@
+FMC Identification
+******************
+
+The FMC standard requires every compliant mezzanine to carry
+identification information in an I2C EEPROM.  The information must be
+laid out according to the "IPMI Platform Management FRU Information",
+where IPMI is a lie I'd better not expand, and FRU means "Field
+Replaceable Unit".
+
+The FRU information is an intricate unreadable binary blob that must
+live at offset 0 of the EEPROM, and typically extends for a few hundred
+bytes. The standard allows the application to use all the remaining
+storage area of the EEPROM as it wants.
+
+This chapter explains how to create your own EEPROM image and how to
+write it in your mezzanine, as well as how devices and drivers are
+paired at run time.  EEPROM programming uses tools that are part of this
+package and SDB (part of the fpga-config-space package).
+
+The first sections are only interesting for manufacturers who need to
+write the EEPROM. If you are just a software developer writing an FMC
+device or driver, you may jump straight to *note SDB Support::.
+
+
+Building the FRU Structure
+==========================
+
+If you want to know the internals of the FRU structure and despair, you
+can retrieve the document from
+`http://download.intel.com/design/servers/ipmi/FRU1011.pdf' .  The
+standard is awful and difficult without reason, so we only support the
+minimum mandatory subset - we create a simple structure and parse it
+back at run time, but we are not able to either generate or parse more
+arcane features like non-english languages and 6-bit text.  If you need
+more items of the FRU standard for your boards, please submit patches.
+
+This package includes the Python script that Matthieu Cattin wrote to
+generate the FRU binary blob, based on an helper libipmi by Manohar
+Vanga and Matthieu himself.  I changed the test script to receive
+parameters from the command line or from the environment (the command
+line takes precedence)
+
+To make a long story short, in order to build a standard-compliant
+binary file to be burned in your EEPROM, you need the following items:
+
+        Environment    Opt     Official Name          Default
+---------------------------------------------------------------------
+        FRU_VENDOR     -v      "Board Manufacturer"   fmc-example
+        FRU_NAME       -n      "Board Product Name"   mezzanine
+        FRU_SERIAL     -s      `Board Serial Number"  0001
+        FRU_PART       -p      "Board Part Number"    sample-part
+        FRU_OUTPUT     -o      not applicable         /dev/stdout
+
+The "Official Name" above is what you find in the FRU official
+documentation, chapter 11, page 7 ("Board Info Area Format").  The
+output option is used to save the generated binary to a specific file
+name instead of stdout.
+
+You can pass the items to the FRU generator either in the environment
+or on the command line.  This package has currently no support for
+specifying power consumption or such stuff, but I plan to add it as
+soon as I find some time for that.
+
+FIXME: consumption etc for FRU are here or in PTS?
+
+The following example creates a binary image for a specific board:
+
+        ./tools/fru-generator -v CERN -n FmcAdc100m14b4cha \
+               -s HCCFFIA___-CR000003 -p EDA-02063-V5-0 > eeprom.bin
+
+The following example shows a script that builds several binary EEPROM
+images for a series of boards, changing the serial number for each of
+them. The script uses a mix of environment variables and command line
+options, and uses the same string patterns shown above.
+
+        #!/bin/sh
+
+        export FRU_VENDOR="CERN"
+        export FRU_NAME="FmcAdc100m14b4cha"
+        export FRU_PART="EDA-02063-V5-0"
+
+        serial="HCCFFIA___-CR"
+
+        for number in $(seq 1 50); do
+           # build number-string "ns"
+           ns="$(printf %06d $number)"
+           ./fru-generator -s "${serial}${ns}" > eeprom-${ns}.bin
+        done
+
+
+Using SDB-FS in the EEPROM
+==========================
+
+If you want to use SDB as a filesystem in the EEPROM device within the
+mezzanine, you should create one such filesystem using gensdbfs, from
+the fpga-config-space package on OHWR.
+
+By using an SBD filesystem you can cluster several files in a single
+EEPROM, so both the host system and a soft-core running in the FPGA (if
+any) can access extra production-time information.
+
+We chose to use SDB as a storage filesystem because the format is very
+simple, and both the host system and the soft-core will likely already
+include support code for such format. The SDB library offered by the
+fpga-config-space is less than 1kB under LM32, so it proves quite up to
+the task.
+
+The SDB entry point (which acts as a directory listing) cannot live at
+offset zero in the flash device, because the FRU information must live
+there.  To avoid wasting precious storage space while still allowing
+for more-than-minimal FRU structures, the fmc.ko will look for the SDB
+record at address 256, 512 and 1024.
+
+In order to generate the complete EEPROM image you'll need a
+configuration file for gensdbfs: you tell the program where to place
+the sdb entry point, and you must force the FRU data file to be placed
+at the beginning of the storage device. If needed, you can also place
+other files at a special offset (we sometimes do it for backward
+compatibility with drivers we wrote before implementing SDB for flash
+memory).
+
+The directory tools/sdbfs of this package includes a well-commented
+example that you may want to use as a starting point (the comments are
+in the file called -SDB-CONFIG-).  Reading documentation for gensdbfs
+is a suggested first step anyways.
+
+This package (generic FMC bus support) only accesses two files in the
+EEPROM: the FRU information, at offset zero, with a suggested filename
+of IPMI-FRU and the short name for the mezzanine, in a file called
+name. The IPMI-FRU name is not mandatory, but a strongly suggested
+choice; the name filename is mandatory, because this is the preferred
+short name used by the FMC core.  For example, a name of "fdelay" may
+supplement a Product Name like "FmcDelay1ns4cha" - exactly as
+demonstrated in `tools/sdbfs'.
+
+Note: SDB access to flash memory is not yet supported, so the short
+name currently in use is just the "Product Name" FRU string.
+
+The example in tools/sdbfs includes an extra file, that is needed by
+the fine-delay driver, and must live at a known address of 0x1800.  By
+running gensdbfs on that directory you can output your binary EEPROM
+image (here below spusa$ is the shell prompt):
+
+        spusa$ ../fru-generator -v CERN -n FmcDelay1ns4cha -s proto-0 \
+                      -p EDA-02267-V3 > IPMI-FRU
+        spusa$ ls -l
+        total 16
+        -rw-rw-r-- 1 rubini staff 975 Nov 19 18:08 --SDB-CONFIG--
+        -rw-rw-r-- 1 rubini staff 216 Nov 19 18:13 IPMI-FRU
+        -rw-rw-r-- 1 rubini staff  11 Nov 19 18:04 fd-calib
+        -rw-rw-r-- 1 rubini staff   7 Nov 19 18:04 name
+        spusa$ sudo gensdbfs . /lib/firmware/fdelay-eeprom.bin
+        spusa$ sdb-read -l -e 0x100 /lib/firmware/fdelay-eeprom.bin
+        /home/rubini/wip/sdbfs/userspace/sdb-read: listing format is to be defined
+        46696c6544617461:2e202020  00000100-000018ff .
+        46696c6544617461:6e616d65  00000200-00000206 name
+        46696c6544617461:66642d63  00001800-000018ff fd-calib
+        46696c6544617461:49504d49  00000000-000000d7 IPMI-FRU
+        spusa$ ../fru-dump /lib/firmware/fdelay-eeprom.bin
+        /lib/firmware/fdelay-eeprom.bin: manufacturer: CERN
+        /lib/firmware/fdelay-eeprom.bin: product-name: FmcDelay1ns4cha
+        /lib/firmware/fdelay-eeprom.bin: serial-number: proto-0
+        /lib/firmware/fdelay-eeprom.bin: part-number: EDA-02267-V3
+
+As expected, the output file is both a proper sdbfs object and an IPMI
+FRU information blob. The fd-calib file lives at offset 0x1800 and is
+over-allocated to 256 bytes, according to the configuration file for
+gensdbfs.
diff --git a/Documentation/fmc/mezzanine.txt b/Documentation/fmc/mezzanine.txt
new file mode 100644
index 0000000..87910db
--- /dev/null
+++ b/Documentation/fmc/mezzanine.txt
@@ -0,0 +1,123 @@
+FMC Driver
+**********
+
+An FMC driver is concerned with the specific mezzanine and associated
+gateware. As such, it is expected to be independent of the carrier
+being used: it will perform I/O accesses only by means of
+carrier-provided functions.
+
+The matching between device and driver is based on the content of the
+EEPROM (as mandated by the FMC standard) or by the actual cores
+configured in the FPGA; the latter technique is used when the FPGA is
+already programmed when the device is registered to the bus core.
+
+In some special cases it is possible for a driver to directly access
+FPGA registers, by means of the `fpga_base' field of the device
+structure. This may be needed for high-bandwidth peripherals like fast
+ADC cards. If the device module registered a remote device (for example
+by means of Etherbone), the `fpga_base' pointer will be NULL.
+Therefore, drivers must be ready to deal with NULL base pointers, and
+fail gracefully.  Most driver, however, are not expected to access the
+pointer directly but run fmc_readl and fmc_writel instead, which will
+work in any case.
+
+In even more special cases, the driver may access carrier-specific
+functionality: the `carrier_name' string allows the driver to check
+which is the current carrier and make use of the `carrier_data'
+pointer.  We chose to use carrier names rather than numeric identifiers
+for greater flexibility, but also to avoid a central registry within
+the `fmc.h' file - we hope other users will exploit our framework with
+their own carriers.  An example use of carrier names is in GPIO setup
+(see *note The GPIO Abstraction::), although the name match is not
+expected to be performed by the driver.  If you depend on specific
+carriers, please check the carrier name and fail gracefully if your
+driver finds it is running in a yet-unknown-to-it environment.
+
+
+ID Table
+========
+
+Like most other Linux drivers, and FMC driver must list all the devices
+which it is able to drive.  This is usually done by means of a device
+table, but in FMC we can match hardware based either on the contents of
+their EEPROM or on the actual FPGA cores that can be enumerated.
+Therefore, we have two tables of identifiers.
+
+Matching of FRU information depends on two names, the manufacturer (or
+vendor) and the device (see *note FMC Identification::); for
+flexibility during production (i.e. before writing to the EEPROM) the
+bus supports a catch-all driver that specifies NULL strings. For this
+reason, the table is specified as pointer-and-length, not a a
+null-terminated array - the entry with NULL names can be a valid entry.
+
+Matching on FPGA cores depends on two numeric fields: the 64-bit vendor
+number and the 32-bit device number. Support for matching based on
+class is not yet implemented.  Each device is expected to be uniquely
+identified by an array of cores (it matches if all of the cores are
+instantiated), and for consistency the list is passed as
+pointer-and-length.  Several similar devices can be driven by the same
+driver, and thus the driver specifies and array of such arrays.
+
+The complete set of involved data structures is thus the following:
+
+        struct fmc_fru_id { char *manufacturer; char *product_name; };
+        struct fmc_sdb_one_id { uint64_t vendor; uint32_t device; };
+        struct fmc_sdb_id { struct fmc_sdb_one_id *cores; int cores_nr; };
+
+        struct fmc_device_id {
+                struct fmc_fru_id *fru_id; int fru_id_nr;
+                struct fmc_sdb_id *sdb_id; int sdb_id_nr;
+        };
+
+A better reference, with full explanation, is the <linux/fmc.h> header.
+
+
+Module Parameters
+=================
+
+Most of the FMC drivers need the same set of kernel parameters. This
+package includes support to implement common parameters by means of
+fields in the `fmc_driver' structure and simple macro definitions.
+
+The parameters are carrier-specific, in that they rely on the busid
+concept, that varies among carriers. For the SPEC, the identifier is a
+PCI bus and devfn number, 16 bits wide in total; drivers for other
+carriers will most likely offer something similar but not identical,
+and some code duplication is unavoidable.
+
+This is the list of parameters that are common to several modules to
+see how they are actually used, please look at spec-trivial.c.
+
+`busid='
+     This is an array of integers, listing carrier-specific
+     identification numbers. For PIC, for example, `0x0400' represents
+     bus 4, slot 0.  If any such ID is specified, the driver will only
+     accept to drive cards that appear in the list (even if the FMC ID
+     matches). This is accomplished by the validate carrier method.
+
+`gateware='
+     The argument is an array of strings. If no busid= is specified,
+     the first string of gateware= is used for all cards; otherwise the
+     identifiers and gateware names are paired one by one, in the order
+     specified.
+
+`show_sdb='
+     For modules supporting it, this parameter asks to show the SDB
+     internal structure by means of kernel messages. It is disabled by
+     default because those lines tend to hide more important messages,
+     if you look at the system console while loading the drivers.
+     Note: the parameter is being obsoleted, because fmc.ko itself now
+     supports dump_sdb= that applies to every client driver.
+
+
+For example, if you are using the trivial driver to load two different
+gateware files to two different cards, you can use the following
+parameters to load different binaries to the cards, after looking up
+the PCI identifiers. This has been tested with a SPEC carrier.
+
+        insmod fmc-trivial.ko \
+                              busid=0x0200,0x0400 \
+                              gateware=fmc/fine-delay.bin,fmc/simple-dio.bin
+
+Please note that not all sub-modules support all of those parameters.
+You can use modinfo to check what is supported by each module.
diff --git a/Documentation/fmc/parameters.txt b/Documentation/fmc/parameters.txt
new file mode 100644
index 0000000..59edf08
--- /dev/null
+++ b/Documentation/fmc/parameters.txt
@@ -0,0 +1,56 @@
+Module Parameters in fmc.ko
+***************************
+
+The core driver receives two module parameters, meant to help debugging
+client modules. Both parameters can be modified by writing to
+/sys/module/fmc/parameters/, because they are used when client drivers
+are devices are registered, not when fmc.ko is loaded.
+
+`dump_eeprom='
+     If not zero, the parameter asks the bus controller to dump the
+     EEPROM of any device that is registered, using printk.
+
+`dump_sdb='
+     If not zero, the parameter prints the SDB tree of every FPGA it is
+     loaded by fmc_reprogram(). If greater than one, it asks to dump
+     the binary content of SDB records.  This currently only dumps the
+     top-level SDB array, though.
+
+
+EEPROM dumping avoids repeating lines, since most of the contents is
+usually empty and all bits are one or zero. This is an example of the
+output:
+
+        [ 6625.850480] spec 0000:02:00.0: FPGA programming successful
+        [ 6626.139949] spec 0000:02:00.0: Manufacturer: CERN
+        [ 6626.144666] spec 0000:02:00.0: Product name: FmcDelay1ns4cha
+        [ 6626.150370] FMC: mezzanine 0: 0000:02:00.0 on SPEC
+        [ 6626.155179] FMC: dumping eeprom 0x2000 (8192) bytes
+        [ 6626.160087] 0000: 01 00 00 01  00 0b 00 f3  01 0a 00 a5  85 87 c4 43
+        [ 6626.167069] 0010: 45 52 4e cf  46 6d 63 44  65 6c 61 79  31 6e 73 34
+        [ 6626.174019] 0020: 63 68 61 c7  70 72 6f 74  6f 2d 30 cc  45 44 41 2d
+        [ 6626.180975] 0030: 30 32 32 36  37 2d 56 33  da 32 30 31  32 2d 31 31
+        [...]
+        [ 6626.371366] 0200: 66 64 65 6c  61 79 0a 00  00 00 00 00  00 00 00 00
+        [ 6626.378359] 0210: 00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00
+        [ 6626.385361] [...]
+        [ 6626.387308] 1800: 70 6c 61 63  65 68 6f 6c  64 65 72 ff  ff ff ff ff
+        [ 6626.394259] 1810: ff ff ff ff  ff ff ff ff  ff ff ff ff  ff ff ff ff
+        [ 6626.401250] [...]
+
+The dump of SDB looks like the following; the example shows the simple
+golden gateware for the SPEC card, removing the leading timestamps to
+fit the page:
+
+        spec 0000:02:00.0: SDB: 00000651:e6a542c9 WB4-Crossbar-GSI
+        spec 0000:02:00.0: SDB: 0000ce42:ff07fc47 WR-Periph-Syscon (00000000-000000ff)
+        FMC: mezzanine 0: 0000:02:00.0 on SPEC
+        FMC: poor dump of sdb first level:
+        0000: 53 44 42 2d  00 02 01 00  00 00 00 00  00 00 00 00
+        0010: 00 00 00 00  00 00 01 ff  00 00 00 00  00 00 06 51
+        0020: e6 a5 42 c9  00 00 00 02  20 12 05 11  57 42 34 2d
+        0030: 43 72 6f 73  73 62 61 72  2d 47 53 49  20 20 20 00
+        0040: 00 00 01 01  00 00 00 07  00 00 00 00  00 00 00 00
+        0050: 00 00 00 00  00 00 00 ff  00 00 00 00  00 00 ce 42
+        0060: ff 07 fc 47  00 00 00 01  20 12 03 05  57 52 2d 50
+        0070: 65 72 69 70  68 2d 53 79  73 63 6f 6e  20 20 20 01
-- 
1.7.7.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 5/8] FMC: add a software carrier driver
  2013-06-12  7:13 [PATCH 0/8] Support for FMC carriers and mezzanines Alessandro Rubini
                   ` (3 preceding siblings ...)
  2013-06-12  7:13 ` [PATCH 4/8] FMC: add documentation for the core Alessandro Rubini
@ 2013-06-12  7:14 ` Alessandro Rubini
  2013-06-17 23:19   ` Greg KH
  2013-06-12  7:14 ` [PATCH 6/8] FMC: add a software mezzanine driver Alessandro Rubini
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 26+ messages in thread
From: Alessandro Rubini @ 2013-06-12  7:14 UTC (permalink / raw)
  To: linux-kernel
  Cc: Juan David Gonzalez Cobas, Emilio G. Cota,
	Samuel Iglesias Gonsalvez, gregkh, Rob Landley, linux-doc

This fake carrier is designed to help FMC users understand how a
carrier driver works, and to experiment the behaviour with EEPROM
reprogramming (with a mezzanine driver commited later). This carrier
can register up to 4 (fake) mezzanines.

We have real carriers (both on PCI-E and VME), but they are bigger
things and are not part of this submission.

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>
---
 Documentation/fmc/00-INDEX        |    3 +
 Documentation/fmc/fmc-fakedev.txt |   36 ++++
 drivers/fmc/Kconfig               |   11 ++
 drivers/fmc/Makefile              |    2 +
 drivers/fmc/fmc-fakedev.c         |  349 +++++++++++++++++++++++++++++++++++++
 5 files changed, 401 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/fmc/fmc-fakedev.txt
 create mode 100644 drivers/fmc/fmc-fakedev.c

diff --git a/Documentation/fmc/00-INDEX b/Documentation/fmc/00-INDEX
index 71304b7..34df5cf 100644
--- a/Documentation/fmc/00-INDEX
+++ b/Documentation/fmc/00-INDEX
@@ -24,3 +24,6 @@ mezzanine.txt
 
 identifiers.txt
 	- how identification and matching works
+
+fmc-fakedev.txt
+	- about drivers/fmc/fmc-fakedev.ko
diff --git a/Documentation/fmc/fmc-fakedev.txt b/Documentation/fmc/fmc-fakedev.txt
new file mode 100644
index 0000000..e85b74a
--- /dev/null
+++ b/Documentation/fmc/fmc-fakedev.txt
@@ -0,0 +1,36 @@
+fmc-fakedev
+===========
+
+This package includes a software-only device, called fmc-fakedev, which
+is able to register up to 4 mezzanines (by default it registers one).
+Unlike the SPEC driver, which creates an FMC device for each PCI cards
+it manages, this module creates a single instance of its set of
+mezzanines.
+
+It is meant as the simplest possible example of how a driver should be
+written, and it includes a fake EEPROM image (built using the tools
+described in *note FMC Identification::),, which by default is
+replicated for each fake mezzanine.
+
+You can also use this device to verify the match algorithms, by asking
+it to test your own EEPROM image. You can provide the image by means of
+the eeprom= module parameter: the new EEPROM image is loaded, as usual,
+by means of the firmware loader.  This example shows the defaults and a
+custom EEPROM image:
+
+     spusa.root# insmod fmc-fakedev.ko
+     [   99.971247]  fake-fmc-carrier: mezzanine 0
+     [   99.975393]       Manufacturer: fake-vendor
+     [   99.979624]       Product name: fake-design-for-testing
+     spusa.root# rmmod fmc-fakedev
+     spusa.root# insmod fmc-fakedev.ko eeprom=fdelay-eeprom.bin
+     [  121.447464]  fake-fmc-carrier: Mezzanine 0: eeprom "fdelay-eeprom.bin"
+     [  121.462725]  fake-fmc-carrier: mezzanine 0
+     [  121.466858]       Manufacturer: CERN
+     [  121.470477]       Product name: FmcDelay1ns4cha
+     spusa.root# rmmod fmc-fakedev
+
+After loading the device, you can use the write_ee method do modify its
+own internal fake EEPROM: whenever the image is overwritten starting at
+offset 0, the module will unregister and register again the FMC device.
+This is shown in fmc-write-eeprom.txt
diff --git a/drivers/fmc/Kconfig b/drivers/fmc/Kconfig
index e287902..505e96b 100644
--- a/drivers/fmc/Kconfig
+++ b/drivers/fmc/Kconfig
@@ -15,3 +15,14 @@ menuconfig FMC
 	  The framework was born outside of the kernel and at this time
 	  the off-tree code base is more complete.  Code and documentation
 	  is at git://ohwr.org/fmc-projects/fmc-bus.git .
+
+if FMC
+
+config FMC_FAKEDEV
+	tristate "FMC fake device (software testing)"
+	help
+	  This is a fake carrier, bringing a default EEPROM content
+	  that can be rewritten at run time and usef for matching
+	  mezzanines.
+
+endif # FMC
diff --git a/drivers/fmc/Makefile b/drivers/fmc/Makefile
index df98939..9832b79 100644
--- a/drivers/fmc/Makefile
+++ b/drivers/fmc/Makefile
@@ -6,3 +6,5 @@ fmc-y += fmc-match.o
 fmc-y += fmc-sdb.o
 fmc-y += fru-parse.o
 fmc-y += fmc-dump.o
+
+obj-$(CONFIG_FMC_FAKEDEV) += fmc-fakedev.o
diff --git a/drivers/fmc/fmc-fakedev.c b/drivers/fmc/fmc-fakedev.c
new file mode 100644
index 0000000..a06e0e7
--- /dev/null
+++ b/drivers/fmc/fmc-fakedev.c
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2012 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released to the public domain, as example to be reused
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/workqueue.h>
+#include <linux/err.h>
+#include <linux/fmc.h>
+
+#define FF_EEPROM_SIZE		8192	/* The standard eeprom size */
+#define FF_MAX_MEZZANINES	4	/* Fakes a multi-mezzanine carrier */
+
+/* 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);
+
+
+/* Lazily, don't support the "standard" module parameters */
+
+/*
+ * Eeprom built from these commands:
+
+	../fru-generator -v fake-vendor -n fake-design-for-testing \
+		-s 01234 -p none > IPMI-FRU
+
+	gensdbfs . ../fake-eeprom.bin
+*/
+static char ff_eeimg[FF_MAX_MEZZANINES][FF_EEPROM_SIZE] = {
+	{
+	0x01, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00, 0xf2, 0x01, 0x0b, 0x00, 0xb2,
+	0x86, 0x87, 0xcb, 0x66, 0x61, 0x6b, 0x65, 0x2d, 0x76, 0x65, 0x6e, 0x64,
+	0x6f, 0x72, 0xd7, 0x66, 0x61, 0x6b, 0x65, 0x2d, 0x64, 0x65, 0x73, 0x69,
+	0x67, 0x6e, 0x2d, 0x66, 0x6f, 0x72, 0x2d, 0x74, 0x65, 0x73, 0x74, 0x69,
+	0x6e, 0x67, 0xc5, 0x30, 0x31, 0x32, 0x33, 0x34, 0xc4, 0x6e, 0x6f, 0x6e,
+	0x65, 0xda, 0x32, 0x30, 0x31, 0x32, 0x2d, 0x31, 0x31, 0x2d, 0x31, 0x39,
+	0x20, 0x32, 0x32, 0x3a, 0x34, 0x32, 0x3a, 0x33, 0x30, 0x2e, 0x30, 0x37,
+	0x34, 0x30, 0x35, 0x35, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87,
+	0x02, 0x02, 0x0d, 0xf7, 0xf8, 0x02, 0xb0, 0x04, 0x74, 0x04, 0xec, 0x04,
+	0x00, 0x00, 0x00, 0x00, 0xe8, 0x03, 0x02, 0x02, 0x0d, 0x5c, 0x93, 0x01,
+	0x4a, 0x01, 0x39, 0x01, 0x5a, 0x01, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x0b,
+	0x02, 0x02, 0x0d, 0x63, 0x8c, 0x00, 0xfa, 0x00, 0xed, 0x00, 0x06, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0xa0, 0x0f, 0x01, 0x02, 0x0d, 0xfb, 0xf5, 0x05,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x01, 0x02, 0x0d, 0xfc, 0xf4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x0d, 0xfd, 0xf3, 0x03,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0xfa, 0x82, 0x0b, 0xea, 0x8f, 0xa2, 0x12, 0x00, 0x00, 0x1e, 0x44, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x53, 0x44, 0x42, 0x2d, 0x00, 0x03, 0x01, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xc4, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61,
+	0x2e, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+	0x2e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc4, 0x46, 0x69, 0x6c, 0x65,
+	0x44, 0x61, 0x74, 0x61, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x00, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x20, 0x20, 0x20,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf,
+	0x46, 0x69, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 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,
+	0x20, 0x20, 0x20, 0x01, 0x66, 0x61, 0x6b, 0x65, 0x0a,
+	},
+};
+
+struct ff_dev {
+	struct fmc_device *fmc[FF_MAX_MEZZANINES];
+	struct device dev;
+};
+
+static struct ff_dev *ff_current_dev; /* We have 1 carrier, 1 slot */
+
+static int ff_reprogram(struct fmc_device *fmc, struct fmc_driver *drv,
+			  char *gw)
+{
+	const struct firmware *fw;
+	int ret;
+
+	if (!gw) {
+		/* program golden: success */
+		fmc->flags &= ~FMC_DEVICE_HAS_CUSTOM;
+		fmc->flags |= FMC_DEVICE_HAS_GOLDEN;
+		return 0;
+	}
+
+	dev_info(&fmc->dev, "reprogramming with %s\n", gw);
+	ret = request_firmware(&fw, gw, &fmc->dev);
+	if (ret < 0) {
+		dev_warn(&fmc->dev, "request firmware \"%s\": error %i\n",
+			 gw, ret);
+		goto out;
+	}
+	fmc->flags &= ~FMC_DEVICE_HAS_GOLDEN;
+	fmc->flags |= FMC_DEVICE_HAS_CUSTOM;
+
+out:
+	release_firmware(fw);
+	return ret;
+}
+
+static int ff_irq_request(struct fmc_device *fmc, irq_handler_t handler,
+			    char *name, int flags)
+{
+	return -EOPNOTSUPP;
+}
+
+/* FIXME: should also have some fake FMC GPIO mapping */
+
+
+/*
+ * This work function is called when we changed the eeprom. It removes the
+ * current fmc device and registers a new one, with different identifiers.
+ */
+static struct ff_dev *ff_dev_create(void); /* defined later */
+
+static void ff_work_fn(struct work_struct *work)
+{
+	struct ff_dev *ff = ff_current_dev;
+	int ret;
+
+	fmc_device_unregister_n(ff->fmc, ff_nr_dev);
+	device_unregister(&ff->dev);
+	ff_current_dev = NULL;
+
+	ff = ff_dev_create();
+	if (IS_ERR(ff)) {
+		pr_warning("%s: can't re-create FMC devices\n", __func__);
+		return;
+	}
+	ret = fmc_device_register_n(ff->fmc, ff_nr_dev);
+	if (ret < 0) {
+		dev_warn(&ff->dev, "can't re-register FMC devices\n");
+		device_unregister(&ff->dev);
+		return;
+	}
+
+	ff_current_dev = ff;
+}
+
+static DECLARE_DELAYED_WORK(ff_work, ff_work_fn);
+
+
+/* low-level i2c */
+static int ff_eeprom_read(struct fmc_device *fmc, uint32_t offset,
+		void *buf, size_t size)
+{
+	if (offset > FF_EEPROM_SIZE)
+		return -EINVAL;
+	if (offset + size > FF_EEPROM_SIZE)
+		size = FF_EEPROM_SIZE - offset;
+	memcpy(buf, fmc->eeprom + offset, size);
+	return size;
+}
+
+static int ff_eeprom_write(struct fmc_device *fmc, uint32_t offset,
+		    const void *buf, size_t size)
+{
+	if (offset > FF_EEPROM_SIZE)
+		return -EINVAL;
+	if (offset + size > FF_EEPROM_SIZE)
+		size = FF_EEPROM_SIZE - offset;
+	dev_info(&fmc->dev, "write_eeprom: offset %i, size %zi\n",
+		 (int)offset, size);
+	memcpy(fmc->eeprom + offset, buf, size);
+	schedule_delayed_work(&ff_work, HZ * 2); /* remove, replug, in 2s */
+	return size;
+}
+
+/* i2c operations for fmc */
+static int ff_read_ee(struct fmc_device *fmc, int pos, void *data, int len)
+{
+	if (!(fmc->flags & FMC_DEVICE_HAS_GOLDEN))
+		return -EOPNOTSUPP;
+	return ff_eeprom_read(fmc, pos, data, len);
+}
+
+static int ff_write_ee(struct fmc_device *fmc, int pos,
+			 const void *data, int len)
+{
+	if (!(fmc->flags & FMC_DEVICE_HAS_GOLDEN))
+		return -EOPNOTSUPP;
+	return ff_eeprom_write(fmc, pos, data, len);
+}
+
+/* readl and writel do not do anything. Don't waste RAM with "base" */
+static uint32_t ff_readl(struct fmc_device *fmc, int offset)
+{
+	return 0;
+}
+
+static void ff_writel(struct fmc_device *fmc, uint32_t value, int offset)
+{
+	return;
+}
+
+/* validate is useful so fmc-write-eeprom will not reprogram every 2 seconds */
+static int ff_validate(struct fmc_device *fmc, struct fmc_driver *drv)
+{
+	int i;
+
+	if (!drv->busid_n)
+		return 0; /* everyhing is valid */
+	for (i = 0; i < drv->busid_n; i++)
+		if (drv->busid_val[i] == fmc->device_id)
+			return i;
+	return -ENOENT;
+}
+
+
+
+static struct fmc_operations ff_fmc_operations = {
+	.readl =		ff_readl,
+	.writel =		ff_writel,
+	.reprogram =		ff_reprogram,
+	.irq_request =		ff_irq_request,
+	.read_ee =		ff_read_ee,
+	.write_ee =		ff_write_ee,
+	.validate =		ff_validate,
+};
+
+/* This device is kmalloced: release it */
+static void ff_dev_release(struct device *dev)
+{
+	struct ff_dev *ff = container_of(dev, struct ff_dev, dev);
+	kfree(ff);
+}
+
+static struct fmc_device ff_template_fmc = {
+	.version = FMC_VERSION,
+	.owner = THIS_MODULE,
+	.carrier_name = "fake-fmc-carrier",
+	.device_id = 0xf001, /* fool */
+	.eeprom_len = sizeof(ff_eeimg[0]),
+	.memlen = 0x1000, /* 4k, to show something */
+	.op = &ff_fmc_operations,
+	.hwdev = NULL, /* filled at creation time */
+	.flags = FMC_DEVICE_HAS_GOLDEN,
+};
+
+static struct ff_dev *ff_dev_create(void)
+{
+	struct ff_dev *ff;
+	struct fmc_device *fmc;
+	int i, ret;
+
+	ff = kzalloc(sizeof(*ff), GFP_KERNEL);
+	if (!ff)
+		return ERR_PTR(-ENOMEM);
+	dev_set_name(&ff->dev, "fake-fmc-carrier");
+	ff->dev.release = ff_dev_release;
+
+	ret = device_register(&ff->dev);
+	if (ret < 0) {
+		put_device(&ff->dev);
+		return ERR_PTR(ret);
+	}
+
+	/* Create fmc structures that refer to this new "hw" device */
+	for (i = 0; i < ff_nr_dev; i++) {
+		fmc = kmemdup(&ff_template_fmc, sizeof(ff_template_fmc),
+			      GFP_KERNEL);
+		fmc->hwdev = &ff->dev;
+		fmc->carrier_data = ff;
+		fmc->nr_slots = ff_nr_dev;
+		/* the following fields are different for each slot */
+		fmc->eeprom = ff_eeimg[i];
+		fmc->eeprom_addr = 0x50 + 2 * i;
+		fmc->slot_id = i;
+		ff->fmc[i] = fmc;
+		/* increment the identifier, each must be different */
+		ff_template_fmc.device_id++;
+	}
+	return ff;
+}
+
+/* init and exit */
+static int ff_init(void)
+{
+	struct ff_dev *ff;
+	const struct firmware *fw;
+	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();
+	if (IS_ERR(ff))
+		return PTR_ERR(ff);
+
+	/* If the user passed "eeprom=" as a parameter, fetch them */
+	for (i = 0; i < ff_nr_eeprom; i++) {
+		if (!strlen(ff_eeprom[i]))
+			continue;
+		ret = request_firmware(&fw, ff_eeprom[i], &ff->dev);
+		if (ret < 0) {
+			dev_err(&ff->dev, "Mezzanine %i: can't load \"%s\" "
+				"(error %i)\n", i, ff_eeprom[i], -ret);
+		} else {
+			len = min_t(size_t, fw->size, (size_t)FF_EEPROM_SIZE);
+			memcpy(ff_eeimg[i], fw->data, len);
+			release_firmware(fw);
+			dev_info(&ff->dev, "Mezzanine %i: eeprom \"%s\"\n", i,
+				ff_eeprom[i]);
+		}
+	}
+
+	ret = fmc_device_register_n(ff->fmc, ff_nr_dev);
+	if (ret) {
+		device_unregister(&ff->dev);
+		return ret;
+	}
+	ff_current_dev = ff;
+	return ret;
+}
+
+static void ff_exit(void)
+{
+	if (ff_current_dev) {
+		fmc_device_unregister_n(ff_current_dev->fmc, ff_nr_dev);
+		device_unregister(&ff_current_dev->dev);
+	}
+	cancel_delayed_work_sync(&ff_work);
+}
+
+module_init(ff_init);
+module_exit(ff_exit);
+
+MODULE_LICENSE("GPL and additional rights");
-- 
1.7.7.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 6/8] FMC: add a software mezzanine driver
  2013-06-12  7:13 [PATCH 0/8] Support for FMC carriers and mezzanines Alessandro Rubini
                   ` (4 preceding siblings ...)
  2013-06-12  7:14 ` [PATCH 5/8] FMC: add a software carrier driver Alessandro Rubini
@ 2013-06-12  7:14 ` Alessandro Rubini
  2013-06-17 23:20   ` Greg KH
  2013-06-12  7:14 ` [PATCH 7/8] FMC: add a driver to write mezzanine EEPROM Alessandro Rubini
  2013-06-12  7:14 ` [PATCH 8/8] FMC: add a char-device mezzanine driver Alessandro Rubini
  7 siblings, 1 reply; 26+ messages in thread
From: Alessandro Rubini @ 2013-06-12  7:14 UTC (permalink / raw)
  To: linux-kernel
  Cc: Juan David Gonzalez Cobas, Emilio G. Cota,
	Samuel Iglesias Gonsalvez, gregkh, Rob Landley, linux-doc

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>
---
 Documentation/fmc/00-INDEX        |    3 +
 Documentation/fmc/fmc-trivial.txt |   17 ++++++
 drivers/fmc/Kconfig               |    7 +++
 drivers/fmc/Makefile              |    1 +
 drivers/fmc/fmc-trivial.c         |  101 +++++++++++++++++++++++++++++++++++++
 5 files changed, 129 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/fmc/fmc-trivial.txt
 create mode 100644 drivers/fmc/fmc-trivial.c

diff --git a/Documentation/fmc/00-INDEX b/Documentation/fmc/00-INDEX
index 34df5cf..c22d687 100644
--- a/Documentation/fmc/00-INDEX
+++ b/Documentation/fmc/00-INDEX
@@ -27,3 +27,6 @@ identifiers.txt
 
 fmc-fakedev.txt
 	- about drivers/fmc/fmc-fakedev.ko
+
+fmc-trivial.txt
+	- about drivers/fmc/fmc-trivial.ko
diff --git a/Documentation/fmc/fmc-trivial.txt b/Documentation/fmc/fmc-trivial.txt
new file mode 100644
index 0000000..d1910bc
--- /dev/null
+++ b/Documentation/fmc/fmc-trivial.txt
@@ -0,0 +1,17 @@
+fmc-trivial
+===========
+
+The simple module fmc-trivial is just a simple client that registers an
+interrupt handler. I used it to verify the basic mechanism of the FMC
+bus and how interrupts worked.
+
+The module implements the generic FMC parameters, so it can program a
+different gateware file in each card. The whole list of parameters it
+accepts are:
+
+`busid='
+`gateware='
+     Generic parameters. See mezzanine.txt
+
+
+This driver is worth reading, in my opinion.
diff --git a/drivers/fmc/Kconfig b/drivers/fmc/Kconfig
index 505e96b..7eacef9 100644
--- a/drivers/fmc/Kconfig
+++ b/drivers/fmc/Kconfig
@@ -25,4 +25,11 @@ config FMC_FAKEDEV
 	  that can be rewritten at run time and usef for matching
 	  mezzanines.
 
+config FMC_TRIVIAL
+	tristate "FMC trivial mezzanine driver (software testing)"
+	help
+	  This is a fake mezzanine driver, to show how FMC works and test it.
+	  The driver also handles interrupts (we used it with a real carrier
+	  before the mezzanines were produced)
+
 endif # FMC
diff --git a/drivers/fmc/Makefile b/drivers/fmc/Makefile
index 9832b79..52624e6 100644
--- a/drivers/fmc/Makefile
+++ b/drivers/fmc/Makefile
@@ -8,3 +8,4 @@ fmc-y += fru-parse.o
 fmc-y += fmc-dump.o
 
 obj-$(CONFIG_FMC_FAKEDEV) += fmc-fakedev.o
+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 0000000..d91b28c
--- /dev/null
+++ b/drivers/fmc/fmc-trivial.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2012 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released to the public domain, as example to be reused
+ */
+
+/* A trivial fmc driver that can load a gateware file and reports interrupts */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/fmc.h>
+
+static struct fmc_driver t_drv; /* initialized later */
+
+static irqreturn_t t_handler(int irq, void *dev_id)
+{
+	struct fmc_device *fmc = dev_id;
+
+	fmc->op->irq_ack(fmc);
+	dev_info(&fmc->dev, "received irq %i\n", irq);
+	return IRQ_HANDLED;
+}
+
+static struct fmc_gpio t_gpio[] = {
+	{
+		.gpio = FMC_GPIO_IRQ(0),
+		.mode = GPIOF_DIR_IN,
+		.irqmode = IRQF_TRIGGER_RISING,
+	}, {
+		.gpio = FMC_GPIO_IRQ(1),
+		.mode = GPIOF_DIR_IN,
+		.irqmode = IRQF_TRIGGER_RISING,
+	}
+};
+
+static int t_probe(struct fmc_device *fmc)
+{
+	int ret;
+	int index = 0;
+
+	if (fmc->op->validate)
+		index = fmc->op->validate(fmc, &t_drv);
+	if (index < 0)
+		return -EINVAL; /* not our device: invalid */
+
+	ret = fmc->op->irq_request(fmc, t_handler, "fmc-trivial", IRQF_SHARED);
+	if (ret < 0)
+		return ret;
+	/* ignore error code of call below, we really don't care */
+	fmc->op->gpio_config(fmc, t_gpio, ARRAY_SIZE(t_gpio));
+
+	/* Reprogram, if asked to. ESRCH == no filename specified */
+	ret = -ESRCH;
+	if (fmc->op->reprogram)
+		ret = fmc->op->reprogram(fmc, &t_drv, "");
+	if (ret == -ESRCH)
+		ret = 0;
+	if (ret < 0)
+		fmc->op->irq_free(fmc);
+
+	/* FIXME: reprogram LM32 too */
+	return ret;
+}
+
+static int t_remove(struct fmc_device *fmc)
+{
+	fmc->op->irq_free(fmc);
+	return 0;
+}
+
+static struct fmc_driver t_drv = {
+	.version = FMC_VERSION,
+	.driver.name = KBUILD_MODNAME,
+	.probe = t_probe,
+	.remove = t_remove,
+	/* no table, as the current match just matches everything */
+};
+
+ /* We accept the generic parameters */
+FMC_PARAM_BUSID(t_drv);
+FMC_PARAM_GATEWARE(t_drv);
+
+static int t_init(void)
+{
+	int ret;
+
+	ret = fmc_driver_register(&t_drv);
+	return ret;
+}
+
+static void t_exit(void)
+{
+	fmc_driver_unregister(&t_drv);
+}
+
+module_init(t_init);
+module_exit(t_exit);
+
+MODULE_LICENSE("GPL and additional rights"); /* public domain */
-- 
1.7.7.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 7/8] FMC: add a driver to write mezzanine EEPROM
  2013-06-12  7:13 [PATCH 0/8] Support for FMC carriers and mezzanines Alessandro Rubini
                   ` (5 preceding siblings ...)
  2013-06-12  7:14 ` [PATCH 6/8] FMC: add a software mezzanine driver Alessandro Rubini
@ 2013-06-12  7:14 ` Alessandro Rubini
  2013-06-12  7:14 ` [PATCH 8/8] FMC: add a char-device mezzanine driver Alessandro Rubini
  7 siblings, 0 replies; 26+ messages in thread
From: Alessandro Rubini @ 2013-06-12  7:14 UTC (permalink / raw)
  To: linux-kernel
  Cc: Juan David Gonzalez Cobas, Emilio G. Cota,
	Samuel Iglesias Gonsalvez, gregkh, Rob Landley, linux-doc

This driver allows to reprogram the EEPROM in a mezzanine, to store
its own identifiers during manufacturing or to save other useful data.

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>
---
 Documentation/fmc/00-INDEX             |    3 +
 Documentation/fmc/fmc-write-eeprom.txt |  125 ++++++++++++++++++++++
 drivers/fmc/Kconfig                    |    8 ++
 drivers/fmc/Makefile                   |    1 +
 drivers/fmc/fmc-write-eeprom.c         |  176 ++++++++++++++++++++++++++++++++
 5 files changed, 313 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/fmc/fmc-write-eeprom.txt
 create mode 100644 drivers/fmc/fmc-write-eeprom.c

diff --git a/Documentation/fmc/00-INDEX b/Documentation/fmc/00-INDEX
index c22d687..177c3e4 100644
--- a/Documentation/fmc/00-INDEX
+++ b/Documentation/fmc/00-INDEX
@@ -30,3 +30,6 @@ fmc-fakedev.txt
 
 fmc-trivial.txt
 	- about drivers/fmc/fmc-trivial.ko
+
+fmc-write-eeprom.txt
+        - about drivers/fmc/fmc-write-eeprom.ko
diff --git a/Documentation/fmc/fmc-write-eeprom.txt b/Documentation/fmc/fmc-write-eeprom.txt
new file mode 100644
index 0000000..44a3bc6
--- /dev/null
+++ b/Documentation/fmc/fmc-write-eeprom.txt
@@ -0,0 +1,125 @@
+fmc-write-eeprom
+================
+
+This module is designed to load a binary file from /lib/firmware and to
+write it to the internal EEPROM of the mezzanine card. This driver uses
+the `busid' generic parameter.
+
+Overwriting the EEPROM is not something you should do daily, and it is
+expected to only happen during manufacturing. For this reason, the
+module makes it unlikely for the random user to change a working EEPROM.
+
+The module takes the following measures:
+
+   * It accepts a `file=' argument (within /lib/firmware) and if no
+     such argument is received, it doesn't write anything to EEPROM
+     (i.e. there is no default file name).
+
+   * If the file name ends with `.bin' it is written verbatim starting
+     at offset 0.
+
+   * If the file name ends with `.tlv' it is interpreted as
+     type-length-value (i.e., it allows writev(2)-like operation).
+
+   * If the file name doesn't match any of the patterns above, it is
+     ignored and no write is performed.
+
+   * Only cards listed with `busid=' are written to. If no busid is
+     specified, no programming is done (and the probe function of the
+     driver will fail).
+
+
+Each TLV tuple is formatted in this way: the header is 5 bytes,
+followed by data. The first byte is `w' for write, the next two bytes
+represent the address, in little-endian byte order, and the next two
+represent the data length, in little-endian order. The length does not
+include the header (it is the actual number of bytes to be written).
+
+This is a real example: that writes 5 bytes at position 0x110:
+
+        spusa.root# od -t x1 -Ax /lib/firmware/try.tlv
+        000000 77 10 01 05 00 30 31 32 33 34
+        00000a
+        spusa.root# insmod /tmp/fmc-write-eeprom.ko busid=0x0200 file=try.tlv
+        [19983.391498] spec 0000:03:00.0: write 5 bytes at 0x0110
+        [19983.414615] spec 0000:03:00.0: write_eeprom: success
+
+Please note that you'll most likely want to use SDBFS to build your
+EEPROM image, at least if your mezzanines are being used in the White
+Rabbit environment. For this reason the TLV format is not expected to
+be used much and is not expected to be developed further.
+
+If you want to try reflashing fake EEPROM devices, you can use the
+fmc-fakedev.ko module (see *note fmc-fakedev::).  Whenever you change
+the image starting at offset 0, it will deregister and register again
+after two seconds.  Please note, however, that if fmc-write-eeprom is
+still loaded, the system will associate it to the new device, which
+will be reprogrammed and thus will be unloaded after two seconds.  The
+following example removes the module after it reflashed fakedev the
+first time.
+
+     spusa.root# insmod fmc-fakedev.ko
+        [   72.984733]  fake-fmc: Manufacturer: fake-vendor
+        [   72.989434]  fake-fmc: Product name: fake-design-for-testing
+        spusa.root# insmod fmc-write-eeprom.ko busid=0 file=fdelay-eeprom.bin; \
+            rmmod fmc-write-eeprom
+        [  130.874098]  fake-fmc: Matching a generic driver (no ID)
+        [  130.887845]  fake-fmc: programming 6155 bytes
+        [  130.894567]  fake-fmc: write_eeprom: success
+        [  132.895794]  fake-fmc: Manufacturer: CERN
+        [  132.899872]  fake-fmc: Product name: FmcDelay1ns4cha
+
+
+Writing to the EEPROM
+=====================
+
+Once you have created a binary file for your EEPROM, you can write it
+to the storage medium using the fmc-write-eeprom (See *note
+fmc-write-eeprom::, while relying on a carrier driver.  The procedure
+here shown here uses the SPEC driver
+(`http://www.ohwr.org/projects/spec-sw').
+
+The example assumes no driver is already loaded (actually, I unloaded
+them by hand as everything loads automatically at boot time after you
+installed the modules), and shows kernel messages together with
+commands. Here the prompt is spusa.root# and two SPEC cards are plugged
+in the system.
+
+     spusa.root# insmod fmc.ko
+     spusa.root# insmod spec.ko
+     [13972.382818] spec 0000:02:00.0:  probe for device 0002:0000
+     [13972.392773] spec 0000:02:00.0: got file "fmc/spec-init.bin", 1484404 (0x16a674) bytes
+     [13972.591388] spec 0000:02:00.0: FPGA programming successful
+     [13972.883011] spec 0000:02:00.0: EEPROM has no FRU information
+     [13972.888719] spec 0000:02:00.0: No device_id filled, using index
+     [13972.894676] spec 0000:02:00.0: No mezzanine_name found
+     [13972.899863] /home/rubini/wip/spec-sw/kernel/spec-gpio.c - spec_gpio_init
+     [13972.906578] spec 0000:04:00.0:  probe for device 0004:0000
+     [13972.916509] spec 0000:04:00.0: got file "fmc/spec-init.bin", 1484404 (0x16a674) bytes
+     [13973.115096] spec 0000:04:00.0: FPGA programming successful
+     [13973.401798] spec 0000:04:00.0: EEPROM has no FRU information
+     [13973.407474] spec 0000:04:00.0: No device_id filled, using index
+     [13973.413417] spec 0000:04:00.0: No mezzanine_name found
+     [13973.418600] /home/rubini/wip/spec-sw/kernel/spec-gpio.c - spec_gpio_init
+     spusa.root# ls /sys/bus/fmc/devices
+     fmc-0000  fmc-0001
+     spusa.root# insmod fmc-write-eeprom.ko busid=0x0200 file=fdelay-eeprom.bin
+     [14103.966259] spec 0000:02:00.0: Matching an generic driver (no ID)
+     [14103.975519] spec 0000:02:00.0: programming 6155 bytes
+     [14126.373762] spec 0000:02:00.0: write_eeprom: success
+     [14126.378770] spec 0000:04:00.0: Matching an generic driver (no ID)
+     [14126.384903] spec 0000:04:00.0: fmc_write_eeprom: no filename given: not programming
+     [14126.392600] fmc_write_eeprom: probe of fmc-0001 failed with error -2
+
+Reading back the EEPROM
+=======================
+
+In order to read back the binary content of the EEPROM of your
+mezzanine device, the bus creates a read-only sysfs file called eeprom
+for each mezzanine it knows about:
+
+   spusa.root# cd /sys/bus/fmc/devices; ls -l */eeprom
+   -r--r--r-- 1 root root 8192 Apr  9 16:53 FmcDelay1ns4cha-f001/eeprom
+   -r--r--r-- 1 root root 8192 Apr  9 17:19 fake-design-for-testing-f002/eeprom
+   -r--r--r-- 1 root root 8192 Apr  9 17:19 fake-design-for-testing-f003/eeprom
+   -r--r--r-- 1 root root 8192 Apr  9 17:19 fmc-f004/eeprom
diff --git a/drivers/fmc/Kconfig b/drivers/fmc/Kconfig
index 7eacef9..2bb1953 100644
--- a/drivers/fmc/Kconfig
+++ b/drivers/fmc/Kconfig
@@ -32,4 +32,12 @@ config FMC_TRIVIAL
 	  The driver also handles interrupts (we used it with a real carrier
 	  before the mezzanines were produced)
 
+config FMC_WRITE_EEPROM
+	tristate "FMC mezzanine driver to write I2C EEPROM"
+	help
+	  This driver matches every mezzanine device and can write the
+	  internal EEPROM of the PCB, using the firmware loader to get
+	  its binary and the function carrier->reprogram to actually do it.
+	  It is useful when the mezzanines are produced.
+
 endif # FMC
diff --git a/drivers/fmc/Makefile b/drivers/fmc/Makefile
index 52624e6..13701fa 100644
--- a/drivers/fmc/Makefile
+++ b/drivers/fmc/Makefile
@@ -9,3 +9,4 @@ fmc-y += fmc-dump.o
 
 obj-$(CONFIG_FMC_FAKEDEV) += fmc-fakedev.o
 obj-$(CONFIG_FMC_TRIVIAL) += fmc-trivial.o
+obj-$(CONFIG_FMC_WRITE_EEPROM) += fmc-write-eeprom.o
diff --git a/drivers/fmc/fmc-write-eeprom.c b/drivers/fmc/fmc-write-eeprom.c
new file mode 100644
index 0000000..2cc680d
--- /dev/null
+++ b/drivers/fmc/fmc-write-eeprom.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2012 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ *
+ * This work is part of the White Rabbit project, a research effort led
+ * by CERN, the European Institute for Nuclear Research.
+ */
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/fmc.h>
+#include <asm/unaligned.h>
+
+/*
+ * This module uses the firmware loader to program the whole or part
+ * of the FMC eeprom. The meat is in the _run functions.  However, no
+ * default file name is provided, to avoid accidental mishaps. Also,
+ * you must pass the busid argument
+ */
+static struct fmc_driver fwe_drv;
+
+FMC_PARAM_BUSID(fwe_drv);
+
+/* The "file=" is like the generic "gateware=" used elsewhere */
+static char *fwe_file[FMC_MAX_CARDS];
+static int fwe_file_n;
+module_param_array_named(file, fwe_file, charp, &fwe_file_n, 444);
+
+static int fwe_run_tlv(struct fmc_device *fmc, const struct firmware *fw,
+	int write)
+{
+	const uint8_t *p = fw->data;
+	int len = fw->size;
+	uint16_t thislen, thisaddr;
+	int err;
+
+	/* format is: 'w' addr16 len16 data... */
+	while (len > 5) {
+		thisaddr = get_unaligned_le16(p+1);
+		thislen = get_unaligned_le16(p+3);
+		if (p[0] != 'w' || thislen + 5 > len) {
+			dev_err(&fmc->dev, "invalid tlv at offset %ti\n",
+				p - fw->data);
+			return -EINVAL;
+		}
+		err = 0;
+		if (write) {
+			dev_info(&fmc->dev, "write %i bytes at 0x%04x\n",
+				 thislen, thisaddr);
+			err = fmc->op->write_ee(fmc, thisaddr, p + 5, thislen);
+		}
+		if (err < 0) {
+			dev_err(&fmc->dev, "write failure @0x%04x\n",
+				thisaddr);
+			return err;
+		}
+		p += 5 + thislen;
+		len -= 5 + thislen;
+	}
+	if (write)
+		dev_info(&fmc->dev, "write_eeprom: success\n");
+	return 0;
+}
+
+static int fwe_run_bin(struct fmc_device *fmc, const struct firmware *fw)
+{
+	int ret;
+
+	dev_info(&fmc->dev, "programming %zi bytes\n", fw->size);
+	ret = fmc->op->write_ee(fmc, 0, (void *)fw->data, fw->size);
+	if (ret < 0) {
+		dev_info(&fmc->dev, "write_eeprom: error %i\n", ret);
+		return ret;
+	}
+	dev_info(&fmc->dev, "write_eeprom: success\n");
+	return 0;
+}
+
+static int fwe_run(struct fmc_device *fmc, const struct firmware *fw, char *s)
+{
+	char *last4 = s + strlen(s) - 4;
+	int err;
+
+	if (!strcmp(last4, ".bin"))
+		return fwe_run_bin(fmc, fw);
+	if (!strcmp(last4, ".tlv")) {
+		err = fwe_run_tlv(fmc, fw, 0);
+		if (!err)
+			err = fwe_run_tlv(fmc, fw, 1);
+		return err;
+	}
+	dev_err(&fmc->dev, "invalid file name \"%s\"\n", s);
+	return -EINVAL;
+}
+
+/*
+ * Programming is done at probe time. Morever, only those listed with
+ * busid= are programmed.
+ * card is probed for, only one is programmed. Unfortunately, it's
+ * difficult to know in advance when probing the first card if others
+ * are there.
+ */
+int fwe_probe(struct fmc_device *fmc)
+{
+	int err, index = 0;
+	const struct firmware *fw;
+	struct device *dev = &fmc->dev;
+	char *s;
+
+	if (!fwe_drv.busid_n) {
+		dev_err(dev, "%s: no busid passed, refusing all cards\n",
+			KBUILD_MODNAME);
+		return -ENODEV;
+	}
+	if (fmc->op->validate)
+		index = fmc->op->validate(fmc, &fwe_drv);
+	if (index < 0) {
+		pr_err("%s: refusing device \"%s\"\n", KBUILD_MODNAME,
+		       dev_name(dev));
+		return -ENODEV;
+	}
+	if (index >= fwe_file_n) {
+		pr_err("%s: no filename for device index %i\n",
+			KBUILD_MODNAME, index);
+		return -ENODEV;
+	}
+	s = fwe_file[index];
+	if (!s) {
+		pr_err("%s: no filename for \"%s\" not programming\n",
+		       KBUILD_MODNAME, dev_name(dev));
+		return -ENOENT;
+	}
+	err = request_firmware(&fw, s, dev);
+	if (err < 0) {
+		dev_err(&fmc->dev, "request firmware \"%s\": error %i\n",
+			s, err);
+		return err;
+	}
+	fwe_run(fmc, fw, s);
+	release_firmware(fw);
+	return 0;
+}
+
+int fwe_remove(struct fmc_device *fmc)
+{
+	return 0;
+}
+
+static struct fmc_driver fwe_drv = {
+	.version = FMC_VERSION,
+	.driver.name = KBUILD_MODNAME,
+	.probe = fwe_probe,
+	.remove = fwe_remove,
+	/* no table, as the current match just matches everything */
+};
+
+static int fwe_init(void)
+{
+	int ret;
+
+	ret = fmc_driver_register(&fwe_drv);
+	return ret;
+}
+
+static void fwe_exit(void)
+{
+	fmc_driver_unregister(&fwe_drv);
+}
+
+module_init(fwe_init);
+module_exit(fwe_exit);
+
+MODULE_LICENSE("GPL");
-- 
1.7.7.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 8/8] FMC: add a char-device mezzanine driver
  2013-06-12  7:13 [PATCH 0/8] Support for FMC carriers and mezzanines Alessandro Rubini
                   ` (6 preceding siblings ...)
  2013-06-12  7:14 ` [PATCH 7/8] FMC: add a driver to write mezzanine EEPROM Alessandro Rubini
@ 2013-06-12  7:14 ` Alessandro Rubini
  7 siblings, 0 replies; 26+ messages in thread
From: Alessandro Rubini @ 2013-06-12  7:14 UTC (permalink / raw)
  To: linux-kernel
  Cc: Juan David Gonzalez Cobas, Emilio G. Cota,
	Samuel Iglesias Gonsalvez, gregkh, Rob Landley, linux-doc

This driver exports the memory area associated with the mezzanine card
as a misc device, so users can access registers.

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>
---
 Documentation/fmc/00-INDEX        |    5 +-
 Documentation/fmc/fmc-chardev.txt |   64 ++++++++++++
 drivers/fmc/Kconfig               |    8 ++
 drivers/fmc/Makefile              |    1 +
 drivers/fmc/fmc-chardev.c         |  197 +++++++++++++++++++++++++++++++++++++
 5 files changed, 274 insertions(+), 1 deletions(-)
 create mode 100644 Documentation/fmc/fmc-chardev.txt
 create mode 100644 drivers/fmc/fmc-chardev.c

diff --git a/Documentation/fmc/00-INDEX b/Documentation/fmc/00-INDEX
index 177c3e4..431c695 100644
--- a/Documentation/fmc/00-INDEX
+++ b/Documentation/fmc/00-INDEX
@@ -32,4 +32,7 @@ fmc-trivial.txt
 	- about drivers/fmc/fmc-trivial.ko
 
 fmc-write-eeprom.txt
-        - about drivers/fmc/fmc-write-eeprom.ko
+	- about drivers/fmc/fmc-write-eeprom.ko
+
+fmc-chardev.txt
+	- about drivers/fmc/fmc-chardev.ko
diff --git a/Documentation/fmc/fmc-chardev.txt b/Documentation/fmc/fmc-chardev.txt
new file mode 100644
index 0000000..d9ccb27
--- /dev/null
+++ b/Documentation/fmc/fmc-chardev.txt
@@ -0,0 +1,64 @@
+fmc-chardev
+===========
+
+This is a simple generic driver, that allows user access by means of a
+character device (actually, one for each mezzanine it takes hold of).
+
+The char device is created as a misc device. Its name in /dev (as
+created by udev) is the same name as the underlying FMC device. Thus,
+the name can be a silly fmc-0000 look-alike if the device has no
+identifiers nor bus_id, a more specific fmc-0400 if the device has a
+bus-specific address but no associated name, or something like
+fdelay-0400 if the FMC core can rely on both a mezzanine name and a bus
+address.
+
+Currently the driver only supports read and write: you can lseek to the
+desired address and read or write a register.
+
+The driver assumes all registers are 32-bit in size, and only accepts a
+single read or write per system call. However, as a result of Unix read
+and write semantics, users can simply fread or fwrite bigger areas in
+order to dump or store bigger memory areas.
+
+There is currently no support for mmap, user-space interrupt management
+and DMA buffers. They may be added in later versions, if the need
+arises.
+
+The example below shows raw access to a SPEC card programmed with its
+golden FPGA file, that features an SDB structure at offset 256 - i.e.
+64 words.  The mezzanine's EEPROM in this case is not programmed, so the
+default name is fmc-<bus><devfn>, and there are two cards in the system:
+
+  spusa.root# insmod fmc-chardev.ko
+  [ 1073.339332] spec 0000:02:00.0: Driver has no ID: matches all
+  [ 1073.345051] spec 0000:02:00.0: Created misc device "fmc-0200"
+  [ 1073.350821] spec 0000:04:00.0: Driver has no ID: matches all
+  [ 1073.356525] spec 0000:04:00.0: Created misc device "fmc-0400"
+  spusa.root# ls -l /dev/fmc*
+  crw------- 1 root root 10, 58 Nov 20 19:23 /dev/fmc-0200
+  crw------- 1 root root 10, 57 Nov 20 19:23 /dev/fmc-0400
+  spusa.root# dd bs=4 skip=64 count=1 if=/dev/fmc-0200 2> /dev/null | od -t x1z
+  0000000 2d 42 44 53                                      >-BDS<
+  0000004
+
+The simple program tools/fmc-mem in this package can access an FMC char
+device and read or write a word or a whole area.  Actually, the program
+is not specific to FMC at all, it just uses lseek, read and write.
+
+Its first argument is the device name, the second the offset, the third
+(if any) the value to write and the optional last argument that must
+begin with "+" is the number of bytes to read or write.  In case of
+repeated reading data is written to stdout; repeated writes read from
+stdin and the value argument is ignored.
+
+The following examples show reading the SDB magic number and the first
+SDB record from a SPEC device programmed with its golden image:
+
+     spusa.root# ./fmc-mem /dev/fmc-0200 100
+     5344422d
+     spusa.root# ./fmc-mem /dev/fmc-0200 100 +40 | od -Ax -t x1z
+     000000 2d 42 44 53 00 01 02 00 00 00 00 00 00 00 00 00  >-BDS............<
+     000010 00 00 00 00 ff 01 00 00 00 00 00 00 51 06 00 00  >............Q...<
+     000020 c9 42 a5 e6 02 00 00 00 11 05 12 20 2d 34 42 57  >.B......... -4BW<
+     000030 73 6f 72 43 72 61 62 73 49 53 47 2d 00 20 20 20  >sorCrabsISG-.   <
+     000040
diff --git a/drivers/fmc/Kconfig b/drivers/fmc/Kconfig
index 2bb1953..c01cf45 100644
--- a/drivers/fmc/Kconfig
+++ b/drivers/fmc/Kconfig
@@ -40,4 +40,12 @@ config FMC_WRITE_EEPROM
 	  its binary and the function carrier->reprogram to actually do it.
 	  It is useful when the mezzanines are produced.
 
+config FMC_CHARDEV
+	tristate "FMC mezzanine driver that registers a char device"
+	help
+	  This driver matches every mezzanine device and allows user
+	  space to read and write registers using a char device. It
+	  can be used to write user-space drivers, or just get
+	  aquainted with a mezzanine before writing its specific driver.
+
 endif # FMC
diff --git a/drivers/fmc/Makefile b/drivers/fmc/Makefile
index 13701fa..b945291 100644
--- a/drivers/fmc/Makefile
+++ b/drivers/fmc/Makefile
@@ -10,3 +10,4 @@ fmc-y += fmc-dump.o
 obj-$(CONFIG_FMC_FAKEDEV) += fmc-fakedev.o
 obj-$(CONFIG_FMC_TRIVIAL) += fmc-trivial.o
 obj-$(CONFIG_FMC_WRITE_EEPROM) += fmc-write-eeprom.o
+obj-$(CONFIG_FMC_CHARDEV) += fmc-chardev.o
diff --git a/drivers/fmc/fmc-chardev.c b/drivers/fmc/fmc-chardev.c
new file mode 100644
index 0000000..b071039
--- /dev/null
+++ b/drivers/fmc/fmc-chardev.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2012 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ *
+ * This work is part of the White Rabbit project, a research effort led
+ * by CERN, the European Institute for Nuclear Research.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/fmc.h>
+#include <linux/uaccess.h>
+
+static LIST_HEAD(fc_devices);
+static DEFINE_SPINLOCK(fc_lock);
+
+struct fc_instance {
+	struct list_head list;
+	struct fmc_device *fmc;
+	struct miscdevice misc;
+};
+
+/* at open time, we must identify our device */
+static int fc_open(struct inode *ino, struct file *f)
+{
+	struct fmc_device *fmc;
+	struct fc_instance *fc;
+	int minor = iminor(ino);
+
+	list_for_each_entry(fc, &fc_devices, list)
+		if (fc->misc.minor == minor)
+			break;
+	if (fc->misc.minor != minor)
+		return -ENODEV;
+	fmc = fc->fmc;
+	if (try_module_get(fmc->owner) == 0)
+		return -ENODEV;
+
+	f->private_data = fmc;
+	return 0;
+}
+
+static int fc_release(struct inode *ino, struct file *f)
+{
+	struct fmc_device *fmc = f->private_data;
+	module_put(fmc->owner);
+	return 0;
+}
+
+/* read and write are simple after the default llseek has been used */
+static ssize_t fc_read(struct file *f, char __user *buf, size_t count,
+		       loff_t *offp)
+{
+	struct fmc_device *fmc = f->private_data;
+	unsigned long addr;
+	uint32_t val;
+
+	if (count < sizeof(val))
+		return -EINVAL;
+	count = sizeof(val);
+
+	addr = *offp;
+	if (addr > fmc->memlen)
+		return -ESPIPE; /* Illegal seek */
+	val = fmc_readl(fmc, addr);
+	if (copy_to_user(buf, &val, count))
+		return -EFAULT;
+	*offp += count;
+	return count;
+}
+
+static ssize_t fc_write(struct file *f, const char __user *buf, size_t count,
+			loff_t *offp)
+{
+	struct fmc_device *fmc = f->private_data;
+	unsigned long addr;
+	uint32_t val;
+
+	if (count < sizeof(val))
+		return -EINVAL;
+	count = sizeof(val);
+
+	addr = *offp;
+	if (addr > fmc->memlen)
+		return -ESPIPE; /* Illegal seek */
+	if (copy_from_user(&val, buf, count))
+		return -EFAULT;
+	fmc_writel(fmc, val, addr);
+	*offp += count;
+	return count;
+}
+
+static const struct file_operations fc_fops = {
+	.owner = THIS_MODULE,
+	.open = fc_open,
+	.release = fc_release,
+	.llseek = generic_file_llseek,
+	.read = fc_read,
+	.write = fc_write,
+};
+
+
+/* Device part .. */
+static int fc_probe(struct fmc_device *fmc);
+static int fc_remove(struct fmc_device *fmc);
+
+static struct fmc_driver fc_drv = {
+	.version = FMC_VERSION,
+	.driver.name = KBUILD_MODNAME,
+	.probe = fc_probe,
+	.remove = fc_remove,
+	/* no table: we want to match everything */
+};
+
+/* We accept the generic busid parameter */
+FMC_PARAM_BUSID(fc_drv);
+
+/* probe and remove must allocate and release a misc device */
+static int fc_probe(struct fmc_device *fmc)
+{
+	int ret;
+	int index = 0;
+
+	struct fc_instance *fc;
+
+	if (fmc->op->validate)
+		index = fmc->op->validate(fmc, &fc_drv);
+	if (index < 0)
+		return -EINVAL; /* not our device: invalid */
+
+	/* Create a char device: we want to create it anew */
+	fc = kzalloc(sizeof(*fc), GFP_KERNEL);
+	fc->fmc = fmc;
+	fc->misc.minor = MISC_DYNAMIC_MINOR;
+	fc->misc.fops = &fc_fops;
+	fc->misc.name = kstrdup(dev_name(&fmc->dev), GFP_KERNEL);
+
+	spin_lock(&fc_lock);
+	ret = misc_register(&fc->misc);
+	if (ret < 0) {
+		kfree(fc->misc.name);
+		kfree(fc);
+	} else {
+		list_add(&fc->list, &fc_devices);
+	}
+	spin_unlock(&fc_lock);
+	dev_info(&fc->fmc->dev, "Created misc device \"%s\"\n",
+		 fc->misc.name);
+	return ret;
+}
+
+static int fc_remove(struct fmc_device *fmc)
+{
+	struct fc_instance *fc;
+
+	list_for_each_entry(fc, &fc_devices, list)
+		if (fc->fmc == fmc)
+			break;
+	if (fc->fmc != fmc) {
+		dev_err(&fmc->dev, "remove called but not found\n");
+		return -ENODEV;
+	}
+
+	spin_lock(&fc_lock);
+	list_del(&fc->list);
+	misc_deregister(&fc->misc);
+	kfree(fc->misc.name);
+	kfree(fc);
+	spin_unlock(&fc_lock);
+
+	return 0;
+}
+
+
+static int fc_init(void)
+{
+	int ret;
+
+	ret = fmc_driver_register(&fc_drv);
+	return ret;
+}
+
+static void fc_exit(void)
+{
+	fmc_driver_unregister(&fc_drv);
+}
+
+module_init(fc_init);
+module_exit(fc_exit);
+
+MODULE_LICENSE("GPL");
-- 
1.7.7.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH 3/8] FMC: add core bus driver
  2013-06-12  7:13 ` [PATCH 3/8] FMC: add core bus driver Alessandro Rubini
@ 2013-06-17 23:15   ` Greg KH
  2013-06-17 23:16   ` Greg KH
  2013-06-17 23:38   ` Alessandro Rubini
  2 siblings, 0 replies; 26+ messages in thread
From: Greg KH @ 2013-06-17 23:15 UTC (permalink / raw)
  To: Alessandro Rubini
  Cc: linux-kernel, Juan David Gonzalez Cobas, Emilio G. Cota,
	Samuel Iglesias Gonsalvez, Rob Landley, linux-doc

On Wed, Jun 12, 2013 at 09:13:47AM +0200, Alessandro Rubini wrote:
> +static void fmc_release(struct device *dev)
> +{
> +	struct fmc_device *fmc = container_of(dev, struct fmc_device, dev);
> +
> +	kfree(fmc);
> +}

I read so many people who write new bus / device code that get this
wrong, that I'm so overjoyed when it is written correctly, that I'm
almost willing to just blindly accept this whole patchset.

Almost :)

Seriously, very nice job on the whole driver model integration for this
code.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 3/8] FMC: add core bus driver
  2013-06-12  7:13 ` [PATCH 3/8] FMC: add core bus driver Alessandro Rubini
  2013-06-17 23:15   ` Greg KH
@ 2013-06-17 23:16   ` Greg KH
  2013-06-17 23:38   ` Alessandro Rubini
  2 siblings, 0 replies; 26+ messages in thread
From: Greg KH @ 2013-06-17 23:16 UTC (permalink / raw)
  To: Alessandro Rubini
  Cc: linux-kernel, Juan David Gonzalez Cobas, Emilio G. Cota,
	Samuel Iglesias Gonsalvez, Rob Landley, linux-doc

On Wed, Jun 12, 2013 at 09:13:47AM +0200, Alessandro Rubini wrote:
> --- a/drivers/fmc/fmc-core.c
> +++ b/drivers/fmc/fmc-core.c
> @@ -1,13 +1,285 @@
> -/* Temporary placeholder so the empty code can build */
> +/*
> + * Copyright (C) 2012 CERN (www.cern.ch)
> + * Author: Alessandro Rubini <rubini@gnudd.com>
> + *
> + * Released according to the GNU GPL, version 2 or any later version.

I have to ask, do you really mean "or any later version"?

> +int fmc_driver_register(struct fmc_driver *drv)
> +{
> +	if (fmc_check_version(drv->version, drv->driver.name))
> +		return -EINVAL;
> +	drv->driver.bus = &fmc_bus_type;
> +	return driver_register(&drv->driver);
> +}
> +EXPORT_SYMBOL(fmc_driver_register);
> +
> +void fmc_driver_unregister(struct fmc_driver *drv)
> +{
> +	driver_unregister(&drv->driver);
> +}
> +EXPORT_SYMBOL(fmc_driver_unregister);

Any specific reason these aren't EXPORT_SYMBOL_GPL()?

Again, just have to ask.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 1/8] FMC: create drivers/fmc and toplevel Kconfig question
  2013-06-12  7:13 ` [PATCH 1/8] FMC: create drivers/fmc and toplevel Kconfig question Alessandro Rubini
@ 2013-06-17 23:17   ` Greg KH
  2013-06-17 23:24   ` Alessandro Rubini
  1 sibling, 0 replies; 26+ messages in thread
From: Greg KH @ 2013-06-17 23:17 UTC (permalink / raw)
  To: Alessandro Rubini
  Cc: linux-kernel, Juan David Gonzalez Cobas, Emilio G. Cota,
	Samuel Iglesias Gonsalvez, Rob Landley, linux-doc

On Wed, Jun 12, 2013 at 09:13:25AM +0200, Alessandro Rubini wrote:
> This commit creates the drivers/fmc directory and puts the necessary
> hooks for kbuild and kconfig.  The code is currently a placeholder
> that only registers an empty bus.
> 
> 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>
> ---
>  MAINTAINERS            |    9 +++++++++
>  drivers/Kconfig        |    2 ++
>  drivers/Makefile       |    1 +
>  drivers/fmc/Kconfig    |   17 +++++++++++++++++
>  drivers/fmc/Makefile   |    4 ++++
>  drivers/fmc/fmc-core.c |   24 ++++++++++++++++++++++++
>  6 files changed, 57 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/fmc/Kconfig
>  create mode 100644 drivers/fmc/Makefile
>  create mode 100644 drivers/fmc/fmc-core.c

{sigh}

If only all patch submissions started out this way, my life would be so
nice and comfy, I could just blindly accept new subsystems knowing that
the submitter understood the development process and how to break things
up properly...

Nice job :)

Want me to take this through my char/misc git tree to get to Linus for
3.11?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 5/8] FMC: add a software carrier driver
  2013-06-12  7:14 ` [PATCH 5/8] FMC: add a software carrier driver Alessandro Rubini
@ 2013-06-17 23:19   ` Greg KH
  0 siblings, 0 replies; 26+ messages in thread
From: Greg KH @ 2013-06-17 23:19 UTC (permalink / raw)
  To: Alessandro Rubini
  Cc: linux-kernel, Juan David Gonzalez Cobas, Emilio G. Cota,
	Samuel Iglesias Gonsalvez, Rob Landley, linux-doc

On Wed, Jun 12, 2013 at 09:14:09AM +0200, Alessandro Rubini wrote:
> --- /dev/null
> +++ b/drivers/fmc/fmc-fakedev.c
> @@ -0,0 +1,349 @@
> +/*
> + * Copyright (C) 2012 CERN (www.cern.ch)
> + * Author: Alessandro Rubini <rubini@gnudd.com>
> + *
> + * Released to the public domain, as example to be reused

I'm sorry, but that's not a "real" license in almost all countries, and
especially not in the USA, where I would have to be applying this patch.

If you want this to be used by anyone, why not just make it BSD?  That
would make the lawyers happy, and allow me to accept this patch {hint}.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 6/8] FMC: add a software mezzanine driver
  2013-06-12  7:14 ` [PATCH 6/8] FMC: add a software mezzanine driver Alessandro Rubini
@ 2013-06-17 23:20   ` Greg KH
  0 siblings, 0 replies; 26+ messages in thread
From: Greg KH @ 2013-06-17 23:20 UTC (permalink / raw)
  To: Alessandro Rubini
  Cc: linux-kernel, Juan David Gonzalez Cobas, Emilio G. Cota,
	Samuel Iglesias Gonsalvez, Rob Landley, linux-doc

On Wed, Jun 12, 2013 at 09:14:20AM +0200, Alessandro Rubini wrote:
> +++ b/drivers/fmc/fmc-trivial.c
> @@ -0,0 +1,101 @@
> +/*
> + * Copyright (C) 2012 CERN (www.cern.ch)
> + * Author: Alessandro Rubini <rubini@gnudd.com>
> + *
> + * Released to the public domain, as example to be reused

Same license issue, sorry.

greg k-h

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 2/8] FMC: add needed headers
  2013-06-12  7:13 ` [PATCH 2/8] FMC: add needed headers Alessandro Rubini
@ 2013-06-17 23:21   ` Greg KH
  2013-06-17 23:29   ` Alessandro Rubini
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 26+ messages in thread
From: Greg KH @ 2013-06-17 23:21 UTC (permalink / raw)
  To: Alessandro Rubini
  Cc: linux-kernel, Juan David Gonzalez Cobas, Emilio G. Cota,
	Samuel Iglesias Gonsalvez, Rob Landley, linux-doc

On Wed, Jun 12, 2013 at 09:13:36AM +0200, Alessandro Rubini wrote:
> +/*
> + * The device reports all information needed to access hw.
> + *
> + * If we have eeprom_len and not contents, the core reads it.
> + * Then, parsing of identifiers is done by the core which fills fmc_fru_id..
> + * Similarly a device that must be matched based on SDB cores must
> + * fill the entry point and the core will scan the bus (FIXME: sdb match)
> + */
> +struct fmc_device {
> +	unsigned long version;
> +	unsigned long flags;
> +	struct module *owner;		/* char device must pin it */
> +	struct fmc_fru_id id;		/* for EEPROM-based match */
> +	struct fmc_operations *op;	/* carrier-provided */
> +	int irq;			/* according to host bus. 0 == none */
> +	int eeprom_len;			/* Usually 8kB, may be less */
> +	int eeprom_addr;		/* 0x50, 0x52 etc */
> +	uint8_t *eeprom;		/* Full contents or leading part */
> +	char *carrier_name;		/* "SPEC" or similar, for special use */
> +	void *carrier_data;		/* "struct spec *" or equivalent */
> +	__iomem void *fpga_base;	/* May be NULL (Etherbone) */
> +	__iomem void *slot_base;	/* Set by the driver */
> +	struct fmc_device **devarray;	/* Allocated by the bus */
> +	int slot_id;			/* Index in the slot array */
> +	int nr_slots;			/* Number of slots in this carrier */
> +	unsigned long memlen;		/* Used for the char device */
> +	struct device dev;		/* For Linux use */
> +	struct device *hwdev;		/* The underlying hardware device */
> +	unsigned long sdbfs_entry;
> +	struct sdb_array *sdb;
> +	uint32_t device_id;		/* Filled by the device */
> +	char *mezzanine_name;		/* Defaults to ``fmc'' */
> +	void *mezzanine_data;
> +};
> +#define to_fmc_device(x) container_of((x), struct fmc_device, dev)
> +
> +#define FMC_DEVICE_HAS_GOLDEN		1
> +#define FMC_DEVICE_HAS_CUSTOM		2
> +#define FMC_DEVICE_NO_MEZZANINE		4
> +#define FMC_DEVICE_MATCH_SDB		8 /* fmc-core must scan sdb in fpga */

Any way you can use MODULE_DEVICE_TABLE() for these devices to get
proper module auto-loading for your drivers?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 1/8] FMC: create drivers/fmc and toplevel Kconfig question
  2013-06-12  7:13 ` [PATCH 1/8] FMC: create drivers/fmc and toplevel Kconfig question Alessandro Rubini
  2013-06-17 23:17   ` Greg KH
@ 2013-06-17 23:24   ` Alessandro Rubini
  2013-06-17 23:35     ` Greg KH
  1 sibling, 1 reply; 26+ messages in thread
From: Alessandro Rubini @ 2013-06-17 23:24 UTC (permalink / raw)
  To: gregkh
  Cc: linux-kernel, juan.david.gonzalez.cobas, cota, siglesias, rob, linux-doc

> Nice job :)

thanks!
 
> Want me to take this through my char/misc git tree to get to Linus for
> 3.11?

Would be great. As for the license issue, shall I send the two patches alone
or the whole set?

thanks
/alessandro

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 2/8] FMC: add needed headers
  2013-06-12  7:13 ` [PATCH 2/8] FMC: add needed headers Alessandro Rubini
  2013-06-17 23:21   ` Greg KH
@ 2013-06-17 23:29   ` Alessandro Rubini
  2013-06-17 23:35     ` Greg KH
  2013-06-19 10:49   ` Arnd Bergmann
  2013-06-19 11:08   ` Alessandro Rubini
  3 siblings, 1 reply; 26+ messages in thread
From: Alessandro Rubini @ 2013-06-17 23:29 UTC (permalink / raw)
  To: gregkh
  Cc: linux-kernel, juan.david.gonzalez.cobas, cota, siglesias, rob, linux-doc

> Any way you can use MODULE_DEVICE_TABLE() for these devices to get
> proper module auto-loading for your drivers?

It's the next step.

We are not doing that in our installations because it is not a trivial
addition to running kernels -- we are plugging FMC devices in
already-deployed kernel versions, sometimes even 2.6.24.

/alessandro

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 1/8] FMC: create drivers/fmc and toplevel Kconfig question
  2013-06-17 23:24   ` Alessandro Rubini
@ 2013-06-17 23:35     ` Greg KH
  0 siblings, 0 replies; 26+ messages in thread
From: Greg KH @ 2013-06-17 23:35 UTC (permalink / raw)
  To: Alessandro Rubini
  Cc: linux-kernel, juan.david.gonzalez.cobas, cota, siglesias, rob, linux-doc

On Tue, Jun 18, 2013 at 01:24:35AM +0200, Alessandro Rubini wrote:
> > Nice job :)
> 
> thanks!
>  
> > Want me to take this through my char/misc git tree to get to Linus for
> > 3.11?
> 
> Would be great. As for the license issue, shall I send the two patches alone
> or the whole set?

Just redo those two patches and send them as 'v2'.  Make sure it was
only those two patches that had the license like that.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 2/8] FMC: add needed headers
  2013-06-17 23:29   ` Alessandro Rubini
@ 2013-06-17 23:35     ` Greg KH
  0 siblings, 0 replies; 26+ messages in thread
From: Greg KH @ 2013-06-17 23:35 UTC (permalink / raw)
  To: Alessandro Rubini
  Cc: linux-kernel, juan.david.gonzalez.cobas, cota, siglesias, rob, linux-doc

On Tue, Jun 18, 2013 at 01:29:02AM +0200, Alessandro Rubini wrote:
> > Any way you can use MODULE_DEVICE_TABLE() for these devices to get
> > proper module auto-loading for your drivers?
> 
> It's the next step.

Ok, good.

> We are not doing that in our installations because it is not a trivial
> addition to running kernels -- we are plugging FMC devices in
> already-deployed kernel versions, sometimes even 2.6.24.

Ick, 2.6.24, you are brave :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 3/8] FMC: add core bus driver
  2013-06-12  7:13 ` [PATCH 3/8] FMC: add core bus driver Alessandro Rubini
  2013-06-17 23:15   ` Greg KH
  2013-06-17 23:16   ` Greg KH
@ 2013-06-17 23:38   ` Alessandro Rubini
  2013-06-18  8:36     ` Juan David Gonzalez Cobas
       [not found]     ` <CADDK0KdrVgmXN-TZw8E43JRNk5Wb_cfM6pUWaSxbJ49GU72cTg@mail.gmail.com>
  2 siblings, 2 replies; 26+ messages in thread
From: Alessandro Rubini @ 2013-06-17 23:38 UTC (permalink / raw)
  To: gregkh
  Cc: linux-kernel, juan.david.gonzalez.cobas, cota, siglesias, rob, linux-doc

>> + * Released according to the GNU GPL, version 2 or any later version.
> 
> I have to ask, do you really mean "or any later version"?

Yes, it was intended. I don't think it's an issue for merging, but
I understand you've seen so many such undesired copy-paste errors.

>> +EXPORT_SYMBOL(fmc_driver_unregister);
> 
> Any specific reason these aren't EXPORT_SYMBOL_GPL()?
> 
> Again, just have to ask.

I think we want to allow for proprietary FMC drivers. I personally
don't feel this is a core kernel technology.  But I'll let David Cobas
expand on CERN policies, as I'm only a consultant.

thanks
/alessandro

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 3/8] FMC: add core bus driver
  2013-06-17 23:38   ` Alessandro Rubini
@ 2013-06-18  8:36     ` Juan David Gonzalez Cobas
       [not found]     ` <CADDK0KdrVgmXN-TZw8E43JRNk5Wb_cfM6pUWaSxbJ49GU72cTg@mail.gmail.com>
  1 sibling, 0 replies; 26+ messages in thread
From: Juan David Gonzalez Cobas @ 2013-06-18  8:36 UTC (permalink / raw)
  To: Alessandro Rubini
  Cc: gregkh, linux-kernel, juan.david.gonzalez.cobas, cota, siglesias,
	rob, linux-doc

On Tue, Jun 18, 2013 at 1:38 AM, Alessandro Rubini <rubini@gnudd.com> wrote:
>>> + * Released according to the GNU GPL, version 2 or any later version.
[...]
> I think we want to allow for proprietary FMC drivers. I personally
> don't feel this is a core kernel technology.  But I'll let David Cobas
> expand on CERN policies, as I'm only a consultant.

After some discussion, we think Alessandro's arguments about keeping
EXPORT_SYMBOL are sound. As mentioned, CERN policies are
non-restrictive in this sense, hence we consider correct to leave
EXPORT_SYMBOLs as they are currently. The BSD licencing for
the other, "public domain" examples is also OK.

Hope this helps,

        david

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 3/8] FMC: add core bus driver
       [not found]     ` <CADDK0KdrVgmXN-TZw8E43JRNk5Wb_cfM6pUWaSxbJ49GU72cTg@mail.gmail.com>
@ 2013-06-18 17:40       ` Greg KH
  2013-06-18 19:52       ` Alessandro Rubini
  1 sibling, 0 replies; 26+ messages in thread
From: Greg KH @ 2013-06-18 17:40 UTC (permalink / raw)
  To: Juan David Gonzalez Cobas
  Cc: Alessandro Rubini, linux-kernel, cota, siglesias, rob, linux-doc

On Tue, Jun 18, 2013 at 09:30:00AM +0200, Juan David Gonzalez Cobas wrote:
> Hi all,
> 
> On Tue, Jun 18, 2013 at 1:38 AM, Alessandro Rubini <rubini@gnudd.com> wrote:
> 
>     [...]
> 
>  
> 
>     Yes, it was intended. I don't think it's an issue for merging, but
>     I understand you've seen so many such undesired copy-paste errors.
> 
> 
> CERN policies favour GPLv3 as default open source license as a recommendation.
> The "GPLv2 or later" is, in effect, intentional.

Ok, thanks for verifying this.

>     >> +EXPORT_SYMBOL(fmc_driver_unregister);
>     >
>     > Any specific reason these aren't EXPORT_SYMBOL_GPL()?
>     >
>     > Again, just have to ask.
> 
>     I think we want to allow for proprietary FMC drivers. I personally
>     don't feel this is a core kernel technology.  But I'll let David Cobas
>     expand on CERN policies, as I'm only a consultant.
> 
> 
> CERN policies (mostly recommendations) concern esp. the return of
> investment of taxpayer money to society. In view of the potential use
> cases of FMC, EXPORT_SYMBOL_GPL would better serve that
> purpose than merely allowing proprietary modules, so GPL exports
> are fine.

Great.

Alessandro, care to respin the patches that I didn't apply and resend
them so that I can?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 3/8] FMC: add core bus driver
       [not found]     ` <CADDK0KdrVgmXN-TZw8E43JRNk5Wb_cfM6pUWaSxbJ49GU72cTg@mail.gmail.com>
  2013-06-18 17:40       ` Greg KH
@ 2013-06-18 19:52       ` Alessandro Rubini
  1 sibling, 0 replies; 26+ messages in thread
From: Alessandro Rubini @ 2013-06-18 19:52 UTC (permalink / raw)
  To: gregkh
  Cc: juan.david.gonzalez.cobas, linux-kernel, cota, siglesias, rob, linux-doc

> Alessandro, care to respin the patches that I didn't apply and resend
> them so that I can?

Yes, ASAP. But, according to the later message by David, I'll respin
with the BSD license for demo code and EXPORT_SYMBOL (no GPL-only
clause):

  https://lkml.org/lkml/2013/6/18/119

  "As mentioned, CERN policies are non-restrictive in this sense,
  hence we consider correct to leave EXPORT_SYMBOLs as they are
  currently. The BSD licencing for the other, "public domain" examples
  is also OK."

Shortly: since FMC is not a core component of the kernel, the market
is better served by allowing use of drivers/fmc also by those
companies whose lawyers are bigger than their developers. (disclaimer:
wording is mine). Even if CERN itself won't buy cards with proprietary
drivers.

/alessandro

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 2/8] FMC: add needed headers
  2013-06-12  7:13 ` [PATCH 2/8] FMC: add needed headers Alessandro Rubini
  2013-06-17 23:21   ` Greg KH
  2013-06-17 23:29   ` Alessandro Rubini
@ 2013-06-19 10:49   ` Arnd Bergmann
  2013-06-19 11:08   ` Alessandro Rubini
  3 siblings, 0 replies; 26+ messages in thread
From: Arnd Bergmann @ 2013-06-19 10:49 UTC (permalink / raw)
  To: Alessandro Rubini
  Cc: linux-kernel, Juan David Gonzalez Cobas, Emilio G. Cota,
	Samuel Iglesias Gonsalvez, gregkh, Rob Landley, linux-doc

On Wednesday 12 June 2013 09:13:36 Alessandro Rubini wrote:
> + */
> +struct fmc_operations {
> +       uint32_t (*readl)(struct fmc_device *fmc, int offset);
> +       void (*writel)(struct fmc_device *fmc, uint32_t value, int offset);
> +       int (*validate)(struct fmc_device *fmc, struct fmc_driver *drv);
> +       int (*reprogram)(struct fmc_device *f, struct fmc_driver *d, char *gw);
> +       int (*irq_request)(struct fmc_device *fmc, irq_handler_t h,
> +                          char *name, int flags);

8<----
Subject: fmc: avoid readl/writel namespace conflict

The use of the 'readl' and 'writel' identifiers here causes build errors on
architectures where those are macros. This renames the fields to read32/write32
to avoid the problem.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>

diff --git a/drivers/fmc/fmc-fakedev.c b/drivers/fmc/fmc-fakedev.c
index bec94ac..941d093 100644
--- a/drivers/fmc/fmc-fakedev.c
+++ b/drivers/fmc/fmc-fakedev.c
@@ -232,8 +232,8 @@ static int ff_validate(struct fmc_device *fmc, struct fmc_driver *drv)
 
 
 static struct fmc_operations ff_fmc_operations = {
-	.readl =		ff_readl,
-	.writel =		ff_writel,
+	.read32 =		ff_readl,
+	.write32 =		ff_writel,
 	.reprogram =		ff_reprogram,
 	.irq_request =		ff_irq_request,
 	.read_ee =		ff_read_ee,
diff --git a/include/linux/fmc.h b/include/linux/fmc.h
index a3c4985..a5f0aa5 100644
--- a/include/linux/fmc.h
+++ b/include/linux/fmc.h
@@ -129,8 +129,8 @@ struct fmc_gpio {
  * the exception.
  */
 struct fmc_operations {
-	uint32_t (*readl)(struct fmc_device *fmc, int offset);
-	void (*writel)(struct fmc_device *fmc, uint32_t value, int offset);
+	uint32_t (*read32)(struct fmc_device *fmc, int offset);
+	void (*write32)(struct fmc_device *fmc, uint32_t value, int offset);
 	int (*validate)(struct fmc_device *fmc, struct fmc_driver *drv);
 	int (*reprogram)(struct fmc_device *f, struct fmc_driver *d, char *gw);
 	int (*irq_request)(struct fmc_device *fmc, irq_handler_t h,
@@ -194,14 +194,14 @@ struct fmc_device {
  */
 static inline uint32_t fmc_readl(struct fmc_device *fmc, int offset)
 {
-	if (unlikely(fmc->op->readl))
-		return fmc->op->readl(fmc, offset);
+	if (unlikely(fmc->op->read32))
+		return fmc->op->read32(fmc, offset);
 	return readl(fmc->fpga_base + offset);
 }
 static inline void fmc_writel(struct fmc_device *fmc, uint32_t val, int off)
 {
-	if (unlikely(fmc->op->writel))
-		fmc->op->writel(fmc, val, off);
+	if (unlikely(fmc->op->write32))
+		fmc->op->write32(fmc, val, off);
 	else
 		writel(val, fmc->fpga_base + off);
 }


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH 2/8] FMC: add needed headers
  2013-06-12  7:13 ` [PATCH 2/8] FMC: add needed headers Alessandro Rubini
                     ` (2 preceding siblings ...)
  2013-06-19 10:49   ` Arnd Bergmann
@ 2013-06-19 11:08   ` Alessandro Rubini
  3 siblings, 0 replies; 26+ messages in thread
From: Alessandro Rubini @ 2013-06-19 11:08 UTC (permalink / raw)
  To: arnd
  Cc: linux-kernel, juan.david.gonzalez.cobas, cota, siglesias, gregkh,
	rob, linux-doc

> The use of the 'readl' and 'writel' identifiers here causes build errors on
> architectures where those are macros. This renames the fields to read32/write32
> to avoid the problem.

Thanks.  I'll apply the same to my repo and related drivers.

Acked-by: Alessandro Rubini <rubini@gnudd.com>

/alessandro

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 2/8] FMC: add needed headers
  2013-02-21 18:13 [PATCH RFC 0/8] drivers/fmc: bus support for ANSI-VITA 57.1 Alessandro Rubini
@ 2013-02-21 18:13 ` Alessandro Rubini
  0 siblings, 0 replies; 26+ messages in thread
From: Alessandro Rubini @ 2013-02-21 18:13 UTC (permalink / raw)
  To: linux-kernel, greg
  Cc: Juan David Gonzalez Cobas, Emilio G. Cota,
	Samuel Iglesias Gonsalvez, Rob Landley, linux-doc

This set of headers comes from commit 55812ecd (current master of the
project on ohwr.org). They define the basic data structures for FMC
and its SDB support.

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>
---
 include/linux/fmc-sdb.h  |   36 +++++++
 include/linux/fmc.h      |  237 ++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/ipmi-fru.h |  135 ++++++++++++++++++++++++++
 include/linux/sdb.h      |  130 +++++++++++++++++++++++++
 4 files changed, 538 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/fmc-sdb.h
 create mode 100644 include/linux/fmc.h
 create mode 100644 include/linux/ipmi-fru.h
 create mode 100644 include/linux/sdb.h

diff --git a/include/linux/fmc-sdb.h b/include/linux/fmc-sdb.h
new file mode 100644
index 0000000..1974317
--- /dev/null
+++ b/include/linux/fmc-sdb.h
@@ -0,0 +1,36 @@
+/*
+ * This file is separate from sdb.h, because I want that one to remain
+ * unchanged (as far as possible) from the official sdb distribution
+ *
+ * This file and associated functionality are a playground for me to
+ * understand stuff which will later be implemented in more generic places.
+ */
+#include <linux/sdb.h>
+
+/* This is the union of all currently defined types */
+union sdb_record {
+	struct sdb_interconnect ic;
+	struct sdb_device dev;
+	struct sdb_bridge bridge;
+	struct sdb_integration integr;
+	struct sdb_empty empty;
+};
+
+struct fmc_device;
+
+/* Every sdb table is turned into this structure */
+struct sdb_array {
+	int len;
+	int level;
+	unsigned long baseaddr;
+	struct fmc_device *fmc;		/* the device that hosts it */
+	struct sdb_array *parent;	/* NULL at root */
+	union sdb_record *record;	/* copies of the struct */
+	struct sdb_array **subtree;	/* only valid for bridge items */
+};
+
+extern int fmc_scan_sdb_tree(struct fmc_device *fmc, unsigned long address);
+extern void fmc_show_sdb_tree(const struct fmc_device *fmc);
+extern signed long fmc_find_sdb_device(struct sdb_array *tree, uint64_t vendor,
+				       uint32_t device, unsigned long *sz);
+extern int fmc_free_sdb_tree(struct fmc_device *fmc);
diff --git a/include/linux/fmc.h b/include/linux/fmc.h
new file mode 100644
index 0000000..4a821e4
--- /dev/null
+++ b/include/linux/fmc.h
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2012 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ *
+ * This work is part of the White Rabbit project, a research effort led
+ * by CERN, the European Institute for Nuclear Research.
+ */
+#ifndef __LINUX_FMC_H__
+#define __LINUX_FMC_H__
+#include <linux/types.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+struct fmc_device;
+struct fmc_driver;
+
+/*
+ * This bus abstraction is developed separately from drivers, so we need
+ * to check the version of the data structures we receive.
+ */
+
+#define FMC_MAJOR	3
+#define FMC_MINOR	0
+#define FMC_VERSION	((FMC_MAJOR << 16) | FMC_MINOR)
+#define __FMC_MAJOR(x)	((x) >> 16)
+#define __FMC_MINOR(x)	((x) & 0xffff)
+
+/*
+ * The device identification, as defined by the IPMI FRU (Field Replaceable
+ * Unit) includes four different strings to describe the device. Here we
+ * only match the "Board Manufacturer" and the "Board Product Name",
+ * ignoring the "Board Serial Number" and "Board Part Number". All 4 are
+ * expected to be strings, so they are treated as zero-terminated C strings.
+ * Unspecified string (NULL) means "any", so if both are unspecified this
+ * is a catch-all driver. So null entries are allowed and we use array
+ * and length. This is unlike pci and usb that use null-terminated arrays
+ */
+struct fmc_fru_id {
+	char *manufacturer;
+	char *product_name;
+};
+
+/*
+ * If the FPGA is already programmed (think Etherbone or the second
+ * SVEC slot), we can match on SDB devices in the memory image. This
+ * match uses an array of devices that must all be present, and the
+ * match is based on vendor and device only. Further checks are expected
+ * to happen in the probe function. Zero means "any" and catch-all is allowed.
+ */
+struct fmc_sdb_one_id {
+	uint64_t vendor;
+	uint32_t device;
+};
+struct fmc_sdb_id {
+	struct fmc_sdb_one_id *cores;
+	int cores_nr;
+};
+
+struct fmc_device_id {
+	struct fmc_fru_id *fru_id;
+	int fru_id_nr;
+	struct fmc_sdb_id *sdb_id;
+	int sdb_id_nr;
+};
+
+/* This sizes the module_param_array used by generic module parameters */
+#define FMC_MAX_CARDS 32
+
+/* The driver is a pretty simple thing */
+struct fmc_driver {
+	unsigned long version;
+	struct device_driver driver;
+	int (*probe)(struct fmc_device *);
+	int (*remove)(struct fmc_device *);
+	const struct fmc_device_id id_table;
+	/* What follows is for generic module parameters */
+	int busid_n;
+	int busid_val[FMC_MAX_CARDS];
+	int gw_n;
+	char *gw_val[FMC_MAX_CARDS];
+};
+#define to_fmc_driver(x) container_of((x), struct fmc_driver, driver)
+
+/* These are the generic parameters, that drivers may instantiate */
+#define FMC_PARAM_BUSID(_d) \
+    module_param_array_named(busid, _d.busid_val, int, &_d.busid_n, 0444)
+#define FMC_PARAM_GATEWARE(_d) \
+    module_param_array_named(gateware, _d.gw_val, charp, &_d.gw_n, 0444)
+
+/*
+ * Drivers may need to configure gpio pins in the carrier. To read input
+ * (a very uncommon operation, and definitely not in the hot paths), just
+ * configure one gpio only and get 0 or 1 as retval of the config method
+ */
+struct fmc_gpio {
+	char *carrier_name; /* name or NULL for virtual pins */
+	int gpio;
+	int _gpio;	/* internal use by the carrier */
+	int mode;	/* GPIOF_DIR_OUT etc, from <linux/gpio.h> */
+	int irqmode;	/* IRQF_TRIGGER_LOW and so on */
+};
+
+/* The numbering of gpio pins allows access to raw pins or virtual roles */
+#define FMC_GPIO_RAW(x)		(x)		/* 4096 of them */
+#define __FMC_GPIO_IS_RAW(x)	((x) < 0x1000)
+#define FMC_GPIO_IRQ(x)		((x) + 0x1000)	/*  256 of them */
+#define FMC_GPIO_LED(x)		((x) + 0x1100)	/*  256 of them */
+#define FMC_GPIO_KEY(x)		((x) + 0x1200)	/*  256 of them */
+#define FMC_GPIO_TP(x)		((x) + 0x1300)	/*  256 of them */
+#define FMC_GPIO_USER(x)	((x) + 0x1400)	/*  256 of them */
+/* We may add SCL and SDA, or other roles if the need arises */
+
+/* GPIOF_DIR_IN etc are missing before 3.0. copy from <linux/gpio.h> */
+#ifndef GPIOF_DIR_IN
+#  define GPIOF_DIR_OUT   (0 << 0)
+#  define GPIOF_DIR_IN    (1 << 0)
+#  define GPIOF_INIT_LOW  (0 << 1)
+#  define GPIOF_INIT_HIGH (1 << 1)
+#endif
+
+/*
+ * The operations are offered by each carrier and should make driver
+ * design completely independent of the carrier. Named GPIO pins may be
+ * the exception.
+ */
+struct fmc_operations {
+	uint32_t (*readl)(struct fmc_device *fmc, int offset);
+	void (*writel)(struct fmc_device *fmc, uint32_t value, int offset);
+	int (*validate)(struct fmc_device *fmc, struct fmc_driver *drv);
+	int (*reprogram)(struct fmc_device *f, struct fmc_driver *d, char *gw);
+	int (*irq_request)(struct fmc_device *fmc, irq_handler_t h,
+			   char *name, int flags);
+	void (*irq_ack)(struct fmc_device *fmc);
+	int (*irq_free)(struct fmc_device *fmc);
+	int (*gpio_config)(struct fmc_device *fmc, struct fmc_gpio *gpio,
+			   int ngpio);
+	int (*read_ee)(struct fmc_device *fmc, int pos, void *d, int l);
+	int (*write_ee)(struct fmc_device *fmc, int pos, const void *d, int l);
+};
+
+/* Prefer this helper rather than calling of fmc->reprogram directly */
+extern int fmc_reprogram(struct fmc_device *f, struct fmc_driver *d, char *gw,
+		     int sdb_entry);
+
+/*
+ * The device reports all information needed to access hw.
+ *
+ * If we have eeprom_len and not contents, the core reads it.
+ * Then, parsing of identifiers is done by the core which fills fmc_fru_id..
+ * Similarly a device that must be matched based on SDB cores must
+ * fill the entry point and the core will scan the bus (FIXME: sdb match)
+ */
+struct fmc_device {
+	unsigned long version;
+	unsigned long flags;
+	struct module *owner;		/* char device must pin it */
+	struct fmc_fru_id id;		/* for EEPROM-based match */
+	struct fmc_operations *op;	/* carrier-provided */
+	int irq;			/* according to host bus. 0 == none */
+	int eeprom_len;			/* Usually 8kB, may be less */
+	int eeprom_addr;		/* 0x50, 0x52 etc */
+	uint8_t *eeprom;		/* Full contents or leading part */
+	char *carrier_name;		/* "SPEC" or similar, for special use */
+	void *carrier_data;		/* "struct spec *" or equivalent */
+	__iomem void *fpga_base;	/* May be NULL (Etherbone) */
+	__iomem void *slot_base;	/* Set by the driver */
+	struct fmc_device **devarray;	/* Allocated by the bus */
+	int slot_id;			/* Index in the slot array */
+	int nr_slots;			/* Number of slots in this carrier */
+	unsigned long memlen;		/* Used for the char device */
+	struct device dev;		/* For Linux use */
+	struct device *hwdev;		/* The underlying hardware device */
+	unsigned long sdbfs_entry;
+	struct sdb_array *sdb;
+	uint32_t device_id;		/* Filled by the device */
+	char *mezzanine_name;		/* Defaults to ``fmc'' */
+	void *mezzanine_data;
+};
+#define to_fmc_device(x) container_of((x), struct fmc_device, dev)
+
+#define FMC_DEVICE_HAS_GOLDEN		1
+#define FMC_DEVICE_HAS_CUSTOM		2
+#define FMC_DEVICE_NO_MEZZANINE		4
+#define FMC_DEVICE_MATCH_SDB		8 /* fmc-core must scan sdb in fpga */
+
+/*
+ * If fpga_base can be used, the carrier offers no readl/writel methods, and
+ * this expands to a single, fast, I/O access.
+ */
+static inline uint32_t fmc_readl(struct fmc_device *fmc, int offset)
+{
+	if (unlikely(fmc->op->readl))
+		return fmc->op->readl(fmc, offset);
+	return readl(fmc->fpga_base + offset);
+}
+static inline void fmc_writel(struct fmc_device *fmc, uint32_t val, int off)
+{
+	if (unlikely(fmc->op->writel))
+		fmc->op->writel(fmc, val, off);
+	else
+		writel(val, fmc->fpga_base + off);
+}
+
+/* pci-like naming */
+static inline void *fmc_get_drvdata(const struct fmc_device *fmc)
+{
+	return dev_get_drvdata(&fmc->dev);
+}
+
+static inline void fmc_set_drvdata(struct fmc_device *fmc, void *data)
+{
+	dev_set_drvdata(&fmc->dev, data);
+}
+
+/* The 4 access points */
+extern int fmc_driver_register(struct fmc_driver *drv);
+extern void fmc_driver_unregister(struct fmc_driver *drv);
+extern int fmc_device_register(struct fmc_device *tdev);
+extern void fmc_device_unregister(struct fmc_device *tdev);
+
+/* Two more for device sets, all driven by the same FPGA */
+extern int fmc_device_register_n(struct fmc_device *fmc, int n);
+extern void fmc_device_unregister_n(struct fmc_device *fmc, int n);
+
+/* Internal cross-calls between files; not exported to other modules */
+extern int fmc_match(struct device *dev, struct device_driver *drv);
+extern int fmc_fill_id_info(struct fmc_device *fmc);
+extern void fmc_free_id_info(struct fmc_device *fmc);
+extern void fmc_dump_eeprom(const struct fmc_device *fmc);
+extern void fmc_dump_sdb(const struct fmc_device *fmc);
+
+#endif /* __LINUX_FMC_H__ */
diff --git a/include/linux/ipmi-fru.h b/include/linux/ipmi-fru.h
new file mode 100644
index 0000000..99bf2ea
--- /dev/null
+++ b/include/linux/ipmi-fru.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2012 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ *
+ * This work is part of the White Rabbit project, a research effort led
+ * by CERN, the European Institute for Nuclear Research.
+ */
+#ifndef __LINUX_IPMI_FRU_H__
+#define __LINUX_IPMI_FRU_H__
+#ifdef __KERNEL__
+#  include <linux/types.h>
+#  include <linux/string.h>
+#else
+#  include <stdint.h>
+#  include <string.h>
+#endif
+
+/*
+ * These structures match the unaligned crap we have in FRU1011.pdf
+ * (http://download.intel.com/design/servers/ipmi/FRU1011.pdf)
+ */
+
+/* chapter 8, page 5 */
+struct fru_common_header {
+	uint8_t format;			/* 0x01 */
+	uint8_t internal_use_off;	/* multiple of 8 bytes */
+	uint8_t chassis_info_off;	/* multiple of 8 bytes */
+	uint8_t board_area_off;		/* multiple of 8 bytes */
+	uint8_t product_area_off;	/* multiple of 8 bytes */
+	uint8_t multirecord_off;	/* multiple of 8 bytes */
+	uint8_t pad;			/* must be 0 */
+	uint8_t checksum;		/* sum modulo 256 must be 0 */
+};
+
+/* chapter 9, page 5 -- internal_use: not used by us */
+
+/* chapter 10, page 6 -- chassis info: not used by us */
+
+/* chapter 13, page 9 -- used by board_info_area below */
+struct fru_type_length {
+	uint8_t type_length;
+	uint8_t data[0];
+};
+
+/* chapter 11, page 7 */
+struct fru_board_info_area {
+	uint8_t format;			/* 0x01 */
+	uint8_t area_len;		/* multiple of 8 bytes */
+	uint8_t language;		/* I hope it's 0 */
+	uint8_t mfg_date[3];		/* LSB, minutes since 1996-01-01 */
+	struct fru_type_length tl[0];	/* type-length stuff follows */
+
+	/*
+	 * the TL there are in order:
+	 * Board Manufacturer
+	 * Board Product Name
+	 * Board Serial Number
+	 * Board Part Number
+	 * FRU File ID (may be null)
+	 * more manufacturer-specific stuff
+	 * 0xc1 as a terminator
+	 * 0x00 pad to a multiple of 8 bytes - 1
+	 * checksum (sum of all stuff module 256 must be zero)
+	 */
+};
+
+enum fru_type {
+	FRU_TYPE_BINARY		= 0x00,
+	FRU_TYPE_BCDPLUS	= 0x40,
+	FRU_TYPE_ASCII6		= 0x80,
+	FRU_TYPE_ASCII		= 0xc0, /* not ascii: depends on language */
+};
+
+/*
+ * some helpers
+ */
+static inline struct fru_board_info_area *fru_get_board_area(
+	const struct fru_common_header *header)
+{
+	/* we know for sure that the header is 8 bytes in size */
+	return (struct fru_board_info_area *)(header + header->board_area_off);
+}
+
+static inline int fru_type(struct fru_type_length *tl)
+{
+	return tl->type_length & 0xc0;
+}
+
+static inline int fru_length(struct fru_type_length *tl)
+{
+	return (tl->type_length & 0x3f) + 1; /* len of whole record */
+}
+
+/* assume ascii-latin1 encoding */
+static inline int fru_strlen(struct fru_type_length *tl)
+{
+	return fru_length(tl) - 1;
+}
+
+static inline char *fru_strcpy(char *dest, struct fru_type_length *tl)
+{
+	int len = fru_strlen(tl);
+	memcpy(dest, tl->data, len);
+	dest[len + 1] = '\0';
+	return dest;
+}
+
+static inline struct fru_type_length *fru_next_tl(struct fru_type_length *tl)
+{
+	return tl + fru_length(tl);
+}
+
+static inline int fru_is_eof(struct fru_type_length *tl)
+{
+	return tl->type_length == 0xc1;
+}
+
+/*
+ * External functions defined in fru-parse.c.
+ */
+extern int fru_header_cksum_ok(struct fru_common_header *header);
+extern int fru_bia_cksum_ok(struct fru_board_info_area *bia);
+
+/* All these 4 return allocated strings by calling fru_alloc() */
+extern char *fru_get_board_manufacturer(struct fru_common_header *header);
+extern char *fru_get_product_name(struct fru_common_header *header);
+extern char *fru_get_serial_number(struct fru_common_header *header);
+extern char *fru_get_part_number(struct fru_common_header *header);
+
+/* This must be defined by the caller of the above functions */
+extern void *fru_alloc(size_t size);
+
+#endif /* __LINUX_IMPI_FRU_H__ */
diff --git a/include/linux/sdb.h b/include/linux/sdb.h
new file mode 100644
index 0000000..5260351
--- /dev/null
+++ b/include/linux/sdb.h
@@ -0,0 +1,130 @@
+/*
+ * This is version 1.0 of sdb.h, as included the specification v1.0
+ */
+#ifndef __SDB_H__
+#define __SDB_H__
+#ifdef __KERNEL__
+#include <linux/types.h>
+#else
+#include <stdint.h>
+#endif
+
+/*
+ * All structures are 64 bytes long and are expected
+ * to live in an array, one for each interconnect.
+ * Most fields of the structures are shared among the
+ * various types, and most-specific fields are at the
+ * beginning (for alignment reasons, and to keep the
+ * magic number at the head of the interconnect record
+ */
+
+/* Product, 40 bytes at offset 24, 8-byte aligned
+ *
+ * device_id is vendor-assigned; version is device-specific,
+ * date is hex (e.g 0x20120501), name is UTF-8, blank-filled
+ * and not terminated with a 0 byte.
+ */
+struct sdb_product {
+	uint64_t		vendor_id;	/* 0x18..0x1f */
+	uint32_t		device_id;	/* 0x20..0x23 */
+	uint32_t		version;	/* 0x24..0x27 */
+	uint32_t		date;		/* 0x28..0x2b */
+	uint8_t			name[19];	/* 0x2c..0x3e */
+	uint8_t			record_type;	/* 0x3f */
+};
+
+/*
+ * Component, 56 bytes at offset 8, 8-byte aligned
+ *
+ * The address range is first to last, inclusive
+ * (for example 0x100000 - 0x10ffff)
+ */
+struct sdb_component {
+	uint64_t		addr_first;	/* 0x08..0x0f */
+	uint64_t		addr_last;	/* 0x10..0x17 */
+	struct sdb_product	product;	/* 0x18..0x3f */
+};
+
+/* Type of the SDB record */
+enum sdb_record_type {
+	sdb_type_interconnect = 0x00,
+	sdb_type_device       = 0x01,
+	sdb_type_bridge       = 0x02,
+	sdb_type_integration  = 0x80,
+	sdb_type_empty        = 0xFF,
+};
+
+/* Type 0: interconnect (first of the array)
+ *
+ * sdb_records is the length of the table including this first
+ *  record, version is 1. The bus type is enumerated later.
+ */
+#define				SDB_MAGIC	0x5344422d /* "SDB-" */
+struct sdb_interconnect {
+	uint32_t		sdb_magic;	/* 0x00-0x03 */
+	uint16_t		sdb_records;	/* 0x04-0x05 */
+	uint8_t			sdb_version;	/* 0x06 */
+	uint8_t			sdb_bus_type;	/* 0x07 */
+	struct sdb_component	sdb_component;	/* 0x08-0x3f */
+};
+
+/* Type 1: device
+ *
+ * class is 0 for "custom device", other values are
+ * to be standardized; ABI version is for the driver,
+ * bus-specific bits are defined by each bus (see below)
+ */
+struct sdb_device {
+	uint16_t		abi_class;	/* 0x00-0x01 */
+	uint8_t			abi_ver_major;	/* 0x02 */
+	uint8_t			abi_ver_minor;	/* 0x03 */
+	uint32_t		bus_specific;	/* 0x04-0x07 */
+	struct sdb_component	sdb_component;	/* 0x08-0x3f */
+};
+
+/* Type 2: bridge
+ *
+ * child is the address of the nested SDB table
+ */
+struct sdb_bridge {
+	uint64_t		sdb_child;	/* 0x00-0x07 */
+	struct sdb_component	sdb_component;	/* 0x08-0x3f */
+};
+
+/* Type 0x80: integration
+ *
+ * all types with bit 7 set are meta-information, so
+ * software can ignore the types it doesn't know. Here we
+ * just provide product information for an aggregate device
+ */
+struct sdb_integration {
+	uint8_t			reserved[24];	/* 0x00-0x17 */
+	struct sdb_product	product;	/* 0x08-0x3f */
+};
+
+/* Type 0xff: empty
+ *
+ * this allows keeping empty slots during development,
+ * so they can be filled later with minimal effort and
+ * no misleading description is ever shipped -- hopefully.
+ * It can also be used to pad a table to a desired length.
+ */
+struct sdb_empty {
+	uint8_t			reserved[63];	/* 0x00-0x3e */
+	uint8_t			record_type;	/* 0x3f */
+};
+
+/* The type of bus, for bus-specific flags (currently only Wishbone) */
+enum sdb_bus_type {
+	sdb_wishbone = 0x00
+};
+
+#define SDB_WB_WIDTH_MASK		0x0f
+#define SDB_WB_ACCESS8		0x01
+#define SDB_WB_ACCESS16		0x02
+#define SDB_WB_ACCESS32		0x04
+#define SDB_WB_ACCESS64		0x08
+
+#define SDB_WB_LITTLE_ENDIAN		0x80
+
+#endif /* __SDB_H__ */
-- 
1.7.7.2

^ permalink raw reply related	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2013-06-19 11:09 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-06-12  7:13 [PATCH 0/8] Support for FMC carriers and mezzanines Alessandro Rubini
2013-06-12  7:13 ` [PATCH 1/8] FMC: create drivers/fmc and toplevel Kconfig question Alessandro Rubini
2013-06-17 23:17   ` Greg KH
2013-06-17 23:24   ` Alessandro Rubini
2013-06-17 23:35     ` Greg KH
2013-06-12  7:13 ` [PATCH 2/8] FMC: add needed headers Alessandro Rubini
2013-06-17 23:21   ` Greg KH
2013-06-17 23:29   ` Alessandro Rubini
2013-06-17 23:35     ` Greg KH
2013-06-19 10:49   ` Arnd Bergmann
2013-06-19 11:08   ` Alessandro Rubini
2013-06-12  7:13 ` [PATCH 3/8] FMC: add core bus driver Alessandro Rubini
2013-06-17 23:15   ` Greg KH
2013-06-17 23:16   ` Greg KH
2013-06-17 23:38   ` Alessandro Rubini
2013-06-18  8:36     ` Juan David Gonzalez Cobas
     [not found]     ` <CADDK0KdrVgmXN-TZw8E43JRNk5Wb_cfM6pUWaSxbJ49GU72cTg@mail.gmail.com>
2013-06-18 17:40       ` Greg KH
2013-06-18 19:52       ` Alessandro Rubini
2013-06-12  7:13 ` [PATCH 4/8] FMC: add documentation for the core Alessandro Rubini
2013-06-12  7:14 ` [PATCH 5/8] FMC: add a software carrier driver Alessandro Rubini
2013-06-17 23:19   ` Greg KH
2013-06-12  7:14 ` [PATCH 6/8] FMC: add a software mezzanine driver Alessandro Rubini
2013-06-17 23:20   ` Greg KH
2013-06-12  7:14 ` [PATCH 7/8] FMC: add a driver to write mezzanine EEPROM Alessandro Rubini
2013-06-12  7:14 ` [PATCH 8/8] FMC: add a char-device mezzanine driver Alessandro Rubini
  -- strict thread matches above, loose matches on Subject: below --
2013-02-21 18:13 [PATCH RFC 0/8] drivers/fmc: bus support for ANSI-VITA 57.1 Alessandro Rubini
2013-02-21 18:13 ` [PATCH 2/8] FMC: add needed headers Alessandro Rubini

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).