summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/drivers/i2c/cs35l53/Kconfig3
-rw-r--r--src/drivers/i2c/cs35l53/Makefile.inc1
-rw-r--r--src/drivers/i2c/cs35l53/chip.h149
-rw-r--r--src/drivers/i2c/cs35l53/cs35l53.c147
4 files changed, 300 insertions, 0 deletions
diff --git a/src/drivers/i2c/cs35l53/Kconfig b/src/drivers/i2c/cs35l53/Kconfig
new file mode 100644
index 0000000000..38d6617a39
--- /dev/null
+++ b/src/drivers/i2c/cs35l53/Kconfig
@@ -0,0 +1,3 @@
+config DRIVERS_I2C_CS35L53
+ bool
+ depends on HAVE_ACPI_TABLES
diff --git a/src/drivers/i2c/cs35l53/Makefile.inc b/src/drivers/i2c/cs35l53/Makefile.inc
new file mode 100644
index 0000000000..21a749f199
--- /dev/null
+++ b/src/drivers/i2c/cs35l53/Makefile.inc
@@ -0,0 +1 @@
+ramstage-$(CONFIG_DRIVERS_I2C_CS35L53) += cs35l53.c
diff --git a/src/drivers/i2c/cs35l53/chip.h b/src/drivers/i2c/cs35l53/chip.h
new file mode 100644
index 0000000000..9aeb874011
--- /dev/null
+++ b/src/drivers/i2c/cs35l53/chip.h
@@ -0,0 +1,149 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <acpi/acpi_device.h>
+
+#define CS35L53_MAX_GPIOS 2
+
+enum cs35l53_boost_type {
+ INTERNAL_BOOST = 0,
+ EXTERNAL_BOOST = 1,
+};
+
+enum cs35l53_boost_ind_nanohenrys {
+ BOOST_IND_1000_NH = 1000,
+ BOOST_IND_1200_NH = 1200,
+ BOOST_IND_1500_NH = 1500,
+ BOOST_IND_2200_NH = 2200,
+};
+
+enum cs35l53_asp_sdout_hiz {
+ ASP_SDOUT_LOGIC0_UNUSED_LOGIC0_DISABLED = 0,
+ ASP_SDOUT_HIZ_UNUSED_LOGIC0_DISABLED = 1,
+ ASP_SDOUT_LOGIC0_UNUSED_HIZ_DISABLED = 2,
+ ASP_SDOUT_HIZ_UNUSED_HIZ_DISABLED = 3,
+};
+
+enum cs35l53_gpio1_src {
+ GPIO1_SRC_HIGH_IMPEDANCE = 0,
+ GPIO1_SRC_GPIO = 1,
+ GPIO1_SRC_SYNC = 2,
+ GPIO1_SRC_MCLK_INPUT = 3,
+};
+
+enum cs35l53_gpio2_src {
+ GPIO2_SRC_HIGH_IMPEDANCE = 0,
+ GPIO2_SRC_GPIO = 1,
+ GPIO2_SRC_OPEN_DRAIN = 2,
+ GPIO2_SRC_MCLK_INPUT = 3,
+ GPIO2_SRC_PUSH_PULL_INTB = 4,
+ GPIO2_SRC_PUSH_PULL_INT = 5,
+};
+
+/*
+ * Cirrus Logic CS35L53 Audio Codec devicetree bindings
+ * linux/Documentation/devicetree/bindings/sound/cirrus,cs35l53.yaml
+ */
+struct drivers_i2c_cs35l53_config {
+ const char *name; /* ACPI Device Name */
+
+ const char *sub; /* SUB ID to uniquely identify system */
+
+ /* Interrupt configuration */
+ struct acpi_irq irq;
+
+ /* Use GPIO based interrupt instead of PIRQ */
+ struct acpi_gpio irq_gpio;
+
+ /* Use GPIO based reset gpio */
+ struct acpi_gpio reset_gpio;
+
+ /* I2C Bus Frequency in Hertz (default 400kHz) */
+ unsigned int bus_speed;
+
+ /* Define cs35l53 parameters */
+ /*
+ * cirrus,boost-type : Configures the type of Boost being used.
+ * Internal boost requires boost-peak-milliamp, boost-ind-nanohenry and
+ * boost-cap-microfarad.
+ * External Boost must have GPIO1 as GPIO output. GPIO1 will be set high to
+ * enable boost voltage.
+ */
+ enum cs35l53_boost_type boost_type;
+
+ /*
+ * cirrus,boost-peak-milliamp : Boost-converter peak current limit in mA.
+ * Configures the peak current by monitoring the current through the boost FET.
+ * Range starts at 1600 mA and goes to a maximum of 4500 mA with increments
+ * of 50 mA. See section 4.3.6 of the datasheet for details.
+ */
+ unsigned int boost_peak_milliamp;
+
+ /*
+ * cirrus,boost-ind-nanohenry : Boost inductor value, expressed in nH. Valid
+ * values include 1000, 1200, 1500 and 2200.
+ */
+ enum cs35l53_boost_ind_nanohenrys boost_ind_nanohenry;
+
+ /*
+ * cirrus,boost-cap-microfarad : Total equivalent boost capacitance on the VBST
+ * and VAMP pins, derated at 11 volts DC. The value must be rounded to the
+ * nearest integer and expressed in uF.
+ */
+ unsigned int boost_cap_microfarad;
+
+ /*
+ * cirrus,asp-sdout-hiz : Audio serial port SDOUT Hi-Z control. Sets the Hi-Z
+ * configuration for SDOUT pin of amplifier.
+ * 0 = Logic 0 during unused slots, and while all transmit channels disabled
+ * 1 = Hi-Z during unused slots but logic 0 while all transmit channels disabled
+ * 2 = Logic 0 during unused slots, but Hi-Z while all transmit channels disabled
+ * 3 = Hi-Z during unused slots and while all transmit channels disabled
+ */
+ enum cs35l53_asp_sdout_hiz asp_sdout_hiz;
+
+ /*
+ * cirrus,gpio1-polarity-invert : Boolean which specifies whether the GPIO1
+ * level is inverted.
+ */
+ bool gpio1_polarity_invert;
+
+ /*
+ * cirrus,gpio2-polarity-invert : Boolean which specifies whether the GPIO2
+ * level is inverted.
+ */
+ bool gpio2_polarity_invert;
+
+ /*
+ * cirrus,gpio1-output-enable : Boolean which specifies whether the GPIO1 pin
+ * is configured as an output.
+ */
+ bool gpio1_output_enable;
+
+ /*
+ * cirrus,gpio2-output-enable : Boolean which specifies whether the GPIO2 pin
+ * is configured as an output.
+ */
+ bool gpio2_output_enable;
+
+ /*
+ * cirrus,gpio1-src-select : Configures the function of the GPIO1 pin.
+ * GPIO1:
+ * 0 = High Impedance (Default)
+ * 1 = GPIO
+ * 2 = Sync
+ * 3 = MCLK input
+ */
+ enum cs35l53_gpio1_src gpio1_src_select;
+
+ /*
+ * cirrus,gpio2-src-select : Configures the function of the GPIO2 pin.
+ * GPIO2:
+ * 0 = High Impedance (Default)
+ * 1 = GPIO
+ * 2 = Open Drain INTB
+ * 3 = MCLK input
+ * 4 = Push-pull INTB (active low)
+ * 5 = Push-pull INT (active high)
+ */
+ enum cs35l53_gpio2_src gpio2_src_select;
+};
diff --git a/src/drivers/i2c/cs35l53/cs35l53.c b/src/drivers/i2c/cs35l53/cs35l53.c
new file mode 100644
index 0000000000..1909bef7b5
--- /dev/null
+++ b/src/drivers/i2c/cs35l53/cs35l53.c
@@ -0,0 +1,147 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <acpi/acpi.h>
+#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 "chip.h"
+
+#define CS35L53_ACPI_HID "CSC3541"
+
+static void cs35l53_fill_ssdt(const struct device *dev)
+{
+ struct drivers_i2c_cs35l53_config *config = dev->chip_info;
+ const char *scope = acpi_device_scope(dev);
+ const char *path = acpi_device_path(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,
+ };
+ struct acpi_dp *dsd;
+ int gpio_index = 0;
+
+ if (!scope)
+ return;
+
+ /* Device */
+ acpigen_write_scope(scope);
+ acpigen_write_device(acpi_device_name(dev));
+ acpigen_write_name_string("_HID", CS35L53_ACPI_HID);
+ acpigen_write_name_integer("_UID", 0);
+ acpigen_write_name_string("_DDN", dev->chip_ops->name);
+ acpigen_write_name_string("_SUB", config->sub);
+ acpigen_write_STA(acpi_device_status(dev));
+
+ /* Resources */
+ acpigen_write_name("_CRS");
+ acpigen_write_resourcetemplate_header();
+ acpi_device_write_i2c(&i2c);
+ /* Use either Interrupt() or GpioInt() */
+ if (config->irq_gpio.pin_count)
+ acpi_device_write_gpio(&config->irq_gpio);
+ else
+ acpi_device_write_interrupt(&config->irq);
+
+ /* for cs35l53 reset gpio */
+ if (config->reset_gpio.pin_count)
+ acpi_device_write_gpio(&config->reset_gpio);
+
+ acpigen_write_resourcetemplate_footer();
+
+ /* Add Child Device Properties */
+ dsd = acpi_dp_new_table("_DSD");
+ if (config->irq_gpio.pin_count)
+ acpi_dp_add_gpio(dsd, "irq-gpios", path,
+ gpio_index++, /* Index = 0 */
+ 0, /* Pin = 0 (There is a single pin in the GPIO resource). */
+ config->irq_gpio.active_low);
+ if (config->reset_gpio.pin_count)
+ acpi_dp_add_gpio(dsd, "reset-gpios", path,
+ gpio_index++, /* Index = 0 or 1 (if irq gpio is written). */
+ 0, /* Pin = 0 (There is a single pin in the GPIO resource). */
+ config->reset_gpio.active_low);
+
+ acpi_dp_add_integer(dsd, "cirrus,boost-type", config->boost_type);
+
+ switch (config->boost_type) {
+ case INTERNAL_BOOST:
+ if ((config->boost_peak_milliamp > 4500) ||
+ (config->boost_peak_milliamp < 1600) ||
+ (config->boost_peak_milliamp % 50)) {
+ printk(BIOS_ERR,
+ "%s: Incorrect boost_peak_milliamp(%d). Using default of 4500 mA\n",
+ __func__, config->boost_peak_milliamp);
+ config->boost_peak_milliamp = 4500;
+ }
+ acpi_dp_add_integer(dsd, "cirrus,boost-peak-milliamp",
+ config->boost_peak_milliamp);
+ acpi_dp_add_integer(dsd, "cirrus,boost-ind-nanohenry",
+ config->boost_ind_nanohenry);
+ acpi_dp_add_integer(dsd, "cirrus,boost-cap-microfarad",
+ config->boost_cap_microfarad);
+ break;
+ case EXTERNAL_BOOST:
+ config->gpio1_output_enable = true;
+ config->gpio1_src_select = GPIO1_SRC_GPIO;
+ break;
+ default:
+ break;
+ }
+
+ acpi_dp_add_integer(dsd, "cirrus,asp-sdout-hiz", config->asp_sdout_hiz);
+ acpi_dp_add_integer(dsd, "cirrus,gpio1-polarity-invert",
+ config->gpio1_polarity_invert);
+ acpi_dp_add_integer(dsd, "cirrus,gpio1-output-enable",
+ config->gpio1_output_enable);
+ acpi_dp_add_integer(dsd, "cirrus,gpio1-src-select", config->gpio1_src_select);
+ acpi_dp_add_integer(dsd, "cirrus,gpio2-polarity-invert",
+ config->gpio2_polarity_invert);
+ acpi_dp_add_integer(dsd, "cirrus,gpio2-output-enable",
+ config->gpio2_output_enable);
+ acpi_dp_add_integer(dsd, "cirrus,gpio2-src-select", config->gpio2_src_select);
+
+ /* Write Device Property Hierarchy */
+ acpi_dp_write(dsd);
+
+ acpigen_pop_len(); /* Device */
+ acpigen_pop_len(); /* Scope */
+
+ printk(BIOS_INFO, "%s: %s address 0%xh irq %d\n",
+ acpi_device_path(dev), dev->chip_ops->name,
+ dev->path.i2c.device, config->irq.pin);
+}
+
+static const char *cs35l53_acpi_name(const struct device *dev)
+{
+ struct drivers_i2c_cs35l53_config *config = dev->chip_info;
+ static char name[ACPI_NAME_BUFFER_SIZE];
+
+ if (config->name)
+ return config->name;
+
+ snprintf(name, sizeof(name), "D%03.3X", dev->path.i2c.device);
+ return name;
+}
+
+static struct device_operations cs35l53_ops = {
+ .read_resources = noop_read_resources,
+ .set_resources = noop_set_resources,
+ .acpi_name = cs35l53_acpi_name,
+ .acpi_fill_ssdt = cs35l53_fill_ssdt,
+};
+
+static void cs35l53_enable(struct device *dev)
+{
+ dev->ops = &cs35l53_ops;
+}
+
+struct chip_operations drivers_i2c_cs35l53_ops = {
+ CHIP_NAME("Cirrus Logic CS35L53 Audio Codec")
+ .enable_dev = cs35l53_enable
+};