driver/i2c: Add sx9360 driver

Add driver for setting up Semtech sx9360 SAR sensor.
The driver is based on sx9310.c. The core of the driver is the same, but
the bindings are slightly different.

Registers are documented in the kernel tree:
Documentation/devicetree/bindings/iio/proximity/semtech,sx9360.yaml
[https://patchwork.kernel.org/project/linux-iio/patch/20211213024057.3824985-4-gwendal@chromium.org/]

Signed-off-by: Seunghwan Kim <sh_.kim@samsung.corp-partner.google.com>
Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
Change-Id: I0a912f184e6f3501f894cca24c0d71a2c3087516
Reviewed-on: https://review.coreboot.org/c/coreboot/+/59608
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Paul Menzel <paulepanter@mailbox.org>
Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
diff --git a/src/drivers/i2c/sx9360/Kconfig b/src/drivers/i2c/sx9360/Kconfig
new file mode 100644
index 0000000..ea28564
--- /dev/null
+++ b/src/drivers/i2c/sx9360/Kconfig
@@ -0,0 +1,6 @@
+config DRIVERS_I2C_SX9360
+	bool
+	default n
+	depends on HAVE_ACPI_TABLES
+	help
+	  Board has a Semtech SX9360 proximity sensor.
diff --git a/src/drivers/i2c/sx9360/Makefile.inc b/src/drivers/i2c/sx9360/Makefile.inc
new file mode 100644
index 0000000..695bb52
--- /dev/null
+++ b/src/drivers/i2c/sx9360/Makefile.inc
@@ -0,0 +1 @@
+ramstage-$(CONFIG_DRIVERS_I2C_SX9360) += sx9360.c
diff --git a/src/drivers/i2c/sx9360/chip.h b/src/drivers/i2c/sx9360/chip.h
new file mode 100644
index 0000000..397ea3f
--- /dev/null
+++ b/src/drivers/i2c/sx9360/chip.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __DRIVERS_I2C_SX9360_CHIP_H__
+#define __DRIVERS_I2C_SX9360_CHIP_H__
+
+#include <acpi/acpi_device.h>
+#include <device/i2c_simple.h>
+
+struct drivers_i2c_sx9360_config {
+	/* Device Description */
+	const char *desc;
+
+	/* ACPI _UID */
+	unsigned int uid;
+
+	/* Bus speed in Hz, default is I2C_SPEED_FAST */
+	enum i2c_speed speed;
+
+	/* Use GPIO-based interrupt instead of IO-APIC */
+	struct acpi_gpio irq_gpio;
+
+	/* IO-APIC interrupt */
+	struct acpi_irq irq;
+
+	/*
+	 * Registers definition in the kernel source tree at:
+	 * Documentation/devicetree/bindings/iio/proximity/semtech,sx9360.yaml
+	 */
+
+	/* Raw Proximity filter strength: When not set, disabled. */
+	uint32_t proxraw_strength;
+
+	/* Average Proximity filter strength: When not set, disabled. */
+	uint32_t avg_pos_strength;
+
+	/* Capacitance measure resolution. Driver default: 128. */
+	uint32_t resolution;
+};
+
+#endif /* __DRIVERS_I2C_SX9360_CHIP_H__ */
diff --git a/src/drivers/i2c/sx9360/sx9360.c b/src/drivers/i2c/sx9360/sx9360.c
new file mode 100644
index 0000000..9ea4c8b
--- /dev/null
+++ b/src/drivers/i2c/sx9360/sx9360.c
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <acpi/acpi_device.h>
+#include <acpi/acpigen.h>
+#include <console/console.h>
+#include <device/i2c_simple.h>
+#include <device/device.h>
+#include <device/path.h>
+#include <string.h>
+#include "chip.h"
+
+#define I2C_SX9360_ACPI_ID	"STH9360"
+#define I2C_SX9360_CHIP_NAME	"Semtech SX9360"
+
+static void i2c_sx9360_fill_ssdt(const struct device *dev)
+{
+	struct drivers_i2c_sx9360_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 = I2C_SPEED_FAST,
+		.resource = scope,
+	};
+	struct acpi_dp *dsd;
+
+	if (!scope || !config)
+		return;
+
+	if (config->speed)
+		i2c.speed = config->speed;
+
+	/* Device */
+	acpigen_write_scope(scope);
+	acpigen_write_device(acpi_device_name(dev));
+	acpigen_write_name_string("_HID", I2C_SX9360_ACPI_ID);
+	acpigen_write_name_integer("_UID", config->uid);
+	acpigen_write_name_string("_DDN", config->desc);
+	acpigen_write_STA(acpi_device_status(dev));
+
+	/* Resources */
+	acpigen_write_name("_CRS");
+	acpigen_write_resourcetemplate_header();
+	acpi_device_write_i2c(&i2c);
+
+	if (config->irq_gpio.pin_count)
+		acpi_device_write_gpio(&config->irq_gpio);
+	else
+		acpi_device_write_interrupt(&config->irq);
+
+	acpigen_write_resourcetemplate_footer();
+
+	/* DSD */
+	dsd = acpi_dp_new_table("_DSD");
+
+	/*
+	 * Format described in linux kernel documentation. See
+	 * https://www.kernel.org/doc/Documentation/devicetree/bindings/iio/proximity/semtech%2Csx9360.yaml
+	 */
+	acpi_dp_add_integer(dsd, "semtech,proxraw-strength",
+			    config->proxraw_strength);
+	acpi_dp_add_integer(dsd, "semtech,avg-pos-strength",
+			    config->avg_pos_strength);
+	acpi_dp_add_integer(dsd, "semtech,resolution",
+			    config->resolution);
+
+	acpi_dp_write(dsd);
+
+	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));
+}
+
+static const char *i2c_sx9360_acpi_name(const struct device *dev)
+{
+	static char name[5];
+
+	snprintf(name, sizeof(name), "SX%02.2X", dev->path.i2c.device);
+	return name;
+}
+
+static struct device_operations i2c_sx9360_ops = {
+	.read_resources		= noop_read_resources,
+	.set_resources		= noop_set_resources,
+	.acpi_name		= i2c_sx9360_acpi_name,
+	.acpi_fill_ssdt		= i2c_sx9360_fill_ssdt,
+};
+
+static void i2c_sx9360_enable(struct device *dev)
+{
+	struct drivers_i2c_sx9360_config *config = config_of(dev);
+
+	if (!is_dev_enabled(dev))
+		return;
+
+	dev->ops = &i2c_sx9360_ops;
+
+	if (config->desc)
+		dev->name = config->desc;
+}
+
+struct chip_operations drivers_i2c_sx9360_ops = {
+	CHIP_NAME(I2C_SX9360_CHIP_NAME)
+	.enable_dev = i2c_sx9360_enable
+};