A cape loader based on DT overlays and DT objects.

Beaglebone cape manager implementation.

Signed-off-by: Pantelis Antoniou <pa...@antoniou-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 <pa...@antoniou-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-omap" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to