summaryrefslogtreecommitdiff
path: root/src/vendorcode/google/chromeos
diff options
context:
space:
mode:
Diffstat (limited to 'src/vendorcode/google/chromeos')
-rw-r--r--src/vendorcode/google/chromeos/Makefile.inc2
-rw-r--r--src/vendorcode/google/chromeos/chromeos.h2
-rw-r--r--src/vendorcode/google/chromeos/vpd_calibration.c197
3 files changed, 200 insertions, 1 deletions
diff --git a/src/vendorcode/google/chromeos/Makefile.inc b/src/vendorcode/google/chromeos/Makefile.inc
index fbd12fbad2..36997ed44d 100644
--- a/src/vendorcode/google/chromeos/Makefile.inc
+++ b/src/vendorcode/google/chromeos/Makefile.inc
@@ -29,7 +29,7 @@ ramstage-y += fmap.c
ramstage-$(CONFIG_CHROMEOS_RAMOOPS) += ramoops.c
smm-y += fmap.c
romstage-y += vpd_decode.c cros_vpd.c
-ramstage-y += vpd_decode.c cros_vpd.c vpd_mac.c
+ramstage-y += vpd_decode.c cros_vpd.c vpd_mac.c vpd_calibration.c
ifeq ($(MOCK_TPM),1)
CFLAGS_common += -DMOCK_TPM=1
diff --git a/src/vendorcode/google/chromeos/chromeos.h b/src/vendorcode/google/chromeos/chromeos.h
index 5ba54571a5..13a4fe3503 100644
--- a/src/vendorcode/google/chromeos/chromeos.h
+++ b/src/vendorcode/google/chromeos/chromeos.h
@@ -83,4 +83,6 @@ static inline void chromeos_ram_oops_init(chromeos_acpi_t *chromeos) {}
static inline void chromeos_reserve_ram_oops(struct device *dev, int idx) {}
#endif /* CONFIG_CHROMEOS_RAMOOPS */
+void cbmem_add_vpd_calibration_data(void);
+
#endif /* __CHROMEOS_H__ */
diff --git a/src/vendorcode/google/chromeos/vpd_calibration.c b/src/vendorcode/google/chromeos/vpd_calibration.c
new file mode 100644
index 0000000000..3bfd843db0
--- /dev/null
+++ b/src/vendorcode/google/chromeos/vpd_calibration.c
@@ -0,0 +1,197 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google 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; version 2 of the License.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <cbmem.h>
+#include <console/console.h>
+#include <string.h>
+
+#include <vendorcode/google/chromeos/chromeos.h>
+#include <vendorcode/google/chromeos/cros_vpd.h>
+
+/*
+ * This file provides functions looking in the VPD for WiFi calibration data,
+ * and if found, copying the calibration blobs into CBMEM.
+ *
+ * Per interface calibration data is stored in the VPD in opaque blobs. The
+ * keys of the blobs follow one of two possible patterns:
+ * "wifi_base64_calibration<N>" or "wifi_calibration<N>", where <N> is the
+ * interface number.
+ *
+ * This function accommodates up to 4 interfaces. All calibration blobs found
+ * in the VPD are packed into a single CBMEM entry as describe by the
+ * structures below:
+ */
+
+/* This structure describes a single calibration data blob */
+struct calibration_blob {
+ uint32_t blob_size; /* Total size. rounded up to fall on a 4 byte
+ boundary. */
+ uint32_t key_size; /* Size of the name of this entry, \0 included. */
+ uint32_t value_size; /* Size of the value of this entry */
+ /* Zero terminated name(key) goes here, immediately followed by value */
+};
+
+/*
+ * This is the structure of the CBMEM entry containing WiFi calibration blobs.
+ * It starts with the total size (header size included) followed by an
+ * arbitrary number of concatenated 4 byte aligned calibration blobs.
+ */
+struct calibration_entry {
+ uint32_t size;
+ struct calibration_blob entries[0]; /* A varialble size container. */
+};
+
+
+#define MAX_WIFI_INTERFACE_COUNT 4
+
+/*
+ * Structure of the cache to keep information about calibration blobs present
+ * in the VPD, one cache entry per VPD blob.
+ *
+ * Maintaing the cache allows to scan the VPD once, determine the CBMEM entry
+ * memory requirements, then allocate as much room as necessary and fill it
+ * up.
+ */
+struct vpd_blob_cache_t {
+ /* The longest name template must fit with an extra character. */
+ char key_name[40];
+ const void *value_pointer;
+ unsigned blob_size;
+ unsigned key_size;
+ unsigned value_size;
+};
+
+static const char * const templates[] = {
+ "wifi_base64_calibrationX",
+ "wifi_calibrationX"
+};
+
+/*
+ * Scan the VPD for WiFi calibration data, checking for all possible key names
+ * and caching discovered blobs.
+ *
+ * Return the sum of sizes of all blobs, as stored in CBMEM.
+ */
+static size_t fill_up_entries_cache(struct vpd_blob_cache_t *cache,
+ size_t max_entries, size_t *filled_entries)
+{
+ int i;
+ int cbmem_entry_size = 0;
+ size_t used_entries = 0;
+
+
+ for (i = 0;
+ (i < ARRAY_SIZE(templates)) && (used_entries < max_entries);
+ i++) {
+ int j;
+ const int index_location = strlen(templates[i]) - 1;
+ const int key_length = index_location + 2;
+
+ if (key_length > sizeof(cache->key_name))
+ continue;
+
+ for (j = 0; j < MAX_WIFI_INTERFACE_COUNT; j++) {
+ const void *payload;
+ int payload_size;
+
+ strcpy(cache->key_name, templates[i]);
+ cache->key_name[index_location] = j + '0';
+
+ payload = cros_vpd_find(cache->key_name, &payload_size);
+ if (!payload)
+ continue;
+
+ cache->value_pointer = payload;
+ cache->key_size = key_length;
+ cache->value_size = payload_size;
+ cache->blob_size =
+ ALIGN(sizeof(struct calibration_blob) +
+ cache->key_size +
+ cache->value_size, 4);
+ cbmem_entry_size += cache->blob_size;
+
+ used_entries++;
+ if (used_entries == max_entries)
+ break;
+
+ cache++;
+ }
+ }
+
+ *filled_entries = used_entries;
+ return cbmem_entry_size;
+}
+
+void cbmem_add_vpd_calibration_data(void)
+{
+ size_t cbmem_entry_size, filled_entries;
+ struct calibration_entry *cbmem_entry;
+ struct calibration_blob *cal_blob;
+ int i;
+ /*
+ * Allocate one more cache entry than max required, to make sure that
+ * the last entry can be identified by the key size of zero.
+ */
+ struct vpd_blob_cache_t vpd_blob_cache[ARRAY_SIZE(templates) *
+ MAX_WIFI_INTERFACE_COUNT];
+
+ cbmem_entry_size = fill_up_entries_cache(vpd_blob_cache,
+ ARRAY_SIZE(vpd_blob_cache),
+ &filled_entries);
+
+ if (!cbmem_entry_size)
+ return; /* No calibration data found in the VPD. */
+
+ cbmem_entry_size += sizeof(struct calibration_entry);
+ cbmem_entry = cbmem_add(CBMEM_ID_WIFI_CALIBRATION, cbmem_entry_size);
+ if (!cbmem_entry) {
+ printk(BIOS_ERR, "%s: no room in cbmem to add %zd bytes\n",
+ __func__, cbmem_entry_size);
+ return;
+ }
+
+ cbmem_entry->size = cbmem_entry_size;
+
+ /* Copy cached data into the CBMEM entry. */
+ cal_blob = cbmem_entry->entries;
+
+ for (i = 0; i < filled_entries; i++) {
+ /* Use this as a pointer to the current cache entry. */
+ struct vpd_blob_cache_t *cache = vpd_blob_cache + i;
+ char *pointer;
+
+ cal_blob->blob_size = cache->blob_size;
+ cal_blob->key_size = cache->key_size;
+ cal_blob->value_size = cache->value_size;
+
+ /* copy the key */
+ pointer = (char *)(cal_blob + 1);
+ memcpy(pointer, cache->key_name, cache->key_size);
+
+ /* and the value */
+ pointer += cache->key_size;
+ memcpy(pointer, cache->value_pointer, cache->value_size);
+
+ printk(BIOS_INFO, "%s: added %s to CBMEM\n",
+ __func__, cache->key_name);
+
+ cal_blob = (struct calibration_blob *)
+ ((char *)cal_blob + cal_blob->blob_size);
+ }
+}