[coreboot-gerrit] Patch set updated for coreboot: soc/intel/common/lpss_i2c: configure buses by rise/fall times

Aaron Durbin (adurbin@chromium.org) gerrit at coreboot.org
Thu Nov 10 07:37:43 CET 2016


Aaron Durbin (adurbin at chromium.org) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/17350

-gerrit

commit daed99d369d8e29295750120e33418a36d085386
Author: Aaron Durbin <adurbin at chromium.org>
Date:   Wed Nov 9 23:20:30 2016 -0600

    soc/intel/common/lpss_i2c: configure buses by rise/fall times
    
    The default register count calculations are leading to higher
    frequencies than expected. Provide an alternative method for
    calculating the register counts by utilizing the rise and
    fall times of the bus. If the rise time is supplied the
    rise/fall time values take precedence over any other bus
    configuration method. This allows platforms to choose which
    ever method works the best.
    
    BUG=chrome-os-partner:58889
    
    Change-Id: I7747613ce51d8151848acd916c09ae97bfc4b86a
    Signed-off-by: Aaron Durbin <adurbin at chromium.org>
---
 src/soc/intel/apollolake/i2c.c  |   2 +-
 src/soc/intel/common/lpss_i2c.c | 208 +++++++++++++++++++++++++++++++++++++++-
 src/soc/intel/common/lpss_i2c.h |   8 +-
 src/soc/intel/skylake/i2c.c     |   2 +-
 4 files changed, 213 insertions(+), 7 deletions(-)

diff --git a/src/soc/intel/apollolake/i2c.c b/src/soc/intel/apollolake/i2c.c
index 69cb4b8..bb30c51 100644
--- a/src/soc/intel/apollolake/i2c.c
+++ b/src/soc/intel/apollolake/i2c.c
@@ -77,7 +77,7 @@ static void i2c_fill_ssdt(struct device *dev)
 		return;
 
 	acpigen_write_scope(acpi_device_path(dev));
-	lpss_i2c_acpi_fill_ssdt(&config->i2c[bus]);
+	lpss_i2c_acpi_fill_ssdt(bus, &config->i2c[bus]);
 	acpigen_pop_len();
 }
 
diff --git a/src/soc/intel/common/lpss_i2c.c b/src/soc/intel/common/lpss_i2c.c
index 51c5915..3b66aff 100644
--- a/src/soc/intel/common/lpss_i2c.c
+++ b/src/soc/intel/common/lpss_i2c.c
@@ -24,6 +24,8 @@
 #include <timer.h>
 #include "lpss_i2c.h"
 
+#define LPSS_DEBUG BIOS_DEBUG
+
 struct lpss_i2c_regs {
 	uint32_t control;
 	uint32_t target_addr;
@@ -94,6 +96,78 @@ enum {
 	MIN_HS_SCL_LOWTIME		= 160,
 };
 
+/* Frequency represented as ticks per ns. Can also be used to calculate
+ * the number of ticks to meet a time target or the period. */
+struct freq {
+	uint32_t ticks;
+	uint32_t ns;
+};
+
+static const struct i2c_descriptor {
+	enum i2c_speed speed;
+	struct freq freq;
+	int min_thigh_ns;
+	int min_tlow_ns;
+} speed_descriptors[] = {
+	{
+		.speed = I2C_SPEED_STANDARD,
+		.freq = {
+			.ticks = 100,
+			.ns = 1000*1000,
+		},
+		.min_thigh_ns = MIN_SS_SCL_HIGHTIME,
+		.min_tlow_ns = MIN_SS_SCL_LOWTIME,
+	},
+	{
+		.speed = I2C_SPEED_FAST,
+		.freq = {
+			.ticks = 400,
+			.ns = 1000*1000,
+		},
+		.min_thigh_ns = MIN_FS_SCL_HIGHTIME,
+		.min_tlow_ns = MIN_FS_SCL_LOWTIME,
+	},
+	{
+		.speed = I2C_SPEED_FAST_PLUS,
+		.freq = {
+			.ticks = 1,
+			.ns = 1000,
+		},
+		.min_thigh_ns = MIN_FP_SCL_HIGHTIME,
+		.min_tlow_ns = MIN_FP_SCL_LOWTIME,
+	},
+	{
+		/* 100pF max capacitance */
+		.speed = I2C_SPEED_HIGH,
+		.freq = {
+			.ticks = 3400,
+			.ns = 1000*1000,
+		},
+		.min_thigh_ns = MIN_HS_SCL_HIGHTIME,
+		.min_tlow_ns = MIN_HS_SCL_LOWTIME,
+	},
+};
+
+static const struct soc_clock {
+	int clk_speed_mhz;
+	struct freq freq;
+} soc_clocks[] = {
+	{
+		.clk_speed_mhz = 120,
+		.freq = {
+			.ticks = 120,
+			.ns = 1000,
+		},
+	},
+	{
+		.clk_speed_mhz = 133,
+		.freq = {
+			.ticks = 400,
+			.ns = 3000,
+		},
+	},
+};
+
 /* Control register definitions */
 enum {
 	CONTROL_MASTER_MODE		= (1 << 0),
@@ -145,6 +219,38 @@ enum {
 	INTR_STAT_GEN_CALL		= (1 << 11),
 };
 
+static const struct i2c_descriptor *get_bus_descriptor(enum i2c_speed speed)
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(speed_descriptors); i++)
+		if (speed == speed_descriptors[i].speed)
+			return &speed_descriptors[i];
+
+	return NULL;
+}
+
+static const struct soc_clock *get_soc_descriptor(int ic_clk)
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(soc_clocks); i++)
+		if (ic_clk == soc_clocks[i].clk_speed_mhz)
+			return &soc_clocks[i];
+
+	return NULL;
+}
+
+static int counts_from_time(const struct freq *f, int ns)
+{
+	return DIV_ROUND_UP(f->ticks * ns, f->ns);
+}
+
+static int counts_from_freq(const struct freq *fast, const struct freq *slow)
+{
+	return DIV_ROUND_UP(fast->ticks * slow->ns, fast->ns * slow->ticks);
+}
+
 /* Enable this I2C controller */
 static void lpss_i2c_enable(struct lpss_i2c_regs *regs)
 {
@@ -411,7 +517,90 @@ static int lpss_i2c_set_speed_config(unsigned bus,
 	return 0;
 }
 
-static int lpss_i2c_gen_speed_config(enum i2c_speed speed,
+static int lpss_i2c_gen_config_rise_fall_time(struct lpss_i2c_regs *regs,
+					enum i2c_speed speed,
+					const struct lpss_i2c_bus_config *bcfg,
+					int ic_clk,
+					struct lpss_i2c_speed_config *config)
+{
+	const struct i2c_descriptor *bus;
+	const struct soc_clock *soc;
+	int fall_cnt, rise_cnt, min_tlow_cnt, min_thigh_cnt, spk_cnt;
+	int hcnt, lcnt, period_cnt, diff, tot;
+
+	bus = get_bus_descriptor(speed);
+	soc = get_soc_descriptor(ic_clk);
+
+	if (bus == NULL) {
+		printk(BIOS_ERR, "lpss_i2c: invalid bus speed %d\n",
+			config->speed);
+		return -1;
+	}
+
+	if (soc == NULL) {
+		printk(BIOS_ERR, "lpss_i2c: invalid SoC clock speed %d MHz\n",
+			soc->clk_speed_mhz);
+		return -1;
+	}
+
+	/* Get the proper spike suppression count based on target speed. */
+	if (speed >= I2C_SPEED_HIGH)
+		spk_cnt = read32(&regs->hs_spklen);
+	else
+		spk_cnt = read32(&regs->fs_spklen);
+
+	/* Find the period, rise, fall, min tlow, and min thigh in terms of
+	 * counts of SoC clock. */
+	period_cnt = counts_from_freq(&soc->freq, &bus->freq);
+	rise_cnt = counts_from_time(&soc->freq, bcfg->rise_time_ns);
+	fall_cnt = counts_from_time(&soc->freq, bcfg->fall_time_ns);
+	min_tlow_cnt = counts_from_time(&soc->freq, bus->min_tlow_ns);
+	min_thigh_cnt = counts_from_time(&soc->freq, bus->min_thigh_ns);
+
+	printk(LPSS_DEBUG, "lpss_i2c: SoC %d/%d ns Bus: %d/%d ns\n",
+		soc->freq.ticks, soc->freq.ns, bus->freq.ticks, bus->freq.ns);
+	printk(LPSS_DEBUG, "lpss_i2c: period %d rise %d fall %d tlow %d thigh %d spk %d\n",
+		period_cnt, rise_cnt, fall_cnt, min_tlow_cnt, min_thigh_cnt,
+		spk_cnt);
+
+	/*
+	 * Back solve for hcnt and lcnt according to the following equations.
+	 * SCL_High_time = [(HCNT + IC_*_SPKLEN + 7) * ic_clk] + SCL_Fall_time
+	 * SCL_Low_time = [(LCNT + 1) * ic_clk] - SCL_Fall_time + SCL_Rise_time
+	 */
+	hcnt = min_thigh_cnt - fall_cnt - 7 - spk_cnt;
+	lcnt = min_tlow_cnt - rise_cnt + fall_cnt - 1;
+
+	if (hcnt < 0 || lcnt < 0) {
+		printk(BIOS_ERR, "lpss_i2c: bad counts. hcnt = %d lcnt = %d\n",
+			hcnt, lcnt);
+		return -1;
+	}
+
+	/* Now add things back up to ensure the period is hit. If off,
+	 * split the difference and bias to lcnt for remainder. */
+	tot = hcnt + lcnt + 7 + spk_cnt + rise_cnt + 1;
+
+	if (tot < period_cnt) {
+		diff = (period_cnt - tot) / 2;
+		hcnt += diff;
+		lcnt += diff;
+		tot = hcnt + lcnt + 7 + spk_cnt + rise_cnt + 1;
+		lcnt += period_cnt - tot;
+	}
+
+	config->scl_lcnt = lcnt;
+	config->scl_hcnt = hcnt;
+	config->sda_hold = counts_from_time(&soc->freq, DEFAULT_SDA_HOLD_TIME);
+
+	printk(LPSS_DEBUG, "lpss_i2c: hcnt = %d lcnt = %d sda hold = %d\n",
+		hcnt, lcnt, config->sda_hold);
+
+	return 0;
+}
+
+static int lpss_i2c_gen_speed_config(struct lpss_i2c_regs *regs,
+					enum i2c_speed speed,
 					const struct lpss_i2c_bus_config *bcfg,
 					struct lpss_i2c_speed_config *config)
 {
@@ -423,6 +612,11 @@ static int lpss_i2c_gen_speed_config(enum i2c_speed speed,
 	if (!ic_clk)
 		return -1;
 
+	/* If rise time is set use the time calculation. */
+	if (bcfg->rise_time_ns)
+		return lpss_i2c_gen_config_rise_fall_time(regs, speed, bcfg,
+							ic_clk, config);
+
 	/* Apply board specific override for this speed if found */
 	for (i = 0; i < LPSS_I2C_SPEED_CONFIG_COUNT; i++) {
 		if (bcfg->speed_config[i].speed != speed)
@@ -484,7 +678,7 @@ static int lpss_i2c_set_speed(unsigned bus, enum i2c_speed speed,
 	}
 
 	/* Generate speed config based on clock */
-	if (lpss_i2c_gen_speed_config(speed, bcfg, &config) < 0)
+	if (lpss_i2c_gen_speed_config(regs, speed, bcfg, &config) < 0)
 		return -1;
 
 	/* Select this speed in the control register */
@@ -496,8 +690,10 @@ static int lpss_i2c_set_speed(unsigned bus, enum i2c_speed speed,
 	return 0;
 }
 
-void lpss_i2c_acpi_fill_ssdt(const struct lpss_i2c_bus_config *bcfg)
+void lpss_i2c_acpi_fill_ssdt(unsigned bus,
+				const struct lpss_i2c_bus_config *bcfg)
 {
+	struct lpss_i2c_regs *regs;
 	struct lpss_i2c_speed_config sgen;
 	enum i2c_speed speeds[LPSS_I2C_SPEED_CONFIG_COUNT] = {
 		I2C_SPEED_STANDARD,
@@ -510,10 +706,14 @@ void lpss_i2c_acpi_fill_ssdt(const struct lpss_i2c_bus_config *bcfg)
 	if (!bcfg)
 		return;
 
+	regs = (struct lpss_i2c_regs *)lpss_i2c_base_address(bus);
+	if (!regs)
+		return;
+
 	/* Report timing values for the OS driver */
 	for (i = 0; i < LPSS_I2C_SPEED_CONFIG_COUNT; i++) {
 		/* Generate speed config. */
-		if (lpss_i2c_gen_speed_config(speeds[i], bcfg, &sgen) < 0)
+		if (lpss_i2c_gen_speed_config(regs, speeds[i], bcfg, &sgen) < 0)
 			continue;
 
 		/* Generate ACPI based on selected speed config */
diff --git a/src/soc/intel/common/lpss_i2c.h b/src/soc/intel/common/lpss_i2c.h
index f4a48bd..9a5d62b 100644
--- a/src/soc/intel/common/lpss_i2c.h
+++ b/src/soc/intel/common/lpss_i2c.h
@@ -55,6 +55,11 @@ struct lpss_i2c_bus_config {
 	int early_init;
 	/* Bus speed in Hz, default is I2C_SPEED_FAST (400 KHz) */
 	enum i2c_speed speed;
+	/* If rise_time_ns is non-zero the calculations for lcnt and hcnt
+	 * registers take into account the times of the bus below instead of
+	 * using the lpss_i2c_speed_config settings. */
+	int rise_time_ns;
+	int fall_time_ns;
 	/* Specific bus speed configuration */
 	struct lpss_i2c_speed_config speed_config[LPSS_I2C_SPEED_CONFIG_COUNT];
 };
@@ -79,7 +84,8 @@ uintptr_t lpss_i2c_base_address(unsigned bus);
  * Generate I2C timing information into the SSDT for the OS driver to consume,
  * optionally applying override values provided by the caller.
  */
-void lpss_i2c_acpi_fill_ssdt(const struct lpss_i2c_bus_config *bcfg);
+void lpss_i2c_acpi_fill_ssdt(unsigned bus,
+				const struct lpss_i2c_bus_config *bcfg);
 
 /*
  * Initialize this bus controller and set the speed.
diff --git a/src/soc/intel/skylake/i2c.c b/src/soc/intel/skylake/i2c.c
index c8f0294..d12323f 100644
--- a/src/soc/intel/skylake/i2c.c
+++ b/src/soc/intel/skylake/i2c.c
@@ -74,7 +74,7 @@ static void i2c_fill_ssdt(struct device *dev)
 		return;
 
 	acpigen_write_scope(acpi_device_path(dev));
-	lpss_i2c_acpi_fill_ssdt(&config->i2c[bus]);
+	lpss_i2c_acpi_fill_ssdt(bus, &config->i2c[bus]);
 	acpigen_pop_len();
 }
 



More information about the coreboot-gerrit mailing list