git: b37e8ffbd9d4 - main - uart: support Meteor Lake-P LPSS UART 0

From: Abdelkader Boudih <seuros_at_FreeBSD.org>
Date: Mon, 19 Oct 2026 15:15:26 UTC
The branch main has been updated by seuros:

URL: https://cgit.FreeBSD.org/src/commit/?id=b37e8ffbd9d4c46c4dc36842a9721fa766e0e61e

commit b37e8ffbd9d4c46c4dc36842a9721fa766e0e61e
Author:     Abdelkader Boudih <seuros@FreeBSD.org>
AuthorDate: 2026-10-19 15:14:58 +0000
Commit:     Abdelkader Boudih <seuros@FreeBSD.org>
CommitDate: 2026-10-19 15:14:58 +0000

    uart: support Meteor Lake-P LPSS UART 0
    
    Recognize Intel 8086:7e25 using 32-bit MMIO with a four-byte register
    stride. Deassert the LPSS functional resets and program the 100 MHz
    reference clock divider for a 44.2368 MHz UART clock before the UART
    core claims the register window, and reprogram both on resume.
    
    Reviewed by:    imp, adrian
    Differential Revision:  https://reviews.freebsd.org/D60858
---
 sys/dev/uart/uart_bus_pci.c | 82 +++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 80 insertions(+), 2 deletions(-)

diff --git a/sys/dev/uart/uart_bus_pci.c b/sys/dev/uart/uart_bus_pci.c
index 97c5ff84d251..39d3b1323bed 100644
--- a/sys/dev/uart/uart_bus_pci.c
+++ b/sys/dev/uart/uart_bus_pci.c
@@ -47,13 +47,14 @@
 static int uart_pci_probe(device_t dev);
 static int uart_pci_attach(device_t dev);
 static int uart_pci_detach(device_t dev);
+static int uart_pci_resume(device_t dev);
 
 static device_method_t uart_pci_methods[] = {
 	/* Device interface */
 	DEVMETHOD(device_probe,		uart_pci_probe),
 	DEVMETHOD(device_attach,	uart_pci_attach),
 	DEVMETHOD(device_detach,	uart_pci_detach),
-	DEVMETHOD(device_resume,	uart_bus_resume),
+	DEVMETHOD(device_resume,	uart_pci_resume),
 	DEVMETHOD_END
 };
 
@@ -72,6 +73,7 @@ struct pci_id {
 	int		rid;
 	int		rclk;
 	int		regshft;
+	int		regiowidth;
 };
 
 struct pci_unique_id {
@@ -80,8 +82,16 @@ struct pci_unique_id {
 };
 
 #define PCI_NO_MSI	0x40000000
+#define PCI_INTEL_LPSS	0x20000000
 #define PCI_RID_MASK	0x0000ffff
 
+/* Intel LPSS private registers, relative to BAR0. */
+#define LPSS_CLK	0x200
+#define LPSS_CLK_EN		(1U << 0)
+#define LPSS_CLK_UPDATE		(1U << 31)
+#define LPSS_RESETS	0x204
+#define LPSS_RESETS_FUNC	0x3
+
 static const struct pci_id pci_ns8250_ids[] = {
 { 0x1028, 0x0008, 0xffff, 0, "Dell Remote Access Card III", 0x14,
 	128 * DEFAULT_RCLK },
@@ -188,6 +198,8 @@ static const struct pci_id pci_ns8250_ids[] = {
 	24 * DEFAULT_RCLK, 2 },
 { 0x8086, 0x5aee, 0xffff, 0, "Intel Apollo Lake SIO/LPSS UART 3", 0x10,
 	24 * DEFAULT_RCLK, 2 },
+{ 0x8086, 0x7e25, 0xffff, 0, "Intel Meteor Lake-P LPSS UART 0",
+	0x10 | PCI_INTEL_LPSS, 24 * DEFAULT_RCLK, 2, 4 },
 { 0x8086, 0x8811, 0xffff, 0, "Intel EG20T Serial Port 0", 0x10 },
 { 0x8086, 0x8812, 0xffff, 0, "Intel EG20T Serial Port 1", 0x10 },
 { 0x8086, 0x8813, 0xffff, 0, "Intel EG20T Serial Port 2", 0x10 },
@@ -237,6 +249,48 @@ uart_pci_match(device_t dev, const struct pci_id *id)
 	return ((id->vendor == vendor && id->device == device) ? id : NULL);
 }
 
+static int
+uart_pci_lpss_setup(struct resource *res)
+{
+	uint32_t clk, resets;
+
+	if (rman_get_size(res) < LPSS_RESETS + sizeof(uint32_t))
+		return (ENXIO);
+
+	resets = bus_read_4(res, LPSS_RESETS);
+	bus_write_4(res, LPSS_RESETS, resets | LPSS_RESETS_FUNC);
+	bus_barrier(res, LPSS_RESETS, sizeof(uint32_t),
+	    BUS_SPACE_BARRIER_WRITE);
+
+	/*
+	 * Divide the 100 MHz reference clock down to 44.2368 MHz.  Bits
+	 * 15:1 hold M and bits 30:16 hold N, giving a rate of ref * M / N.
+	 */
+	clk = (15625U << 16) | (6912U << 1);
+	bus_write_4(res, LPSS_CLK, clk);
+	bus_barrier(res, LPSS_CLK, sizeof(uint32_t),
+	    BUS_SPACE_BARRIER_WRITE);
+	bus_write_4(res, LPSS_CLK, clk | LPSS_CLK_EN | LPSS_CLK_UPDATE);
+	(void)bus_read_4(res, LPSS_CLK);
+
+	return (0);
+}
+
+static int
+uart_pci_lpss_init(device_t dev, const struct pci_id *id)
+{
+	struct resource *res;
+	int error, rid;
+
+	rid = id->rid & PCI_RID_MASK;
+	res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+	if (res == NULL)
+		return (ENXIO);
+	error = uart_pci_lpss_setup(res);
+	bus_release_resource(dev, SYS_RES_MEMORY, rid, res);
+	return (error);
+}
+
 extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs;
 
 /* PCI vendor/device pairs of devices guaranteed to be unique on a system. */
@@ -317,7 +371,13 @@ uart_pci_attach(device_t dev)
 
 	sc = device_get_softc(dev);
 
-	result = uart_bus_probe(dev, id->regshft, 0, id->rclk,
+	if ((id->rid & PCI_INTEL_LPSS) != 0) {
+		result = uart_pci_lpss_init(dev, id);
+		if (result != 0)
+			return (result);
+	}
+
+	result = uart_bus_probe(dev, id->regshft, id->regiowidth, id->rclk,
 	    id->rid & PCI_RID_MASK, 0, 0);
 	/* Bail out on error. */
 	if (result > 0)
@@ -362,4 +422,22 @@ uart_pci_detach(device_t dev)
 	return (uart_bus_detach(dev));
 }
 
+static int
+uart_pci_resume(device_t dev)
+{
+	const struct pci_id *id;
+	struct uart_softc *sc;
+
+	id = uart_pci_match(dev, pci_ns8250_ids);
+	sc = device_get_softc(dev);
+
+	/* The UART core holds the register window across suspend. */
+	if (id != NULL && (id->rid & PCI_INTEL_LPSS) != 0 &&
+	    sc->sc_rres != NULL && sc->sc_rtype == SYS_RES_MEMORY) {
+		if (uart_pci_lpss_setup(sc->sc_rres) != 0)
+			device_printf(dev, "LPSS reinitialization failed\n");
+	}
+	return (uart_bus_resume(dev));
+}
+
 DRIVER_MODULE(uart, pci, uart_pci_driver, NULL, NULL);