[coreboot-gerrit] New patch to review for coreboot: i2c: Add a generic i2c driver

Duncan Laurie (dlaurie@chromium.org) gerrit at coreboot.org
Tue May 31 18:55:01 CEST 2016


Duncan Laurie (dlaurie at chromium.org) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/15016

-gerrit

commit 83d09e2d4fd92452bf113695fb26c59113a8cde1
Author: Duncan Laurie <dlaurie at chromium.org>
Date:   Wed May 11 09:50:59 2016 -0700

    i2c: Add a generic i2c driver
    
    This adds a generic I2C driver that can be described in the devicetree
    and used to generate ACPI objects in the SSDT based on the information
    provided in the config registers.
    
    The I2C bus can be configured and the device can provide an interrupt and
    wake capability to the OS.  A configuration option allows for a GPIO to
    be provided that will be checked to determine if the device is preset on
    the board before including it in the generated SSDT.
    
    The driver is generic enough to be used for basic I2C devices that do
    not have special configuration needs such as touchpads, touchscreens,
    sensors, some audio codec/amplifiers, etc.
    
    Sample usage for a touchpad device:
    
    device pci 15.1 on
      chip drivers/i2c/generic
        register "hid" = ""ELAN0000""
        register "desc" = "ELAN Touchpad"
        register "irq" = "IRQ_EDGE_LOW(GPP_B3_IRQ)"
        register "wake" = "GPE0_DW0_05"
        device i2c 15.0 on end
      end
    end
    
    Will result in the following code in the SSDT:
    
    Scope (\_SB.PCI0.I2C1) {
      Device (D015) {
        Name (_HID, "ELAN0000")
        Name (_UID, 0)
        Name (_S0W, 4)
        Name (_PRW, Package () { 5, 3 })
        Method (_STA) { Return (0x0f) }
        Name (_CRS, ResourceTemplate () {
          I2cSerialBus (0x15, ControllerInitiated, 400000, AddressingMode7Bit,
                        "\\_S.PCI0.I2C1", 0, ResourceConsumer)
          Interrupt (ResourceConsumer, Edge, ActiveLow) { 51 }
        })
      }
    }
    
    Change-Id: Ib32055720835b70e91ede5e4028ecd91894d70d5
    Signed-off-by: Duncan Laurie <dlaurie at chromium.org>
---
 src/drivers/i2c/generic/Kconfig      |   2 +
 src/drivers/i2c/generic/Makefile.inc |   1 +
 src/drivers/i2c/generic/chip.h       |  15 +++++
 src/drivers/i2c/generic/generic.c    | 123 +++++++++++++++++++++++++++++++++++
 4 files changed, 141 insertions(+)

diff --git a/src/drivers/i2c/generic/Kconfig b/src/drivers/i2c/generic/Kconfig
new file mode 100644
index 0000000..a4c0104
--- /dev/null
+++ b/src/drivers/i2c/generic/Kconfig
@@ -0,0 +1,2 @@
+config DRIVERS_I2C_GENERIC
+	bool
diff --git a/src/drivers/i2c/generic/Makefile.inc b/src/drivers/i2c/generic/Makefile.inc
new file mode 100644
index 0000000..86cbc7b
--- /dev/null
+++ b/src/drivers/i2c/generic/Makefile.inc
@@ -0,0 +1 @@
+ramstage-$(CONFIG_DRIVERS_I2C_GENERIC) += generic.c
diff --git a/src/drivers/i2c/generic/chip.h b/src/drivers/i2c/generic/chip.h
new file mode 100644
index 0000000..7b371c4
--- /dev/null
+++ b/src/drivers/i2c/generic/chip.h
@@ -0,0 +1,15 @@
+#include <arch/acpi_device.h>
+
+struct drivers_i2c_generic_config {
+	const char *hid;	/* ACPI _HID (required) */
+	const char *name;	/* ACPI Device Name */
+	const char *desc;	/* Device Description */
+	unsigned uid;		/* ACPI _UID */
+	unsigned bus_speed;	/* I2C Bus Frequency (default=400kHz) */
+	unsigned wake;		/* Wake GPE */
+	struct acpi_irq irq;	/* Interrupt */
+
+	/* GPIO used to indicate if this device is present */
+	unsigned device_present_gpio;
+	unsigned device_present_gpio_invert;
+};
diff --git a/src/drivers/i2c/generic/generic.c b/src/drivers/i2c/generic/generic.c
new file mode 100644
index 0000000..a12c273
--- /dev/null
+++ b/src/drivers/i2c/generic/generic.c
@@ -0,0 +1,123 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2016 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.
+ */
+
+#include <arch/acpi_device.h>
+#include <arch/acpigen.h>
+#include <console/console.h>
+#include <device/i2c.h>
+#include <device/device.h>
+#include <device/path.h>
+#include <gpio.h>
+#include <stdint.h>
+#include <string.h>
+#include "chip.h"
+
+#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
+static void i2c_generic_fill_ssdt(struct device *dev)
+{
+	struct drivers_i2c_generic_config *config = dev->chip_info;
+	const char *scope = acpi_device_scope(dev);
+	struct acpi_i2c i2c = {
+		.address = dev->path.i2c.device,
+		.mode_10bit = dev->path.i2c.mode_10bit,
+		.speed = config->bus_speed ? : I2C_SPEED_FAST,
+		.resource = scope,
+	};
+
+	if (!dev->enabled || !scope)
+		return;
+
+	if (!config->hid) {
+		printk(BIOS_ERR, "%s: ERROR: HID required\n", dev_path(dev));
+		return;
+	}
+
+	/* Device */
+	acpigen_write_scope(scope);
+	acpigen_write_device(acpi_device_name(dev));
+	acpigen_write_name_string("_HID", config->hid);
+	acpigen_write_name_integer("_UID", config->uid);
+	acpigen_write_name_string("_DDN", config->desc);
+	acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_ON);
+
+	/* Resources */
+	acpigen_write_name("_CRS");
+	acpigen_write_resourcetemplate_header();
+	acpi_device_write_i2c(&i2c);
+	acpi_device_write_interrupt(&config->irq);
+	acpigen_write_resourcetemplate_footer();
+
+	/* Wake capabilities */
+	if (config->wake) {
+		acpigen_write_name_integer("_S0W", 4);
+		acpigen_write_PRW(config->wake, 3);
+	}
+
+	acpigen_pop_len(); /* Device */
+	acpigen_pop_len(); /* Scope */
+
+	printk(BIOS_INFO, "%s: %s at %s\n", acpi_device_path(dev),
+	       config->desc ? : dev->chip_ops->name, dev_path(dev));
+}
+
+/* Use name specified in config or build one from I2C address */
+static const char *i2c_generic_acpi_name(struct device *dev)
+{
+	struct drivers_i2c_generic_config *config = dev->chip_info;
+	static char name[5];
+
+	if (config->name)
+		return name;
+
+	snprintf(name, sizeof(name), "D%03X", dev->path.i2c.device);
+	return name;
+}
+#endif
+
+static struct device_operations i2c_generic_ops = {
+	.read_resources		  = DEVICE_NOOP,
+	.set_resources		  = DEVICE_NOOP,
+	.enable_resources	  = DEVICE_NOOP,
+#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
+	.acpi_name		  = &i2c_generic_acpi_name,
+	.acpi_fill_ssdt_generator = &i2c_generic_fill_ssdt,
+#endif
+};
+
+static void i2c_generic_enable(struct device *dev)
+{
+	struct drivers_i2c_generic_config *config = dev->chip_info;
+
+	/* Check if device is present by reading GPIO */
+	if (config->device_present_gpio) {
+		int present = gpio_get(config->device_present_gpio);
+		present ^= config->device_present_gpio_invert;
+
+		printk(BIOS_INFO, "%s is %spresent\n",
+		       dev->chip_ops->name, present ? "" : "not ");
+
+		if (!present) {
+			dev->enabled = 0;
+			return;
+		}
+	}
+
+	dev->ops = &i2c_generic_ops;
+}
+
+struct chip_operations drivers_i2c_generic_ops = {
+	CHIP_NAME("I2C Device")
+	.enable_dev = &i2c_generic_enable
+};



More information about the coreboot-gerrit mailing list