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.we...@collabora.co.uk>
---
 drivers/platform/chrome/Kconfig             |  13 +++
 drivers/platform/chrome/Makefile            |   1 +
 drivers/platform/chrome/chromeos_firmware.c | 156 ++++++++++++++++++++++++++++
 3 files changed, 170 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..ab8540a
--- /dev/null
+++ b/drivers/platform/chrome/chromeos_firmware.c
@@ -0,0 +1,156 @@
+/*
+ * 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_data {
+       int wp;
+       int rec;
+       int dev;
+};
+
+static int dt_gpio_init(struct platform_device *pdev, const char *of_list_name,
+                       const char *gpio_desc_name, const char *sysfs_name,
+                       int *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 = of_get_named_gpio_flags(cnp, "gpios", 0, &of_flags);
+       if (!gpio_is_valid(*gpio)) {
+               err = -EINVAL;
+               goto err_prop;
+       }
+
+       if (of_flags & OF_GPIO_ACTIVE_LOW)
+               flags |= GPIOF_ACTIVE_LOW;
+
+       err = gpio_request_one(*gpio, flags, gpio_desc_name);
+       if (err)
+               goto err_prop;
+
+       err = gpio_export_link(&pdev->dev, sysfs_name, *gpio);
+       if (err)
+               goto err_gpio;
+
+       return 0;
+
+err_gpio:
+       gpio_free(*gpio);
+err_prop:
+       of_node_put(cnp);
+
+       return err;
+}
+
+static void chromeos_firmware_rem(int gpio)
+{
+       gpio_unexport(gpio);
+
+       gpio_free(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(gpios->rec);
+
+err_rec:
+       chromeos_firmware_rem(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(gpios->dev);
+       chromeos_firmware_rem(gpios->rec);
+       chromeos_firmware_rem(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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to