diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index aae88c806b..6bf1190d87 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -63,4 +63,10 @@ if(CONFIG_UART_BTH4) list(APPEND SRCS uart_bth4.c) endif() +# UART PCI drivers + +if(CONFIG_16550_PCI_UART) + list(APPEND SRCS uart_pci_16550.c) +endif() + target_sources(drivers PRIVATE ${SRCS}) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index ee312a2b9a..66f1ba6230 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -79,6 +79,10 @@ if 16550_UART source "drivers/serial/Kconfig-16550" endif +if PCI +source "drivers/serial/Kconfig-pci" +endif + # # MCU serial peripheral driver? # diff --git a/drivers/serial/Kconfig-pci b/drivers/serial/Kconfig-pci new file mode 100644 index 0000000000..ff977b4de2 --- /dev/null +++ b/drivers/serial/Kconfig-pci @@ -0,0 +1,327 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config 16550_PCI_UART + bool "16550 UART PCI support" + default n + select 16550_UART + +if 16550_PCI_UART + +config 16550_PCI_UART0 + bool "16550 UART0 PCI" + default n + ---help--- + Support for PCI UART0, will be registered as /dev/ttyS0 + +config 16550_PCI_UART1 + bool "16550 UART1 PCI" + default n + ---help--- + Support for PCI UART1, will be registered as /dev/ttyS1 + +config 16550_PCI_UART2 + bool "16550 UART2 PCI" + default n + ---help--- + Support for PCI UART2, will be registered as /dev/ttyS2 + +config 16550_PCI_UART3 + bool "16550 UART3 PCI" + default n + ---help--- + Support for PCI UART3, will be registered as /dev/ttyS3 + +config 16550_PCI_UART_QEMU + bool "16550 PCI QEMU devices" + default n + ---help--- + Enable support for QEMU serial devices (pci-serial, pci-serial-2x + pci-serial-4x) + +config 16550_PCI_UART_AX99100 + bool "16550 PCI AX99100 device" + default n + ---help--- + Enable support for AX99100 serial port device. + +endif # 16550_PCI_UART + +if 16550_PCI_UART0 + +config 16550_PCI_UART0_VENDOR + hex "16550 UART0 PCI vendor" + ---help--- + PCI vendor number that will be associated with UART0 + +config 16550_PCI_UART0_DEVICE + hex "16550 UART0 PCI device" + ---help--- + PCI device number that will be associated with UART0 + +config 16550_PCI_UART0_PORT + int "16550 UART0 PCI port" + default 0 + ---help--- + Port in multi-port device. Starts from 0. + Must be set to 0 for single port devices . + +config 16550_PCI_UART0_CLOCK + int "16550 UART0 PCI clock" + +config 16550_PCI_UART0_BAUD + int "16550 UART0 PCI BAUD" + default 115200 + +config 16550_PCI_UART0_PARITY + int "16550 UART0 PCI parity" + default 0 + range 0 2 + ---help--- + 16550 UART0 PCI parity. 0=None, 1=Odd, 2=Even. Default: None + +config 16550_PCI_UART0_BITS + int "16550 UART0 PCI number of bits" + default 8 + ---help--- + 16550 UART0 PCI number of bits. Default: 8 + +config 16550_PCI_UART0_2STOP + int "16550 UART0 PCI two stop bits" + default 0 + ---help--- + 0=1 stop bit, 1=Two stop bits. Default: 1 stop bit + +config 16550_PCI_UART0_RXBUFSIZE + int "16550 UART0 PCI Rx buffer size" + default 256 + ---help--- + 16550 UART0 PCI Rx buffer size. Default: 256 + +config 16550_PCI_UART0_TXBUFSIZE + int "16550 UART0 PCI Tx buffer size" + default 256 + ---help--- + 16550 UART0 PCI Tx buffer size. Default: 256 + +endif # 16550_PCI_UART0 + +if 16550_PCI_UART1 + +config 16550_PCI_UART1_VENDOR + hex "16550 UART1 PCI vendor" + default 16550_PCI_UART0_VENDOR + ---help--- + PCI vendor number that will be associated with UART1 + +config 16550_PCI_UART1_DEVICE + hex "16550 UART1 PCI device" + default 16550_PCI_UART0_DEVICE + ---help--- + PCI device number that will be associated with UART1 + +config 16550_PCI_UART1_PORT + int "16550 UART1 PCI port" + default 1 + ---help--- + Port in multi-port device. Starts from 0. + Must be set to 0 for single port devices . + +config 16550_PCI_UART1_CLOCK + int "16550 UART1 PCI clock" + +config 16550_PCI_UART1_BAUD + int "16550 UART1 PCI BAUD" + default 115200 + +config 16550_PCI_UART1_PARITY + int "16550 UART1 PCI parity" + default 0 + range 0 2 + ---help--- + 16550 UART1 PCI parity. 0=None, 1=Odd, 2=Even. Default: None + +config 16550_PCI_UART1_BITS + int "16550 UART1 PCI number of bits" + default 8 + ---help--- + 16550 UART1 PCI number of bits. Default: 8 + +config 16550_PCI_UART1_2STOP + int "16550 UART1 PCI two stop bits" + default 0 + ---help--- + 0=1 stop bit, 1=Two stop bits. Default: 1 stop bit + +config 16550_PCI_UART1_RXBUFSIZE + int "16550 UART1 PCI Rx buffer size" + default 256 + ---help--- + 16550 UART1 PCI Rx buffer size. Default: 256 + +config 16550_PCI_UART1_TXBUFSIZE + int "16550 UART1 PCI Tx buffer size" + default 256 + ---help--- + 16550 UART1 PCI Tx buffer size. Default: 256 + +endif # 16550_PCI_UART1 + +if 16550_PCI_UART2 + +config 16550_PCI_UART2_VENDOR + hex "16550 UART2 PCI vendor" + default 16550_PCI_UART1_VENDOR + ---help--- + PCI vendor number that will be associated with UART2 + +config 16550_PCI_UART2_DEVICE + hex "16550 UART2 PCI device" + default 16550_PCI_UART1_DEVICE + ---help--- + PCI device number that will be associated with UART2 + +config 16550_PCI_UART2_PORT + int "16550 UART2 PCI port" + default 2 + ---help--- + Port in multi-port device. Starts from 0. + Must be set to 0 for single port devices . + +config 16550_PCI_UART2_CLOCK + int "16550 UART2 PCI clock" + +config 16550_PCI_UART2_BAUD + int "16550 UART2 PCI BAUD" + default 115200 + +config 16550_PCI_UART2_PARITY + int "16550 UART2 PCI parity" + default 0 + range 0 2 + ---help--- + 16550 UART2 PCI parity. 0=None, 1=Odd, 2=Even. Default: None + +config 16550_PCI_UART2_BITS + int "16550 UART2 PCI number of bits" + default 8 + ---help--- + 16550 UART2 PCI number of bits. Default: 8 + +config 16550_PCI_UART2_2STOP + int "16550 UART2 PCI two stop bits" + default 0 + ---help--- + 0=1 stop bit, 1=Two stop bits. Default: 1 stop bit + +config 16550_PCI_UART2_RXBUFSIZE + int "16550 UART2 PCI Rx buffer size" + default 256 + ---help--- + 16550 UART2 PCI Rx buffer size. Default: 256 + +config 16550_PCI_UART2_TXBUFSIZE + int "16550 UART2 PCI Tx buffer size" + default 256 + ---help--- + 16550 UART2 PCI Tx buffer size. Default: 256 + +endif # 16550_PCI_UART2 + +if 16550_PCI_UART3 + +config 16550_PCI_UART3_VENDOR + hex "16550 UART3 PCI vendor" + default 16550_PCI_UART2_VENDOR + ---help--- + PCI vendor number that will be associated with UART3 + +config 16550_PCI_UART3_DEVICE + hex "16550 UART3 PCI device" + default 16550_PCI_UART2_DEVICE + ---help--- + PCI device number that will be associated with UART3 + +config 16550_PCI_UART3_PORT + int "16550 UART3 PCI port" + default 3 + ---help--- + Port in multi-port device. Starts from 0. + Must be set to 0 for single port devices . + +config 16550_PCI_UART3_CLOCK + int "16550 UART3 PCI clock" + +config 16550_PCI_UART3_BAUD + int "16550 UART3 PCI BAUD" + default 115200 + +config 16550_PCI_UART3_PARITY + int "16550 UART3 PCI parity" + default 0 + range 0 2 + ---help--- + 16550 UART3 PCI parity. 0=None, 1=Odd, 2=Even. Default: None + +config 16550_PCI_UART3_BITS + int "16550 UART3 PCI number of bits" + default 8 + ---help--- + 16550 UART3 PCI number of bits. Default: 8 + +config 16550_PCI_UART3_2STOP + int "16550 UART3 PCI two stop bits" + default 0 + ---help--- + 0=1 stop bit, 1=Two stop bits. Default: 1 stop bit + +config 16550_PCI_UART3_RXBUFSIZE + int "16550 UART3 PCI Rx buffer size" + default 256 + ---help--- + 16550 UART3 PCI Rx buffer size. Default: 256 + +config 16550_PCI_UART3_TXBUFSIZE + int "16550 UART3 PCI Tx buffer size" + default 256 + ---help--- + 16550 UART3 PCI Tx buffer size. Default: 256 + +endif # 16550_PCI_UART3 + +config 16550_PCI_CONSOLE + bool + depends on 16550_PCI_UART && 16550_NO_SERIAL_CONSOLE + select SERIAL_CONSOLE + +choice + prompt "16550 PCI Serial Console" + default 16550_PCI_NO_SERIAL_CONSOLE + depends on DEV_CONSOLE && 16550_PCI_UART + +config 16550_PCI_UART0_SERIAL_CONSOLE + bool "16550 PCI UART0 serial console" + depends on 16550_PCI_UART0 + select 16550_PCI_CONSOLE + +config 16550_PCI_UART1_SERIAL_CONSOLE + bool "16550 PCI UART1 serial console" + depends on 16550_PCI_UART1 + select 16550_PCI_CONSOLE + +config 16550_PCI_UART2_SERIAL_CONSOLE + bool "16550 PCI UART2 serial console" + depends on 16550_PCI_UART2 + select 16550_PCI_CONSOLE + +config 16550_PCI_UART3_SERIAL_CONSOLE + bool "16550 PCI UART3 serial console" + depends on 16550_PCI_UART3 + select 16550_PCI_CONSOLE + +config 16550_PCI_NO_SERIAL_CONSOLE + bool "No 16550 PCI serial console" + +endchoice # 16550 Serial Console diff --git a/drivers/serial/Make.defs b/drivers/serial/Make.defs index 28665d01f2..7a8f9cfb74 100644 --- a/drivers/serial/Make.defs +++ b/drivers/serial/Make.defs @@ -75,6 +75,12 @@ ifeq ($(CONFIG_RAM_UART),y) CSRCS += uart_ram.c endif +# UART PCI drivers + +ifeq ($(CONFIG_16550_PCI_UART),y) + CSRCS += uart_pci_16550.c +endif + # Include serial build support DEPPATH += --dep-path serial diff --git a/drivers/serial/uart_pci_16550.c b/drivers/serial/uart_pci_16550.c new file mode 100644 index 0000000000..e4c2ca5623 --- /dev/null +++ b/drivers/serial/uart_pci_16550.c @@ -0,0 +1,810 @@ +/***************************************************************************** + * drivers/serial/uart_pci_16550.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + *****************************************************************************/ + +/* Serial driver for 16550 UART PCI */ + +/***************************************************************************** + * Included Files + *****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/***************************************************************************** + * Pre-processor Definitions + *****************************************************************************/ + +#define PCI_U16550_DEV_PATH0 "/dev/ttyS0" +#define PCI_U16550_DEV_PATH1 "/dev/ttyS1" +#define PCI_U16550_DEV_PATH2 "/dev/ttyS2" +#define PCI_U16550_DEV_PATH3 "/dev/ttyS3" + +/* UART PCI console support */ + +#if defined(CONFIG_16550_PCI_UART0_SERIAL_CONSOLE) +# define CONSOLE_DEV g_pci_u16550_dev0 +#elif defined(CONFIG_16550_PCI_UART1_SERIAL_CONSOLE) +# define CONSOLE_DEV g_pci_u16550_dev1 +#elif defined(CONFIG_16550_PCI_UART2_SERIAL_CONSOLE) +# define CONSOLE_DEV g_pci_u16550_dev2 +#elif defined(CONFIG_16550_PCI_UART3_SERIAL_CONSOLE) +# define CONSOLE_DEV g_pci_u16550_dev3 +#endif + +/***************************************************************************** + * Private Types + *****************************************************************************/ + +/* Extend default PCI devie type */ + +struct pci_u16550_type_s +{ + uint8_t ports; /* Number of ports */ + uint8_t regincr; /* Address increment */ + uint8_t portincr; /* Port address increment */ +}; + +/* Extend default UART 16550 strucutre */ + +struct pci_u16550_priv_s +{ + /* Common UART 16550 data must be first */ + + struct u16550_s common; + FAR struct pci_device_s *pcidev; + uint16_t vendor; + uint16_t device; + uint8_t port; + FAR const char *path; +}; + +/***************************************************************************** + * Private Functions Definitions + *****************************************************************************/ + +static uart_datawidth_t pci_u16550_getreg_mem(FAR struct u16550_s *priv, + unsigned int offset); +static void pci_u16550_putreg_mem(FAR struct u16550_s *priv, + unsigned int offset, + uart_datawidth_t value); +static uart_datawidth_t pci_u16550_getreg_io(FAR struct u16550_s *priv, + unsigned int offset); +static void pci_u16550_putreg_io(FAR struct u16550_s *priv, + unsigned int offset, + uart_datawidth_t value); +static int pci_u16550_ioctl(FAR struct u16550_s *priv, int cmd, + unsigned long arg); + +static FAR struct dma_chan_s *pci_u16550_dmachan(FAR struct u16550_s *priv, + unsigned int ident); +static int pci_u16550_interrupt(int irq, FAR void *context, FAR void *arg); +static int pci_u16550_initialize(FAR struct pci_u16550_priv_s *priv, + FAR const struct pci_u16550_type_s *type, + uintptr_t base, + FAR struct pci_device_s *dev, + bool mmio); +static int pci_u16550_register(FAR uart_dev_t *dev); +static int pci_u16550_probe(FAR struct pci_device_s *dev); + +/***************************************************************************** + * Private Data + *****************************************************************************/ + +#ifdef CONFIG_16550_PCI_UART_QEMU + +static const struct pci_u16550_type_s g_pci_u16550_qemu_x1 = +{ + .ports = 1, + .regincr = 1, + .portincr = 0, +}; + +static const struct pci_u16550_type_s g_pci_u16550_qemu_x2 = +{ + .ports = 2, + .regincr = 1, + .portincr = 8, +}; + +static const struct pci_u16550_type_s g_pci_u16550_qemu_x4 = +{ + .ports = 4, + .regincr = 1, + .portincr = 8, +}; +#endif /* CONFIG_16550_PCI_UART_QEMU */ + +#ifdef CONFIG_16550_PCI_UART_AX99100 +static const struct pci_u16550_type_s g_pci_u16550_ax99100_x2 = +{ + .ports = 2, + .regincr = 1, + .portincr = 8, +}; +#endif /* CONFIG_16550_PCI_UART_AX99100 */ + +static const struct pci_device_id_s g_pci_u16550_id_table[] = +{ +#ifdef CONFIG_16550_PCI_UART_QEMU + { + PCI_DEVICE(0x1b36, 0x0002), + .driver_data = (uintptr_t)&g_pci_u16550_qemu_x1 + }, + { + PCI_DEVICE(0x1b36, 0x0003), + .driver_data = (uintptr_t)&g_pci_u16550_qemu_x2 + }, + { + PCI_DEVICE(0x1b36, 0x0004), + .driver_data = (uintptr_t)&g_pci_u16550_qemu_x4 + }, +#endif +#ifdef CONFIG_16550_PCI_UART_AX99100 + { + PCI_DEVICE(0x125b, 0x9100), + .driver_data = (uintptr_t)&g_pci_u16550_ax99100_x2 + }, +#endif + { } +}; + +static struct pci_driver_s g_pci_u16550_drv = +{ + .id_table = g_pci_u16550_id_table, + .probe = pci_u16550_probe, +}; + +/* UART 16550 ops for MMIO operations */ + +static const struct u16550_ops_s g_pci_u16550_mem_ops = +{ + .isr = pci_u16550_interrupt, + .getreg = pci_u16550_getreg_mem, + .putreg = pci_u16550_putreg_mem, + .ioctl = pci_u16550_ioctl, + .dmachan = pci_u16550_dmachan, +}; + +/* UART 16550 ops for IO operations */ + +static const struct u16550_ops_s g_pci_u16550_io_ops = +{ + .isr = pci_u16550_interrupt, + .getreg = pci_u16550_getreg_io, + .putreg = pci_u16550_putreg_io, + .ioctl = pci_u16550_ioctl, + .dmachan = pci_u16550_dmachan, +}; + +/* I/O buffers */ + +#ifdef CONFIG_16550_PCI_UART0 +static char g_pci_u16550_rxbuffer0[CONFIG_16550_PCI_UART0_RXBUFSIZE]; +static char g_pci_u16550_txbuffer0[CONFIG_16550_PCI_UART0_TXBUFSIZE]; +#endif + +#ifdef CONFIG_16550_PCI_UART1 +static char g_pci_u16550_rxbuffer1[CONFIG_16550_PCI_UART1_RXBUFSIZE]; +static char g_pci_u16550_txbuffer1[CONFIG_16550_PCI_UART1_TXBUFSIZE]; +#endif + +#ifdef CONFIG_16550_PCI_UART2 +static char g_pci_u16550_rxbuffer2[CONFIG_16550_PCI_UART2_RXBUFSIZE]; +static char g_pci_u16550_txbuffer2[CONFIG_16550_PCI_UART2_TXBUFSIZE]; +#endif + +#ifdef CONFIG_16550_PCI_UART3 +static char g_pci_u16550_rxbuffer3[CONFIG_16550_PCI_UART3_RXBUFSIZE]; +static char g_pci_u16550_txbuffer3[CONFIG_16550_PCI_UART3_TXBUFSIZE]; +#endif + +/* This describes the state of the 16550 UART0 PCI port. */ + +#ifdef CONFIG_16550_PCI_UART0 +static struct pci_u16550_priv_s g_pci_u16550_priv0 = +{ + /* UART 16550 common data */ + + .common = + { + .baud = CONFIG_16550_PCI_UART0_BAUD, + .uartclk = CONFIG_16550_PCI_UART0_CLOCK, + .parity = CONFIG_16550_PCI_UART0_PARITY, + .bits = CONFIG_16550_PCI_UART0_BITS, + .stopbits2 = CONFIG_16550_PCI_UART0_2STOP, +#if defined(CONFIG_16550_PCI_UART0_IFLOWCONTROL) || \ + defined(CONFIG_16550_PCI_UART0_OFLOWCONTROL) + .flow = true, +#endif + .rxtrigger = 2, + }, + + /* PCI specific data */ + + .vendor = CONFIG_16550_PCI_UART0_VENDOR, + .device = CONFIG_16550_PCI_UART0_DEVICE, + .port = CONFIG_16550_PCI_UART0_PORT, + .path = PCI_U16550_DEV_PATH0 +}; + +static uart_dev_t g_pci_u16550_dev0 = +{ + .recv = + { + .size = CONFIG_16550_PCI_UART0_RXBUFSIZE, + .buffer = g_pci_u16550_rxbuffer0, + }, + .xmit = + { + .size = CONFIG_16550_PCI_UART0_TXBUFSIZE, + .buffer = g_pci_u16550_txbuffer0, + }, + .priv = &g_pci_u16550_priv0.common, +#ifdef CONFIG_16550_PCI_UART0_SERIAL_CONSOLE + .isconsole = true, +#endif +}; +#endif + +/* This describes the state of the 16550 UART1 PCI port. */ + +#ifdef CONFIG_16550_PCI_UART1 +static struct pci_u16550_priv_s g_pci_u16550_priv1 = +{ + /* UART 16550 common data */ + + .common = + { + .baud = CONFIG_16550_PCI_UART1_BAUD, + .uartclk = CONFIG_16550_PCI_UART1_CLOCK, + .parity = CONFIG_16550_PCI_UART1_PARITY, + .bits = CONFIG_16550_PCI_UART1_BITS, + .stopbits2 = CONFIG_16550_PCI_UART1_2STOP, +#if defined(CONFIG_16550_PCI_UART1_IFLOWCONTROL) || \ + defined(CONFIG_16550_PCI_UART1_OFLOWCONTROL) + .flow = true, +#endif + .rxtrigger = 2, + }, + + /* PCI specific data */ + + .vendor = CONFIG_16550_PCI_UART1_VENDOR, + .device = CONFIG_16550_PCI_UART1_DEVICE, + .port = CONFIG_16550_PCI_UART1_PORT, + .path = PCI_U16550_DEV_PATH1 +}; + +static uart_dev_t g_pci_u16550_dev1 = +{ + .recv = + { + .size = CONFIG_16550_PCI_UART1_RXBUFSIZE, + .buffer = g_pci_u16550_rxbuffer1, + }, + .xmit = + { + .size = CONFIG_16550_PCI_UART1_TXBUFSIZE, + .buffer = g_pci_u16550_txbuffer1, + }, + .priv = &g_pci_u16550_priv1.common, +#ifdef CONFIG_16550_PCI_UART1_SERIAL_CONSOLE + .isconsole = true, +#endif +}; +#endif + +/* This describes the state of the 16550 UART2 PCI port. */ + +#ifdef CONFIG_16550_PCI_UART2 +static struct pci_u16550_priv_s g_pci_u16550_priv2 = +{ + /* UART 16550 common data */ + + .common = + { + .baud = CONFIG_16550_PCI_UART2_BAUD, + .uartclk = CONFIG_16550_PCI_UART2_CLOCK, + .parity = CONFIG_16550_PCI_UART2_PARITY, + .bits = CONFIG_16550_PCI_UART2_BITS, + .stopbits2 = CONFIG_16550_PCI_UART2_2STOP, +#if defined(CONFIG_16550_PCI_UART2_IFLOWCONTROL) || \ + defined(CONFIG_16550_PCI_UART2_OFLOWCONTROL) + .flow = true, +#endif + .rxtrigger = 2, + }, + + /* PCI specific data */ + + .vendor = CONFIG_16550_PCI_UART2_VENDOR, + .device = CONFIG_16550_PCI_UART2_DEVICE, + .port = CONFIG_16550_PCI_UART2_PORT, + .path = PCI_U16550_DEV_PATH2 +}; + +static uart_dev_t g_pci_u16550_dev2 = +{ + .recv = + { + .size = CONFIG_16550_PCI_UART2_RXBUFSIZE, + .buffer = g_pci_u16550_rxbuffer2, + }, + .xmit = + { + .size = CONFIG_16550_PCI_UART2_TXBUFSIZE, + .buffer = g_pci_u16550_txbuffer2, + }, + .priv = &g_pci_u16550_priv2.common, +#ifdef CONFIG_16550_PCI_UART2_SERIAL_CONSOLE + .isconsole = true, +#endif +}; +#endif + +#ifdef CONFIG_16550_PCI_UART3 +static struct pci_u16550_priv_s g_pci_u16550_priv3 = +{ + /* UART 16550 common data */ + + .common = + { + .baud = CONFIG_16550_PCI_UART3_BAUD, + .uartclk = CONFIG_16550_PCI_UART3_CLOCK, + .parity = CONFIG_16550_PCI_UART3_PARITY, + .bits = CONFIG_16550_PCI_UART3_BITS, + .stopbits2 = CONFIG_16550_PCI_UART3_2STOP, +#if defined(CONFIG_16550_PCI_UART3_IFLOWCONTROL) || \ + defined(CONFIG_16550_PCI_UART3_OFLOWCONTROL) + .flow = true, +#endif + .rxtrigger = 2, + }, + + /* PCI specific data */ + + .vendor = CONFIG_16550_PCI_UART3_VENDOR, + .device = CONFIG_16550_PCI_UART3_DEVICE, + .port = CONFIG_16550_PCI_UART3_PORT, + .path = PCI_U16550_DEV_PATH3 +}; + +static uart_dev_t g_pci_u16550_dev3 = +{ + .recv = + { + .size = CONFIG_16550_PCI_UART3_RXBUFSIZE, + .buffer = g_pci_u16550_rxbuffer3, + }, + .xmit = + { + .size = CONFIG_16550_PCI_UART3_TXBUFSIZE, + .buffer = g_pci_u16550_txbuffer3, + }, + .priv = &g_pci_u16550_priv3.common, +#ifdef CONFIG_16550_PCI_UART3_SERIAL_CONSOLE + .isconsole = true, +#endif +}; +#endif + +/* PCI devices */ + +static uart_dev_t *const g_pci_u16550_dev[] = +{ +#ifdef CONFIG_16550_PCI_UART0 + &g_pci_u16550_dev0, +#endif +#ifdef CONFIG_16550_PCI_UART1 + &g_pci_u16550_dev1, +#endif +#ifdef CONFIG_16550_PCI_UART2 + &g_pci_u16550_dev2, +#endif +#ifdef CONFIG_16550_PCI_UART3 + &g_pci_u16550_dev3, +#endif +}; + +/***************************************************************************** + * Private Functions + *****************************************************************************/ + +/***************************************************************************** + * Name: pci_u16550_getreg_mem + *****************************************************************************/ + +static uart_datawidth_t pci_u16550_getreg_mem(FAR struct u16550_s *priv, + unsigned int offset) +{ + uintptr_t addr = priv->uartbase + offset; + + return *((FAR volatile uart_datawidth_t *)addr); +} + +/***************************************************************************** + * Name: pci_u16550_putreg_mem + *****************************************************************************/ + +static void pci_u16550_putreg_mem(FAR struct u16550_s *priv, + unsigned int offset, + uart_datawidth_t value) +{ + uintptr_t addr = priv->uartbase + offset; + + *((FAR volatile uart_datawidth_t *)addr) = value; +} + +/***************************************************************************** + * Name: pci_u16550_getreg_io + *****************************************************************************/ + +static uart_datawidth_t pci_u16550_getreg_io(FAR struct u16550_s *priv, + unsigned int offset) +{ + FAR struct pci_u16550_priv_s *p = (FAR struct pci_u16550_priv_s *)priv; + uintptr_t addr = priv->uartbase + offset; + uint8_t ret = 0; + + pci_read_io_byte(p->pcidev, addr, &ret); + return ret; +} + +/***************************************************************************** + * Name: pci_u16550_putreg_io + *****************************************************************************/ + +static void pci_u16550_putreg_io(FAR struct u16550_s *priv, + unsigned int offset, + uart_datawidth_t value) +{ + FAR struct pci_u16550_priv_s *p = (FAR struct pci_u16550_priv_s *)priv; + uintptr_t addr = priv->uartbase + offset; + + pci_write_io_byte(p->pcidev, addr, value); +} + +/***************************************************************************** + * Name: pci_u16550_ioctl + *****************************************************************************/ + +static int pci_u16550_ioctl(FAR struct u16550_s *priv, int cmd, + unsigned long arg) +{ + return -ENOTTY; +} + +/***************************************************************************** + * Name: pci_u16550_dmachan + *****************************************************************************/ + +static FAR struct dma_chan_s *pci_u16550_dmachan(FAR struct u16550_s *priv, + unsigned int ident) +{ + return NULL; +} + +/***************************************************************************** + * Name: pci_u16550_interrupt + * + * Description: + * Handle PCI interrupt. + * + *****************************************************************************/ + +static int pci_u16550_interrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct uart_dev_s *dev = (FAR struct uart_dev_s *)arg; + + DEBUGASSERT(dev != NULL); + u16550_interrupt(0, NULL, dev); + + return OK; +} + +/***************************************************************************** + * Name: pci_u16550_initialize + * + * Description: + * Initialize UART 16550 PCI device. + * + *****************************************************************************/ + +static int pci_u16550_initialize(FAR struct pci_u16550_priv_s *priv, + FAR const struct pci_u16550_type_s *type, + uintptr_t base, + FAR struct pci_device_s *dev, + bool mmio) +{ + int ret = 0; + + /* Configure UART PCI */ + + priv->common.uartbase = base; + + if (mmio) + { + priv->common.ops = &g_pci_u16550_mem_ops; + } + else + { + priv->common.ops = &g_pci_u16550_io_ops; + } + + priv->common.regincr = type->regincr; + priv->pcidev = dev; + + /* Allocate and connect MSI if supported */ + + ret = pci_alloc_irq(dev, &priv->common.irq, 1); + if (ret != 1) + { + pcierr("Failed to allocate MSI %d\n", ret); + goto legacy_irq; + } + + ret = pci_connect_irq(dev, &priv->common.irq, 1); + if (ret == OK) + { + return OK; + } + + pci_release_irq(dev, &priv->common.irq, 1); + +legacy_irq: + + /* Get legacy IRQ if MSI not supported */ + + priv->common.irq = pci_get_irq(dev); + + /* Attach interrupts early to prevent unexpected isr fault */ + + ret = irq_attach(priv->common.irq, priv->common.ops->isr, dev); + if (ret != OK) + { + pcierr("Failed to attach irq %d\n", ret); + } + + return OK; +} + +/***************************************************************************** + * Name: pci_u16550_register + * + * Description: + * Register UART 16550 PCI device. + * + *****************************************************************************/ + +static int pci_u16550_register(FAR uart_dev_t *dev) +{ + FAR struct pci_u16550_priv_s *priv = + (FAR struct pci_u16550_priv_s *)dev->priv; + int ret = OK; + + /* Bind with 16550 common driver */ + + ret = u16550_bind(dev); + if (ret < 0) + { + /* No associated device found */ + + return ret; + } + + DEBUGASSERT(dev->ops); + + /* Register driver */ + + pciinfo("Register %s", priv->path); + + ret = uart_register(priv->path, dev); + if (ret < 0) + { + return ret; + } + +#ifdef CONFIG_16550_PCI_CONSOLE + /* Register console */ + + if (dev->isconsole) + { + ret = uart_register("/dev/console", dev); + } +#endif + + return ret; +} + +/***************************************************************************** + * Name: pci_u16550_probe + * + * Description: + * Initialize device. + * + *****************************************************************************/ + +static int pci_u16550_probe(FAR struct pci_device_s *dev) +{ + FAR const struct pci_u16550_type_s *type = NULL; + FAR uart_dev_t *udev = NULL; + FAR struct pci_u16550_priv_s *priv = NULL; + uintptr_t base = 0; + size_t i; + uint8_t port; + bool mmio = false; + int ret; + + /* Get type data associated with this PCI device card */ + + type = (FAR const struct pci_u16550_type_s *)dev->id->driver_data; + + /* Not found private data */ + + if (type == NULL) + { + return -ENODEV; + } + + pci_set_master(dev); + pciinfo("Enabled bus mastering\n"); + pci_enable_device(dev); + pciinfo("Enabled memory resources\n"); + + /* Hardcode BAR 0 for now */ + + if (pci_resource_flags(dev, 0) == PCI_RESOURCE_IO) + { + base = pci_resource_start(dev, 0); + } + else + { + /* If the BAR is MMIO then it must be mapped */ + + base = (uintptr_t)pci_map_bar(dev, 0); + mmio = true; + } + + for (port = 0; port < type->ports; port++) + { + /* Get port address */ + + base += type->portincr * port; + + /* Take the instance that matches the configuration */ + + udev = NULL; + for (i = 0; i < nitems(g_pci_u16550_dev); i++) + { + udev = g_pci_u16550_dev[i]; + priv = (FAR struct pci_u16550_priv_s *)udev->priv; + + if (priv->vendor == dev->vendor && + priv->device == dev->device && + priv->port == port) + { + break; + } + } + + /* Not found */ + + if (udev == NULL) + { + return -ENODEV; + } + + /* Device already registered */ + + if (udev->ops != NULL) + { + return -EBUSY; + } + + /* Initialize device */ + + ret = pci_u16550_initialize(priv, type, base, dev, mmio); + if (ret < 0) + { + return ret; + } + + /* Register UART device */ + + ret = pci_u16550_register(udev); + if (ret < 0) + { + return ret; + } + } + + return OK; +} + +/***************************************************************************** + * Name: up_putc + * + * Description: + * Provide priority, low-level access to support OS debug writes. + * + *****************************************************************************/ + +#ifdef CONFIG_16550_PCI_CONSOLE +int up_putc(int ch) +{ + irqstate_t flags; + + /* Console not initialized yet */ + + if (CONSOLE_DEV.ops == NULL) + { + return ch; + } + + /* All interrupts must be disabled to prevent re-entrancy and to prevent + * interrupts from firing in the serial driver code. + */ + + flags = spin_lock_irqsave(NULL); + + /* Check for LF */ + + if (ch == '\n') + { + /* Add CR */ + + u16550_putc(CONSOLE_DEV.priv, '\r'); + } + + u16550_putc(CONSOLE_DEV.priv, ch); + spin_unlock_irqrestore(NULL, flags); + + return ch; +} +#endif + +/***************************************************************************** + * Public Functions + *****************************************************************************/ + +/***************************************************************************** + * Name: pci_u16550_init + * + * Description: + * Register a pci driver + * + *****************************************************************************/ + +int pci_u16550_init(void) +{ + return pci_register_driver(&g_pci_u16550_drv); +} diff --git a/include/nuttx/serial/uart_pci_16550.h b/include/nuttx/serial/uart_pci_16550.h new file mode 100644 index 0000000000..bd63cdc8ef --- /dev/null +++ b/include/nuttx/serial/uart_pci_16550.h @@ -0,0 +1,51 @@ +/**************************************************************************** + * include/nuttx/serial/uart_pci_16550.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_SERIAL_UART_PCI_16550_H +#define __INCLUDE_NUTTX_SERIAL_UART_PCI_16550_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +#ifdef CONFIG_16550_PCI_UART +int pci_u16550_init(void); +#endif + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_SERIAL_UART_PCI_16550_H */