lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1357584666-17374-2-git-send-email-panto@antoniou-consulting.com>
Date:	Mon,  7 Jan 2013 20:51:02 +0200
From:	Pantelis Antoniou <panto@...oniou-consulting.com>
To:	Grant Likely <grant.likely@...retlab.ca>
Cc:	Rob Herring <rob.herring@...xeda.com>,
	Rob Landley <rob@...dley.net>, Jon Loeliger <jdl@....com>,
	Tony Lindgren <tony@...mide.com>,
	Stephen Warren <swarren@...dotorg.org>,
	David Gibson <david@...son.dropbear.id.au>,
	Benoit Cousson <b-cousson@...com>,
	Mitch Bradley <wmb@...mworks.com>,
	Alan Tull <atull@...era.com>, Arnd Bergmann <arnd@...db.de>,
	Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
	linux-omap@...r.kernel.org, devicetree-discuss@...ts.ozlabs.org,
	linux-doc@...r.kernel.org, linux-kernel@...r.kernel.org,
	Matt Porter <mporter@...com>, Russ Dill <Russ.Dill@...com>,
	Koen Kooi <koen@...inion.thruhere.net>,
	Joel A Fernandes <agnel.joel@...il.com>,
	Rob Clark <robdclark@...il.com>,
	Jason Kridner <jkridner@...gleboard.org>,
	Matt Ranostay <mranostay@...il.com>,
	Pantelis Antoniou <panto@...oniou-consulting.com>
Subject: [PATCH 1/5] capemgr: Beaglebone DT overlay based cape manager

A cape loader based on DT overlays and DT objects.

Beaglebone cape manager implementation.

Signed-off-by: Pantelis Antoniou <panto@...oniou-consulting.com>
---
 arch/arm/mach-omap2/Kconfig            |    2 +
 drivers/misc/Kconfig                   |    2 +
 drivers/misc/Makefile                  |    1 +
 drivers/misc/cape/Kconfig              |    5 +
 drivers/misc/cape/Makefile             |    5 +
 drivers/misc/cape/beaglebone/Kconfig   |   11 +
 drivers/misc/cape/beaglebone/Makefile  |    5 +
 drivers/misc/cape/beaglebone/capemgr.c | 1835 ++++++++++++++++++++++++++++++++
 8 files changed, 1866 insertions(+)
 create mode 100644 drivers/misc/cape/Kconfig
 create mode 100644 drivers/misc/cape/Makefile
 create mode 100644 drivers/misc/cape/beaglebone/Kconfig
 create mode 100644 drivers/misc/cape/beaglebone/Makefile
 create mode 100644 drivers/misc/cape/beaglebone/capemgr.c

diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 41b581f..f0c2eab 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -18,6 +18,8 @@ config ARCH_OMAP2PLUS_TYPICAL
 	select TWL4030_CORE if ARCH_OMAP3 || ARCH_OMAP4
 	select TWL4030_POWER if ARCH_OMAP3 || ARCH_OMAP4
 	select VFP
+	select OF_OVERLAY
+	select OF_RESOLVE
 	help
 	  Compile a kernel suitable for booting most boards
 
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index b151b7c..45558a3 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -507,4 +507,6 @@ source "drivers/misc/lis3lv02d/Kconfig"
 source "drivers/misc/carma/Kconfig"
 source "drivers/misc/altera-stapl/Kconfig"
 source "drivers/misc/mei/Kconfig"
+source "drivers/misc/cape/Kconfig"
+
 endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 2129377..c06d457 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -49,3 +49,4 @@ obj-y				+= carma/
 obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
 obj-$(CONFIG_ALTERA_STAPL)	+=altera-stapl/
 obj-$(CONFIG_INTEL_MEI)		+= mei/
+obj-y				+= cape/
diff --git a/drivers/misc/cape/Kconfig b/drivers/misc/cape/Kconfig
new file mode 100644
index 0000000..a2ef85e
--- /dev/null
+++ b/drivers/misc/cape/Kconfig
@@ -0,0 +1,5 @@
+#
+# Capes
+#
+
+source "drivers/misc/cape/beaglebone/Kconfig"
diff --git a/drivers/misc/cape/Makefile b/drivers/misc/cape/Makefile
new file mode 100644
index 0000000..7c4eb96
--- /dev/null
+++ b/drivers/misc/cape/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for cape like devices
+#
+
+obj-y				+= beaglebone/
diff --git a/drivers/misc/cape/beaglebone/Kconfig b/drivers/misc/cape/beaglebone/Kconfig
new file mode 100644
index 0000000..99a31ec
--- /dev/null
+++ b/drivers/misc/cape/beaglebone/Kconfig
@@ -0,0 +1,11 @@
+#
+# Beaglebone capes
+#
+
+config CAPE_BEAGLEBONE
+	tristate "Beaglebone cape support"
+	depends on ARCH_OMAP2PLUS && OF && I2C
+	default n
+	select OF_PLUGIN
+	help
+	  Say Y here to include support for beaglebone capes
diff --git a/drivers/misc/cape/beaglebone/Makefile b/drivers/misc/cape/beaglebone/Makefile
new file mode 100644
index 0000000..5b4549f
--- /dev/null
+++ b/drivers/misc/cape/beaglebone/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for beaglebone capes
+#
+
+obj-$(CONFIG_CAPE_BEAGLEBONE)		+= capemgr.o
diff --git a/drivers/misc/cape/beaglebone/capemgr.c b/drivers/misc/cape/beaglebone/capemgr.c
new file mode 100644
index 0000000..651f48d
--- /dev/null
+++ b/drivers/misc/cape/beaglebone/capemgr.c
@@ -0,0 +1,1835 @@
+/*
+ * TI Beaglebone cape controller
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@...oniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_i2c.h>
+#include <linux/of_device.h>
+#include <linux/of_fdt.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/firmware.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/memory.h>
+#include <linux/i2c.h>
+#include <linux/i2c/eeprom.h>
+#include <linux/kthread.h>
+
+/* extra command line overrides */
+static char *extra_override = NULL;
+module_param(extra_override, charp, 0444);
+
+struct bone_capemgr_info;
+
+struct slot_ee_attribute {
+	struct device_attribute devattr;
+	unsigned int field;
+	struct bone_cape_slot *slot;	/* this is filled when instantiated */
+};
+#define to_slot_ee_attribute(x) \
+	container_of((x), struct slot_ee_attribute, devattr)
+
+struct bbrd_ee_attribute {
+	struct device_attribute devattr;
+	unsigned int field;
+};
+#define to_bbrd_ee_attribute(x) \
+	container_of((x), struct bbrd_ee_attribute, devattr)
+
+struct bone_cape_slot {
+	struct list_head	node;
+	struct bone_capemgr_info *info;
+	int			slotno;
+	u32			eeprom_handle;
+	int			eeprom_addr;
+	struct i2c_client	*client;
+	struct memory_accessor	*macc;
+	unsigned int		probed : 1;
+	unsigned int		probe_failed : 1;
+	unsigned int		override : 1;
+	char			text_id[256];
+	char			signature[256];
+	/* quick access */
+	char			board_name[32+1];
+	char 			version[4+1];
+	char 			manufacturer[16+1];
+	char 			part_number[16+1];
+
+	/* attribute group */
+	char			*ee_attr_name;
+	int			ee_attrs_count;
+	struct slot_ee_attribute *ee_attrs;
+	struct attribute	**ee_attrs_tab;
+	struct attribute_group	attrgroup;
+
+	unsigned int		loading : 1;
+	unsigned int		loaded : 1;
+	char			*dtbo;
+	const struct firmware	*fw;
+	struct device_node	*overlay;
+	int			ovinfo_cnt;
+	struct of_overlay_info	*ovinfo;
+
+	/* loader thread */
+	struct task_struct	*loader_thread;
+};
+
+struct bone_capemap {
+	struct list_head node;
+	char *part_number;
+	struct device_node *map_node;
+};
+
+struct bone_baseboard {
+
+	/* from the matched boardmap node */
+	char			*compatible_name;
+
+	/* filled in by reading the eeprom */
+	char			signature[256];
+	char			text_id[64+1];
+
+	/* quick access */
+	char			board_name[8+1];
+	char 			revision[4+1];
+	char 			serial_number[12+1];
+
+	/* access to the eeprom */
+	u32			eeprom_handle;
+	int			eeprom_addr;
+	struct i2c_client	*client;
+	struct memory_accessor	*macc;
+	unsigned int		probed : 1;
+	unsigned int		probe_failed : 1;
+	unsigned int		override : 1;
+};
+
+struct bone_capemgr_info {
+	struct platform_device	*pdev;
+
+	atomic_t next_slot_nr;
+	struct list_head	slot_list;
+	struct mutex		slots_list_mutex;
+
+	int capemaps_nr;
+	struct list_head	capemap_list;
+	struct mutex		capemap_mutex;
+
+	/* baseboard EEPROM data */
+	struct bone_baseboard	baseboard;
+};
+
+static int bone_slot_fill_override(struct bone_cape_slot *slot,
+		struct device_node *node,
+		const char *part_number, const char *version);
+static struct bone_cape_slot *bone_capemgr_add_slot(
+		struct bone_capemgr_info *info, struct device_node *node,
+		const char *part_number, const char *version);
+static int bone_capemgr_load(struct bone_cape_slot *slot);
+static int bone_capemgr_unload(struct bone_cape_slot *slot);
+
+/* baseboard EEPROM field definition */
+#define BBRD_EE_FIELD_HEADER		0
+#define BBRD_EE_FIELD_BOARD_NAME	1
+#define BBRD_EE_FIELD_REVISION		2
+#define BBRD_EE_FIELD_SERIAL_NUMBER	3
+#define BBRD_EE_FIELD_CONFIG_OPTION	4
+#define BBRD_EE_FILED_RSVD1		5
+#define BBRD_EE_FILED_RSVD2		6
+#define BBRD_EE_FILED_RSVD3		7
+
+/* cape EEPROM field definitions */
+#define CAPE_EE_FIELD_HEADER		0
+#define CAPE_EE_FIELD_EEPROM_REV	1
+#define CAPE_EE_FIELD_BOARD_NAME	2
+#define CAPE_EE_FIELD_VERSION		3
+#define CAPE_EE_FIELD_MANUFACTURER	4
+#define CAPE_EE_FIELD_PART_NUMBER	5
+#define CAPE_EE_FIELD_NUMBER_OF_PINS	6
+#define CAPE_EE_FIELD_SERIAL_NUMBER	7
+#define CAPE_EE_FIELD_PIN_USAGE		8
+#define CAPE_EE_FIELD_VDD_3V3EXP	9
+#define CAPE_EE_FIELD_VDD_5V		10
+#define CAPE_EE_FIELD_SYS_5V		11
+#define CAPE_EE_FIELD_DC_SUPPLIED	12
+#define CAPE_EE_FIELD_FIELDS_NR		13
+
+#define EE_FIELD_MAKE_HEADER(p)	\
+	({ \
+		const u8 *_p = (p); \
+		(((u32)_p[0] << 24) | ((u32)_p[1] << 16) | \
+		( (u32)_p[2] <<  8) |  (u32)_p[3]      ); \
+	})
+
+#define EE_FIELD_HEADER_VALID	0xaa5533ee
+
+struct ee_field {
+	const char 	*name;
+	int 		start;
+	int		size;
+	unsigned int	ascii : 1;
+	unsigned int	strip_trailing_dots : 1;
+	const char	*override;
+};
+
+/* baseboard EEPROM definitions */
+static const struct ee_field bbrd_sig_fields[] = {
+	[BBRD_EE_FIELD_HEADER] = {
+		.name		= "header",
+		.start		= 0,
+		.size		= 4,
+		.ascii		= 0,
+		.override	= "\xaa\x55\x33\xee",	/* AA 55 33 EE */
+	},
+	[BBRD_EE_FIELD_BOARD_NAME] = {
+		.name		= "board-name",
+		.start		= 4,
+		.size		= 8,
+		.ascii		= 1,
+		.strip_trailing_dots = 1,
+		.override	= "Board Name",
+	},
+	[BBRD_EE_FIELD_REVISION] = {
+		.name		= "revision",
+		.start		= 12,
+		.size		= 4,
+		.ascii		= 1,
+		.override	= "00A0",
+	},
+	[BBRD_EE_FIELD_SERIAL_NUMBER] = {
+		.name		= "serial-number",
+		.start		= 16,
+		.size		= 12,
+		.ascii		= 1,
+		.override	= "0000000000",
+	},
+	[BBRD_EE_FIELD_CONFIG_OPTION] = {
+		.name		= "config-option",
+		.start		= 28,
+		.size		= 32,
+	},
+};
+
+/* cape EEPROM definitions */
+static const struct ee_field cape_sig_fields[] = {
+	[CAPE_EE_FIELD_HEADER] = {
+		.name		= "header",
+		.start		= 0,
+		.size		= 4,
+		.ascii		= 0,
+		.override	= "\xaa\x55\x33\xee",	/* AA 55 33 EE */
+	},
+	[CAPE_EE_FIELD_EEPROM_REV] = {
+		.name		= "eeprom-format-revision",
+		.start		= 4,
+		.size		= 2,
+		.ascii		= 1,
+		.override	= "A0",
+	},
+	[CAPE_EE_FIELD_BOARD_NAME] = {
+		.name		= "board-name",
+		.start		= 6,
+		.size		= 32,
+		.ascii		= 1,
+		.strip_trailing_dots = 1,
+		.override	= "Override Board Name",
+	},
+	[CAPE_EE_FIELD_VERSION] = {
+		.name		= "version",
+		.start		= 38,
+		.size		= 4,
+		.ascii		= 1,
+		.override	= "00A0",
+	},
+	[CAPE_EE_FIELD_MANUFACTURER] = {
+		.name		= "manufacturer",
+		.start		= 42,
+		.size		= 16,
+		.ascii		= 1,
+		.strip_trailing_dots = 1,
+		.override	= "Override Manuf",
+	},
+	[CAPE_EE_FIELD_PART_NUMBER] = {
+		.name		= "part-number",
+		.start		= 58,
+		.size		= 16,
+		.ascii		= 1,
+		.strip_trailing_dots = 1,
+		.override	= "Override Part#",
+	},
+	[CAPE_EE_FIELD_NUMBER_OF_PINS] = {
+		.name		= "number-of-pins",
+		.start		= 74,
+		.size		= 2,
+		.ascii		= 0,
+		.override	= NULL,
+	},
+	[CAPE_EE_FIELD_SERIAL_NUMBER] = {
+		.name		= "serial-number",
+		.start		= 76,
+		.size		= 12,
+		.ascii		= 1,
+		.override	= "0000000000",
+	},
+	[CAPE_EE_FIELD_PIN_USAGE] = {
+		.name		= "pin-usage",
+		.start		= 88,
+		.size		= 140,
+		.ascii		= 0,
+		.override	= NULL,
+	},
+	[CAPE_EE_FIELD_VDD_3V3EXP] = {
+		.name		= "vdd-3v3exp",
+		.start		= 228,
+		.size		= 2,
+		.ascii		= 0,
+		.override	= NULL,
+	},
+	[CAPE_EE_FIELD_VDD_5V] = {
+		.name		= "vdd-5v",
+		.start		= 230,
+		.size		= 2,
+		.ascii		= 0,
+		.override	= NULL,
+	},
+	[CAPE_EE_FIELD_SYS_5V] = {
+		.name		= "sys-5v",
+		.start		= 232,
+		.size		= 2,
+		.ascii		= 0,
+		.override	= NULL,
+	},
+	[CAPE_EE_FIELD_DC_SUPPLIED] = {
+		.name		= "dc-supplied",
+		.start		= 234,
+		.size		= 2,
+		.ascii		= 0,
+		.override	= NULL,
+	},
+};
+
+static char *ee_field_get(const struct ee_field *sig_field,
+		const void *data, int field, char *buf, int bufsz)
+{
+	int len;
+
+	/* enough space? */
+	if (bufsz < sig_field->size + sig_field->ascii)
+		return NULL;
+
+	memcpy(buf, (char *)data + sig_field->start, sig_field->size);
+
+	/* terminate ascii field */
+	if (sig_field->ascii)
+		buf[sig_field->size] = '\0';;
+
+	if (sig_field->strip_trailing_dots) {
+		len = strlen(buf);
+		while (len > 1 && buf[len - 1] == '.')
+			buf[--len] = '\0';
+	}
+
+	return buf;
+}
+
+char *bbrd_ee_field_get(const void *data,
+		int field, char *buf, int bufsz)
+{
+	if ((unsigned int)field >= ARRAY_SIZE(bbrd_sig_fields))
+		return NULL;
+
+	return ee_field_get(&bbrd_sig_fields[field], data, field, buf, bufsz);
+}
+
+char *cape_ee_field_get(const void *data,
+		int field, char *buf, int bufsz)
+{
+	if ((unsigned int)field >= ARRAY_SIZE(cape_sig_fields))
+		return NULL;
+
+	return ee_field_get(&cape_sig_fields[field], data, field, buf, bufsz);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id bone_capemgr_of_match[] = {
+	{
+		.compatible = "ti,bone-capemgr",
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, bone_capemgr_of_match);
+
+#endif
+
+static int bone_baseboard_scan(struct bone_baseboard *bbrd)
+{
+	struct bone_capemgr_info *info = container_of(bbrd,
+			struct bone_capemgr_info, baseboard);
+	struct memory_accessor *macc = bbrd->macc;
+	const u8 *p;
+	int i, r;
+
+	/* need to read EEPROM? */
+	if (bbrd->probed)
+		goto bbrd_fail_check;
+
+	bbrd->probed = 1;
+
+	if (!bbrd->override) {
+
+		if (macc == NULL || macc->read == NULL) {
+			dev_err(&info->pdev->dev,
+				"bone: No memory accessor for baseboard\n");
+			return -ENODEV;
+		}
+
+		for (i = 0; i < 10; i++) {
+
+			/* perform read */
+			r = macc->read(macc, bbrd->signature,
+					0, sizeof(bbrd->signature));
+
+			if (r == sizeof(bbrd->signature))
+				break;
+
+			dev_info(&info->pdev->dev,
+				"bone: scan failed (%d time)\n", i + 1);
+
+			msleep(500);
+		}
+
+		if (i >= 10) {
+			bbrd->probe_failed = 1;
+			return r >= 0 ? -EINVAL : r;
+		}
+
+	} else
+		dev_info(&info->pdev->dev,
+			"bone: Using override eeprom data for baseboard\n");
+
+	p = bbrd->signature;
+	if (EE_FIELD_MAKE_HEADER(p) != EE_FIELD_HEADER_VALID) {
+		dev_err(&info->pdev->dev, "bone: Invalid signature "
+			"'%08x' at baseboard\n",
+			EE_FIELD_MAKE_HEADER(p));
+		bbrd->probe_failed = 1;
+		return -ENODEV;
+	}
+
+	bbrd_ee_field_get(bbrd->signature,
+			BBRD_EE_FIELD_BOARD_NAME,
+			bbrd->board_name, sizeof(bbrd->board_name));
+	bbrd_ee_field_get(bbrd->signature,
+			BBRD_EE_FIELD_REVISION,
+			bbrd->revision, sizeof(bbrd->revision));
+	bbrd_ee_field_get(bbrd->signature,
+			BBRD_EE_FIELD_SERIAL_NUMBER,
+			bbrd->serial_number, sizeof(bbrd->serial_number));
+
+	/* board_name,version,manufacturer,part_number */
+	snprintf(bbrd->text_id, sizeof(bbrd->text_id) - 1,
+			"%s,%s,%s", bbrd->board_name, bbrd->revision,
+			bbrd->serial_number);
+
+	/* terminate always */
+	bbrd->text_id[sizeof(bbrd->text_id) - 1] = '\0';
+
+bbrd_fail_check:
+	/* bbrd has failed and we don't support hotpluging */
+	if (bbrd->probe_failed)
+		return -ENODEV;
+
+	return 0;
+}
+
+static int bone_slot_scan(struct bone_cape_slot *slot)
+{
+	struct bone_capemgr_info *info = slot->info;
+	struct memory_accessor *macc = slot->macc;
+	const u8 *p;
+	int r;
+
+	/* need to read EEPROM? */
+	if (slot->probed)
+		goto slot_fail_check;
+
+	slot->probed = 1;
+
+	if (!slot->override) {
+
+		if (macc == NULL || macc->read == NULL) {
+			dev_err(&info->pdev->dev,
+				"bone: No memory accessor for slot %d\n",
+				slot->slotno);
+			return -ENODEV;
+		}
+
+		/* perform read */
+		r = macc->read(macc, slot->signature,
+				0, sizeof(slot->signature));
+
+		if (r != sizeof(slot->signature)) {
+			slot->probe_failed = 1;
+			return r >= 0 ? -EINVAL : r;
+		}
+	} else
+		dev_info(&info->pdev->dev,
+			"bone: Using override eeprom data at slot %d\n",
+			slot->slotno);
+
+	p = slot->signature;
+	if (EE_FIELD_MAKE_HEADER(p) != EE_FIELD_HEADER_VALID) {
+		dev_err(&info->pdev->dev, "bone: Invalid signature "
+			"'%08x' at slot %d\n",
+			EE_FIELD_MAKE_HEADER(p),
+			slot->slotno);
+		slot->probe_failed = 1;
+		return -ENODEV;
+	}
+
+	cape_ee_field_get(slot->signature,
+			CAPE_EE_FIELD_BOARD_NAME,
+			slot->board_name, sizeof(slot->board_name));
+	cape_ee_field_get(slot->signature,
+			CAPE_EE_FIELD_VERSION,
+			slot->version, sizeof(slot->version));
+	cape_ee_field_get(slot->signature,
+			CAPE_EE_FIELD_MANUFACTURER,
+			slot->manufacturer, sizeof(slot->manufacturer));
+	cape_ee_field_get(slot->signature,
+			CAPE_EE_FIELD_PART_NUMBER,
+			slot->part_number, sizeof(slot->part_number));
+
+	/* board_name,version,manufacturer,part_number */
+	snprintf(slot->text_id, sizeof(slot->text_id) - 1,
+			"%s,%s,%s,%s", slot->board_name, slot->version,
+			slot->manufacturer, slot->part_number);
+
+	/* terminate always */
+	slot->text_id[sizeof(slot->text_id) - 1] = '\0';
+
+slot_fail_check:
+	/* slot has failed and we don't support hotpluging */
+	if (slot->probe_failed)
+		return -ENODEV;
+
+	return 0;
+}
+
+/* check an override slot node if it's compatible */
+static int bone_is_compatible_override(struct device_node *node,
+		const char *compatible_name)
+{
+	struct property *prop;
+	char *buf, *s, *e, *sn;
+	const char *part_number;
+	const char *version;
+	char *tmp_part_number, *tmp_version;
+	int found;
+
+	/* check if the slot is compatible with the board */
+	prop = of_find_property(node, "compatible", NULL);
+
+	/* no prop, it's something that's compatible with everything */
+	if (prop == NULL)
+		return 1;
+
+	/* check if it's directly compatible with the baseboard */
+	if (of_multi_prop_cmp(prop, compatible_name) == 0)
+		return 1;
+
+	/* final try, check if it's specified in the kernel command line */
+	if (extra_override == NULL)
+		return 0;
+
+	/* the compatible name should have kernel-command-line in it */
+	if (of_multi_prop_cmp(prop, "kernel-command-line") != 0)
+		return 0;
+
+	/* we must have at least the part-name */
+	if (of_property_read_string(node, "part-number",
+				&part_number) != 0)
+		return 0;
+
+	/* read the version (if it exists) */
+	if (of_property_read_string(node, "version", &version) != 0)
+		version = NULL;
+
+	/* copy the argument to work on it */
+	buf = kstrdup(extra_override, GFP_KERNEL);
+
+	/* no memory, too bad... */
+	if (buf == NULL)
+		return 0;
+
+	found = 0;
+	s = buf;
+	e = s + strlen(s);
+	while (s < e) {
+		/* find comma separator */
+		sn = strchr(s, ',');
+		if (sn != NULL)
+			*sn++ = '\0';
+		else
+			sn = e;
+		tmp_part_number = s;
+		tmp_version = strchr(tmp_part_number, ':');
+		if (tmp_version != NULL)
+			*tmp_version++ = '\0';
+		s = sn;
+
+		/* the part names must match */
+		if (strcmp(tmp_part_number, part_number) != 0)
+			continue;
+
+		pr_info("override: part-number='%s' version='%s' "
+				"tmp_version='%s'\n",
+				part_number,
+				version ? version : "N/A",
+				tmp_version ? tmp_version : "N/A");
+
+		/* if there's no version, match any */
+		if (version == NULL || tmp_version == NULL ||
+			strcmp(version, tmp_version) == 0) {
+			found = 1;
+			break;
+		}
+	}
+
+	kfree(buf);
+
+	return found;
+}
+
+static int bone_is_compatible_runtime_override(struct device_node *node,
+		const char *req_part_number, const char *req_version)
+{
+	struct property *prop;
+	const char *part_number;
+	const char *version;
+
+	/* only check overrides */
+	if (!of_property_read_bool(node, "ti,cape-override"))
+		return 0;
+
+	/* check if the slot is compatible with the board */
+	prop = of_find_property(node, "compatible", NULL);
+
+	/* no prop, it's something that's compatible with everything */
+	if (prop == NULL)
+		return 1;
+
+	/* the compatible name should have runtime in it */
+	if (of_multi_prop_cmp(prop, "runtime") != 0)
+		return 0;
+
+	/* we must have at least the part-name */
+	if (of_property_read_string(node, "part-number",
+				&part_number) != 0)
+		return 0;
+
+	/* read the version (if it exists) */
+	if (of_property_read_string(node, "version", &version) != 0)
+		version = NULL;
+
+	/* the part names must match */
+	if (strcmp(req_part_number, part_number) != 0)
+		return 0;
+
+	/* if any version is null, any version matches */
+	if (version == NULL || req_version == NULL)
+		return 1;
+
+	/* finally versions must match */
+	return strcmp(req_version, version) == 0;
+}
+
+
+static ssize_t slot_ee_attr_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct slot_ee_attribute *ee_attr = to_slot_ee_attribute(attr);
+	struct bone_cape_slot *slot = ee_attr->slot;
+	const struct ee_field *sig_field;
+	int i, len;
+	char *p, *s;
+	u16 val;
+
+	/* add newline for ascii fields */
+	sig_field = &cape_sig_fields[ee_attr->field];
+
+	len = sig_field->size + sig_field->ascii;
+	p = kmalloc(len, GFP_KERNEL);
+	if (p == NULL)
+		return -ENOMEM;
+
+	s = cape_ee_field_get(slot->signature, ee_attr->field, p, len);
+	if (s == NULL)
+		return -EINVAL;
+
+	/* add newline for ascii fields and return */
+	if (sig_field->ascii) {
+		len = sprintf(buf, "%s\n", s);
+		goto out;
+	}
+
+	/* case by case handling */
+	switch (ee_attr->field) {
+		case CAPE_EE_FIELD_HEADER:
+			len = sprintf(buf, "%02x %02x %02x %02x\n",
+					s[0], s[1], s[2], s[3]);
+			break;
+
+			/* 2 bytes */
+		case CAPE_EE_FIELD_NUMBER_OF_PINS:
+		case CAPE_EE_FIELD_VDD_3V3EXP:
+		case CAPE_EE_FIELD_VDD_5V:
+		case CAPE_EE_FIELD_SYS_5V:
+		case CAPE_EE_FIELD_DC_SUPPLIED:
+			/* the bone is LE */
+			val = s[0] & (s[1] << 8);
+			len = sprintf(buf, "%u\n", (unsigned int)val & 0xffff);
+			break;
+
+		case CAPE_EE_FIELD_PIN_USAGE:
+
+			len = 0;
+			for (i = 0; i < sig_field->size / 2; i++) {
+				/* the bone is LE */
+				val = s[0] & (s[1] << 8);
+				sprintf(buf, "%04x\n", val);
+				buf += 5;
+				len += 5;
+				s += 2;
+			}
+
+			break;
+
+		default:
+			*buf = '\0';
+			len = 0;
+			break;
+	}
+
+out:
+	kfree(p);
+
+	return len;
+}
+
+#define SLOT_EE_ATTR(_name, _field) \
+	{ \
+		.devattr = __ATTR(_name, 0440, slot_ee_attr_show, NULL), \
+		.field = CAPE_EE_FIELD_##_field , \
+		.slot = NULL, \
+	}
+
+static const struct slot_ee_attribute slot_ee_attrs[] = {
+	SLOT_EE_ATTR(header, HEADER),
+	SLOT_EE_ATTR(eeprom-format-revision, EEPROM_REV),
+	SLOT_EE_ATTR(board-name, BOARD_NAME),
+	SLOT_EE_ATTR(version, VERSION),
+	SLOT_EE_ATTR(manufacturer, MANUFACTURER),
+	SLOT_EE_ATTR(part-number, PART_NUMBER),
+	SLOT_EE_ATTR(number-of-pins, NUMBER_OF_PINS),
+	SLOT_EE_ATTR(serial-number, SERIAL_NUMBER),
+	SLOT_EE_ATTR(pin-usage, PIN_USAGE),
+	SLOT_EE_ATTR(vdd-3v3exp, VDD_3V3EXP),
+	SLOT_EE_ATTR(vdd-5v, VDD_5V),
+	SLOT_EE_ATTR(sys-5v, SYS_5V),
+	SLOT_EE_ATTR(dc-supplied, DC_SUPPLIED),
+};
+
+static int bone_cape_slot_sysfs_register(struct bone_cape_slot *slot)
+{
+	struct bone_capemgr_info *info = slot->info;
+	struct device *dev = &info->pdev->dev;
+	struct slot_ee_attribute *ee_attr;
+	struct attribute_group *attrgroup;
+	int i, err, sz;
+
+	slot->ee_attr_name = kasprintf(GFP_KERNEL, "slot-%d", slot->slotno);
+	if (slot->ee_attr_name == NULL) {
+		dev_err(dev, "slot #%d: Failed to allocate ee_attr_name\n",
+				slot->slotno);
+		err = -ENOMEM;
+		goto err_fail_no_ee_attr_name;
+	}
+
+	slot->ee_attrs_count = ARRAY_SIZE(slot_ee_attrs);
+
+	sz = slot->ee_attrs_count * sizeof(*slot->ee_attrs);
+	slot->ee_attrs = kmalloc(sz, GFP_KERNEL);
+	if (slot->ee_attrs == NULL) {
+		dev_err(dev, "slot #%d: Failed to allocate ee_attrs\n",
+				slot->slotno);
+		err = -ENOMEM;
+		goto err_fail_no_ee_attrs;
+	}
+
+	attrgroup = &slot->attrgroup;
+	memset(attrgroup, 0, sizeof(*attrgroup));
+	attrgroup->name = slot->ee_attr_name;
+
+	sz = sizeof(*slot->ee_attrs_tab) * (slot->ee_attrs_count + 1);
+	attrgroup->attrs = kmalloc(sz, GFP_KERNEL);
+	if (attrgroup->attrs == NULL) {
+		dev_err(dev, "slot #%d: Failed to allocate ee_attrs_tab\n",
+				slot->slotno);
+		err = -ENOMEM;
+		goto err_fail_no_ee_attrs_tab;
+	}
+	/* copy everything over */
+	memcpy(slot->ee_attrs, slot_ee_attrs, sizeof(slot_ee_attrs));
+
+	/* bind this attr to the slot */
+	for (i = 0; i < slot->ee_attrs_count; i++) {
+		ee_attr = &slot->ee_attrs[i];
+		ee_attr->slot = slot;
+		attrgroup->attrs[i] = &ee_attr->devattr.attr;
+	}
+	attrgroup->attrs[i] = NULL;
+
+	err = sysfs_create_group(&dev->kobj, attrgroup);
+	if (err != 0) {
+		dev_err(dev, "slot #%d: Failed to allocate ee_attrs_tab\n",
+				slot->slotno);
+		err = -ENOMEM;
+		goto err_fail_no_ee_attrs_group;
+	}
+
+	return 0;
+
+err_fail_no_ee_attrs_group:
+	kfree(slot->ee_attrs_tab);
+err_fail_no_ee_attrs_tab:
+	kfree(slot->ee_attrs);
+err_fail_no_ee_attrs:
+	kfree(slot->ee_attr_name);
+err_fail_no_ee_attr_name:
+	return err;
+}
+
+static void bone_cape_slot_sysfs_unregister(struct bone_cape_slot *slot)
+{
+	struct bone_capemgr_info *info = slot->info;
+	struct device *dev = &info->pdev->dev;
+
+	sysfs_remove_group(&dev->kobj, &slot->attrgroup);
+	kfree(slot->ee_attrs_tab);
+	kfree(slot->ee_attrs);
+	kfree(slot->ee_attr_name);
+}
+
+static ssize_t bbrd_ee_attr_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct bbrd_ee_attribute *ee_attr = to_bbrd_ee_attribute(attr);
+	struct platform_device *pdev = to_platform_device(dev);
+	struct bone_capemgr_info *info = platform_get_drvdata(pdev);
+	struct bone_baseboard *bbrd = &info->baseboard;
+	const struct ee_field *sig_field;
+	u16 val;
+	int i, len;
+	char *p, *s;
+
+	/* add newline for ascii fields */
+	sig_field = &bbrd_sig_fields[ee_attr->field];
+
+	len = sig_field->size + sig_field->ascii;
+	p = kmalloc(len, GFP_KERNEL);
+	if (p == NULL)
+		return -ENOMEM;
+
+	s = bbrd_ee_field_get(bbrd->signature, ee_attr->field, p, len);
+	if (s == NULL)
+		return -EINVAL;
+
+	/* add newline for ascii fields and return */
+	if (sig_field->ascii) {
+		len = sprintf(buf, "%s\n", s);
+		goto out;
+	}
+
+	/* case by case handling */
+	switch (ee_attr->field) {
+		case BBRD_EE_FIELD_HEADER:
+			len = sprintf(buf, "%02x %02x %02x %02x\n",
+					s[0], s[1], s[2], s[3]);
+			break;
+
+		case BBRD_EE_FIELD_CONFIG_OPTION:
+			len = 0;
+			for (i = 0; i < sig_field->size / 2; i++) {
+				/* the bone is LE */
+				val = s[0] & (s[1] << 8);
+				sprintf(buf, "%04x\n", val);
+				buf += 5;
+				len += 5;
+				s += 2;
+			}
+			break;
+
+		default:
+			*buf = '\0';
+			len = 0;
+			break;
+	}
+
+out:
+	kfree(p);
+
+	return len;
+}
+
+#define BBRD_EE_ATTR(_name, _field) \
+	{ \
+		.devattr = __ATTR(_name, 0440, bbrd_ee_attr_show, NULL), \
+		.field = BBRD_EE_FIELD_##_field , \
+	}
+
+static struct bbrd_ee_attribute bbrd_ee_attrs[] = {
+	BBRD_EE_ATTR(header, HEADER),
+	BBRD_EE_ATTR(board-name, BOARD_NAME),
+	BBRD_EE_ATTR(revision, REVISION),
+	BBRD_EE_ATTR(serial-number, SERIAL_NUMBER),
+	BBRD_EE_ATTR(config-option, CONFIG_OPTION),
+};
+
+static struct attribute *bbrd_attrs_flat[] = {
+	&bbrd_ee_attrs[BBRD_EE_FIELD_HEADER	 	].devattr.attr,
+	&bbrd_ee_attrs[BBRD_EE_FIELD_BOARD_NAME	 	].devattr.attr,
+	&bbrd_ee_attrs[BBRD_EE_FIELD_REVISION		].devattr.attr,
+	&bbrd_ee_attrs[BBRD_EE_FIELD_SERIAL_NUMBER	].devattr.attr,
+	&bbrd_ee_attrs[BBRD_EE_FIELD_CONFIG_OPTION	].devattr.attr,
+	NULL,
+};
+
+static const struct attribute_group bbrd_attr_group = {
+	.name	= "baseboard",
+	.attrs	= bbrd_attrs_flat,
+};
+
+static ssize_t slots_show(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct bone_capemgr_info *info = platform_get_drvdata(pdev);
+	struct bone_cape_slot *slot;
+	ssize_t len, sz;
+
+	mutex_lock(&info->slots_list_mutex);
+	sz = 0;
+	list_for_each_entry(slot, &info->slot_list, node) {
+
+		len = sprintf(buf, "%2d: %02x:%c%c%c%c%c %s\n",
+				slot->slotno,
+				(int)slot->client ?
+					slot->client->addr & 0x7f : 0xff,
+				slot->probed       ? 'P' : '-',
+				slot->probe_failed ? 'F' : '-',
+				slot->override     ? 'O' : '-',
+				slot->loading	   ? 'l' : '-',
+				slot->loaded	   ? 'L' : '-',
+				slot->text_id);
+
+		buf += len;
+		sz += len;
+	}
+	mutex_unlock(&info->slots_list_mutex);
+
+	return sz;
+}
+
+static ssize_t slots_store(struct device *dev, struct device_attribute *attr,
+		 const char *buf, size_t count)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct bone_capemgr_info *info = platform_get_drvdata(pdev);
+	struct bone_cape_slot *slot;
+	struct device_node *pnode, *node, *slots_node;
+	char *s, *part_number, *version;
+	int ret;
+	int slotno;
+
+	/* check for remove slot */
+	if (strlen(buf) > 0 && buf[0] == '-') {
+		slotno = simple_strtoul(buf + 1, NULL, 10);
+
+		/* now load each (take lock to be sure */
+		mutex_lock(&info->slots_list_mutex);
+		list_for_each_entry(slot, &info->slot_list, node) {
+			if (slotno == slot->slotno)
+				break;
+		}
+		mutex_unlock(&info->slots_list_mutex);
+
+		if (slot == NULL)
+			return -ENODEV;
+
+		bone_capemgr_unload(slot);
+
+		return strlen(buf);
+	}
+
+	part_number = kstrdup(buf, GFP_KERNEL);
+	if (part_number == NULL)
+		return -ENOMEM;
+
+	/* remove trailing spaces dots and newlines */
+	s = part_number + strlen(part_number);
+	while (s > part_number &&
+			(isspace(s[-1]) || s[-1] == '\n' || s[-1] == '.'))
+		*--s = '\0';
+
+	version = strchr(part_number, ':');
+	if (version != NULL)
+		*version++ = '\0';
+
+	dev_info(&pdev->dev, "part_number '%s', version '%s'\n",
+			part_number, version ? version : "N/A");
+
+	pnode = pdev->dev.of_node;
+	node = NULL;
+	slot = NULL;
+	ret = 0;
+
+	/* iterate over any slots */
+	slots_node = of_get_child_by_name(pnode, "slots");
+	if (slots_node != NULL) {
+		for_each_child_of_node(slots_node, node) {
+
+			/* check if the override is compatible */
+			if (!bone_is_compatible_runtime_override(node,
+						part_number, version))
+				continue;
+
+			slot = bone_capemgr_add_slot(info, node,
+					part_number, version);
+			if (IS_ERR(slot)) {
+				dev_err(&pdev->dev, "Failed to add slot #%d\n",
+					atomic_read(&info->next_slot_nr) - 1);
+				ret = PTR_ERR(slot);
+				slot = NULL;
+				goto err_fail;
+			}
+			break;
+		}
+		of_node_put(node);
+		of_node_put(slots_node);
+	}
+	slots_node = NULL;
+
+	/* no specific slot found, try immediate */
+	if (!slot)
+		slot = bone_capemgr_add_slot(info, NULL,
+				part_number, version);
+
+	if (IS_ERR_OR_NULL(slot)) {
+		dev_err(&pdev->dev, "Failed to add slot #%d\n",
+			atomic_read(&info->next_slot_nr) - 1);
+		ret = slot ? PTR_ERR(slot) : -ENODEV;
+		slot = NULL;
+		goto err_fail;
+	}
+
+	kfree(part_number);
+
+	ret = bone_capemgr_load(slot);
+
+	return ret == 0 ? strlen(buf) : ret;
+err_fail:
+	of_node_put(node);
+	of_node_put(slots_node);
+	kfree(part_number);
+	return ret;
+}
+
+static DEVICE_ATTR(slots, 0644, slots_show, slots_store);
+
+static int bone_capemgr_info_sysfs_register(struct bone_capemgr_info *info)
+{
+	struct device *dev = &info->pdev->dev;
+	int ret;
+
+	ret = device_create_file(dev, &dev_attr_slots);
+	if (ret != 0)
+		goto err_fail_no_slots;
+
+	ret = sysfs_create_group(&dev->kobj, &bbrd_attr_group);
+	if (ret != 0)
+		goto err_fail_no_bbrd_grp;
+
+	return 0;
+err_fail_no_bbrd_grp:
+	device_remove_file(dev, &dev_attr_slots);
+err_fail_no_slots:
+	return ret;
+}
+
+static void bone_capemgr_info_sysfs_unregister(struct bone_capemgr_info *info)
+{
+	struct device *dev = &info->pdev->dev;
+
+	sysfs_remove_group(&dev->kobj, &bbrd_attr_group);
+	device_remove_file(dev, &dev_attr_slots);
+}
+
+static int bone_capemgr_load(struct bone_cape_slot *slot)
+{
+	struct bone_capemgr_info *info = slot->info;
+	struct device *dev = &info->pdev->dev;
+	struct device_node *node;
+	struct property *prop;
+	const char *dtbo;
+	int found, err;
+	struct bone_capemap *capemap;
+
+	if (slot->probe_failed)
+		return -ENODEV;
+
+	if (slot->loaded)
+		return -EAGAIN;
+
+	mutex_lock(&info->capemap_mutex);
+	found = 0;
+	list_for_each_entry(capemap, &info->capemap_list, node) {
+		if (strcmp(capemap->part_number, slot->part_number) == 0) {
+			found = 1;
+			break;
+		}
+	}
+
+	/* found? */
+	if (found) {
+		if (capemap->map_node == NULL) {
+			mutex_unlock(&info->capemap_mutex);
+			/* need to match programatically; not supported yet */
+			dev_err(dev, "slot #%d: Failed to find capemap "
+					"for '%s'\n",
+					slot->slotno, slot->part_number);
+			return -ENODEV;
+		}
+
+		/* locate first match */
+		dtbo = NULL;
+		for_each_child_of_node(capemap->map_node, node) {
+
+			/* dtbo must exist */
+			if (of_property_read_string(node, "dtbo", &dtbo) != 0)
+				continue;
+
+			/* get version property (if any) */
+			prop = of_find_property(node, "version", NULL);
+
+			/* if no version node exists, we match */
+			if (prop == NULL)
+				break;
+
+			if (of_multi_prop_cmp(prop, slot->version) == 0)
+				break;
+		}
+
+		if (node == NULL) {
+			/* can't find dtbo version node? try the default */
+			if (of_property_read_string(capemap->map_node,
+						"dtbo", &dtbo) != 0) {
+				mutex_unlock(&info->capemap_mutex);
+				dev_err(dev, "slot #%d: Failed to find dtbo "
+						"for '%s'\n",
+						slot->slotno,
+						slot->part_number);
+				return -ENODEV;
+			}
+		}
+
+		slot->dtbo = kstrdup(dtbo, GFP_KERNEL);
+		of_node_put(node);	/* handles NULL */
+	} else {
+		dev_info(dev, "slot #%d: Requesting part number/version based "
+				"'%s-%s.dtbo\n",
+				slot->slotno,
+				slot->part_number, slot->version);
+
+		/* no specific capemap node; request the part number + .dtbo*/
+		slot->dtbo = kasprintf(GFP_KERNEL, "%s-%s.dtbo",
+				slot->part_number, slot->version);
+	}
+
+	if (slot->dtbo == NULL) {
+		mutex_unlock(&info->capemap_mutex);
+		dev_err(dev, "slot #%d: Failed to get dtbo '%s'\n",
+				slot->slotno, dtbo);
+		return -ENOMEM;
+	}
+
+	dev_info(dev, "slot #%d: Requesting firmware '%s' for board-name '%s'"
+			", version '%s'\n",
+			slot->slotno,
+			slot->dtbo, slot->board_name, slot->version);
+
+	err = request_firmware(&slot->fw, slot->dtbo, dev);
+	if (err != 0) {
+		dev_err(dev, "failed to load firmware '%s'\n", slot->dtbo);
+		mutex_unlock(&info->capemap_mutex);
+		goto err_fail_no_fw;
+	}
+
+	dev_info(dev, "slot #%d: dtbo '%s' loaded; converting to live tree\n",
+			slot->slotno, slot->dtbo);
+
+	mutex_unlock(&info->capemap_mutex);
+
+	of_fdt_unflatten_tree((void *)slot->fw->data, &slot->overlay);
+	if (slot->overlay == NULL) {
+		dev_err(dev, "slot #%d: Failed to unflatten\n",
+				slot->slotno);
+		err = -EINVAL;
+		goto err_fail;
+	}
+
+	/* mark it as detached */
+	of_node_set_flag(slot->overlay, OF_DETACHED);
+
+	/* perform resolution */
+	err = of_resolve(slot->overlay);
+	if (err != 0) {
+		dev_err(dev, "slot #%d: Failed to resolve tree\n",
+				slot->slotno);
+		goto err_fail;
+	}
+
+	/* now build an overlay info array */
+	err = of_build_overlay_info(slot->overlay,
+			&slot->ovinfo_cnt, &slot->ovinfo);
+	if (err != 0) {
+		dev_err(dev, "slot #%d: Failed to build overlay info\n",
+				slot->slotno);
+		goto err_fail;
+	}
+
+	dev_info(dev, "slot #%d: #%d overlays\n",
+			slot->slotno, slot->ovinfo_cnt);
+
+	err = of_overlay(slot->ovinfo_cnt, slot->ovinfo);
+	if (err != 0) {
+		if (err != 0) {
+			dev_err(dev, "slot #%d: Failed to overlay\n",
+					slot->slotno);
+			goto err_fail_overlay;
+		}
+	}
+
+	dev_info(dev, "slot #%d: Applied #%d overlays.\n",
+			slot->slotno, slot->ovinfo_cnt);
+
+	slot->loading = 0;
+	slot->loaded = 1;
+
+	return 0;
+
+err_fail_overlay:
+
+	of_free_overlay_info(slot->ovinfo_cnt, slot->ovinfo);
+	slot->ovinfo_cnt = 0;
+	slot->ovinfo = NULL;
+
+err_fail:
+
+	/* we can't free the overlay, because the unflatten method is a mess */
+	/* __of_free_tree(slot->overlay); */
+	slot->overlay = NULL;
+
+	release_firmware(slot->fw);
+	slot->fw = NULL;
+
+err_fail_no_fw:
+	return err;
+}
+
+static int bone_capemgr_unload(struct bone_cape_slot *slot)
+{
+	if (!slot->loaded || slot->ovinfo == NULL)
+		return -EINVAL;
+
+	of_overlay_revert(slot->ovinfo_cnt, slot->ovinfo);
+
+	slot->ovinfo_cnt = 0;
+	kfree(slot->ovinfo);
+
+	slot->loaded = 0;
+
+	return 0;
+
+}
+
+static int bone_slot_fill_override(struct bone_cape_slot *slot,
+		struct device_node *node,
+		const char *part_number, const char *version)
+{
+	const struct ee_field *sig_field;
+	struct property *prop;
+	int i, len, has_part_number;
+	char *p;
+
+	slot->probe_failed = 0;
+	slot->probed = 0;
+
+	/* zero out signature */
+	memset(slot->signature, 0,
+			sizeof(slot->signature));
+
+	/* first, fill in all with override defaults */
+	for (i = 0; i < ARRAY_SIZE(cape_sig_fields); i++) {
+
+		sig_field = &cape_sig_fields[i];
+
+		/* point to the entry */
+		p = slot->signature + sig_field->start;
+
+		if (sig_field->override)
+			memcpy(p, sig_field->override,
+					sig_field->size);
+		else
+			memset(p, 0, sig_field->size);
+	}
+
+	/* and now, fill any override data from the node */
+	has_part_number = 0;
+	if (node != NULL) {
+		for (i = 0; i < ARRAY_SIZE(cape_sig_fields); i++) {
+
+			sig_field = &cape_sig_fields[i];
+
+			/* find property with the same name (if any) */
+			prop = of_find_property(node, sig_field->name, NULL);
+			if (prop == NULL)
+				continue;
+
+			/* point to the entry */
+			p = slot->signature + sig_field->start;
+
+			/* copy and zero out any remainder */
+			len = prop->length;
+			if (prop->length > sig_field->size)
+				len = sig_field->size;
+			memcpy(p, prop->value, len);
+			if (len < sig_field->size)
+				memset(p + len, 0, sig_field->size - len);
+
+			/* remember if we got a part number which is required */
+			if (i == CAPE_EE_FIELD_PART_NUMBER && len > 0)
+				has_part_number = 1;
+		}
+	}
+
+	/* if a part_number is supplied use it */
+	if (part_number && (len = strlen(part_number)) > 0) {
+		sig_field = &cape_sig_fields[CAPE_EE_FIELD_PART_NUMBER];
+
+		/* point to the entry */
+		p = slot->signature + sig_field->start;
+
+		/* copy and zero out any remainder */
+		if (len > sig_field->size)
+			len = sig_field->size;
+		memcpy(p, part_number, len);
+		if (len < sig_field->size)
+			memset(p + len, 0, sig_field->size - len);
+
+		has_part_number = 1;
+	}
+
+	/* if a version is supplied use it */
+	if (version && (len = strlen(version)) > 0) {
+		sig_field = &cape_sig_fields[CAPE_EE_FIELD_VERSION];
+
+		/* point to the entry */
+		p = slot->signature + sig_field->start;
+
+		/* copy and zero out any remainder */
+		if (len > sig_field->size)
+			len = sig_field->size;
+		memcpy(p, version, len);
+		if (len < sig_field->size)
+			memset(p + len, 0, sig_field->size - len);
+	}
+
+	/* we must have a part number */
+	if (!has_part_number)
+		return -EINVAL;
+
+	slot->override = 1;
+
+	return 0;
+}
+
+static struct bone_cape_slot *
+bone_capemgr_add_slot(struct bone_capemgr_info *info, struct device_node *node,
+		const char *part_number, const char *version)
+{
+	struct device_node *eeprom_node;
+	struct bone_cape_slot *slot;
+	struct device *dev = &info->pdev->dev;
+	int slotno;
+	int ret;
+
+	eeprom_node = NULL;
+
+	slotno = atomic_inc_return(&info->next_slot_nr) - 1;
+
+	slot = devm_kzalloc(dev, sizeof(*slot), GFP_KERNEL);
+	if (slot == NULL) {
+		ret = -ENOMEM;
+		goto err_no_mem;
+	}
+	slot->info = info;
+	slot->slotno = slotno;
+
+	if (node && !of_property_read_bool(node, "ti,cape-override")) {
+		ret = of_property_read_u32(node, "eeprom",
+				&slot->eeprom_handle);
+		if (ret != 0) {
+			dev_err(dev, "slot #%d: failed to locate eeprom\n",
+					slotno);
+			goto err_no_eeprom;
+		}
+		eeprom_node = of_find_node_by_phandle(slot->eeprom_handle);
+		if (eeprom_node == NULL) {
+			dev_err(dev, "slot #%d: failed to find eeprom node\n",
+					slotno);
+			ret = -ENODEV;
+			goto err_no_eeprom_node;
+		}
+		slot->client = of_find_i2c_device_by_node(eeprom_node);
+		if (slot->client == NULL) {
+			dev_err(dev, "slot #%d: failed to find i2c client\n",
+					slotno);
+			ret = -ENODEV;
+			goto err_no_eeprom_client;
+		}
+		/* release ref to the node & get ref of the i2c client */
+		of_node_put(eeprom_node);
+		eeprom_node = NULL;
+		i2c_use_client(slot->client);
+
+		/* grab the memory accessor of the eeprom */
+		slot->macc = i2c_eeprom_get_memory_accessor(slot->client);
+		if (IS_ERR_OR_NULL(slot->macc)) {
+			dev_err(dev, "slot #%d: failed to get "
+					"memory accessor\n", slotno);
+			ret = slot->macc == NULL ? -ENODEV :
+				PTR_ERR(slot->macc);
+			slot->macc = NULL;
+			goto err_no_eeprom_macc;
+		}
+
+	} else {
+		if (node)
+			dev_info(dev, "slot #%d: specific override\n", slotno);
+		else
+			dev_info(dev, "slot #%d: generic override\n", slotno);
+
+		/* fill in everything with defaults first */
+		ret = bone_slot_fill_override(slot, node, part_number, version);
+		if (ret != 0) {
+			dev_err(dev, "slot #%d: override failed\n",
+					slotno);
+			goto err_no_eeprom;
+		}
+	}
+
+	ret = bone_slot_scan(slot);
+	if (ret != 0) {
+
+		if (!slot->probe_failed) {
+			dev_err(dev, "slot #%d: scan failed\n",
+					slotno);
+			goto err_bad_scan;
+		}
+
+		dev_err(dev, "slot #%d: No cape found\n",
+				slotno);
+		/* but all is fine */
+	} else {
+		dev_info(dev, "slot #%d: '%s'\n",
+				slotno, slot->text_id);
+
+		ret = bone_cape_slot_sysfs_register(slot);
+		if (ret != 0) {
+			dev_err(dev, "slot #%d: sysfs register failed\n",
+					slotno);
+			goto err_no_sysfs;
+		}
+
+	}
+
+	/* add to the slot list */
+	mutex_lock(&info->slots_list_mutex);
+	list_add_tail(&slot->node, &info->slot_list);
+	mutex_unlock(&info->slots_list_mutex);
+
+	return slot;
+
+err_no_sysfs:
+err_bad_scan:
+err_no_eeprom_macc:
+	i2c_release_client(slot->client);
+err_no_eeprom_client:
+	of_node_put(eeprom_node);	/* handles NULL */
+err_no_eeprom_node:
+	/* nothing */
+err_no_eeprom:
+	devm_kfree(dev, slot);
+
+err_no_mem:
+	return ERR_PTR(ret);
+}
+
+static int bone_capemgr_loader(void *data)
+{
+	struct bone_cape_slot *slot = data;
+
+	return bone_capemgr_load(slot);
+}
+
+static int __devinit
+bone_capemgr_probe(struct platform_device *pdev)
+{
+	struct bone_capemgr_info *info;
+	struct bone_baseboard *bbrd;
+	struct bone_cape_slot *slot;
+	struct device_node *pnode = pdev->dev.of_node;
+	struct device_node *baseboardmaps_node;
+	struct device_node *slots_node, *capemaps_node, *node;
+	struct device_node *eeprom_node;
+	const char *part_number;
+	const char *board_name;
+	const char *compatible_name;
+	struct bone_capemap *capemap;
+	int ret, len;
+
+	/* we don't use platform_data at all; we require OF */
+	if (pnode == NULL)
+		return -ENOTSUPP;
+
+	info = devm_kzalloc(&pdev->dev,
+			sizeof(struct bone_capemgr_info), GFP_KERNEL);
+	if (!info) {
+		dev_err(&pdev->dev, "Failed to allocate device structure\n");
+		return -ENOMEM;
+	}
+
+	info->pdev = pdev;
+	platform_set_drvdata(pdev, info);
+
+	atomic_set(&info->next_slot_nr, 0);
+	INIT_LIST_HEAD(&info->slot_list);
+	mutex_init(&info->slots_list_mutex);
+
+	INIT_LIST_HEAD(&info->capemap_list);
+	mutex_init(&info->capemap_mutex);
+
+	baseboardmaps_node = NULL;
+	capemaps_node = NULL;
+
+	/* find the baseboard */
+	bbrd = &info->baseboard;
+
+	baseboardmaps_node = of_get_child_by_name(pnode, "baseboardmaps");
+	if (baseboardmaps_node == NULL) {
+		dev_err(&pdev->dev, "Failed to get baseboardmaps node");
+		ret = -ENODEV;
+		goto err_exit;
+	}
+
+	/* get eeprom of the baseboard */
+	ret = of_property_read_u32(pnode, "eeprom",
+			&bbrd->eeprom_handle);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to locate baseboard eeprom\n");
+		goto err_exit;
+	}
+	eeprom_node = of_find_node_by_phandle(bbrd->eeprom_handle);
+	if (eeprom_node == NULL) {
+		dev_err(&pdev->dev, "Failed to find baseboard eeprom node\n");
+		ret = -ENODEV;
+		goto err_exit;
+	}
+	bbrd->client = of_find_i2c_device_by_node(eeprom_node);
+	of_node_put(eeprom_node);
+	eeprom_node = NULL;
+	if (bbrd->client == NULL) {
+		dev_err(&pdev->dev, "Failed to find baseboard i2c client\n");
+		ret = -ENODEV;
+		goto err_exit;
+	}
+
+	/* release ref to the node & get ref of the i2c client */
+	i2c_use_client(bbrd->client);
+
+	/* grab the memory accessor of the eeprom */
+	bbrd->macc = i2c_eeprom_get_memory_accessor(bbrd->client);
+	if (IS_ERR_OR_NULL(bbrd->macc)) {
+		dev_err(&pdev->dev, "Failed to get "
+				"baseboard memory accessor\n");
+		ret = bbrd->macc == NULL ? -ENODEV :
+			PTR_ERR(bbrd->macc);
+		bbrd->macc = NULL;
+		goto err_exit;
+	}
+
+	ret = bone_baseboard_scan(bbrd);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to scan baseboard eeprom\n");
+		goto err_exit;
+	}
+
+	dev_info(&pdev->dev, "Baseboard: '%s'\n", bbrd->text_id);
+
+	board_name = NULL;
+	compatible_name = NULL;
+	for_each_child_of_node(baseboardmaps_node, node) {
+		/* there must be board-name */
+		if (of_property_read_string(node, "board-name",
+					&board_name) != 0 ||
+		    of_property_read_string(node, "compatible-name",
+					&compatible_name) != 0)
+			continue;
+
+		if (strcmp(bbrd->board_name, board_name) == 0)
+			break;
+	}
+	of_node_put(baseboardmaps_node);
+	baseboardmaps_node = NULL;
+
+	if (node == NULL) {
+		dev_err(&pdev->dev, "Failed to find compatible map for %s\n",
+				bbrd->board_name);
+		ret = -ENODEV;
+		goto err_exit;
+	}
+	bbrd->compatible_name = kstrdup(compatible_name, GFP_KERNEL);
+	if (bbrd->compatible_name == NULL) {
+		dev_err(&pdev->dev, "Failed to allocate compatible name\n");
+		ret = -ENOMEM;
+		goto err_exit;
+	}
+	of_node_put(node);
+
+	dev_info(&pdev->dev, "compatible-baseboard=%s\n",
+			bbrd->compatible_name);
+
+	/* iterate over any capemaps */
+	capemaps_node = of_get_child_by_name(pnode, "capemaps");
+	if (capemaps_node != NULL) {
+
+		for_each_child_of_node(capemaps_node, node) {
+
+			/* there must be part-number */
+			if (of_property_read_string(node, "part-number",
+						&part_number) != 0)
+				continue;
+
+			len = sizeof(*capemap) + strlen(part_number) + 1;
+			capemap = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+			if (capemap == NULL) {
+				dev_err(&pdev->dev, "Failed to allocate "
+						"capemap\n");
+				ret = -ENOMEM;
+				goto err_exit;
+			}
+			capemap->part_number = (char *)(capemap + 1);
+			capemap->map_node = of_node_get(node);
+			strcpy(capemap->part_number, part_number);
+
+			/* add to the slot list */
+			mutex_lock(&info->capemap_mutex);
+			list_add_tail(&capemap->node, &info->capemap_list);
+			info->capemaps_nr++;
+			mutex_unlock(&info->capemap_mutex);
+		}
+		of_node_put(capemaps_node);
+		capemaps_node = NULL;
+	}
+
+	/* iterate over any slots */
+	slots_node = of_get_child_by_name(pnode, "slots");
+	if (slots_node != NULL) {
+		for_each_child_of_node(slots_node, node) {
+
+			/* check if the override is compatible */
+			if (!bone_is_compatible_override(node,
+						bbrd->compatible_name))
+				continue;
+
+			slot = bone_capemgr_add_slot(info, node,
+					NULL, NULL);
+			if (IS_ERR(slot)) {
+				dev_err(&pdev->dev, "Failed to add slot #%d\n",
+					atomic_read(&info->next_slot_nr));
+				ret = PTR_ERR(slot);
+				goto err_exit;
+			}
+		}
+		of_node_put(slots_node);
+	}
+	slots_node = NULL;
+
+	pm_runtime_enable(&pdev->dev);
+	ret = pm_runtime_get_sync(&pdev->dev);
+	if (IS_ERR_VALUE(ret)) {
+		dev_err(&pdev->dev, "Failed to pm_runtime_get_sync()\n");
+		goto err_exit;
+	}
+
+	pm_runtime_put(&pdev->dev);
+
+	bone_capemgr_info_sysfs_register(info);
+
+	/* now load each (take lock to be sure */
+	mutex_lock(&info->slots_list_mutex);
+	list_for_each_entry(slot, &info->slot_list, node) {
+		if (!slot->probe_failed && !slot->loaded) {
+			slot->loading = 1;
+			slot->loader_thread = kthread_run(bone_capemgr_loader,
+					slot, "capemgr-loader-%d",
+					slot->slotno);
+			if (IS_ERR(slot->loader_thread)) {
+				dev_warn(&pdev->dev, "slot #%d: Failed to "
+						"start loader\n", slot->slotno);
+				slot->loader_thread = NULL;
+			}
+		}
+	}
+
+	mutex_unlock(&info->slots_list_mutex);
+
+
+	dev_info(&pdev->dev, "initialized OK.\n");
+
+	return 0;
+
+err_exit:
+	of_node_put(baseboardmaps_node);
+	of_node_put(capemaps_node);
+	platform_set_drvdata(pdev, NULL);
+	devm_kfree(&pdev->dev, info);
+
+	return ret;
+}
+
+static int __devexit bone_capemgr_remove(struct platform_device *pdev)
+{
+	struct bone_capemgr_info *info = platform_get_drvdata(pdev);
+	struct bone_cape_slot *slot, *slotn;
+	int ret;
+
+	mutex_lock(&info->slots_list_mutex);
+	list_for_each_entry_safe(slot, slotn, &info->slot_list, node) {
+
+		/* unload just in case */
+		bone_capemgr_unload(slot);
+
+		/* if probed OK, remove the sysfs nodes */
+		if (slot->probed && !slot->probe_failed)
+			bone_cape_slot_sysfs_unregister(slot);
+
+		/* remove it from the list */
+		list_del(&slot->node);
+
+	}
+	mutex_unlock(&info->slots_list_mutex);
+
+	bone_capemgr_info_sysfs_unregister(info);
+
+	platform_set_drvdata(pdev, NULL);
+
+	ret = pm_runtime_get_sync(&pdev->dev);
+	if (IS_ERR_VALUE(ret))
+		return ret;
+
+	pm_runtime_put(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	devm_kfree(&pdev->dev, info);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+#ifdef CONFIG_PM_RUNTIME
+static int bone_capemgr_runtime_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct bone_capemgr_info *_dev = platform_get_drvdata(pdev);
+
+	(void)_dev;
+	return 0;
+}
+
+static int bone_capemgr_runtime_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct bone_capemgr_info *_dev = platform_get_drvdata(pdev);
+
+	(void)_dev;
+	return 0;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
+static struct dev_pm_ops bone_capemgr_pm_ops = {
+	SET_RUNTIME_PM_OPS(bone_capemgr_runtime_suspend,
+			   bone_capemgr_runtime_resume, NULL)
+};
+#define BONE_CAPEMGR_PM_OPS (&bone_capemgr_pm_ops)
+#else
+#define BONE_CAPEMGR_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver bone_capemgr_driver = {
+	.probe		= bone_capemgr_probe,
+	.remove		= __devexit_p(bone_capemgr_remove),
+	.driver		= {
+		.name	= "bone-capemgr",
+		.owner	= THIS_MODULE,
+		.pm	= BONE_CAPEMGR_PM_OPS,
+		.of_match_table = of_match_ptr(bone_capemgr_of_match),
+	},
+};
+
+module_platform_driver(bone_capemgr_driver);
+
+MODULE_AUTHOR("Pantelis Antoniou");
+MODULE_DESCRIPTION("Beaglebone cape manager");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bone_capemgr");
-- 
1.7.12

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ