Merge tag 'u-boot-stm32-20211012' of https://source.denx.de/u-boot/custodians/u-boot-stm
[platform/kernel/u-boot.git] / board / google / chromebook_coral / coral.c
index 4e34710..53c5171 100644 (file)
  * Copyright 2019 Google LLC
  */
 
+#define LOG_CATEGORY   UCLASS_SYSINFO
+
 #include <common.h>
+#include <bloblist.h>
+#include <command.h>
+#include <cros_ec.h>
+#include <dm.h>
+#include <init.h>
+#include <log.h>
+#include <sysinfo.h>
+#include <acpi/acpigen.h>
+#include <asm-generic/gpio.h>
+#include <asm/acpi_nhlt.h>
+#include <asm/cb_sysinfo.h>
+#include <asm/intel_gnvs.h>
+#include <asm/intel_pinctrl.h>
+#include <dm/acpi.h>
+#include <linux/delay.h>
+#include "variant_gpio.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct cros_gpio_info {
+       const char *linux_name;
+       enum cros_gpio_t type;
+       int gpio_num;
+       int flags;
+};
+
+int misc_init_f(void)
+{
+       if (!ll_boot_init()) {
+               printf("Running as secondary loader");
+               if (gd->arch.coreboot_table) {
+                       int ret;
+
+                       printf(" (found coreboot table at %lx)",
+                              gd->arch.coreboot_table);
+
+                       ret = get_coreboot_info(&lib_sysinfo);
+                       if (ret) {
+                               printf("\nFailed to parse coreboot tables (err=%d)\n",
+                                      ret);
+                               return ret;
+                       }
+               }
+
+               printf("\n");
+       }
+
+       return 0;
+}
 
 int arch_misc_init(void)
 {
        return 0;
 }
 
-/* This function is needed if CONFIG_CMDLINE is not enabled */
-int board_run_command(const char *cmdline)
+static int get_memconfig(struct udevice *dev)
 {
-       printf("No command line\n");
+       struct gpio_desc gpios[4];
+       int cfg;
+       int ret;
+
+       ret = gpio_request_list_by_name(dev, "memconfig-gpios", gpios,
+                                       ARRAY_SIZE(gpios),
+                                       GPIOD_IS_IN | GPIOD_PULL_UP);
+       if (ret < 0) {
+               log_debug("Cannot get GPIO list '%s' (%d)\n", dev->name, ret);
+               return ret;
+       }
+
+       /* Give the lines time to settle */
+       udelay(10);
+
+       ret = dm_gpio_get_values_as_int(gpios, ARRAY_SIZE(gpios));
+       if (ret < 0)
+               return log_msg_ret("get", ret);
+       cfg = ret;
+
+       ret = gpio_free_list(dev, gpios, ARRAY_SIZE(gpios));
+       if (ret)
+               return log_msg_ret("free", ret);
+
+       return cfg;
+}
+
+/**
+ * get_skuconfig() - Get the SKU number either from pins or the EC
+ *
+ * Two options are supported:
+ *     skuconfig-gpios - two pins in the device tree (tried first)
+ *     EC              - reading from the EC (backup)
+ *
+ * @dev: sysinfo device to use
+ * @return SKU ID, or -ve error if not found
+ */
+static int get_skuconfig(struct udevice *dev)
+{
+       struct gpio_desc gpios[2];
+       int cfg;
+       int ret;
+
+       ret = gpio_request_list_by_name(dev, "skuconfig-gpios", gpios,
+                                       ARRAY_SIZE(gpios),
+                                       GPIOD_IS_IN);
+       if (ret != ARRAY_SIZE(gpios)) {
+               struct udevice *cros_ec;
+
+               log_debug("Cannot get GPIO list '%s' (%d)\n", dev->name, ret);
+
+               /* Try the EC */
+               ret = uclass_first_device_err(UCLASS_CROS_EC, &cros_ec);
+               if (ret < 0) {
+                       log_err("Cannot find EC for SKU details\n");
+                       return log_msg_ret("sku", ret);
+               }
+               ret = cros_ec_get_sku_id(cros_ec);
+               if (ret < 0) {
+                       log_err("Cannot read SKU details\n");
+                       return log_msg_ret("sku", ret);
+               }
+
+               return ret;
+       }
+
+       ret = dm_gpio_get_values_as_int_base3(gpios, ARRAY_SIZE(gpios));
+       if (ret < 0)
+               return log_msg_ret("get", ret);
+       cfg = ret;
+
+       ret = gpio_free_list(dev, gpios, ARRAY_SIZE(gpios));
+       if (ret)
+               return log_msg_ret("free", ret);
+
+       return cfg;
+}
+
+static int coral_get_str(struct udevice *dev, int id, size_t size, char *val)
+{
+       int ret;
+
+       if (IS_ENABLED(CONFIG_SPL_BUILD))
+               return -ENOSYS;
+
+       switch (id) {
+       case SYSINFO_ID_SMBIOS_SYSTEM_VERSION:
+       case SYSINFO_ID_SMBIOS_BASEBOARD_VERSION: {
+               ret = get_skuconfig(dev);
+
+               if (ret < 0)
+                       return ret;
+               if (size < 15)
+                       return -ENOSPC;
+               sprintf(val, "rev%d", ret);
+               break;
+       }
+       case SYSINFO_ID_BOARD_MODEL: {
+               int mem_config, sku_config;
+               const char *model;
+
+               ret = get_memconfig(dev);
+               if (ret < 0)
+                       log_warning("Unable to read memconfig (err=%d)\n", ret);
+               mem_config = ret;
+               ret = get_skuconfig(dev);
+               if (ret < 0)
+                       log_warning("Unable to read skuconfig (err=%d)\n", ret);
+               sku_config = ret;
+               model = fdt_getprop(gd->fdt_blob, 0, "model", NULL);
+               snprintf(val, size, "%s (memconfig %d, SKU %d)", model,
+                        mem_config, sku_config);
+               break;
+       }
+       default:
+               return -ENOENT;
+       }
+
+       return 0;
+}
+
+int chromeos_get_gpio(const struct udevice *dev, const char *prop,
+                     enum cros_gpio_t type, struct cros_gpio_info *info)
+{
+       struct udevice *pinctrl;
+       struct gpio_desc desc;
+       int ret;
+
+       ret = gpio_request_by_name((struct udevice *)dev, prop, 0, &desc, 0);
+       if (ret == -ENOTBLK) {
+               info->gpio_num = CROS_GPIO_VIRTUAL;
+               log_debug("GPIO '%s' is virtual\n", prop);
+       } else if (ret) {
+               return log_msg_ret("gpio", ret);
+       } else {
+               info->gpio_num = desc.offset;
+               dm_gpio_free((struct udevice *)dev, &desc);
+       }
+       info->linux_name = dev_read_string(desc.dev, "linux-name");
+       if (!info->linux_name)
+               return log_msg_ret("linux-name", -ENOENT);
+       info->type = type;
+       /* Get ACPI pin from GPIO library if available */
+       if (info->gpio_num != CROS_GPIO_VIRTUAL) {
+               pinctrl = dev_get_parent(desc.dev);
+               info->gpio_num = intel_pinctrl_get_acpi_pin(pinctrl,
+                                                           info->gpio_num);
+       }
+       info->flags = desc.flags & GPIOD_ACTIVE_LOW ? CROS_GPIO_ACTIVE_LOW :
+               CROS_GPIO_ACTIVE_HIGH;
+       if (!ret)
+               dm_gpio_free(desc.dev, &desc);
+
+       return 0;
+}
+
+static int chromeos_acpi_gpio_generate(const struct udevice *dev,
+                                      struct acpi_ctx *ctx)
+{
+       struct cros_gpio_info info[3];
+       int count, i;
+       int ret;
+
+       count = 3;
+       ret = chromeos_get_gpio(dev, "recovery-gpios", CROS_GPIO_REC, &info[0]);
+       if (ret)
+               return log_msg_ret("rec", ret);
+       ret = chromeos_get_gpio(dev, "write-protect-gpios", CROS_GPIO_WP,
+                               &info[1]);
+       if (ret)
+               return log_msg_ret("wp", ret);
+       ret = chromeos_get_gpio(dev, "phase-enforce-gpios", CROS_GPIO_PE,
+                               &info[2]);
+       if (ret)
+               return log_msg_ret("phase", ret);
+       acpigen_write_scope(ctx, "\\");
+       acpigen_write_name(ctx, "OIPG");
+       acpigen_write_package(ctx, count);
+       for (i = 0; i < count; i++) {
+               acpigen_write_package(ctx, 4);
+               acpigen_write_integer(ctx, info[i].type);
+               acpigen_write_integer(ctx, info[i].flags);
+               acpigen_write_integer(ctx, info[i].gpio_num);
+               acpigen_write_string(ctx, info[i].linux_name);
+               acpigen_pop_len(ctx);
+       }
+
+       acpigen_pop_len(ctx);
+       acpigen_pop_len(ctx);
 
        return 0;
 }
+
+static int coral_write_acpi_tables(const struct udevice *dev,
+                                  struct acpi_ctx *ctx)
+{
+       struct acpi_global_nvs *gnvs;
+       struct nhlt *nhlt;
+       const char *oem_id = "coral";
+       const char *oem_table_id = "coral";
+       u32 oem_revision = 3;
+       int ret;
+
+       gnvs = bloblist_find(BLOBLISTT_ACPI_GNVS, sizeof(*gnvs));
+       if (!gnvs)
+               return log_msg_ret("bloblist", -ENOENT);
+
+       nhlt = nhlt_init();
+       if (!nhlt)
+               return -ENOMEM;
+
+       log_debug("Setting up NHLT\n");
+       ret = acpi_setup_nhlt(ctx, nhlt);
+       if (ret)
+               return log_msg_ret("setup", ret);
+
+       /* Update NHLT GNVS Data */
+       gnvs->nhla = (uintptr_t)ctx->current;
+       gnvs->nhll = nhlt_current_size(nhlt);
+
+       ret = nhlt_serialise_oem_overrides(ctx, nhlt, oem_id, oem_table_id,
+                                          oem_revision);
+       if (ret)
+               return log_msg_ret("serialise", ret);
+
+       return 0;
+}
+
+struct acpi_ops coral_acpi_ops = {
+       .write_tables   = coral_write_acpi_tables,
+       .inject_dsdt    = chromeos_acpi_gpio_generate,
+};
+
+struct sysinfo_ops coral_sysinfo_ops = {
+       .get_str        = coral_get_str,
+};
+
+#if CONFIG_IS_ENABLED(OF_REAL)
+static const struct udevice_id coral_ids[] = {
+       { .compatible = "google,coral" },
+       { }
+};
+#endif
+
+U_BOOT_DRIVER(coral_drv) = {
+       .name           = "coral",
+       .id             = UCLASS_SYSINFO,
+       .of_match       = of_match_ptr(coral_ids),
+       .ops            = &coral_sysinfo_ops,
+       ACPI_OPS_PTR(&coral_acpi_ops)
+};