[U-Boot,v3,092/108] x86: apollolake: Add UART driver
diff mbox series

Message ID 20191021033913.220758-87-sjg@chromium.org
State New
Delegated to: Bin Meng
Headers show
Series
  • x86: Add initial support for apollolake
Related show

Commit Message

Simon Glass Oct. 21, 2019, 3:38 a.m. UTC
Add a driver for the apollolake UART. It uses the standard ns16550 device
but also sets up the input clock with LPSS and supports configuration via
of-platdata.

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

Changes in v3:
- Use the LPSS code from a separate file

Changes in v2: None

 arch/x86/cpu/apollolake/Makefile            |   1 +
 arch/x86/cpu/apollolake/uart.c              | 141 ++++++++++++++++++++
 arch/x86/include/asm/arch-apollolake/uart.h |  17 +++
 3 files changed, 159 insertions(+)
 create mode 100644 arch/x86/cpu/apollolake/uart.c
 create mode 100644 arch/x86/include/asm/arch-apollolake/uart.h

Comments

Andy Shevchenko Oct. 21, 2019, 7:46 a.m. UTC | #1
On Mon, Oct 21, 2019 at 7:28 AM Simon Glass <sjg@chromium.org> wrote:
>
> Add a driver for the apollolake UART. It uses the standard ns16550 device
> but also sets up the input clock with LPSS and supports configuration via
> of-platdata.

This must be generic driver. The LPSS block is the same for all Intel
SoCs starting from Skylake.
Making it generic will reduce double efforts in the future.
Simon Glass Oct. 21, 2019, 10:53 p.m. UTC | #2
Hi Andy,

On Mon, 21 Oct 2019 at 01:46, Andy Shevchenko <andy.shevchenko@gmail.com> wrote:
>
> On Mon, Oct 21, 2019 at 7:28 AM Simon Glass <sjg@chromium.org> wrote:
> >
> > Add a driver for the apollolake UART. It uses the standard ns16550 device
> > but also sets up the input clock with LPSS and supports configuration via
> > of-platdata.
>
> This must be generic driver. The LPSS block is the same for all Intel
> SoCs starting from Skylake.
> Making it generic will reduce double efforts in the future.

This uses ns16550.

Are you saying that lpss.c should go in intel_common? I can do that.

Regards,
Simon
Andy Shevchenko Oct. 22, 2019, 8:21 a.m. UTC | #3
On Tue, Oct 22, 2019 at 1:54 AM Simon Glass <sjg@chromium.org> wrote:
> On Mon, 21 Oct 2019 at 01:46, Andy Shevchenko <andy.shevchenko@gmail.com> wrote:
> >
> > On Mon, Oct 21, 2019 at 7:28 AM Simon Glass <sjg@chromium.org> wrote:
> > >
> > > Add a driver for the apollolake UART. It uses the standard ns16550 device
> > > but also sets up the input clock with LPSS and supports configuration via
> > > of-platdata.
> >
> > This must be generic driver. The LPSS block is the same for all Intel
> > SoCs starting from Skylake.
> > Making it generic will reduce double efforts in the future.
>
> This uses ns16550.
>
> Are you saying that lpss.c should go in intel_common? I can do that.

Yes.

Patch
diff mbox series

diff --git a/arch/x86/cpu/apollolake/Makefile b/arch/x86/cpu/apollolake/Makefile
index b58ef8e019c..ff42d0fd619 100644
--- a/arch/x86/cpu/apollolake/Makefile
+++ b/arch/x86/cpu/apollolake/Makefile
@@ -4,3 +4,4 @@ 
 
 obj-y += lpss.o
 obj-y += pmc.o
+obj-y += uart.o
diff --git a/arch/x86/cpu/apollolake/uart.c b/arch/x86/cpu/apollolake/uart.c
new file mode 100644
index 00000000000..66744937dcb
--- /dev/null
+++ b/arch/x86/cpu/apollolake/uart.c
@@ -0,0 +1,141 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Special driver to handle of-platdata
+ *
+ * Copyright 2019 Google LLC
+ *
+ * Some code from coreboot lpss.c
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dt-structs.h>
+#include <ns16550.h>
+#include <spl.h>
+#include <asm/io.h>
+#include <asm/pci.h>
+#include <asm/lpss.h>
+
+/* Low-power Subsystem (LPSS) clock register */
+enum {
+	LPSS_CLOCK_CTL_REG	= 0x200,
+	LPSS_CNT_CLOCK_EN	= 1,
+	LPSS_CNT_CLK_UPDATE	= 1U << 31,
+	LPSS_CLOCK_DIV_N_SHIFT	= 16,
+	LPSS_CLOCK_DIV_N_MASK	= 0x7fff << LPSS_CLOCK_DIV_N_SHIFT,
+	LPSS_CLOCK_DIV_M_SHIFT	= 1,
+	LPSS_CLOCK_DIV_M_MASK	= 0x7fff << LPSS_CLOCK_DIV_M_SHIFT,
+
+	/* These set the UART input clock speed */
+	LPSS_UART_CLK_M_VAL	= 0x25a,
+	LPSS_UART_CLK_N_VAL	= 0x7fff,
+};
+
+static void lpss_clk_update(void *regs, u32 clk_m_val, u32 clk_n_val)
+{
+	u32 clk_sel;
+
+	clk_sel = clk_n_val << LPSS_CLOCK_DIV_N_SHIFT |
+		 clk_m_val << LPSS_CLOCK_DIV_M_SHIFT;
+	clk_sel |= LPSS_CNT_CLK_UPDATE | LPSS_CNT_CLOCK_EN;
+
+	writel(clk_sel, regs + LPSS_CLOCK_CTL_REG);
+}
+
+static void uart_lpss_init(void *regs)
+{
+	/* Take UART out of reset */
+	lpss_reset_release(regs);
+
+	/* Set M and N divisor inputs and enable clock */
+	lpss_clk_update(regs, LPSS_UART_CLK_M_VAL, LPSS_UART_CLK_N_VAL);
+}
+
+void apl_uart_init(pci_dev_t bdf, ulong base)
+{
+	/* Set UART base address */
+	pci_x86_write_config(bdf, PCI_BASE_ADDRESS_0, base, PCI_SIZE_32);
+
+	/* Enable memory access and bus master */
+	pci_x86_write_config(bdf, PCI_COMMAND, PCI_COMMAND_MEMORY |
+			     PCI_COMMAND_MASTER, PCI_SIZE_32);
+
+	uart_lpss_init((void *)base);
+}
+
+/*
+ * This driver uses its own compatible string but almost everything else from
+ * the standard ns16550 driver. This allows us to provide an of-platdata
+ * implementation, since the platdata produced by of-platdata does not match
+ * struct ns16550_platdata.
+ *
+ * When running with of-platdata (generally TPL), the platdata is converted to
+ * something that ns16550 expects. When running withoutof-platdata (SPL, U-Boot
+ * proper), we use ns16550's ofdata_to_platdata routine.
+ */
+
+static int apl_ns16550_probe(struct udevice *dev)
+{
+	struct ns16550_platdata *plat = dev_get_platdata(dev);
+
+	if (!CONFIG_IS_ENABLED(PCI))
+		apl_uart_init(plat->bdf, plat->base);
+
+	return ns16550_serial_probe(dev);
+}
+
+static int apl_ns16550_ofdata_to_platdata(struct udevice *dev)
+{
+	struct ns16550_platdata *plat;
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+	struct dtd_intel_apl_ns16550 *dtplat = dev_get_platdata(dev);
+
+	/*
+	 * Convert our platdata to the ns16550's platdata, so we can just use
+	 * that driver
+	 */
+	plat = malloc(sizeof(*plat));
+	if (!plat)
+		return -ENOMEM;
+	plat->base = dtplat->early_regs[0];
+	plat->reg_width = 1;
+	plat->reg_shift = dtplat->reg_shift;
+	plat->reg_offset = 0;
+	plat->clock = dtplat->clock_frequency;
+	plat->fcr = UART_FCR_DEFVAL;
+	plat->bdf = pci_x86_ofplat_get_devfn(dtplat->reg[0]);
+	dev->platdata = plat;
+#else
+	int ret;
+
+	ret = ns16550_serial_ofdata_to_platdata(dev);
+	if (ret)
+		return ret;
+	if (!CONFIG_IS_ENABLED(OF_TRANSLATE)) {
+		/*
+		 * Without address translation we cannot get correct PCI
+		 * address, so just read the BAR manually.
+		 */
+		plat = dev_get_platdata(dev);
+		plat->base = dm_pci_read_bar32(dev, 0);
+	}
+#endif /* OF_PLATDATA */
+
+	return 0;
+}
+
+static const struct udevice_id apl_ns16550_serial_ids[] = {
+	{ .compatible = "intel,apl-ns16550" },
+	{ },
+};
+
+U_BOOT_DRIVER(apl_ns16550) = {
+	.name	= "intel_apl_ns16550",
+	.id	= UCLASS_SERIAL,
+	.of_match = apl_ns16550_serial_ids,
+	.platdata_auto_alloc_size = sizeof(struct ns16550_platdata),
+	.priv_auto_alloc_size = sizeof(struct NS16550),
+	.ops	= &ns16550_serial_ops,
+	.ofdata_to_platdata = apl_ns16550_ofdata_to_platdata,
+	.probe = apl_ns16550_probe,
+};
diff --git a/arch/x86/include/asm/arch-apollolake/uart.h b/arch/x86/include/asm/arch-apollolake/uart.h
new file mode 100644
index 00000000000..521316d0a93
--- /dev/null
+++ b/arch/x86/include/asm/arch-apollolake/uart.h
@@ -0,0 +1,17 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2019 Google LLC
+ */
+
+#ifndef __ASM_ARCH_UART_H
+#define __ASM_ARCH_UART_H
+
+/**
+ * apl_uart_init() - Set up the APL UART device and clock
+ *
+ * The UART won't actually work unless the GPIO settings are correct and the
+ * signals actually exit the SoC. See init_for_uart() for that.
+ */
+int apl_uart_init(pci_dev_t bdf, ulong base);
+
+#endif