62d403a90a0a1f18a5fb5f163c2fc6bdb4993cb4
[lunaix-os.git] / lunaix-os / hal / char / uart / 16550_pmio.c
1 /**
2  * @file 16550_pmio.c
3  * @author Lunaixsky (lunaxisky@qq.com)
4  * @brief 16550 UART with port mapped IO
5  * @version 0.1
6  * @date 2023-08-30
7  *
8  * @copyright Copyright (c) 2023
9  *
10  */
11 #include <lunaix/device.h>
12 #include <lunaix/generic/isrm.h>
13
14 #include <sys/port_io.h>
15
16 #include "16550.h"
17
18 #define DELAY 50
19
20 static DEFINE_LLIST(com_ports);
21
22 static u32_t
23 com_regread(struct uart16550* uart, ptr_t regoff)
24 {
25     u8_t val = port_rdbyte(uart->base_addr + regoff);
26     port_delay(DELAY);
27
28     return (u32_t)val;
29 }
30
31 static void
32 com_regwrite(struct uart16550* uart, ptr_t regoff, u32_t val)
33 {
34     port_wrbyte(uart->base_addr + regoff, (u8_t)val);
35     port_delay(DELAY);
36 }
37
38 static void
39 com_irq_handler(const struct hart_state* hstate)
40 {
41     int vector = hart_vector_stamp(hstate);
42     uart_general_irq_handler(vector, &com_ports);
43 }
44
45 static int
46 upiom_init(struct device_def* def)
47 {
48     int irq3 = 3, irq4 = 4;
49     u16_t ioports[] = { 0x3F8, 0x2F8, 0x3E8, 0x2E8 };
50     int* irqs[] = { &irq4, &irq3, &irq4, &irq3 };
51
52     struct uart16550* uart = NULL;
53
54     // COM 1...4
55     for (size_t i = 0; i < 4; i++) {
56         if (!uart) {
57             uart = uart_alloc(ioports[i]);
58             uart->read_reg = com_regread;
59             uart->write_reg = com_regwrite;
60         } else {
61             uart->base_addr = ioports[i];
62         }
63
64         if (!uart_testport(uart, 0xe3)) {
65             continue;
66         }
67
68         int irq = *irqs[i];
69         if (irq) {
70             /*
71              *  Since these irqs are overlapped, this particular setup is needed
72              * to avoid double-bind
73              */
74             uart->iv = isrm_bindirq(irq, com_irq_handler);
75             *((volatile int*)irqs[i]) = 0;
76         }
77
78         uart_enable_fifo(uart, UART_FIFO8);
79         llist_append(&com_ports, &uart->local_ports);
80
81         struct serial_dev* sdev = serial_create(&def->class, "S");
82         sdev->backend = uart;
83         sdev->write = uart_general_tx;
84         sdev->exec_cmd = uart_general_exec_cmd;
85
86         uart->sdev = sdev;
87
88         uart_setup(uart);
89         uart_setie(uart);
90         uart = NULL;
91     }
92
93     if (uart) {
94         uart_free(uart);
95     }
96
97     return 0;
98 }
99
100 static struct device_def uart_pmio_def = {
101     .class = DEVCLASS(DEVIF_SOC, DEVFN_CHAR, DEV_UART16550),
102     .name = "16550 Generic UART (I/O)",
103     .init = upiom_init
104 };
105 EXPORT_DEVICE(uart16550_pmio, &uart_pmio_def, load_onboot);