2019-08-01 13:04:53 -07:00
|
|
|
/*
|
2020-06-12 10:02:50 -07:00
|
|
|
* Copyright (c) 2020 Intel Corporation
|
2019-08-01 13:04:53 -07:00
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
2019-11-19 18:08:21 -08:00
|
|
|
#include <kernel.h>
|
2020-06-26 11:56:10 -07:00
|
|
|
#include <sys/device_mmio.h>
|
2019-12-09 11:18:21 -06:00
|
|
|
#include <sys/util.h>
|
arch/x86: early_serial cleanup
Various cleanups to the x86 early serial driver, mostly with the goal
of simplifying its deployment during board bringup (which is really
the only reason it exists in the first place):
+ Configure it =y by default. While there are surely constrained
environments that will want to disable it, this is a TINY driver,
and it serves a very important role for niche tasks. It should be
built always to make sure it works everywhere.
+ Decouple from devicetree as much as possible. This code HAS to work
during board bringup, often with configurations cribbed from other
machines, before proper configuration gets written. Experimentally,
devicetree errors tend to be easy to make, and without a working
console impossible to diagnose. Specify the device via integer
constants in soc.h (in the case of IOPORT access, we already had
such a symbol) so that the path from what the developer intends to
what the code executes is as short and obvious as possible.
Unfortunately I'm not allowed to remove devicetree entirely here,
but at least a developer adding a new platform will be able to
override it in an obvious way instead of banging blindly on the
other side of a DTS compiler.
+ Don't try to probe the PCI device by ID to "verify". While this
sounds like a good idea, in practice it's just an extra thing to get
wrong. If we bail on our early console because someone (yes, that's
me) got the bus/device/function right but typoed the VID/DID
numbers, we're doing no one any favors.
+ Remove the word-sized-I/O feature. This is a x86 driver for a PCI
device. No known PC hardware requires that UART register access be
done in dword units (in fact doing so would be a violation of the
PCI specifciation as I understand it). It looks to have been cut
and pasted from the ns16550 driver, remove.
Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
2020-06-12 07:59:28 -07:00
|
|
|
#include <drivers/pcie/pcie.h>
|
2020-05-05 14:14:34 -07:00
|
|
|
#include <soc.h>
|
|
|
|
|
2020-06-26 11:56:10 -07:00
|
|
|
|
2020-06-12 10:02:50 -07:00
|
|
|
#ifdef UART_NS16550_ACCESS_IOPORT
|
2020-06-26 11:56:10 -07:00
|
|
|
/* Legacy I/O Port Access to a NS16550 UART */
|
2020-06-12 10:02:50 -07:00
|
|
|
#define IN(reg) sys_in8(reg + UART_NS16550_ACCESS_IOPORT)
|
|
|
|
#define OUT(reg, val) sys_out8(val, reg + UART_NS16550_ACCESS_IOPORT)
|
2020-06-26 11:56:10 -07:00
|
|
|
#elif defined(X86_SOC_EARLY_SERIAL_PCIDEV)
|
2020-06-12 10:02:50 -07:00
|
|
|
/* "Modern" mapping of a UART into a PCI MMIO device. The registers
|
|
|
|
* are still bytes, but spaced at a 32 bit stride instead of packed
|
|
|
|
* together.
|
|
|
|
*/
|
2020-06-26 11:56:10 -07:00
|
|
|
static mm_reg_t mmio;
|
|
|
|
#define IN(reg) (sys_read32(mmio + reg * 4) & 0xff)
|
|
|
|
#define OUT(reg, val) sys_write32((val) & 0xff, mmio + reg * 4)
|
|
|
|
#elif defined(X86_SOC_EARLY_SERIAL_MMIO8_ADDR)
|
2020-06-12 10:02:50 -07:00
|
|
|
/* Still other devices use a MMIO region containing packed byte
|
|
|
|
* registers
|
|
|
|
*/
|
2020-12-17 15:18:18 +02:00
|
|
|
#ifdef DEVICE_MMIO_IS_IN_RAM
|
2020-06-26 11:56:10 -07:00
|
|
|
static mm_reg_t mmio;
|
|
|
|
#define BASE mmio
|
|
|
|
#else
|
|
|
|
#define BASE X86_SOC_EARLY_SERIAL_MMIO8_ADDR
|
|
|
|
#endif /* DEVICE_MMIO_IS_IN_RAM */
|
|
|
|
#define IN(reg) sys_read8(BASE + reg)
|
|
|
|
#define OUT(reg, val) sys_write8(val, BASE + reg)
|
|
|
|
#else
|
|
|
|
#error "Unsupported configuration"
|
2020-05-05 14:14:34 -07:00
|
|
|
#endif
|
2019-08-01 13:04:53 -07:00
|
|
|
|
2020-06-12 10:02:50 -07:00
|
|
|
#define REG_THR 0x00 /* Transmitter holding reg. */
|
|
|
|
#define REG_IER 0x01 /* Interrupt enable reg. */
|
|
|
|
#define REG_FCR 0x02 /* FIFO control reg. */
|
|
|
|
#define REG_LCR 0x03 /* Line control reg. */
|
|
|
|
#define REG_MCR 0x04 /* Modem control reg. */
|
|
|
|
#define REG_LSR 0x05 /* Line status reg. */
|
|
|
|
#define REG_BRDL 0x00 /* Baud rate divisor (LSB) */
|
|
|
|
#define REG_BRDH 0x01 /* Baud rate divisor (MSB) */
|
|
|
|
|
|
|
|
#define IER_DISABLE 0x00
|
|
|
|
#define LCR_8N1 (BIT(0) | BIT(1))
|
|
|
|
#define LCR_DLAB_SELECT BIT(7)
|
|
|
|
#define MCR_DTR BIT(0)
|
|
|
|
#define MCR_RTS BIT(1)
|
|
|
|
#define LSR_THRE BIT(5)
|
|
|
|
|
|
|
|
#define FCR_FIFO BIT(0) /* enable XMIT and RCVR FIFO */
|
|
|
|
#define FCR_RCVRCLR BIT(1) /* clear RCVR FIFO */
|
|
|
|
#define FCR_XMITCLR BIT(2) /* clear XMIT FIFO */
|
|
|
|
#define FCR_FIFO_1 0 /* 1 byte in RCVR FIFO */
|
|
|
|
|
2020-12-30 15:31:55 +02:00
|
|
|
static bool early_serial_init_done;
|
|
|
|
static uint32_t suppressed_chars;
|
|
|
|
|
2019-08-01 13:04:53 -07:00
|
|
|
static void serout(int c)
|
|
|
|
{
|
2020-06-12 10:02:50 -07:00
|
|
|
while ((IN(REG_LSR) & LSR_THRE) == 0) {
|
2019-08-01 13:04:53 -07:00
|
|
|
}
|
2020-06-12 10:02:50 -07:00
|
|
|
OUT(REG_THR, c);
|
2019-08-01 13:04:53 -07:00
|
|
|
}
|
|
|
|
|
2020-06-12 10:02:50 -07:00
|
|
|
int arch_printk_char_out(int c)
|
2019-08-01 13:04:53 -07:00
|
|
|
{
|
2020-12-30 15:31:55 +02:00
|
|
|
if (!early_serial_init_done) {
|
|
|
|
suppressed_chars++;
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2019-08-01 13:04:53 -07:00
|
|
|
if (c == '\n') {
|
|
|
|
serout('\r');
|
|
|
|
}
|
|
|
|
serout(c);
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
void z_x86_early_serial_init(void)
|
|
|
|
{
|
2020-06-26 11:56:10 -07:00
|
|
|
#if defined(DEVICE_MMIO_IS_IN_RAM) && !defined(UART_NS16550_ACCESS_IOPORT)
|
2020-06-12 10:02:50 -07:00
|
|
|
#ifdef X86_SOC_EARLY_SERIAL_PCIDEV
|
2020-11-05 00:15:25 +01:00
|
|
|
struct pcie_mbar mbar;
|
|
|
|
pcie_get_mbar(X86_SOC_EARLY_SERIAL_PCIDEV, 0, &mbar);
|
2020-06-12 10:02:50 -07:00
|
|
|
pcie_set_cmd(X86_SOC_EARLY_SERIAL_PCIDEV, PCIE_CONF_CMDSTAT_MEM, true);
|
2020-11-05 00:15:25 +01:00
|
|
|
device_map(&mmio, mbar.phys_addr, mbar.size, K_MEM_CACHE_NONE);
|
2020-06-26 11:56:10 -07:00
|
|
|
#else
|
2020-11-05 00:15:25 +01:00
|
|
|
device_map(&mmio, X86_SOC_EARLY_SERIAL_MMIO8_ADDR, 0x1000, K_MEM_CACHE_NONE);
|
2020-05-05 14:14:34 -07:00
|
|
|
#endif
|
2020-11-05 00:15:25 +01:00
|
|
|
|
2020-06-26 11:56:10 -07:00
|
|
|
#endif /* DEVICE_MMIO_IS_IN_RAM */
|
2020-05-05 14:14:34 -07:00
|
|
|
|
2020-06-12 10:02:50 -07:00
|
|
|
OUT(REG_IER, IER_DISABLE); /* Disable interrupts */
|
|
|
|
OUT(REG_LCR, LCR_DLAB_SELECT); /* DLAB select */
|
|
|
|
OUT(REG_BRDL, 1); /* Baud divisor = 1 */
|
|
|
|
OUT(REG_BRDH, 0);
|
|
|
|
OUT(REG_LCR, LCR_8N1); /* LCR = 8n1 + DLAB off */
|
|
|
|
OUT(REG_MCR, MCR_DTR | MCR_RTS);
|
2020-05-05 14:14:34 -07:00
|
|
|
|
|
|
|
/* Turn on FIFO. Some hardware needs this before transmitting */
|
2020-06-12 10:02:50 -07:00
|
|
|
OUT(REG_FCR, FCR_FIFO | FCR_FIFO_1 | FCR_RCVRCLR | FCR_XMITCLR);
|
2020-12-30 15:31:55 +02:00
|
|
|
|
|
|
|
early_serial_init_done = true;
|
|
|
|
|
|
|
|
if (suppressed_chars) {
|
|
|
|
printk("WARNING: %u chars lost before early serial init\n",
|
|
|
|
suppressed_chars);
|
|
|
|
}
|
2019-08-01 13:04:53 -07:00
|
|
|
}
|