grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 3/3] serial: Add serial driver for ns8250 compatible PCI UART


From: Matthias Lange
Subject: [PATCH 3/3] serial: Add serial driver for ns8250 compatible PCI UART
Date: Wed, 22 Feb 2017 11:09:27 +0100

Currently grub only supports ns8250 UARTs using IO ports. However
there are ns8250 compatible UART implementations using PCI and an
MMIO interface.

This change adds support for PCI UARTs using an OXSemiconductor
chip. That UART must be programmed using MMIO.

Signed-off-by: Matthias Lange <address@hidden>
---
 grub-core/Makefile.core.def      |   1 +
 grub-core/term/ns8250-pci-mmio.c | 186 +++++++++++++++++++++++++++++++++++++++
 grub-core/term/serial.c          |   9 +-
 include/grub/pci.h               |   8 ++
 include/grub/serial.h            |  16 ++++
 5 files changed, 218 insertions(+), 2 deletions(-)
 create mode 100644 grub-core/term/ns8250-pci-mmio.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 2dfa22a..dc1827a 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1887,6 +1887,7 @@ module = {
   name = serial;
   common = term/serial.c;
   x86 = term/ns8250.c;
+  x86 = term/ns8250-pci-mmio.c;
   ieee1275 = term/ieee1275/serial.c;
   mips_arc = term/arc/serial.c;
   efi = term/efi/serial.c;
diff --git a/grub-core/term/ns8250-pci-mmio.c b/grub-core/term/ns8250-pci-mmio.c
new file mode 100644
index 0000000..58beb1d
--- /dev/null
+++ b/grub-core/term/ns8250-pci-mmio.c
@@ -0,0 +1,186 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009,2010  Free 
Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/serial.h>
+#include <grub/ns8250.h>
+#include <grub/types.h>
+#include <grub/dl.h>
+#include <grub/pci.h>
+
+enum { GRUB_SERIAL_PCI_PORT_NUM = 8 };
+
+static inline void
+mmio_write (struct grub_serial_port *port, unsigned char val, grub_addr_t addr)
+{
+  *((volatile char *)(port->mmio_base + addr)) = val;
+}
+
+static inline unsigned char
+mmio_read (struct grub_serial_port *port, grub_addr_t addr)
+{
+  return *((volatile unsigned char *)(port->mmio_base + addr));
+}
+
+struct grub_serial_driver grub_ns8250_pci_mmio_driver =
+  {
+    .configure = serial_hw_configure,
+    .fetch = serial_hw_fetch,
+    .put = serial_hw_put,
+    .reg_write = mmio_write,
+    .reg_read = mmio_read,
+  };
+
+static char com_names[GRUB_SERIAL_PCI_PORT_NUM][20];
+static struct grub_serial_port ports[GRUB_SERIAL_PCI_PORT_NUM];
+
+struct drv_data
+{
+  unsigned cards;
+  unsigned num_ports;
+};
+
+static void
+read_bars(grub_pci_device_t dev, struct grub_serial_board *board)
+{
+  for (unsigned bar = 0; bar < NUM_BARS; ++bar)
+    {
+      int a = GRUB_PCI_REG_ADDRESSES + bar * 4;
+      grub_pci_address_t addr = grub_pci_make_address(dev, a);
+      unsigned v = grub_pci_read(addr);
+      grub_pci_write(addr, ~0U);
+      unsigned x = grub_pci_read(addr);
+      grub_pci_write(addr, v);
+
+      if (!v)
+        continue;
+
+      int s;
+      for (s = 2; s < 32; ++s)
+        if ((x >> s) & 1)
+          break;
+
+      if (0)
+        grub_printf("bar=%x (%s, sz=%d)\n", v, (v & 1) ? "IO" : "MEM", 1 << s);
+
+      board->bars[bar].base = v & ~3UL;
+      board->bars[bar].len = 1 << s;
+      board->bars[bar].type = (v & 1) ? IO_BAR : MEM_BAR;
+    }
+}
+
+static
+void enable_mmio(grub_pci_device_t dev)
+{
+  grub_pci_address_t addr = grub_pci_make_address(dev, GRUB_PCI_REG_COMMAND);
+  unsigned cmd = grub_pci_read_word(addr);
+  grub_pci_write_word(addr, cmd | GRUB_PCI_COMMAND_MEM_ENABLED);
+}
+
+static int
+ns8250_pci_mmio_iter(grub_pci_device_t dev, grub_pci_id_t pciid, void *data)
+{
+  unsigned vendor, device;
+  struct drv_data *d = (struct drv_data *)data;
+
+  if (d->num_ports >= GRUB_SERIAL_PCI_PORT_NUM)
+    return 0;
+
+  vendor = pciid & 0xffff;
+  device = pciid >> 16;
+
+  if (vendor == 0x1415 && device == 0xc158)
+    {
+      struct grub_serial_port *port = &ports[d->num_ports];
+      struct grub_serial_board *b = &ports[d->num_ports].board;
+
+      if (0)
+        grub_printf("Found OXSEMI PCI UART\n");
+
+      read_bars(dev, b);
+
+      // this card has two serial ports
+      port->board.num_ports = 2;
+      port->board.base_baud = 4000000;
+      port->board.port_offset = 0x200;
+      port->board.base_offset = 0x1000;
+
+      port->pcidev.bus = dev.bus;
+      port->pcidev.device = dev.device;
+      port->pcidev.function = dev.function;
+      port->port_num = 0;
+      port->card = d->cards;
+      port->mmio_base = port->board.bars[0].base + port->board.base_offset +
+                         port->port_num * port->board.port_offset;
+      d->num_ports += 1;
+
+      if (d->num_ports >= GRUB_SERIAL_PCI_PORT_NUM)
+        return 0;
+
+      port = &ports[d->num_ports];
+
+      port->board.num_ports = 2;
+      port->board.base_baud = 4000000;
+      port->board.port_offset = 0x200;
+      port->board.base_offset = 0x1000;
+
+      port->pcidev.bus = dev.bus;
+      port->pcidev.device = dev.device;
+      port->pcidev.function = dev.function;
+      port->port_num = 1;
+      port->card = d->cards;
+      port->mmio_base = port->board.bars[0].base + port->board.base_offset +
+                         port->port_num * port->board.port_offset;
+
+      enable_mmio (dev);
+
+      d->num_ports += 1;
+      d->cards += 1;
+    }
+
+  return 0;
+}
+
+void
+grub_ns8250_pci_mmio_init (void)
+{
+  struct drv_data data = { .cards = 0, .num_ports = 0 };
+  unsigned i;
+
+  for (i = 0; i < GRUB_SERIAL_PCI_PORT_NUM; ++i)
+    {
+      ports[i].card = ~0UL;
+      ports[i].port_num = -1;
+    }
+
+  grub_pci_iterate(ns8250_pci_mmio_iter, &data);
+
+  for (i = 0; i < data.num_ports; i++)
+    {
+      grub_err_t err = 0;
+
+      grub_snprintf (com_names[i], sizeof (com_names[i]), "pci:%lu:%u",
+                     ports[i].card, ports[i].port_num);
+      ports[i].name = com_names[i];
+      ports[i].driver = &grub_ns8250_pci_mmio_driver;
+      err = grub_serial_config_defaults (&ports[i]);
+      if (err)
+        grub_print_error ();
+
+      grub_serial_register (&ports[i]);
+    }
+}
diff --git a/grub-core/term/serial.c b/grub-core/term/serial.c
index db80b3b..8cdea15 100644
--- a/grub-core/term/serial.c
+++ b/grub-core/term/serial.c
@@ -201,8 +201,12 @@ grub_cmd_serial (grub_extcmd_context_t ctxt, int argc, 
char **args)
 
   if (state[OPTION_PORT].set)
     {
-      grub_snprintf (pname, sizeof (pname), "port%lx",
-                    grub_strtoul (state[1].arg, 0, 0));
+      if (grub_memcmp (state[OPTION_PORT].arg, "pci:", 4) == 0)
+          grub_snprintf(pname, sizeof (pname), "%s", state[1].arg);
+      else
+          grub_snprintf (pname, sizeof (pname), "port%lx",
+                         grub_strtoul (state[1].arg, 0, 0));
+
       name = pname;
     }
 
@@ -438,6 +442,7 @@ GRUB_MOD_INIT(serial)
 
 #if !defined (GRUB_MACHINE_EMU) && !defined(GRUB_MACHINE_ARC) && 
(defined(__mips__) || defined (__i386__) || defined (__x86_64__))
   grub_ns8250_init ();
+  grub_ns8250_pci_mmio_init ();
 #endif
 #ifdef GRUB_MACHINE_IEEE1275
   grub_ofserial_init ();
diff --git a/include/grub/pci.h b/include/grub/pci.h
index 70d9a05..e816d03 100644
--- a/include/grub/pci.h
+++ b/include/grub/pci.h
@@ -102,6 +102,14 @@ enum
 
 typedef grub_uint32_t grub_pci_id_t;
 
+enum { NO_BAR, IO_BAR, MEM_BAR };
+struct grub_pci_resource
+{
+  unsigned type;
+  unsigned long base;
+  unsigned long len;
+};
+
 #ifdef GRUB_MACHINE_EMU
 #include <grub/pciutils.h>
 #else
diff --git a/include/grub/serial.h b/include/grub/serial.h
index 82d127e..acd8b5e 100644
--- a/include/grub/serial.h
+++ b/include/grub/serial.h
@@ -23,6 +23,7 @@
 #include <grub/types.h>
 #if defined(__mips__) || defined (__i386__) || defined (__x86_64__)
 #include <grub/cpu/io.h>
+#include <grub/pci.h>
 #endif
 #include <grub/usb.h>
 #include <grub/list.h>
@@ -74,9 +75,15 @@ struct grub_serial_config
   int rtscts;
 };
 
+enum { NUM_BARS = 6 };
 struct grub_serial_board
 {
+  unsigned num_ports;
   unsigned base_baud;
+  unsigned port_offset;
+  unsigned base_offset;
+
+  struct grub_pci_resource bars[NUM_BARS];
 };
 
 struct grub_serial_port
@@ -100,6 +107,13 @@ struct grub_serial_port
 #endif
     struct
     {
+      grub_pci_device_t pcidev;
+      unsigned long card;
+      grub_port_t port_num;
+      grub_addr_t mmio_base;
+    };
+    struct
+    {
       grub_usb_device_t usbdev;
       int configno;
       int interfno;
@@ -186,6 +200,7 @@ grub_serial_config_defaults (struct grub_serial_port *port)
 
 #if defined(__mips__) || defined (__i386__) || defined (__x86_64__)
 void grub_ns8250_init (void);
+void grub_ns8250_pci_mmio_init (void);
 char *grub_serial_ns8250_add_port (grub_port_t port);
 #endif
 #ifdef GRUB_MACHINE_IEEE1275
@@ -204,6 +219,7 @@ grub_arcserial_add_port (const char *path);
 
 struct grub_serial_port *grub_serial_find (const char *name);
 extern struct grub_serial_driver grub_ns8250_driver;
+extern struct grub_serial_driver grub_ns8250_pci_mmio_driver;
 void EXPORT_FUNC(grub_serial_unregister_driver) (struct grub_serial_driver 
*driver);
 
 #ifndef GRUB_MACHINE_EMU
-- 
2.7.4




reply via email to

[Prev in Thread] Current Thread [Next in Thread]