]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
term/ns8250: Support more MMIO access sizes
authorBenjamin Herrenschmidt <benh@amazon.com>
Fri, 23 Dec 2022 01:47:58 +0000 (12:47 +1100)
committerDaniel Kiper <daniel.kiper@oracle.com>
Thu, 19 Jan 2023 16:39:04 +0000 (17:39 +0100)
It is common for PCI based UARTs to use larger than one byte access
sizes. This adds support for this and uses the information present
in SPCR accordingly.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
grub-core/term/ns8250-spcr.c
grub-core/term/ns8250.c
include/grub/serial.h

index 8885d24b4ce49e850508fcb9326c94ea3bbaf766..1adc6063bfc39a76c9ff2eec890b65d9f27d507a 100644 (file)
@@ -76,7 +76,8 @@ grub_ns8250_spcr_init (void)
   switch (spcr->base_addr.space_id)
     {
       case GRUB_ACPI_GENADDR_MEM_SPACE:
-        return grub_serial_ns8250_add_mmio (spcr->base_addr.addr, &config);
+        return grub_serial_ns8250_add_mmio (spcr->base_addr.addr,
+                                            spcr->base_addr.access_size, &config);
       case GRUB_ACPI_GENADDR_IO_SPACE:
         return grub_serial_ns8250_add_port (spcr->base_addr.addr, &config);
       default:
index 7294d451bbf6ea56da3e0991843aeb230d27584e..172bca9c420871f1b736bd4285025f660b44a5ab 100644 (file)
@@ -42,7 +42,31 @@ ns8250_reg_read (struct grub_serial_port *port, grub_addr_t reg)
 {
   asm volatile("" : : : "memory");
   if (port->mmio == true)
-    return *((volatile grub_uint8_t *) (port->mmio_base + reg));
+    {
+      /*
+       * Note: we assume MMIO UARTs are little endian. This is not true of all
+       * embedded platforms but will do for now.
+       */
+      switch(port->access_size)
+        {
+        default:
+          /* ACPI tables occasionally uses "0" (legacy) as equivalent to "1" (byte). */
+        case 1:
+          return *((volatile grub_uint8_t *) (port->mmio_base + reg));
+        case 2:
+          return grub_le_to_cpu16 (*((volatile grub_uint16_t *) (port->mmio_base + (reg << 1))));
+        case 3:
+          return grub_le_to_cpu32 (*((volatile grub_uint32_t *) (port->mmio_base + (reg << 2))));
+        case 4:
+          /*
+           * This will only work properly on 64-bit systems since 64-bit
+           * accessors aren't atomic on 32-bit hardware. Thankfully the
+           * case of a UART with a 64-bit register spacing on 32-bit
+           * also probably doesn't exist.
+           */
+          return grub_le_to_cpu64 (*((volatile grub_uint64_t *) (port->mmio_base + (reg << 3))));
+        }
+    }
   return grub_inb (port->port + reg);
 }
 
@@ -51,7 +75,26 @@ ns8250_reg_write (struct grub_serial_port *port, grub_uint8_t value, grub_addr_t
 {
   asm volatile("" : : : "memory");
   if (port->mmio == true)
-    *((volatile grub_uint8_t *) (port->mmio_base + reg)) = value;
+    {
+      switch(port->access_size)
+        {
+        default:
+          /* ACPI tables occasionally uses "0" (legacy) as equivalent to "1" (byte). */
+        case 1:
+          *((volatile grub_uint8_t *) (port->mmio_base + reg)) = value;
+          break;
+        case 2:
+          *((volatile grub_uint16_t *) (port->mmio_base + (reg << 1))) = grub_cpu_to_le16 (value);
+          break;
+        case 3:
+          *((volatile grub_uint32_t *) (port->mmio_base + (reg << 2))) = grub_cpu_to_le32 (value);
+          break;
+        case 4:
+          /* See commment in ns8250_reg_read(). */
+          *((volatile grub_uint64_t *) (port->mmio_base + (reg << 3))) = grub_cpu_to_le64 (value);
+          break;
+        }
+    }
   else
     grub_outb (value, port->port + reg);
 }
@@ -286,6 +329,7 @@ grub_ns8250_init (void)
          grub_print_error ();
 
        grub_serial_register (&com_ports[i]);
+       com_ports[i].access_size = 1;
       }
 }
 
@@ -340,6 +384,7 @@ grub_serial_ns8250_add_port (grub_port_t port, struct grub_serial_config *config
   p->driver = &grub_ns8250_driver;
   p->mmio = false;
   p->port = port;
+  p->access_size = 1;
   if (config != NULL)
     grub_serial_port_configure (p, config);
   else
@@ -350,7 +395,8 @@ grub_serial_ns8250_add_port (grub_port_t port, struct grub_serial_config *config
 }
 
 char *
-grub_serial_ns8250_add_mmio (grub_addr_t addr, struct grub_serial_config *config)
+grub_serial_ns8250_add_mmio (grub_addr_t addr, unsigned int acc_size,
+                             struct grub_serial_config *config)
 {
   struct grub_serial_port *p;
   unsigned i;
@@ -375,6 +421,7 @@ grub_serial_ns8250_add_mmio (grub_addr_t addr, struct grub_serial_config *config
   p->driver = &grub_ns8250_driver;
   p->mmio = true;
   p->mmio_base = addr;
+  p->access_size = acc_size;
   if (config != NULL)
     grub_serial_port_configure (p, config);
   else
index 8d6ed56a3aeb485b9cd868b3d90c4e5440dfa74b..36be4b7ee76aa8f9fedcd3a7f636ebd3e1428bc6 100644 (file)
@@ -94,7 +94,12 @@ struct grub_serial_port
 #if defined(__mips__) || defined (__i386__) || defined (__x86_64__)
         grub_port_t port;
 #endif
-        grub_addr_t mmio_base;
+        struct
+        {
+          grub_addr_t mmio_base;
+          /* Access size uses ACPI definition. */
+          grub_uint8_t access_size;
+        };
       };
     };
     struct
@@ -187,7 +192,8 @@ grub_serial_config_defaults (struct grub_serial_port *port)
 void grub_ns8250_init (void);
 char *grub_ns8250_spcr_init (void);
 char *grub_serial_ns8250_add_port (grub_port_t port, struct grub_serial_config *config);
-char *grub_serial_ns8250_add_mmio (grub_addr_t addr, struct grub_serial_config *config);
+char *grub_serial_ns8250_add_mmio (grub_addr_t addr, unsigned int acc_size,
+                                   struct grub_serial_config *config);
 #endif
 #ifdef GRUB_MACHINE_IEEE1275
 void grub_ofserial_init (void);