[PATCH v2] Add support for monitoring Chrome OS firmware signals

Martyn Welch
2015-12-01T20:19:58Z

Select Chromebooks have gpio attached to signals used to cause the firmware
to enter alternative modes of operation and/or control other device
characteristics (such as write protection on flash devices). This patch
adds a driver that exposes a read-only interface to allow these signals to
be read from user space.

Signed-off-by: Martyn Welch <martyn.welch@collabora.co.uk>
---

v2:
- Added missing call to remove sysfs link.

drivers/platform/chrome/Kconfig | 13 +++
drivers/platform/chrome/Makefile | 1 +
drivers/platform/chrome/chromeos_firmware.c | 166 ++++++++++++++++++++++++++++
3 files changed, 180 insertions(+)
create mode 100644 drivers/platform/chrome/chromeos_firmware.c

diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
index d03df4a..d55ceef 100644
--- a/drivers/platform/chrome/Kconfig
+++ b/drivers/platform/chrome/Kconfig
@@ -38,6 +38,19 @@ config CHROMEOS_PSTORE
If you have a supported Chromebook, choose Y or M here.
The module will be called chromeos_pstore.

+config CHROMEOS_FIRMWARE
+ tristate "Chrome OS firmware signal monitoring"
+ depends on GPIO_SYSFS
+ ---help---
+ Many chromebooks have gpio attached to signals used to cause the
+ firmware to enter alternative modes of operation and/or control other
+ device characteristics (such as write protection on flash devices).
+ This driver exposes a read-only interface to allow these signals to be
+ read from user space.
+
+ If you have a supported Chromebook, choose Y or M here.
+ The module will be called chromeos_firmware.
+
config CROS_EC_CHARDEV
tristate "Chrome OS Embedded Controller userspace device interface"
depends on MFD_CROS_EC
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index bc498bd..2453adf 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -1,6 +1,7 @@

obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o
obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o
+obj-$(CONFIG_CHROMEOS_FIRMWARE) += chromeos_firmware.o
cros_ec_devs-objs := cros_ec_dev.o cros_ec_sysfs.o \
cros_ec_lightbar.o cros_ec_vbc.o
obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_devs.o
diff --git a/drivers/platform/chrome/chromeos_firmware.c b/drivers/platform/chrome/chromeos_firmware.c
new file mode 100644
index 0000000..a08cb57
--- /dev/null
+++ b/drivers/platform/chrome/chromeos_firmware.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2015 Collabora Ltd.
+ *
+ * based on vendor driver,
+ *
+ * Copyright (C) 2011 The Chromium OS Authors
+ *
+ * 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/bcd.h>
+#include <linux/gpio.h>
+#include <linux/notifier.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct chromeos_firmware_gpio_info {
+ int gpio;
+ const char *link;
+};
+
+struct chromeos_firmware_data {
+ struct chromeos_firmware_gpio_info wp;
+ struct chromeos_firmware_gpio_info rec;
+ struct chromeos_firmware_gpio_info dev;
+};
+
+static int dt_gpio_init(struct platform_device *pdev, const char *of_list_name,
+ const char *gpio_desc_name, const char *sysfs_name,
+ struct chromeos_firmware_gpio_info *gpio)
+{
+ int err;
+ enum of_gpio_flags of_flags;
+ unsigned long flags = GPIOF_DIR_IN | GPIOF_EXPORT;
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *cnp;
+
+ cnp = of_get_child_by_name(np, of_list_name);
+ if (!cnp)
+ /*
+ * We don't necessarily expect to find all of the devices, so
+ * return without generating an error.
+ */
+ return 0;
+
+ gpio->gpio = of_get_named_gpio_flags(cnp, "gpios", 0, &of_flags);
+ if (!gpio_is_valid(gpio->gpio)) {
+ err = -EINVAL;
+ goto err_prop;
+ }
+
+ if (of_flags & OF_GPIO_ACTIVE_LOW)
+ flags |= GPIOF_ACTIVE_LOW;
+
+ err = gpio_request_one(gpio->gpio, flags, gpio_desc_name);
+ if (err)
+ goto err_prop;
+
+ err = gpio_export_link(&pdev->dev, sysfs_name, gpio->gpio);
+ if (err)
+ goto err_gpio;
+
+ gpio->link = sysfs_name;
+
+ return 0;
+
+err_gpio:
+ gpio_free(gpio->gpio);
+err_prop:
+ of_node_put(cnp);
+
+ return err;
+}
+
+static void chromeos_firmware_rem(struct device *dev,
+ struct chromeos_firmware_gpio_info *gpio)
+{
+ sysfs_remove_link(&dev->kobj, gpio->link);
+
+ gpio_unexport(gpio->gpio);
+
+ gpio_free(gpio->gpio);
+}
+
+static int chromeos_firmware_probe(struct platform_device *pdev)
+{
+ int err;
+ struct chromeos_firmware_data *gpios;
+
+ gpios = devm_kmalloc(&pdev->dev, sizeof(gpios), GFP_KERNEL);
+ if (!gpios)
+ return -ENOMEM;
+
+ err = dt_gpio_init(pdev, "write-protect", "firmware-write-protect",
+ "write-protect", &gpios->wp);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to init write-protect.\n");
+ goto err_wp;
+ }
+
+ err = dt_gpio_init(pdev, "recovery-switch", "firmware-recovery-switch",
+ "recovery-switch", &gpios->rec);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to init recovery-switch.\n");
+ goto err_rec;
+ }
+
+ err = dt_gpio_init(pdev, "developer-switch",
+ "firmware-developer-switch", "developer-switch",
+ &gpios->dev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to init developer-switch.\n");
+ goto err_dev;
+ }
+
+ platform_set_drvdata(pdev, gpios);
+
+ return 0;
+
+err_dev:
+ chromeos_firmware_rem(&pdev->dev, &gpios->rec);
+
+err_rec:
+ chromeos_firmware_rem(&pdev->dev, &gpios->wp);
+err_wp:
+ return err;
+}
+
+static int chromeos_firmware_remove(struct platform_device *pdev)
+{
+ struct chromeos_firmware_data *gpios = platform_get_drvdata(pdev);
+
+ chromeos_firmware_rem(&pdev->dev, &gpios->dev);
+ chromeos_firmware_rem(&pdev->dev, &gpios->rec);
+ chromeos_firmware_rem(&pdev->dev, &gpios->wp);
+
+ return 0;
+}
+
+static const struct of_device_id chromeos_firmware_of_match[] = {
+ { .compatible = "google,gpio-firmware" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, chromeos_firmware_of_match);
+
+static struct platform_driver chromeos_firmware_driver = {
+ .probe = chromeos_firmware_probe,
+ .remove = chromeos_firmware_remove,
+ .driver = {
+ .name = "chromeos_firmware",
+ .of_match_table = chromeos_firmware_of_match,
+ },
+};
+module_platform_driver(chromeos_firmware_driver);
+
+MODULE_LICENSE("GPL");
--
2.1.4