[PATCH 1/5] capemgr: Beaglebone DT overlay based cape manager
Pantelis Antoniou
panto at antoniou-consulting.com
Tue Jan 8 05:51:02 EST 2013
A cape loader based on DT overlays and DT objects.
Beaglebone cape manager implementation.
Signed-off-by: Pantelis Antoniou <panto at 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 <panto at 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
More information about the devicetree-discuss
mailing list