text: // Copyright 2019 The Fuchsia Authors
  // Use of this source code is governed by a MIT-style
  // license that can be found in the LICENSE file or at
  // https://opensource.org/licenses/MIT
  
  #include <lib/cbuf.h>
  #include <lib/debuglog.h>
  #include <lib/zircon-internal/macros.h>
  #include <lib/zx/status.h>
  #include <reg.h>
  #include <stdio.h>
  #include <trace.h>
  #include <zircon/boot/driver-config.h>
  
  #include <arch/arm64/periphmap.h>
  #include <dev/interrupt.h>
  #include <dev/uart.h>
  #include <kernel/lockdep.h>
  #include <kernel/thread.h>
  #include <pdev/driver.h>
  #include <pdev/uart.h>
  #include <platform/debug.h>
  
  // DW8250 implementation
  
  // clang-format off
  // UART Registers
  
  #define UART_RBR                    (0x0)   // RX Buffer Register (read-only)
  #define UART_THR                    (0x0)   // TX Buffer Register (write-only)
  #define UART_DLL                    (0x0)   // Divisor Latch Low (Only when LCR[7] = 1)
  #define UART_DLH                    (0x4)   // Divisor Latch High (Only when LCR[7] = 1)
  #define UART_IER                    (0x4)   // Interrupt Enable Register
  #define UART_IIR                    (0x8)   // Interrupt Identification Register (read-only)
  #define UART_FCR                    (0x8)   // FIFO Control Register (write-only)
  #define UART_LCR                    (0xc)   // Line Control Register
  #define UART_MCR                    (0x10)  // Modem Control Register
  #define UART_LSR                    (0x14)  // Line Status Register (read-only)
  #define UART_MSR                    (0x18)  // Modem Status Register (read-only)
  #define UART_SCR                    (0x1c)  // Scratch Register
  #define UART_LPDLL                  (0x20)  // Low Power Divisor Latch (Low) Register
  #define UART_LPDLH                  (0x24)  // Low Power Divisor Latch (High) Register
  #define UART_SRBR                   (0x30)  // Shadow Receive Buffer Register (read-only)
  #define UART_STHR                   (0x34)  // Shadow Transmit Holding Register
  #define UART_FAR                    (0x70)  // FIFO Access Register
  #define UART_TFR                    (0x74)  // Transmit FIFO Read Register (read-only)
  #define UART_RFW                    (0x78)  // Receive FIFO Write Register (write-only)
  #define UART_USR                    (0x7C)  // UART Status Register (read-only)
  #define UART_TFL                    (0x80)  // Transmit FIFO Level Register (read-only)
  #define UART_RFL                    (0x84)  // Receive FIFO Level Register (read-only)
  #define UART_SRR                    (0x88)  // Software Reser Register
  #define UART_SRTS                   (0x8C)  // Shadow Request to Send Register
  #define UART_SBCR                   (0x90)  // Shadow Break Control Register
  #define UART_SDMAM                  (0x94)  // Shadow DMA Mode Register
  #define UART_SFE                    (0x98)  // Shadow FIFO Enable Register
  #define UART_SRT                    (0x9C)  // Shadow RCVR Trigger Register
  #define UART_STET                   (0xA0)  // Shadow TX Empty Trigger Register
  #define UART_HTX                    (0xA4)  // Halt TX Register
  #define UART_DMASA                  (0xA8)  // DMA Software Acknowledge Register (write-only)
  #define UART_CPR                    (0xF4)  // Component Parameter Register (read-only)
  #define UART_UCV                    (0xF8)  // UART Component Version Register (read-only)
  #define UART_CTR                    (0xFC)  // Component Type Register
  
  // IER
  #define UART_IER_ERBFI              (1 << 0)
  #define UART_IER_ETBEI              (1 << 1)
  #define UART_IER_ELSI               (1 << 2)
  #define UART_IER_EDSSI              (1 << 3)
  #define UART_IER_PTIME              (1 << 5)
  
  // IIR
  #define UART_IIR_RLS                (0x06)  // Receiver Line Status
  #define UART_IIR_RDA                (0x04)  // Receive Data Available
  #define UART_IIR_BUSY               (0x07)  // Busy Detect Indication
  #define UART_IIR_CTI                (0x0C)  // Character Timeout Indicator
  #define UART_IIR_THRE               (0x02)  // Transmit Holding Register Empty
  #define UART_IIR_MS                 (0x00)  // Check Modem Status Register
  #define UART_IIR_SW_FLOW_CTRL       (0x10)  // Receive XOFF characters
  #define UART_IIR_HW_FLOW_CTRL       (0x20)  // CTS or RTS Rising Edge
  #define UART_IIR_FIFO_EN            (0xc0)
  #define UART_IIR_INT_MASK           (0x1f)
  
  // LSR
  #define UART_LSR_DR                 (1 << 0)
  #define UART_LSR_OE                 (1 << 1)
  #define UART_LSR_PE                 (1 << 2)
  #define UART_LSR_FE                 (1 << 3)
  #define UART_LSR_BI                 (1 << 4)
  #define UART_LSR_THRE               (1 << 5)
  #define UART_LSR_TEMT               (1 << 6)
  #define UART_LSR_FIFOERR            (1 << 7)
  // clang-format on
  
  #define RXBUF_SIZE 32
  
  // values read from zbi
  static bool initialized = false;
  static vaddr_t uart_base = 0;
  static uint32_t uart_irq = 0;
  
  static Cbuf uart_rx_buf;
  static bool uart_tx_irq_enabled = false;
  static AutounsignalEvent uart_dputc_event{true};
  
  namespace {
  DECLARE_SINGLETON_SPINLOCK_WITH_TYPE(uart_spinlock, MonitoredSpinLock);
  }  // namespace
  
  #define UARTREG(reg) (*(volatile uint32_t*)((uart_base) + (reg)))
  
  static inline void uartreg_and_eq(ptrdiff_t reg, uint32_t flags) {
    volatile uint32_t* ptr = reinterpret_cast<volatile uint32_t*>(uart_base + reg);
    *ptr = *ptr & flags;
  }
  
  static inline void uartreg_or_eq(ptrdiff_t reg, uint32_t flags) {
    volatile uint32_t* ptr = reinterpret_cast<volatile uint32_t*>(uart_base + reg);
    *ptr = *ptr | flags;
  }
  
  static interrupt_eoi dw8250_uart_irq(void* arg) {
    if ((UARTREG(UART_IIR) & UART_IIR_BUSY) == UART_IIR_BUSY) {
      // To clear the USR (UART Status Register) we need to read it.
      volatile uint32_t unused = UARTREG(UART_USR);
      static_cast<void>(unused);
    }
  
    // read interrupt status and mask
    while (UARTREG(UART_LSR) & UART_LSR_DR) {
      if (uart_rx_buf.Full()) {
        break;
      }
      char c = UARTREG(UART_RBR) & 0xFF;
      uart_rx_buf.WriteChar(c);
    }
  
    // Signal if anyone is waiting to TX
    if (UARTREG(UART_LSR) & UART_LSR_THRE) {
      uartreg_and_eq(UART_IER, ~UART_IER_ETBEI);  // Disable TX interrupt
      Guard<MonitoredSpinLock, NoIrqSave> guard{uart_spinlock::Get(), SOURCE_TAG};
      // TODO(andresoportus): Revisit all UART drivers usage of events, from event.h:
      // 1. The reschedule flag is not supposed to be true in interrupt context.
      // 2. AutounsignalEvent only wakes up one thread per Signal().
      uart_dputc_event.Signal();
    }
  
    return IRQ_EOI_DEACTIVATE;
  }
  
  /* panic-time getc/putc */
  static void dw8250_uart_pputc(char c) {
    // spin while fifo is full
    while (!(UARTREG(UART_LSR) & UART_LSR_THRE))
      ;
    UARTREG(UART_THR) = c;
  }
  
  static int dw8250_uart_pgetc() {
    // spin while fifo is empty
    while (!(UARTREG(UART_LSR) & UART_LSR_DR))
      ;
    return UARTREG(UART_RBR);
  }
  
  static int dw8250_uart_getc(bool wait) {
    if (initialized) {
      zx::status<char> result = uart_rx_buf.ReadChar(wait);
      if (result.is_ok()) {
        return result.value();
      }
      return result.error_value();
    } else {
      // Interrupts are not enabled yet. Use panic calls for now
      return dw8250_uart_pgetc();
    }
  }
  
  static void dw8250_dputs(const char* str, size_t len, bool block, bool map_NL) {
    bool copied_CR = false;
  
    if (!uart_tx_irq_enabled) {
      block = false;
    }
    Guard<MonitoredSpinLock, IrqSave> guard{uart_spinlock::Get(), SOURCE_TAG};
  
    while (len > 0) {
      // is FIFO full?
      while (!(UARTREG(UART_LSR) & UART_LSR_THRE)) {
        guard.CallUnlocked([&block]() {
          if (block) {
            uartreg_or_eq(UART_IER, UART_IER_ETBEI);  // Enable TX interrupt.
            uart_dputc_event.Wait();
          } else {
            arch::Yield();
          }
        });
      }
      if (*str == '\n' && map_NL && !copied_CR) {
        copied_CR = true;
        dw8250_uart_pputc('\r');
      } else {
        copied_CR = false;
        dw8250_uart_pputc(*str++);
        len--;
      }
    }
  }
  
  static void dw8250_uart_init(const void* driver_data, uint32_t length) {
    // Initialize circular buffer to hold received data.
    uart_rx_buf.Initialize(RXBUF_SIZE, malloc(RXBUF_SIZE));
  
    if (dlog_bypass() == true) {
      uart_tx_irq_enabled = false;
      return;
    }
  
    zx_status_t status =
        configure_interrupt(uart_irq, IRQ_TRIGGER_MODE_LEVEL, IRQ_POLARITY_ACTIVE_HIGH);
    if (status != ZX_OK) {
      printf("UART: configure_interrupt failed %d\n", status);
      return;
    }
  
    status = register_permanent_int_handler(uart_irq, &dw8250_uart_irq, NULL);
    if (status != ZX_OK) {
      printf("UART: register_permanent_int_handler failed %d\n", status);
      return;
    }
    // enable interrupt
    status = unmask_interrupt(uart_irq);
    if (status != ZX_OK) {
      printf("UART: unmask_interrupt failed %d\n", status);
      return;
    }
  
    uartreg_or_eq(UART_IER, UART_IER_ERBFI);  // Enable RX interrupt.
    initialized = true;
    // Start up tx driven output.
    printf("UART: starting IRQ driven TX\n");
    uart_tx_irq_enabled = true;
  }
  
  static void dw8250_start_panic() { uart_tx_irq_enabled = false; }
  
  static const struct pdev_uart_ops uart_ops = {
      .getc = dw8250_uart_getc,
      .pputc = dw8250_uart_pputc,
      .pgetc = dw8250_uart_pgetc,
      .start_panic = dw8250_start_panic,
      .dputs = dw8250_dputs,
  };
  
  extern "C" void uart_mark(unsigned char x);
  static void dw8250_uart_init_early(const void* driver_data, uint32_t length) {
    ASSERT(length >= sizeof(dcfg_simple_t));
    auto driver = static_cast<const dcfg_simple_t*>(driver_data);
    ASSERT(driver->mmio_phys && driver->irq);
  
    uart_base = periph_paddr_to_vaddr(driver->mmio_phys);
    ASSERT(uart_base);
    uart_irq = driver->irq;
  
    pdev_register_uart(&uart_ops);
  }
  
  LK_PDEV_INIT(dw8250_uart_init_early, KDRV_DW8250_UART, dw8250_uart_init_early,
               LK_INIT_LEVEL_PLATFORM_EARLY)
  LK_PDEV_INIT(dw8250_uart_init, KDRV_DW8250_UART, dw8250_uart_init, LK_INIT_LEVEL_PLATFORM)
  