diff mbox series

[v3,39/57] tpm: cr50: Add ACPI support

Message ID 20200906154340.v3.39.I1a9174fd9af26d0891e4ccfac247c7359b28b23e@changeid
State Superseded
Delegated to: Bin Meng
Headers show
Series dm: Add programatic generation of ACPI tables (part D) | expand

Commit Message

Simon Glass Sept. 6, 2020, 9:43 p.m. UTC
Generate ACPI information for this device so that Linux can use it
correctly.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

(no changes since v1)

Changes in v1:
- Capitalise ACPI_OPS_PTR
- Update for acpi_device_write_i2c_dev() return-value change
- Use acpi,ddn instead of acpi,desc

 drivers/tpm/cr50_i2c.c | 55 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

Comments

Andy Shevchenko Sept. 21, 2020, 11:50 a.m. UTC | #1
On Sun, Sep 06, 2020 at 03:43:47PM -0600, Simon Glass wrote:
> Generate ACPI information for this device so that Linux can use it
> correctly.

> +	ret = acpi_device_write_interrupt_or_gpio(ctx, (struct udevice *)dev,
> +						  "ready-gpios");
> +	if (ret < 0)
> +		return log_msg_ret("irq_gpio", ret);

I looked a bit closer at the acpi_table.c and would like to emphasize on
lessons learn from BIOS mistakes found in the wild with GPIOs.

Because GPIO resources are quite badly described in ACPI (it seems MS failed to
deliver GPIO abstraction to ACPI and to Windows API), there are some corner
cases, in order to mitigate which we need to consider the following to avoid
potential glitches and misconfiguration:

- GpioIo() doesn't have any means of Active Low / High setting, the _DSD must
  be provided to mitigate this.

- GpioIo() doesn't properly communicate the initial state of the output pin,
  thus Linux assumes the simple rule:

  Pull Bias	  Polarity	Requested...

  Implicit	  x		AS IS (assumed firmware configured for us)
  Explicit	  x (no _DSD)	as Pull Bias (Up == High, Down == Low),
  		  		assuming non-active (Polarity = !Pull Bias)

  Down		  Low		as low, assuming active
  Down		  High		as high, assuming non-active
  Up		  Low		as low, assuming non-active
  Up		  High		as high, assuming active

Hopefully this helps (and maybe can be added to some documentation).

P.S. Why I2cSerialBus() and not I2cSerialBusV2() ?
Andy Shevchenko Sept. 21, 2020, 11:53 a.m. UTC | #2
On Mon, Sep 21, 2020 at 02:50:56PM +0300, Andy Shevchenko wrote:
> On Sun, Sep 06, 2020 at 03:43:47PM -0600, Simon Glass wrote:
> > Generate ACPI information for this device so that Linux can use it
> > correctly.
> 
> > +	ret = acpi_device_write_interrupt_or_gpio(ctx, (struct udevice *)dev,
> > +						  "ready-gpios");
> > +	if (ret < 0)
> > +		return log_msg_ret("irq_gpio", ret);
> 
> I looked a bit closer at the acpi_table.c and would like to emphasize on
> lessons learn from BIOS mistakes found in the wild with GPIOs.
> 
> Because GPIO resources are quite badly described in ACPI (it seems MS failed to
> deliver GPIO abstraction to ACPI and to Windows API), there are some corner
> cases, in order to mitigate which we need to consider the following to avoid
> potential glitches and misconfiguration:
> 
> - GpioIo() doesn't have any means of Active Low / High setting, the _DSD must
>   be provided to mitigate this.
> 
> - GpioIo() doesn't properly communicate the initial state of the output pin,
>   thus Linux assumes the simple rule:
> 
>   Pull Bias	  Polarity	Requested...
> 
>   Implicit	  x		AS IS (assumed firmware configured for us)
>   Explicit	  x (no _DSD)	as Pull Bias (Up == High, Down == Low),
>   		  		assuming non-active (Polarity = !Pull Bias)
> 
>   Down		  Low		as low, assuming active
>   Down		  High		as high, assuming non-active
>   Up		  Low		as low, assuming non-active
>   Up		  High		as high, assuming active
> 
> Hopefully this helps (and maybe can be added to some documentation).
> 
> P.S. Why I2cSerialBus() and not I2cSerialBusV2() ?

Forgot one important aspect.

GpioIo() can be used as interrupt and in this case the IoRestriction mustn't be
OutputOnly. It also requires active_low flag from _DSD in cases where it's
needed (better to always provide than rely on above assumption made on OS level).
Bin Meng Sept. 22, 2020, 8:15 a.m. UTC | #3
Hi Simon,

On Mon, Sep 21, 2020 at 7:51 PM Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
>
> On Sun, Sep 06, 2020 at 03:43:47PM -0600, Simon Glass wrote:
> > Generate ACPI information for this device so that Linux can use it
> > correctly.
>
> > +     ret = acpi_device_write_interrupt_or_gpio(ctx, (struct udevice *)dev,
> > +                                               "ready-gpios");
> > +     if (ret < 0)
> > +             return log_msg_ret("irq_gpio", ret);
>
> I looked a bit closer at the acpi_table.c and would like to emphasize on
> lessons learn from BIOS mistakes found in the wild with GPIOs.
>
> Because GPIO resources are quite badly described in ACPI (it seems MS failed to
> deliver GPIO abstraction to ACPI and to Windows API), there are some corner
> cases, in order to mitigate which we need to consider the following to avoid
> potential glitches and misconfiguration:
>
> - GpioIo() doesn't have any means of Active Low / High setting, the _DSD must
>   be provided to mitigate this.
>
> - GpioIo() doesn't properly communicate the initial state of the output pin,
>   thus Linux assumes the simple rule:
>
>   Pull Bias       Polarity      Requested...
>
>   Implicit        x             AS IS (assumed firmware configured for us)
>   Explicit        x (no _DSD)   as Pull Bias (Up == High, Down == Low),
>                                 assuming non-active (Polarity = !Pull Bias)
>
>   Down            Low           as low, assuming active
>   Down            High          as high, assuming non-active
>   Up              Low           as low, assuming non-active
>   Up              High          as high, assuming active
>
> Hopefully this helps (and maybe can be added to some documentation).
>
> P.S. Why I2cSerialBus() and not I2cSerialBusV2() ?
>

Will you address Andy's comments by adding documentation in the next version?

Regards,
Bin
Andy Shevchenko Sept. 22, 2020, 8:27 a.m. UTC | #4
On Mon, Sep 21, 2020 at 2:51 PM Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
>
> On Sun, Sep 06, 2020 at 03:43:47PM -0600, Simon Glass wrote:
> > Generate ACPI information for this device so that Linux can use it
> > correctly.
>
> > +     ret = acpi_device_write_interrupt_or_gpio(ctx, (struct udevice *)dev,
> > +                                               "ready-gpios");
> > +     if (ret < 0)
> > +             return log_msg_ret("irq_gpio", ret);
>
> I looked a bit closer at the acpi_table.c and would like to emphasize on
> lessons learn from BIOS mistakes found in the wild with GPIOs.
>
> Because GPIO resources are quite badly described in ACPI (it seems MS failed to
> deliver GPIO abstraction to ACPI and to Windows API), there are some corner
> cases, in order to mitigate which we need to consider the following to avoid
> potential glitches and misconfiguration:
>
> - GpioIo() doesn't have any means of Active Low / High setting, the _DSD must
>   be provided to mitigate this.
>
> - GpioIo() doesn't properly communicate the initial state of the output pin,
>   thus Linux assumes the simple rule:
>
>   Pull Bias       Polarity      Requested...
>
>   Implicit        x             AS IS (assumed firmware configured for us)
>   Explicit        x (no _DSD)   as Pull Bias (Up == High, Down == Low),
>                                 assuming non-active (Polarity = !Pull Bias)


>   Down            Low           as low, assuming active
>   Down            High          as high, assuming non-active
>   Up              Low           as low, assuming non-active
>   Up              High          as high, assuming active

I re-read the above piece of the table and found that I mistakenly
placed words after 'as ' part.
Should be
 as low, ...
 as low, ...
 as high, ...
 as high, ...

So, request follows the bias setting, but polarity, if explicitly
present, defines active/non-active state.

> Hopefully this helps (and maybe can be added to some documentation).
>
> P.S. Why I2cSerialBus() and not I2cSerialBusV2() ?
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
diff mbox series

Patch

diff --git a/drivers/tpm/cr50_i2c.c b/drivers/tpm/cr50_i2c.c
index 1942c07c605..64831a42232 100644
--- a/drivers/tpm/cr50_i2c.c
+++ b/drivers/tpm/cr50_i2c.c
@@ -14,11 +14,14 @@ 
 #include <log.h>
 #include <spl.h>
 #include <tpm-v2.h>
+#include <acpi/acpigen.h>
+#include <acpi/acpi_device.h>
 #include <asm/gpio.h>
 #include <asm/io.h>
 #include <asm/arch/iomap.h>
 #include <asm/arch/pm.h>
 #include <linux/delay.h>
+#include <dm/acpi.h>
 
 enum {
 	TIMEOUT_INIT_MS		= 30000, /* Very long timeout for TPM init */
@@ -581,6 +584,53 @@  static int cr50_i2c_cleanup(struct udevice *dev)
 	return 0;
 }
 
+static int cr50_acpi_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx)
+{
+	char scope[ACPI_PATH_MAX];
+	char name[ACPI_NAME_MAX];
+	const char *hid;
+	int ret;
+
+	ret = acpi_device_scope(dev, scope, sizeof(scope));
+	if (ret)
+		return log_msg_ret("scope", ret);
+	ret = acpi_get_name(dev, name);
+	if (ret)
+		return log_msg_ret("name", ret);
+
+	hid = dev_read_string(dev, "acpi,hid");
+	if (!hid)
+		return log_msg_ret("hid", ret);
+
+	/* Device */
+	acpigen_write_scope(ctx, scope);
+	acpigen_write_device(ctx, name);
+	acpigen_write_name_string(ctx, "_HID", hid);
+	acpigen_write_name_integer(ctx, "_UID",
+				   dev_read_u32_default(dev, "acpi,uid", 0));
+	acpigen_write_name_string(ctx, "_DDN",
+				  dev_read_string(dev, "acpi,ddn"));
+	acpigen_write_sta(ctx, acpi_device_status(dev));
+
+	/* Resources */
+	acpigen_write_name(ctx, "_CRS");
+	acpigen_write_resourcetemplate_header(ctx);
+	ret = acpi_device_write_i2c_dev(ctx, dev);
+	if (ret < 0)
+		return log_msg_ret("i2c", ret);
+	ret = acpi_device_write_interrupt_or_gpio(ctx, (struct udevice *)dev,
+						  "ready-gpios");
+	if (ret < 0)
+		return log_msg_ret("irq_gpio", ret);
+
+	acpigen_write_resourcetemplate_footer(ctx);
+
+	acpigen_pop_len(ctx); /* Device */
+	acpigen_pop_len(ctx); /* Scope */
+
+	return 0;
+}
+
 enum {
 	TPM_TIMEOUT_MS		= 5,
 	SHORT_TIMEOUT_MS	= 750,
@@ -653,6 +703,10 @@  static int cr50_i2c_probe(struct udevice *dev)
 	return 0;
 }
 
+struct acpi_ops cr50_acpi_ops = {
+	.fill_ssdt	= cr50_acpi_fill_ssdt,
+};
+
 static const struct tpm_ops cr50_i2c_ops = {
 	.open		= cr50_i2c_open,
 	.get_desc	= cr50_i2c_get_desc,
@@ -675,5 +729,6 @@  U_BOOT_DRIVER(cr50_i2c) = {
 	.probe	= cr50_i2c_probe,
 	.remove	= cr50_i2c_cleanup,
 	.priv_auto_alloc_size = sizeof(struct cr50_priv),
+	ACPI_OPS_PTR(&cr50_acpi_ops)
 	.flags		= DM_FLAG_OS_PREPARE,
 };