[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1431503985-31853-2-git-send-email-pantelis.antoniou@konsulko.com>
Date: Wed, 13 May 2015 10:59:41 +0300
From: Pantelis Antoniou <pantelis.antoniou@...sulko.com>
To: Greg Kroah-Hartman <gregkh@...uxfoundation.org>
Cc: Matt Porter <mporter@...sulko.com>,
Koen Kooi <koen@...inion.thruhere.net>,
Robert Nelson <robertcnelson@...il.com>,
Rob Herring <robherring2@...il.com>,
Grant Likely <grant.likely@...retlab.ca>,
Jonathan Corbet <corbet@....net>,
Srinivas Kandagatla <srinivas.kandagatla@...aro.org>,
Guenter Roeck <linux@...ck-us.net>,
Geert Uytterhoeven <geert+renesas@...der.be>,
BenoƮt Cousso <bcousson@...libre.com>,
linux-api@...r.kernel.org, linux-doc@...r.kernel.org,
devicetree@...r.kernel.org, linux-kernel@...r.kernel.org,
Pantelis Antoniou <pantelis.antoniou@...sulko.com>,
Pantelis Antoniou <panto@...oniou-consulting.com>
Subject: [PATCH 1/5] misc: Beaglebone capemanager
A cape loader based on DT overlays and DT objects.
This is the beaglebone cape manager which allows capes to be automatically
probed and instantiated via means of a device tree overlay deduced from
the part-number and version contained on the cape's EEPROM.
The reference manual contains information about the specification
and the contents of the EEPROM.
http://beagleboard.org/static/beaglebone/latest/Docs/Hardware/BONE_SRM.pdf
Documentation about the workings of the cape manager is located
in Documentation/misc-devices/bone_capemgr.txt
This driver is using the EEPROM framework interface to retrieve
the data stored on the baseboard and cape EEPROMs.
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@...sulko.com>
---
drivers/misc/Kconfig | 10 +
drivers/misc/Makefile | 1 +
drivers/misc/bone_capemgr.c | 1926 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1937 insertions(+)
create mode 100644 drivers/misc/bone_capemgr.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 006242c..f9e09e1 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -515,6 +515,16 @@ config VEXPRESS_SYSCFG
bus. System Configuration interface is one of the possible means
of generating transactions on this bus.
+config BONE_CAPEMGR
+ tristate "Beaglebone cape manager"
+ depends on ARCH_OMAP2PLUS && OF
+ select EEPROM
+ select OF_OVERLAY
+ default n
+ help
+ Say Y here to include support for automatic loading of
+ beaglebone capes.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7d5c4cd..659b78b 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -56,3 +56,4 @@ obj-$(CONFIG_GENWQE) += genwqe/
obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
obj-$(CONFIG_CXL_BASE) += cxl/
+obj-$(CONFIG_BONE_CAPEMGR) += bone_capemgr.o
diff --git a/drivers/misc/bone_capemgr.c b/drivers/misc/bone_capemgr.c
new file mode 100644
index 0000000..423719c
--- /dev/null
+++ b/drivers/misc/bone_capemgr.c
@@ -0,0 +1,1926 @@
+/*
+ * TI Beaglebone cape manager
+ *
+ * Copyright (C) 2012 Texas Instruments Inc.
+ * Copyright (C) 2012-2015 Konsulko Group.
+ * Author: Pantelis Antoniou <pantelis.antoniou@...sulko.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.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_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/kthread.h>
+#include <linux/wait.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/eeprom-consumer.h>
+
+/* disabled capes */
+static char *disable_partno;
+module_param(disable_partno, charp, 0444);
+MODULE_PARM_DESC(disable_partno,
+ "Comma delimited list of PART-NUMBER[:REV] of disabled capes");
+
+/* enable capes */
+static char *enable_partno;
+module_param(enable_partno, charp, 0444);
+MODULE_PARM_DESC(enable_partno,
+ "Comma delimited list of PART-NUMBER[:REV] of enabled capes");
+
+/* delay to scan on boot until rootfs appears */
+static int boot_scan_period = 1000;
+module_param(boot_scan_period, int, 0444);
+MODULE_PARM_DESC(boot_scan_period,
+ "boot scan period until rootfs firmware is available");
+
+struct 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 capemgr_info *info;
+ int slotno;
+ u32 eeprom_handle;
+ int eeprom_addr;
+ struct eeprom_cell *eeprom_cell;
+
+ 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;
+
+ /* state flags */
+ unsigned int probed : 1;
+ unsigned int probe_failed : 1;
+ unsigned int override : 1;
+ unsigned int loading : 1;
+ unsigned int loaded : 1;
+ unsigned int retry_loading : 1;
+ unsigned int disabled : 1;
+
+ char *dtbo;
+ const struct firmware *fw;
+ struct device_node *overlay;
+ int overlay_id;
+
+ /* loader thread */
+ struct task_struct *loader_thread;
+
+ /* load priority */
+ int priority;
+};
+
+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 eeprom_cell *eeprom_cell;
+};
+
+struct capemgr_info {
+ struct platform_device *pdev;
+
+ atomic_t next_slot_nr;
+ struct list_head slot_list;
+ struct mutex slots_list_mutex;
+
+ /* baseboard EEPROM data */
+ struct bone_baseboard baseboard;
+
+ /* wait queue for keeping the priorities straight */
+ wait_queue_head_t load_wq;
+};
+
+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 *capemgr_add_slot(
+ struct capemgr_info *info, struct device_node *node,
+ const char *part_number, const char *version, int prio);
+static int capemgr_remove_slot_no_lock(struct bone_cape_slot *slot);
+static int capemgr_remove_slot(struct bone_cape_slot *slot);
+static int capemgr_load_slot(struct bone_cape_slot *slot);
+static int capemgr_unload_slot(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 capemgr_of_match[] = {
+ {
+ .compatible = "ti,bone-capemgr",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, capemgr_of_match);
+
+#endif
+
+static int bone_baseboard_scan(struct bone_baseboard *bbrd)
+{
+ struct capemgr_info *info = container_of(bbrd,
+ struct capemgr_info, baseboard);
+ const u8 *p;
+ int ret;
+ size_t len;
+
+ p = eeprom_cell_read(bbrd->eeprom_cell, &len);
+ if (IS_ERR(p)) {
+ ret = PTR_ERR(p);
+ dev_err(&info->pdev->dev,
+ "Cannot read cell (ret=%d)\n", ret);
+ return ret;
+ }
+ if (len < sizeof(bbrd->signature)) {
+ dev_info(&info->pdev->dev,
+ "Short read %d (should be >= %d bytes)\n",
+ len, sizeof(bbrd->signature));
+ return -EINVAL;
+ }
+ memcpy(bbrd->signature, p, sizeof(bbrd->signature));
+
+ p = bbrd->signature;
+ if (EE_FIELD_MAKE_HEADER(p) != EE_FIELD_HEADER_VALID) {
+ dev_err(&info->pdev->dev, "Invalid board signature '%08x'\n",
+ EE_FIELD_MAKE_HEADER(p));
+ 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';
+
+ return 0;
+}
+
+static int bone_slot_scan(struct bone_cape_slot *slot)
+{
+ struct capemgr_info *info = slot->info;
+ const u8 *p;
+ int r;
+ ssize_t len;
+
+ /* need to read EEPROM? */
+ if (slot->probed)
+ goto slot_fail_check;
+
+ slot->probed = 1;
+
+ if (!slot->override) {
+
+ p = eeprom_cell_read(slot->eeprom_cell, &len);
+ if (IS_ERR(p)) {
+ r = PTR_ERR(p);
+ slot->probe_failed = 1;
+
+ /* timeout is normal when no cape is present */
+ if (r != -ETIMEDOUT)
+ dev_err(&info->pdev->dev,
+ "Cannot read cell (ret=%d)\n", r);
+ return r;
+ }
+ if (len < sizeof(slot->signature)) {
+ dev_info(&info->pdev->dev,
+ "Short read %d (should be >= %d bytes)\n",
+ len, sizeof(slot->signature));
+ return -EINVAL;
+ }
+ memcpy(slot->signature, p, sizeof(slot->signature));
+
+ } else
+ dev_info(&info->pdev->dev,
+ "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,
+ "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;
+}
+
+/* return 0 if not matched,, 1 if matched */
+static int bone_match_cape(const char *match,
+ const char *part_number, const char *version)
+{
+ char *tmp_part_number, *tmp_version;
+ char *buf, *s, *e, *sn;
+ int found;
+
+ if (match == NULL || part_number == NULL)
+ return 0;
+
+ /* copy the argument to work on it */
+ buf = kstrdup(match, 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;
+
+ /* 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;
+}
+
+/* helper method */
+static int of_multi_prop_cmp(const struct property *prop, const char *value)
+{
+ const char *cp;
+ int cplen, vlen, l;
+
+ /* check if it's directly compatible */
+ cp = prop->value;
+ cplen = prop->length;
+ vlen = strlen(value);
+
+ while (cplen > 0) {
+ /* compatible? */
+ if (of_compat_cmp(cp, value, vlen) == 0)
+ return 0;
+ l = strlen(cp) + 1;
+ cp += l;
+ cplen -= l;
+ }
+ return -1;
+}
+
+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, S_IRUGO, 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 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;
+
+ /* make lockdep happy */
+ for (i = 0; i < slot->ee_attrs_count; i++) {
+ ee_attr = &slot->ee_attrs[i];
+ sysfs_attr_init(&ee_attr->devattr.attr);
+ }
+
+ 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 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 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);
+static ssize_t slots_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static DEVICE_ATTR(slots, 0644, slots_show, slots_store);
+
+static struct attribute *root_attrs_flat[] = {
+ &dev_attr_slots.attr,
+ NULL,
+};
+
+static const struct attribute_group root_attr_group = {
+ .attrs = root_attrs_flat,
+};
+
+static const struct attribute_group *attr_groups[] = {
+ &root_attr_group,
+ &bbrd_attr_group,
+ NULL,
+};
+
+static ssize_t slots_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct 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: %c%c%c%c%c%c %3d %s\n",
+ slot->slotno,
+ slot->probed ? 'P' : '-',
+ slot->probe_failed ? 'F' : '-',
+ slot->override ? 'O' : '-',
+ slot->loading ? 'l' : '-',
+ slot->loaded ? 'L' : '-',
+ slot->disabled ? 'D' : '-',
+ slot->overlay_id, 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 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] == '-') {
+ ret = kstrtoint(buf + 1, 10, &slotno);
+ if (ret != 0)
+ return ret;
+
+ /* 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)
+ goto found;
+ }
+
+ mutex_unlock(&info->slots_list_mutex);
+ return -ENODEV;
+found:
+ /* the hardware slots just get unloaded */
+ if (!slot->override) {
+ ret = capemgr_unload_slot(slot);
+ if (ret == 0)
+ dev_info(&pdev->dev,
+ "Unloaded slot #%d\n", slotno);
+ else
+ dev_err(&pdev->dev,
+ "Failed to unload slot #%d\n", slotno);
+ } else {
+ ret = capemgr_remove_slot_no_lock(slot);
+ if (ret == 0)
+ dev_info(&pdev->dev,
+ "Removed slot #%d\n", slotno);
+ else
+ dev_err(&pdev->dev,
+ "Failed to remove slot #%d\n", slotno);
+ }
+ mutex_unlock(&info->slots_list_mutex);
+
+ return ret == 0 ? strlen(buf) : ret;
+ }
+
+ 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;
+
+ /* no specific slot found, try immediate */
+ slot = capemgr_add_slot(info, NULL, part_number, version, 0);
+
+ 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 = capemgr_load_slot(slot);
+ if (ret != 0)
+ capemgr_remove_slot(slot);
+
+ return ret == 0 ? strlen(buf) : ret;
+err_fail:
+ of_node_put(node);
+ of_node_put(slots_node);
+ kfree(part_number);
+ return ret;
+}
+
+/* verify the overlay */
+static int capemgr_verify_overlay(struct bone_cape_slot *slot)
+{
+ struct capemgr_info *info = slot->info;
+ struct device *dev = &info->pdev->dev;
+ struct bone_baseboard *bbrd = &info->baseboard;
+ struct device_node *node = slot->overlay;
+ struct property *prop;
+ struct bone_cape_slot *slotn;
+ int err, counta, countb, i, j;
+ const char *ra, *rb;
+
+ /* validate */
+ if (node == NULL) {
+ dev_err(dev, "slot #%d: No overlay for '%s'\n",
+ slot->slotno, slot->part_number);
+ return -EINVAL;
+ }
+
+ /* check if the slot is compatible with the board */
+ prop = of_find_property(node, "compatible", NULL);
+
+ /* no compatible property? */
+ if (prop == NULL) {
+ dev_err(dev, "slot #%d: No compatible property for '%s'\n",
+ slot->slotno, slot->part_number);
+ return -EINVAL;
+ }
+
+ /* verify that the cape is baseboard compatible */
+ if (of_multi_prop_cmp(prop, bbrd->compatible_name) != 0) {
+ dev_err(dev, "slot #%d: Incompatible with baseboard for '%s'\n",
+ slot->slotno, slot->part_number);
+ return -EINVAL;
+ }
+
+ /* count the strings */
+ counta = of_property_count_strings(node, "exclusive-use");
+ /* no valid property, or no resources; no matter, it's OK */
+ if (counta <= 0)
+ return 0;
+
+ /* and now check if there's a resource conflict */
+ err = 0;
+ mutex_lock(&info->slots_list_mutex);
+ for (i = 0; i < counta; i++) {
+
+ ra = NULL;
+ err = of_property_read_string_index(node, "exclusive-use",
+ i, &ra);
+ if (err != 0) {
+ dev_err(dev, "slot #%d: Could not read string #%d\n",
+ slot->slotno, i);
+ break;
+ }
+
+ list_for_each_entry(slotn, &info->slot_list, node) {
+
+ /* don't check against self */
+ if (slot == slotn)
+ continue;
+
+ /* only check against loaded or loading slots */
+ if (!slotn->loaded && !slotn->loading)
+ continue;
+
+ countb = of_property_count_strings(slotn->overlay,
+ "exclusive-use");
+ /* no valid property, or resources; it's OK */
+ if (countb <= 0)
+ continue;
+
+
+ for (j = 0; j < countb; j++) {
+
+ /* count the resources */
+ rb = NULL;
+ err = of_property_read_string_index(
+ slotn->overlay, "exclusive-use",
+ j, &rb);
+ if (err != 0) {
+ /* error, but we don't care */
+ err = 0;
+ break;
+ }
+
+ /* ignore case; just in case ;) */
+ if (strcasecmp(ra, rb) == 0) {
+
+ /* resource conflict */
+ err = -EEXIST;
+ dev_err(dev,
+ "slot #%d: %s conflict %s (#%d:%s)\n",
+ slot->slotno,
+ slot->part_number, ra,
+ slotn->slotno,
+ slotn->part_number);
+ goto out;
+ }
+ }
+ }
+ }
+out:
+ mutex_unlock(&info->slots_list_mutex);
+
+ return err;
+}
+
+static int capemgr_load_slot(struct bone_cape_slot *slot)
+{
+ struct capemgr_info *info = slot->info;
+ struct device *dev = &info->pdev->dev;
+ const char *dtbo;
+ int err;
+
+ if (slot->probe_failed) {
+ dev_err(dev, "slot #%d: probe failed for '%s'\n",
+ slot->slotno, slot->part_number);
+ return -ENODEV;
+ }
+
+ if (slot->loaded) {
+ dev_err(dev, "slot #%d: already loaded for '%s'\n",
+ slot->slotno, slot->part_number);
+ return -EAGAIN;
+ }
+
+ /* make sure we don't leak this on repeated calls */
+ kfree(slot->dtbo);
+ slot->dtbo = NULL;
+
+ dev_dbg(dev, "slot #%d: Requesting part number/version based '%s-%s.dtbo\n",
+ slot->slotno, slot->part_number, slot->version);
+
+ /* request the part number + .dtbo*/
+ slot->dtbo = kasprintf(GFP_KERNEL, "%s-%s.dtbo",
+ slot->part_number, slot->version);
+ if (slot->dtbo == NULL) {
+ dev_err(dev, "slot #%d: Failed to get dtbo '%s'\n",
+ slot->slotno, dtbo);
+ return -ENOMEM;
+ }
+
+ dev_dbg(dev, "slot #%d: Requesting firmware '%s' for board-name '%s', version '%s'%s\n",
+ slot->slotno,
+ slot->dtbo, slot->board_name, slot->version,
+ system_state == SYSTEM_BOOTING ? " - booting" : "");
+
+ err = request_firmware_direct(&slot->fw, slot->dtbo, dev);
+ if (err != 0) {
+ dev_dbg(dev, "failed to load firmware '%s'\n", slot->dtbo);
+ goto err_fail_no_fw;
+ }
+
+ dev_dbg(dev, "slot #%d: dtbo '%s' loaded; converting to live tree\n",
+ slot->slotno, slot->dtbo);
+
+ 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_phandles(slot->overlay);
+ if (err != 0) {
+ dev_err(dev, "slot #%d: Failed to resolve tree\n",
+ slot->slotno);
+ goto err_fail;
+ }
+
+ err = capemgr_verify_overlay(slot);
+ if (err != 0) {
+ dev_err(dev, "slot #%d: Failed verification\n",
+ slot->slotno);
+ goto err_fail;
+ }
+
+ err = of_overlay_create(slot->overlay);
+ if (err < 0) {
+ dev_err(dev, "slot #%d: Failed to create overlay\n",
+ slot->slotno);
+ goto err_fail;
+ }
+ slot->overlay_id = err;
+
+ slot->loading = 0;
+ slot->loaded = 1;
+
+ dev_info(dev, "slot #%d: dtbo '%s' loaded; overlay id #%d\n",
+ slot->slotno, slot->dtbo, slot->overlay_id);
+
+ return 0;
+
+err_fail:
+
+ /* TODO: free the overlay, we can't right now cause
+ * the unflatten method does not track it */
+ slot->overlay = NULL;
+
+ release_firmware(slot->fw);
+ slot->fw = NULL;
+
+err_fail_no_fw:
+ slot->loading = 0;
+ return err;
+}
+
+static int capemgr_unload_slot(struct bone_cape_slot *slot)
+{
+ if (!slot->loaded || slot->overlay_id == -1)
+ return -EINVAL;
+
+ of_overlay_destroy(slot->overlay_id);
+ slot->overlay_id = -1;
+
+ slot->loaded = 0;
+
+ return 0;
+
+}
+
+/* slots_list_mutex must be taken */
+static int capemgr_remove_slot_no_lock(struct bone_cape_slot *slot)
+{
+ struct capemgr_info *info = slot->info;
+ struct device *dev = &info->pdev->dev;
+ int ret;
+
+ if (slot == NULL)
+ return 0;
+
+ if (slot->loaded && slot->overlay_id >= 0) {
+ /* unload just in case */
+ ret = capemgr_unload_slot(slot);
+ if (ret != 0) {
+ dev_err(dev, "Unable to unload slot #%d\n",
+ slot->slotno);
+ return ret;
+ }
+ }
+
+ /* 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);
+
+ if (slot->eeprom_cell)
+ eeprom_cell_put(slot->eeprom_cell);
+ devm_kfree(dev, slot);
+ return 0;
+}
+
+static int capemgr_remove_slot(struct bone_cape_slot *slot)
+{
+ struct capemgr_info *info = slot->info;
+ int ret;
+
+ mutex_lock(&info->slots_list_mutex);
+ ret = capemgr_remove_slot_no_lock(slot);
+ mutex_unlock(&info->slots_list_mutex);
+
+ return ret;
+}
+
+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;
+ u32 val;
+ 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 (of_property_read_u32(node, "priority", &val) != 0)
+ val = 0;
+ slot->priority = val;
+ }
+
+ /* if a part_number is supplied use it */
+ len = part_number ? strlen(part_number) : 0;
+ if (len > 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 */
+ len = version ? strlen(version) : 0;
+ if (len > 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 *
+capemgr_add_slot(struct capemgr_info *info, struct device_node *node,
+ const char *part_number, const char *version, int prio)
+{
+ struct bone_cape_slot *slot;
+ struct device *dev = &info->pdev->dev;
+ int slotno;
+ int ret;
+
+ slotno = atomic_inc_return(&info->next_slot_nr) - 1;
+
+ slot = devm_kzalloc(dev, sizeof(*slot), GFP_KERNEL);
+ if (slot == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ slot->info = info;
+ slot->slotno = slotno;
+ slot->priority = prio;
+ slot->overlay_id = -1;
+
+ if (node) {
+ slot->eeprom_cell = of_eeprom_cell_get(node, "eeprom");
+ if (IS_ERR(slot->eeprom_cell)) {
+ ret = PTR_ERR(slot->eeprom_cell);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get slot eeprom cell\n");
+ slot->eeprom_cell = NULL;
+ goto err_out;
+ }
+ } else {
+ dev_info(dev, "slot #%d: 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_out;
+ }
+ }
+
+ ret = bone_slot_scan(slot);
+ if (ret != 0) {
+
+ if (!slot->probe_failed) {
+ dev_err(dev, "slot #%d: scan failed\n",
+ slotno);
+ goto err_out;
+ }
+
+ 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_out;
+ }
+
+ }
+
+ /* 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_out:
+ if (slot->eeprom_cell)
+ eeprom_cell_put(slot->eeprom_cell);
+ devm_kfree(dev, slot);
+ return ERR_PTR(ret);
+}
+
+/* return 1 if it makes sense to retry loading */
+static int retry_loading_condition(struct bone_cape_slot *slot)
+{
+ struct capemgr_info *info = slot->info;
+ struct device *dev = &info->pdev->dev;
+ struct bone_cape_slot *slotn;
+ int ret;
+
+ dev_dbg(dev, "loader: retry_loading slot-%d %s:%s (prio %d)\n",
+ slot->slotno, slot->part_number, slot->version,
+ slot->priority);
+
+ mutex_lock(&info->slots_list_mutex);
+ ret = 0;
+ list_for_each_entry(slotn, &info->slot_list, node) {
+ /* if same slot or not loading skip */
+ if (!slotn->loading || slotn->retry_loading)
+ continue;
+ /* at least one cape is still loading (without retrying) */
+ ret = 1;
+ }
+ mutex_unlock(&info->slots_list_mutex);
+ return ret;
+}
+
+/* return 1 if this slot is clear to try to load now */
+static int clear_to_load_condition(struct bone_cape_slot *slot)
+{
+ struct capemgr_info *info = slot->info;
+ int my_prio = slot->priority;
+ struct device *dev = &info->pdev->dev;
+ int ret;
+
+ dev_dbg(dev, "loader: check slot-%d %s:%s (prio %d)\n", slot->slotno,
+ slot->part_number, slot->version, slot->priority);
+
+ mutex_lock(&info->slots_list_mutex);
+ ret = 1;
+ list_for_each_entry(slot, &info->slot_list, node) {
+ /* if any slot is loading with lowest priority */
+ if (!slot->loading)
+ continue;
+ if (slot->priority < my_prio) {
+ ret = 0;
+ break;
+ }
+ }
+ mutex_unlock(&info->slots_list_mutex);
+ return ret;
+}
+
+static int capemgr_loader(void *data)
+{
+ struct bone_cape_slot *slot = data;
+ struct capemgr_info *info = slot->info;
+ struct device *dev = &info->pdev->dev;
+ int ret, done, other_loading, booting;
+
+ done = 0;
+
+ slot->retry_loading = 0;
+
+ dev_dbg(dev, "loader: before slot-%d %s:%s (prio %d)\n", slot->slotno,
+ slot->part_number, slot->version, slot->priority);
+
+ /*
+ * We have a basic priority based arbitration system
+ * Slots have priorities, so the lower priority ones
+ * should start loading first. So each time we end up
+ * here.
+ */
+ ret = wait_event_interruptible(info->load_wq,
+ clear_to_load_condition(slot));
+ if (ret < 0) {
+ dev_warn(dev, "loader, Signal pending\n");
+ return ret;
+ }
+
+ dev_dbg(dev, "loader: after slot-%d %s:%s (prio %d)\n", slot->slotno,
+ slot->part_number, slot->version, slot->priority);
+
+ /* using the return value */
+ ret = capemgr_load_slot(slot);
+
+ /* wake up all just in case */
+ wake_up_interruptible_all(&info->load_wq);
+
+ if (ret == 0)
+ goto done;
+
+ dev_dbg(dev, "loader: retrying slot-%d %s:%s (prio %d)\n", slot->slotno,
+ slot->part_number, slot->version, slot->priority);
+
+ /* first attempt has failed; now try each time there's any change */
+ slot->retry_loading = 1;
+
+ for (;;) {
+ booting = (system_state == SYSTEM_BOOTING);
+ other_loading = retry_loading_condition(slot);
+ if (!booting && !other_loading)
+ break;
+
+ /* simple wait for someone to kick us */
+ if (other_loading) {
+ DEFINE_WAIT(__wait);
+
+ prepare_to_wait(&info->load_wq, &__wait,
+ TASK_INTERRUPTIBLE);
+ finish_wait(&info->load_wq, &__wait);
+ } else {
+ /* always delay when booting */
+ msleep(boot_scan_period);
+ }
+
+ if (signal_pending(current)) {
+ dev_warn(dev, "loader, Signal pending\n");
+ ret = -ERESTARTSYS;
+ goto done;
+ }
+
+ /* using the return value */
+ ret = capemgr_load_slot(slot);
+ if (ret == 0)
+ goto done;
+
+ /* wake up all just in case */
+ wake_up_interruptible_all(&info->load_wq);
+ }
+
+done:
+ slot->loading = 0;
+ slot->retry_loading = 0;
+
+ if (ret == 0) {
+ dev_dbg(dev, "loader: done slot-%d %s:%s (prio %d)\n",
+ slot->slotno, slot->part_number, slot->version,
+ slot->priority);
+ } else {
+ dev_err(dev, "loader: failed to load slot-%d %s:%s (prio %d)\n",
+ slot->slotno, slot->part_number, slot->version,
+ slot->priority);
+
+ /* if it's a override slot remove it */
+ if (slot->override)
+ capemgr_remove_slot(slot);
+ }
+
+ return ret;
+}
+
+static int
+capemgr_probe(struct platform_device *pdev)
+{
+ struct 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, *node;
+ const char *part_number;
+ const char *version;
+ const char *board_name;
+ const char *compatible_name;
+ int ret, len, prio;
+ long val;
+ char *wbuf, *s, *p, *e;
+
+ /* we don't use platform_data at all; we require OF */
+ if (pnode == NULL)
+ return -ENOTSUPP;
+
+ info = devm_kzalloc(&pdev->dev,
+ sizeof(struct capemgr_info), GFP_KERNEL);
+ if (!info)
+ 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_waitqueue_head(&info->load_wq);
+
+ baseboardmaps_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;
+ }
+
+ bbrd->eeprom_cell = of_eeprom_cell_get(pnode, "eeprom");
+ if (IS_ERR(bbrd->eeprom_cell)) {
+ ret = PTR_ERR(bbrd->eeprom_cell);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to get baseboard eeprom cell\n");
+ bbrd->eeprom_cell = 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) {
+ ret = -ENOMEM;
+ goto err_exit;
+ }
+ of_node_put(node);
+
+ dev_info(&pdev->dev, "compatible-baseboard=%s\n",
+ bbrd->compatible_name);
+
+ /* 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) {
+
+ slot = capemgr_add_slot(info, node, NULL, NULL, 0);
+ 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;
+ }
+ /* note that slot may be NULL (means it was disabled) */
+ }
+ of_node_put(slots_node);
+ }
+ slots_node = NULL;
+
+ /* iterate over enable_partno (if there) */
+ if (enable_partno && strlen(enable_partno) > 0) {
+
+ /* allocate a temporary buffer */
+ wbuf = devm_kzalloc(&pdev->dev, PAGE_SIZE, GFP_KERNEL);
+ if (wbuf == NULL) {
+ ret = -ENOMEM;
+ goto err_exit;
+ }
+
+ /* add any enable_partno capes */
+ s = enable_partno;
+ while (*s) {
+ /* form is PART[:REV[:PRIO]],PART.. */
+ p = strchr(s, ',');
+ if (p == NULL)
+ e = s + strlen(s);
+ else
+ e = p;
+
+ /* copy to temp buffer */
+ len = e - s;
+ if (len >= PAGE_SIZE - 1)
+ len = PAGE_SIZE - 1;
+ memcpy(wbuf, s, len);
+ wbuf[len] = '\0';
+
+ /* move to the next */
+ s = *e ? e + 1 : e;
+
+ part_number = wbuf;
+
+ /* default version is NULL & prio is 0 */
+ version = NULL;
+ prio = 0;
+
+ /* now split the rev & prio part */
+ p = strchr(wbuf, ':');
+ if (p != NULL) {
+ *p++ = '\0';
+ if (*p != ':')
+ version = p;
+ p = strchr(p, ':');
+ if (p != NULL) {
+ *p++ = '\0';
+ ret = kstrtol(p, 10, &val);
+ if (ret == 0)
+ prio = val;
+ }
+ }
+
+ dev_info(&pdev->dev,
+ "enabled_partno PARTNO '%s' VER '%s' PR '%d'\n",
+ part_number,
+ version ? version : "N/A", prio);
+
+ /* only immediate slots are allowed here */
+ slot = capemgr_add_slot(info, NULL,
+ part_number, version, prio);
+
+ /* we continue even in case of an error */
+ if (IS_ERR_OR_NULL(slot)) {
+ dev_warn(&pdev->dev, "Failed to add slot #%d\n",
+ atomic_read(&info->next_slot_nr) - 1);
+ }
+ }
+
+ devm_kfree(&pdev->dev, wbuf);
+ }
+
+ 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);
+
+ /* it is safe to create the attribute groups */
+ ret = sysfs_create_groups(&pdev->dev.kobj, attr_groups);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to create sysfs attributes\n");
+ goto err_exit;
+ }
+ /* automatically cleared by driver core now */
+ pdev->dev.groups = attr_groups;
+
+ /* now load each (take lock to be sure */
+ mutex_lock(&info->slots_list_mutex);
+
+ list_for_each_entry(slot, &info->slot_list, node) {
+
+ /* if matches the disabled ones skip */
+ if (bone_match_cape(disable_partno, slot->part_number,
+ slot->version)) {
+ dev_info(&pdev->dev,
+ "Skipping loading of disabled cape with part# %s\n",
+ slot->part_number);
+ slot->disabled = 1;
+ continue;
+ }
+
+ if (!slot->probe_failed && !slot->loaded)
+ slot->loading = 1;
+ }
+
+ /* now start the loader thread(s) (all at once) */
+ list_for_each_entry(slot, &info->slot_list, node) {
+
+ if (!slot->loading)
+ continue;
+
+ slot->loader_thread = kthread_run(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:
+ if (bbrd->eeprom_cell)
+ eeprom_cell_put(bbrd->eeprom_cell);
+ of_node_put(baseboardmaps_node);
+ platform_set_drvdata(pdev, NULL);
+ devm_kfree(&pdev->dev, info);
+
+ return ret;
+}
+
+static int capemgr_remove(struct platform_device *pdev)
+{
+ struct capemgr_info *info = platform_get_drvdata(pdev);
+ struct bone_baseboard *bbrd = &info->baseboard;
+ 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)
+ capemgr_remove_slot_no_lock(slot);
+ mutex_unlock(&info->slots_list_mutex);
+
+ 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);
+
+ if (bbrd->eeprom_cell)
+ eeprom_cell_put(bbrd->eeprom_cell);
+ devm_kfree(&pdev->dev, info);
+
+ return 0;
+}
+
+static struct platform_driver capemgr_driver = {
+ .probe = capemgr_probe,
+ .remove = capemgr_remove,
+ .driver = {
+ .name = "bone_capemgr",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(capemgr_of_match),
+ },
+};
+
+module_platform_driver(capemgr_driver);
+
+MODULE_AUTHOR("Pantelis Antoniou");
+MODULE_DESCRIPTION("Beaglebone cape manager");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bone_capemgr");
--
1.7.12
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists