Skip to contentSkip to conversation

ACPI: Generate SPCR table

PATCH Superseded Andy Shevchenko 4 participants 10 messages Sep 22, 2018 – Nov 15, 2018 x86

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

cover ACPI: Generate SPCR table

This is a WIP series (I have no time to continue with it, so, that's why WIP) to enable SPCR table generation in U-Boot. This table is useful to get early console in Linux for debugging purposes. The benefit of using it is not only for x86, but also for arm64 community (actually they introduced support in Linux kernel).

This PoC implementation has been tested on Intel Edison with enabled ACPI support.

Known issues:
- there is no ->getconfig() callback, thus some data is hard coded
- if serial parameters are changed at run time, SPCR is not regenerated, thus, might not produce what user excepts (this is more likely design issue of U-Boot itself)
- implementation is not final and might not utilize best practices in U-Boot

So, if there is any interest, I would be happy to help, but as I mentioned above I have no capacity to continue this myself.

The patches would be also available through my U-Boot tree on GH.

Andy Shevchenko (2):
WIP: serial: Introduce ->getinfo() callback
WIP: x86: acpi: Generate SPCR table

 arch/x86/include/asm/acpi_table.h | 25 ++++++++++
 arch/x86/lib/acpi_table.c         | 82 +++++++++++++++++++++++++++++++
 drivers/serial/ns16550.c          | 14 ++++++
 drivers/serial/serial-uclass.c    | 21 ++++++++
 include/common.h                  |  3 ++
 include/serial.h                  | 14 ++++++
 6 files changed, 159 insertions(+)
Signature · 1 line
2.18.0
#1 lore raw

PATCH 1/2 WIP: serial: Introduce ->getinfo() callback

TBD

---
 drivers/serial/ns16550.c       | 14 ++++++++++++++
 drivers/serial/serial-uclass.c | 21 +++++++++++++++++++++
 include/common.h               |  3 +++
 include/serial.h               | 14 ++++++++++++++
 4 files changed, 52 insertions(+)
drivers/serial/ns16550.c +14 -0
index f9041aa626..a996446936 100644
@@ -334,6 +334,19 @@ static int ns16550_serial_setbrg(struct udevice *dev, int baudrate)
 	return 0;
 }
 
+static int ns16550_serial_getinfo(struct udevice *dev, struct serial_device_info *info)
+{
+	struct NS16550 *const com_port = dev_get_priv(dev);
+	struct ns16550_platdata *plat = com_port->plat;
+
+	info->addr_space = 0;
+	info->reg_width = 8;
+	info->reg_shift = plat->reg_shift;
+	info->reg_offset = plat->reg_offset;
+	info->addr = plat->base;
+	return 0;
+}
+
 int ns16550_serial_probe(struct udevice *dev)
 {
 	struct NS16550 *const com_port = dev_get_priv(dev);
@@ -440,6 +453,7 @@ const struct dm_serial_ops ns16550_serial_ops = {
 	.pending = ns16550_serial_pending,
 	.getc = ns16550_serial_getc,
 	.setbrg = ns16550_serial_setbrg,
+	.getinfo = ns16550_serial_getinfo,
 };
 
 #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
drivers/serial/serial-uclass.c +21 -0
index ffdcae0931..4778302e8f 100644
@@ -304,6 +304,25 @@ int serial_setconfig(uint config)
 	return 0;
 }
 
+int serial_getinfo(struct serial_device_info *info)
+{
+	struct dm_serial_ops *ops;
+
+	if (!gd->cur_serial_dev)
+		return -ENODEV;
+
+	if (!info)
+		return -EINVAL;
+
+	info->baudrate = gd->baudrate;
+
+	ops = serial_get_ops(gd->cur_serial_dev);
+	if (ops->getinfo)
+		return ops->getinfo(gd->cur_serial_dev, info);
+
+	return -EINVAL;
+}
+
 void serial_stdio_init(void)
 {
 }
@@ -421,6 +440,8 @@ static int serial_post_probe(struct udevice *dev)
 	if (ops->loop)
 		ops->loop += gd->reloc_off
 #endif
+	if (ops->getinfo)
+		ops->getinfo += gd->reloc_off;
 #endif
 	/* Set the baud rate */
 	if (ops->setbrg) {
include/common.h +3 -0
index 83b3bdc58d..0479f3eac1 100644
@@ -349,6 +349,8 @@ void smp_set_core_boot_addr(unsigned long addr, int corenr);
 void smp_kick_all_cpus(void);
 
 /* $(CPU)/serial.c */
+struct serial_device_info;
+
 int	serial_init   (void);
 void	serial_setbrg (void);
 void	serial_putc   (const char);
@@ -357,6 +359,7 @@ void	serial_puts   (const char *);
 int	serial_getc   (void);
 int	serial_tstc   (void);
 int	serial_setconfig(uint config);
+int	serial_getinfo(struct serial_device_info *info);
 
 /* $(CPU)/speed.c */
 int	get_clocks (void);
include/serial.h +14 -0
index 020cd392e8..c73477b079 100644
@@ -111,6 +111,16 @@ enum serial_stop {
 				SERIAL_8_BITS << SERIAL_BITS_SHIFT | \
 				SERIAL_ONE_STOP << SERIAL_STOP_SHIFT
 
+/* REVISIT: ACPI GAS specification implied */
+struct serial_device_info {
+	unsigned int baudrate;
+	u8	addr_space; /* 0 - MMIO, 1 - IO */
+	u8	reg_width;
+	u8	reg_offset;
+	u8	reg_shift;
+	u64	addr;
+};
+
 /**
  * struct struct dm_serial_ops - Driver model serial operations
  *
@@ -201,6 +211,10 @@ struct dm_serial_ops {
 	 * @return 0 if OK, -ve on error
 	 */
 	int (*setconfig)(struct udevice *dev, uint serial_config);
+	/**
+	 * getinfo() - Get serial device information
+	 */
+	int (*getinfo)(struct udevice *dev, struct serial_device_info *info);
 };
 
 /**
Signature · 1 line
2.18.0
#2 lore raw

PATCH 1/2 WIP: serial: Introduce ->getinfo() callback

Hi Andy

Quoted 1 line · Andy Shevchenko

TBD

You forgot to add a commit message ;-)

Quoted 134 lines
---
 drivers/serial/ns16550.c       | 14 ++++++++++++++
 drivers/serial/serial-uclass.c | 21 +++++++++++++++++++++
 include/common.h               |  3 +++
 include/serial.h               | 14 ++++++++++++++
 4 files changed, 52 insertions(+)
drivers/serial/ns16550.c +14 -0
index f9041aa626..a996446936 100644
@@ -334,6 +334,19 @@ static int ns16550_serial_setbrg(struct udevice *dev, int baudrate)
 	return 0;
 }
 
+static int ns16550_serial_getinfo(struct udevice *dev, struct serial_device_info *info)
+{
+	struct NS16550 *const com_port = dev_get_priv(dev);
+	struct ns16550_platdata *plat = com_port->plat;
+
+	info->addr_space = 0;
+	info->reg_width = 8;
+	info->reg_shift = plat->reg_shift;
+	info->reg_offset = plat->reg_offset;
+	info->addr = plat->base;
+	return 0;
+}
+
 int ns16550_serial_probe(struct udevice *dev)
 {
 	struct NS16550 *const com_port = dev_get_priv(dev);
@@ -440,6 +453,7 @@ const struct dm_serial_ops ns16550_serial_ops = {
 	.pending = ns16550_serial_pending,
 	.getc = ns16550_serial_getc,
 	.setbrg = ns16550_serial_setbrg,
+	.getinfo = ns16550_serial_getinfo,
 };
 
 #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
drivers/serial/serial-uclass.c +21 -0
index ffdcae0931..4778302e8f 100644
@@ -304,6 +304,25 @@ int serial_setconfig(uint config)
 	return 0;
 }
 
+int serial_getinfo(struct serial_device_info *info)
+{
+	struct dm_serial_ops *ops;
+
+	if (!gd->cur_serial_dev)
+		return -ENODEV;
+
+	if (!info)
+		return -EINVAL;
+
+	info->baudrate = gd->baudrate;
+
+	ops = serial_get_ops(gd->cur_serial_dev);
+	if (ops->getinfo)
+		return ops->getinfo(gd->cur_serial_dev, info);
+
+	return -EINVAL;
+}
+
 void serial_stdio_init(void)
 {
 }
@@ -421,6 +440,8 @@ static int serial_post_probe(struct udevice *dev)
 	if (ops->loop)
 		ops->loop += gd->reloc_off
 #endif
+	if (ops->getinfo)
+		ops->getinfo += gd->reloc_off;
 #endif
 	/* Set the baud rate */
 	if (ops->setbrg) {
include/common.h +3 -0
index 83b3bdc58d..0479f3eac1 100644
@@ -349,6 +349,8 @@ void smp_set_core_boot_addr(unsigned long addr, int corenr);
 void smp_kick_all_cpus(void);
 
 /* $(CPU)/serial.c */
+struct serial_device_info;
+
 int	serial_init   (void);
 void	serial_setbrg (void);
 void	serial_putc   (const char);
@@ -357,6 +359,7 @@ void	serial_puts   (const char *);
 int	serial_getc   (void);
 int	serial_tstc   (void);
 int	serial_setconfig(uint config);
+int	serial_getinfo(struct serial_device_info *info);
 
 /* $(CPU)/speed.c */
 int	get_clocks (void);
include/serial.h +14 -0
index 020cd392e8..c73477b079 100644
@@ -111,6 +111,16 @@ enum serial_stop {
 				SERIAL_8_BITS << SERIAL_BITS_SHIFT | \
 				SERIAL_ONE_STOP << SERIAL_STOP_SHIFT
 
+/* REVISIT: ACPI GAS specification implied */
+struct serial_device_info {
+	unsigned int baudrate;
+	u8	addr_space; /* 0 - MMIO, 1 - IO */
+	u8	reg_width;
+	u8	reg_offset;
+	u8	reg_shift;
+	u64	addr;
+};
+
 /**
  * struct struct dm_serial_ops - Driver model serial operations
  *
@@ -201,6 +211,10 @@ struct dm_serial_ops {
 	 * @return 0 if OK, -ve on error
 	 */
 	int (*setconfig)(struct udevice *dev, uint serial_config);
+	/**
+	 * getinfo() - Get serial device information
+	 */
+	int (*getinfo)(struct udevice *dev, struct serial_device_info *info);
 };
 
 /**
#3 lore raw

PATCH 1/2 WIP: serial: Introduce ->getinfo() callback

Quoted 7 lines · Patrice CHOTARD

Hi Andy

Quoted 1 line · Andy Shevchenko

TBD

You forgot to add a commit message ;-)

Yes, cover letter explains why.
It's a halfbaked stuff in case someone is interested to continue.

For your convenience:
patch 1: introduces a hook to retrieve necessary data from UART driver (in particular ns16550)
patch 2: adds SPCR data structures and uses hook from patch 1 to generate the table

Quoted 133 lines
Quoted 133 lines
---
 drivers/serial/ns16550.c       | 14 ++++++++++++++
 drivers/serial/serial-uclass.c | 21 +++++++++++++++++++++
 include/common.h               |  3 +++
 include/serial.h               | 14 ++++++++++++++
 4 files changed, 52 insertions(+)
drivers/serial/ns16550.c +14 -0
index f9041aa626..a996446936 100644
@@ -334,6 +334,19 @@ static int ns16550_serial_setbrg(struct udevice *dev, int baudrate)
      return 0;
 }
 
+static int ns16550_serial_getinfo(struct udevice *dev, struct serial_device_info *info)
+{
+     struct NS16550 *const com_port = dev_get_priv(dev);
+     struct ns16550_platdata *plat = com_port->plat;
+
+     info->addr_space = 0;
+     info->reg_width = 8;
+     info->reg_shift = plat->reg_shift;
+     info->reg_offset = plat->reg_offset;
+     info->addr = plat->base;
+     return 0;
+}
+
 int ns16550_serial_probe(struct udevice *dev)
 {
      struct NS16550 *const com_port = dev_get_priv(dev);
@@ -440,6 +453,7 @@ const struct dm_serial_ops ns16550_serial_ops = {
      .pending = ns16550_serial_pending,
      .getc = ns16550_serial_getc,
      .setbrg = ns16550_serial_setbrg,
+     .getinfo = ns16550_serial_getinfo,
 };
 
 #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
drivers/serial/serial-uclass.c +21 -0
index ffdcae0931..4778302e8f 100644
@@ -304,6 +304,25 @@ int serial_setconfig(uint config)
      return 0;
 }
 
+int serial_getinfo(struct serial_device_info *info)
+{
+     struct dm_serial_ops *ops;
+
+     if (!gd->cur_serial_dev)
+             return -ENODEV;
+
+     if (!info)
+             return -EINVAL;
+
+     info->baudrate = gd->baudrate;
+
+     ops = serial_get_ops(gd->cur_serial_dev);
+     if (ops->getinfo)
+             return ops->getinfo(gd->cur_serial_dev, info);
+
+     return -EINVAL;
+}
+
 void serial_stdio_init(void)
 {
 }
@@ -421,6 +440,8 @@ static int serial_post_probe(struct udevice *dev)
      if (ops->loop)
              ops->loop += gd->reloc_off
 #endif
+     if (ops->getinfo)
+             ops->getinfo += gd->reloc_off;
 #endif
      /* Set the baud rate */
      if (ops->setbrg) {
include/common.h +3 -0
index 83b3bdc58d..0479f3eac1 100644
@@ -349,6 +349,8 @@ void smp_set_core_boot_addr(unsigned long addr, int corenr);
 void smp_kick_all_cpus(void);
 
 /* $(CPU)/serial.c */
+struct serial_device_info;
+
 int  serial_init   (void);
 void serial_setbrg (void);
 void serial_putc   (const char);
@@ -357,6 +359,7 @@ void      serial_puts   (const char *);
 int  serial_getc   (void);
 int  serial_tstc   (void);
 int  serial_setconfig(uint config);
+int  serial_getinfo(struct serial_device_info *info);
 
 /* $(CPU)/speed.c */
 int  get_clocks (void);
include/serial.h +14 -0
index 020cd392e8..c73477b079 100644
@@ -111,6 +111,16 @@ enum serial_stop {
                              SERIAL_8_BITS << SERIAL_BITS_SHIFT | \
                              SERIAL_ONE_STOP << SERIAL_STOP_SHIFT
 
+/* REVISIT: ACPI GAS specification implied */
+struct serial_device_info {
+     unsigned int baudrate;
+     u8      addr_space; /* 0 - MMIO, 1 - IO */
+     u8      reg_width;
+     u8      reg_offset;
+     u8      reg_shift;
+     u64     addr;
+};
+
 /**
  * struct struct dm_serial_ops - Driver model serial operations
  *
@@ -201,6 +211,10 @@ struct dm_serial_ops {
       * @return 0 if OK, -ve on error
       */
      int (*setconfig)(struct udevice *dev, uint serial_config);
+     /**
+      * getinfo() - Get serial device information
+      */
+     int (*getinfo)(struct udevice *dev, struct serial_device_info *info);
 };
 
Quoted 6 lines

include/serial.h struct dm_serial_ops { · line 220

Quoted 2 lines
include/serial.h +0 -0
 /**

_______________________________________________
U-Boot mailing list
U-Boot at lists.denx.de
https://lists.denx.de/listinfo/u-boot

--
With Best Regards,
Andy Shevchenko

#4 lore raw

PATCH 1/2 WIP: serial: Introduce ->getinfo() callback

In reply to Andy Shevchenko

Hi Andy,

Quoted 16 lines · Andy Shevchenko

TBD

---
 drivers/serial/ns16550.c       | 14 ++++++++++++++
 drivers/serial/serial-uclass.c | 21 +++++++++++++++++++++
 include/common.h               |  3 +++
 include/serial.h               | 14 ++++++++++++++
 4 files changed, 52 insertions(+)
drivers/serial/ns16550.c +0 -0
index f9041aa626..a996446936 100644

drivers/serial/ns16550.c static int ns16550_serial_setbrg(struct udevice *dev, int baudrate) · line 342

Quoted 9 lines
drivers/serial/ns16550.c +6 -0
@@ -334,6 +334,19 @@ static int ns16550_serial_setbrg(struct udevice *dev, int baudrate)
        return 0;
 }
 
+static int ns16550_serial_getinfo(struct udevice *dev, struct serial_device_info *info)
+{
+       struct NS16550 *const com_port = dev_get_priv(dev);
+       struct ns16550_platdata *plat = com_port->plat;
+
+       info->addr_space = 0;

This one is tricky. Looks current 8250 DT binding does not have a property to describe the address space to be MMIO or PIO. I am not sure what's the best option here.

drivers/serial/ns16550.c static int ns16550_serial_setbrg(struct udevice *dev, int baudrate) · line 343

Quoted 1 line
drivers/serial/ns16550.c +1 -0
+       info->reg_width = 8;

For this one, I believe we can use "reg-io-width" property from device tree.

Quoted 99 lines
drivers/serial/ns16550.c +7 -0
+       info->reg_shift = plat->reg_shift;
+       info->reg_offset = plat->reg_offset;
+       info->addr = plat->base;
+       return 0;
+}
+
 int ns16550_serial_probe(struct udevice *dev)
 {
        struct NS16550 *const com_port = dev_get_priv(dev);
@@ -440,6 +453,7 @@ const struct dm_serial_ops ns16550_serial_ops = {
        .pending = ns16550_serial_pending,
        .getc = ns16550_serial_getc,
        .setbrg = ns16550_serial_setbrg,
+       .getinfo = ns16550_serial_getinfo,
 };
 
 #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
drivers/serial/serial-uclass.c +21 -0
index ffdcae0931..4778302e8f 100644
@@ -304,6 +304,25 @@ int serial_setconfig(uint config)
        return 0;
 }
 
+int serial_getinfo(struct serial_device_info *info)
+{
+       struct dm_serial_ops *ops;
+
+       if (!gd->cur_serial_dev)
+               return -ENODEV;
+
+       if (!info)
+               return -EINVAL;
+
+       info->baudrate = gd->baudrate;
+
+       ops = serial_get_ops(gd->cur_serial_dev);
+       if (ops->getinfo)
+               return ops->getinfo(gd->cur_serial_dev, info);
+
+       return -EINVAL;
+}
+
 void serial_stdio_init(void)
 {
 }
@@ -421,6 +440,8 @@ static int serial_post_probe(struct udevice *dev)
        if (ops->loop)
                ops->loop += gd->reloc_off
 #endif
+       if (ops->getinfo)
+               ops->getinfo += gd->reloc_off;
 #endif
        /* Set the baud rate */
        if (ops->setbrg) {
include/common.h +3 -0
index 83b3bdc58d..0479f3eac1 100644
@@ -349,6 +349,8 @@ void smp_set_core_boot_addr(unsigned long addr, int corenr);
 void smp_kick_all_cpus(void);
 
 /* $(CPU)/serial.c */
+struct serial_device_info;
+
 int    serial_init   (void);
 void   serial_setbrg (void);
 void   serial_putc   (const char);
@@ -357,6 +359,7 @@ void        serial_puts   (const char *);
 int    serial_getc   (void);
 int    serial_tstc   (void);
 int    serial_setconfig(uint config);
+int    serial_getinfo(struct serial_device_info *info);
 
 /* $(CPU)/speed.c */
 int    get_clocks (void);
include/serial.h +10 -0
index 020cd392e8..c73477b079 100644
@@ -111,6 +111,16 @@ enum serial_stop {
                                SERIAL_8_BITS << SERIAL_BITS_SHIFT | \
                                SERIAL_ONE_STOP << SERIAL_STOP_SHIFT
 
+/* REVISIT: ACPI GAS specification implied */
+struct serial_device_info {
+       unsigned int baudrate;
+       u8      addr_space; /* 0 - MMIO, 1 - IO */
+       u8      reg_width;
+       u8      reg_offset;
+       u8      reg_shift;
+       u64     addr;
+};
+
 /**
  * struct struct dm_serial_ops - Driver model serial operations
  *

include/serial.h struct dm_serial_ops { · line 218

Quoted 8 lines
include/serial.h +4 -0
@@ -201,6 +211,10 @@ struct dm_serial_ops {
         * @return 0 if OK, -ve on error
         */
        int (*setconfig)(struct udevice *dev, uint serial_config);
+       /**
+        * getinfo() - Get serial device information
+        */
+       int (*getinfo)(struct udevice *dev, struct serial_device_info *info);
 };

Generally this patch looks OK to me.

BTW: why doesn't the kernel use the 'earlycon' command line?

Regards,
Bin

#5 lore raw

PATCH 1/2 WIP: serial: Introduce ->getinfo() callback

Quoted 2 lines · Bin Meng

On Sat, Sep 22, 2018 at 9:05 PM Andy Shevchenko
<[email protected]> wrote:

I found a bit of time to look at this again.

Quoted 10 lines

drivers/serial/ns16550.c static int ns16550_serial_setbrg(struct udevice *dev, int baudrate) · line 342

Quoted 6 lines
drivers/serial/ns16550.c +6 -0
+static int ns16550_serial_getinfo(struct udevice *dev, struct serial_device_info *info)
+{
+       struct NS16550 *const com_port = dev_get_priv(dev);
+       struct ns16550_platdata *plat = com_port->plat;
+
+       info->addr_space = 0;

This one is tricky. Looks current 8250 DT binding does not have a property to describe the address space to be MMIO or PIO. I am not sure what's the best option here.

For now I suggest to leave it hard coded to MMIO.

Quoted 3 lines
Quoted 1 line

+ info->reg_width = 8;

For this one, I believe we can use "reg-io-width" property from device tree.

I will try this path.

Quoted 7 lines

drivers/serial/ns16550.c static int ns16550_serial_setbrg(struct udevice *dev, int baudrate) · line 348

Quoted 5 lines
drivers/serial/ns16550.c +5 -0
+       info->reg_shift = plat->reg_shift;
+       info->reg_offset = plat->reg_offset;
+       info->addr = plat->base;
+       return 0;
+}

Generally this patch looks OK to me.

Thanks! Still I have few amendments for the real submission of the series.

Quoted 1 line

BTW: why doesn't the kernel use the 'earlycon' command line?

Not all hardware supports it or supports it in a right way.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#6 lore raw

PATCH 2/2 WIP: x86: acpi: Generate SPCR table

In reply to Andy Shevchenko

TBD

---
 arch/x86/include/asm/acpi_table.h | 25 ++++++++++
 arch/x86/lib/acpi_table.c         | 82 +++++++++++++++++++++++++++++++
 2 files changed, 107 insertions(+)
arch/x86/include/asm/acpi_table.h +25 -0
index 95fae036f6..27435871b9 100644
@@ -303,6 +303,31 @@ struct acpi_mcfg_mmconfig {
 /* ACPI global NVS structure */
 struct acpi_global_nvs;
 
+/* SPCR (Serial Port Console Redirection table) */
+struct __packed acpi_spcr {
+	struct acpi_table_header header;	/* Common ACPI table header */
+	u8 interface_type;	/* 0=full 16550, 1=subset of 16550 */
+	u8 reserved[3];
+	struct acpi_gen_regaddr serial_port;	/* The base address of the Serial Port register set */
+	u8 interrupt_type;
+	u8 pc_interrupt;
+	u32 interrupt;		/* Global system interrupt */
+	u8 baud_rate;
+	u8 parity;
+	u8 stop_bits;
+	u8 flow_control;
+	u8 terminal_type;
+	u8 reserved1;
+	u16 pci_device_id;	/* Must be 0xffff if not PCI device */
+	u16 pci_vendor_id;	/* Must be 0xffff if not PCI device */
+	u8 pci_bus;
+	u8 pci_device;
+	u8 pci_function;
+	u32 pci_flags;
+	u8 pci_segment;
+	u32 reserved2;
+};
+
 /* These can be used by the target port */
 
 void acpi_fill_header(struct acpi_table_header *header, char *signature);
arch/x86/lib/acpi_table.c +82 -0

106 lines not on this page · raw message

Signature · 1 line
2.18.0
#7 lore raw

PATCH 2/2 WIP: x86: acpi: Generate SPCR table

Quoted 1 line · Andy Shevchenko

TBD

Same remark as Patch 1

Quoted 158 lines
---
 arch/x86/include/asm/acpi_table.h | 25 ++++++++++
 arch/x86/lib/acpi_table.c         | 82 +++++++++++++++++++++++++++++++
 2 files changed, 107 insertions(+)
arch/x86/include/asm/acpi_table.h +25 -0
index 95fae036f6..27435871b9 100644
@@ -303,6 +303,31 @@ struct acpi_mcfg_mmconfig {
 /* ACPI global NVS structure */
 struct acpi_global_nvs;
 
+/* SPCR (Serial Port Console Redirection table) */
+struct __packed acpi_spcr {
+	struct acpi_table_header header;	/* Common ACPI table header */
+	u8 interface_type;	/* 0=full 16550, 1=subset of 16550 */
+	u8 reserved[3];
+	struct acpi_gen_regaddr serial_port;	/* The base address of the Serial Port register set */
+	u8 interrupt_type;
+	u8 pc_interrupt;
+	u32 interrupt;		/* Global system interrupt */
+	u8 baud_rate;
+	u8 parity;
+	u8 stop_bits;
+	u8 flow_control;
+	u8 terminal_type;
+	u8 reserved1;
+	u16 pci_device_id;	/* Must be 0xffff if not PCI device */
+	u16 pci_vendor_id;	/* Must be 0xffff if not PCI device */
+	u8 pci_bus;
+	u8 pci_device;
+	u8 pci_function;
+	u32 pci_flags;
+	u8 pci_segment;
+	u32 reserved2;
+};
+
 /* These can be used by the target port */
 
 void acpi_fill_header(struct acpi_table_header *header, char *signature);
arch/x86/lib/acpi_table.c +82 -0
index c6b2026613..551a78b11a 100644
@@ -12,6 +12,7 @@
 #include <cpu.h>
 #include <dm.h>
 #include <dm/uclass-internal.h>
+#include <serial.h>
 #include <version.h>
 #include <asm/acpi/global_nvs.h>
 #include <asm/acpi_table.h>
@@ -338,6 +339,79 @@ static void acpi_create_mcfg(struct acpi_mcfg *mcfg)
 	header->checksum = table_compute_checksum((void *)mcfg, header->length);
 }
 
+static void acpi_create_spcr(struct acpi_spcr *spcr)
+{
+	struct acpi_table_header *header = &(spcr->header);
+	struct serial_device_info info = {0};
+	int access_size;
+	int ret;
+
+	/* Fill out header fields */
+	acpi_fill_header(header, "SPCR");
+	header->length = sizeof(struct acpi_spcr);
+	header->revision = 2;
+
+	ret = serial_getinfo(&info);
+	if (ret)
+		debug("Can't get information of serial device: %d\n", ret);
+
+	/* Encode baud rate */
+	switch (info.baudrate) {
+	case 9600:
+		spcr->baud_rate = 3;
+		break;
+	case 19200:
+		spcr->baud_rate = 4;
+		break;
+	case 57600:
+		spcr->baud_rate = 6;
+		break;
+	case 115200:
+		spcr->baud_rate = 7;
+		break;
+	default:
+		spcr->baud_rate = 0;
+		break;
+	}
+
+	/* Encode register access size */
+	switch (info.reg_shift) {
+	case 0:
+		access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS;
+		break;
+	case 1:
+		access_size = ACPI_ACCESS_SIZE_WORD_ACCESS;
+		break;
+	case 2:
+		access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS;
+		break;
+	case 3:
+		access_size = ACPI_ACCESS_SIZE_QWORD_ACCESS;
+		break;
+	default:
+		access_size = ACPI_ACCESS_SIZE_UNDEFINED;
+		break;
+	}
+
+	spcr->serial_port.space_id = ACPI_ADDRESS_SPACE_MEMORY;
+	spcr->serial_port.bit_width = info.reg_width;
+	spcr->serial_port.bit_offset = info.reg_offset;
+	spcr->serial_port.access_size = access_size;
+	spcr->serial_port.addrl = info.addr >> 0;
+	spcr->serial_port.addrh = info.addr >> 32;
+
+	/* Hard coded values for now */
+	spcr->parity = 0;
+	spcr->stop_bits = 1;
+
+	/* No PCI devices for now */
+	spcr->pci_device_id = 0xffff;
+	spcr->pci_vendor_id = 0xffff;
+
+	/* Fix checksum */
+	header->checksum = table_compute_checksum((void *)spcr, header->length);
+}
+
 /*
  * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c
  */
@@ -352,6 +426,7 @@ ulong write_acpi_tables(ulong start)
 	struct acpi_fadt *fadt;
 	struct acpi_mcfg *mcfg;
 	struct acpi_madt *madt;
+	struct acpi_spcr *spcr;
 	int i;
 
 	current = start;
@@ -440,6 +515,13 @@ ulong write_acpi_tables(ulong start)
 	acpi_add_table(rsdp, mcfg);
 	current = ALIGN(current, 16);
 
+	debug("ACPI:    * SPCR\n");
+	spcr = (struct acpi_spcr *)current;
+	acpi_create_spcr(spcr);
+	current += spcr->header.length;
+	acpi_add_table(rsdp, spcr);
+	current = ALIGN(current, 16);
+
 	debug("current = %x\n", current);
 
 	acpi_rsdp_addr = (unsigned long)rsdp;
#8 lore raw

PATCH 2/2 WIP: x86: acpi: Generate SPCR table

In reply to Andy Shevchenko

Hi Andy,

Quoted 120 lines · Andy Shevchenko

TBD

---
 arch/x86/include/asm/acpi_table.h | 25 ++++++++++
 arch/x86/lib/acpi_table.c         | 82 +++++++++++++++++++++++++++++++
 2 files changed, 107 insertions(+)
arch/x86/include/asm/acpi_table.h +25 -0
index 95fae036f6..27435871b9 100644
@@ -303,6 +303,31 @@ struct acpi_mcfg_mmconfig {
 /* ACPI global NVS structure */
 struct acpi_global_nvs;
 
+/* SPCR (Serial Port Console Redirection table) */
+struct __packed acpi_spcr {
+       struct acpi_table_header header;        /* Common ACPI table header */
+       u8 interface_type;      /* 0=full 16550, 1=subset of 16550 */
+       u8 reserved[3];
+       struct acpi_gen_regaddr serial_port;    /* The base address of the Serial Port register set */
+       u8 interrupt_type;
+       u8 pc_interrupt;
+       u32 interrupt;          /* Global system interrupt */
+       u8 baud_rate;
+       u8 parity;
+       u8 stop_bits;
+       u8 flow_control;
+       u8 terminal_type;
+       u8 reserved1;
+       u16 pci_device_id;      /* Must be 0xffff if not PCI device */
+       u16 pci_vendor_id;      /* Must be 0xffff if not PCI device */
+       u8 pci_bus;
+       u8 pci_device;
+       u8 pci_function;
+       u32 pci_flags;
+       u8 pci_segment;
+       u32 reserved2;
+};
+
 /* These can be used by the target port */
 
 void acpi_fill_header(struct acpi_table_header *header, char *signature);
arch/x86/lib/acpi_table.c +60 -0
index c6b2026613..551a78b11a 100644
@@ -12,6 +12,7 @@
 #include <cpu.h>
 #include <dm.h>
 #include <dm/uclass-internal.h>
+#include <serial.h>
 #include <version.h>
 #include <asm/acpi/global_nvs.h>
 #include <asm/acpi_table.h>
@@ -338,6 +339,79 @@ static void acpi_create_mcfg(struct acpi_mcfg *mcfg)
        header->checksum = table_compute_checksum((void *)mcfg, header->length);
 }
 
+static void acpi_create_spcr(struct acpi_spcr *spcr)
+{
+       struct acpi_table_header *header = &(spcr->header);
+       struct serial_device_info info = {0};
+       int access_size;
+       int ret;
+
+       /* Fill out header fields */
+       acpi_fill_header(header, "SPCR");
+       header->length = sizeof(struct acpi_spcr);
+       header->revision = 2;
+
+       ret = serial_getinfo(&info);
+       if (ret)
+               debug("Can't get information of serial device: %d\n", ret);
+
+       /* Encode baud rate */
+       switch (info.baudrate) {
+       case 9600:
+               spcr->baud_rate = 3;
+               break;
+       case 19200:
+               spcr->baud_rate = 4;
+               break;
+       case 57600:
+               spcr->baud_rate = 6;
+               break;
+       case 115200:
+               spcr->baud_rate = 7;
+               break;
+       default:
+               spcr->baud_rate = 0;
+               break;
+       }
+
+       /* Encode register access size */
+       switch (info.reg_shift) {
+       case 0:
+               access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS;
+               break;
+       case 1:
+               access_size = ACPI_ACCESS_SIZE_WORD_ACCESS;
+               break;
+       case 2:
+               access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS;
+               break;
+       case 3:
+               access_size = ACPI_ACCESS_SIZE_QWORD_ACCESS;
+               break;
+       default:
+               access_size = ACPI_ACCESS_SIZE_UNDEFINED;
+               break;
+       }
+
+       spcr->serial_port.space_id = ACPI_ADDRESS_SPACE_MEMORY;
+       spcr->serial_port.bit_width = info.reg_width;
+       spcr->serial_port.bit_offset = info.reg_offset;
+       spcr->serial_port.access_size = access_size;
+       spcr->serial_port.addrl = info.addr >> 0;

arch/x86/lib/acpi_table.c static void acpi_create_mcfg(struct acpi_mcfg *mcfg) · line 410

Quoted 10 lines
arch/x86/lib/acpi_table.c +10 -0
+       spcr->serial_port.addrh = info.addr >> 32;
+
+       /* Hard coded values for now */
+       spcr->parity = 0;
+       spcr->stop_bits = 1;
+
+       /* No PCI devices for now */
+       spcr->pci_device_id = 0xffff;
+       spcr->pci_vendor_id = 0xffff;
+

I see not every member of 'struct __packed acpi_spcr' is populated. Are they not used by kernel? If kernel does not use it, it is used by Windows?

Quoted 20 lines
arch/x86/lib/acpi_table.c +6 -0
+       /* Fix checksum */
+       header->checksum = table_compute_checksum((void *)spcr, header->length);
+}
+
 /*
  * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c
  */
@@ -352,6 +426,7 @@ ulong write_acpi_tables(ulong start)
        struct acpi_fadt *fadt;
        struct acpi_mcfg *mcfg;
        struct acpi_madt *madt;
+       struct acpi_spcr *spcr;
        int i;
 
        current = start;
@@ -440,6 +515,13 @@ ulong write_acpi_tables(ulong start)
        acpi_add_table(rsdp, mcfg);
        current = ALIGN(current, 16);
 
+       debug("ACPI:    * SPCR\n");

arch/x86/lib/acpi_table.c ulong write_acpi_tables(ulong start)

Quoted 10 lines
arch/x86/lib/acpi_table.c +6 -1
+       spcr = (struct acpi_spcr *)current;
+       acpi_create_spcr(spcr);
+       current += spcr->header.length;
+       acpi_add_table(rsdp, spcr);
+       current = ALIGN(current, 16);
+
        debug("current = %x\n", current);
 
        acpi_rsdp_addr = (unsigned long)rsdp;
--

Regards,
Bin

#9 lore raw

PATCH 2/2 WIP: x86: acpi: Generate SPCR table

Quoted 23 lines · Bin Meng
Quoted 20 lines · Andy Shevchenko
arch/x86/include/asm/acpi_table.h +20 -0
+/* SPCR (Serial Port Console Redirection table) */
+struct __packed acpi_spcr {
+       struct acpi_table_header header;        /* Common ACPI table header */
+       u8 interface_type;      /* 0=full 16550, 1=subset of 16550 */
+       u8 reserved[3];
+       struct acpi_gen_regaddr serial_port;    /* The base address of the Serial Port register set */
+       u8 interrupt_type;
+       u8 pc_interrupt;
+       u32 interrupt;          /* Global system interrupt */
+       u8 baud_rate;
+       u8 parity;
+       u8 stop_bits;
+       u8 flow_control;
+       u8 terminal_type;
+       u8 reserved1;
+       u16 pci_device_id;      /* Must be 0xffff if not PCI device */
+       u16 pci_vendor_id;      /* Must be 0xffff if not PCI device */
+       u8 pci_bus;
+       u8 pci_device;
+       u8 pci_function;
Quoted 8 lines

arch/x86/include/asm/acpi_table.h struct acpi_mcfg_mmconfig { · line 329

Quoted 4 lines
arch/x86/include/asm/acpi_table.h +4 -0
+       u32 pci_flags;
+       u8 pci_segment;
+       u32 reserved2;
+};

I see not every member of 'struct __packed acpi_spcr' is populated. Are they not used by kernel? If kernel does not use it, it is used by Windows?

No idea about Windows, but there are two statements:
- *not all* should be filled, otherwise it would be contradictory (PCI vs. non-PCI device, for example)
- Linux doesn't use all of them, indeed

Signature · 2 lines
With Best Regards,
Andy Shevchenko