1d8afd874b1e2322e12843aef551e3678e19154f
[lunaix-os.git] / lunaix-os / hal / char / uart / 16x50_pci.c
1 #include <lunaix/device.h>
2 #include <asm-generic/isrm.h>
3 #include <lunaix/syslog.h>
4 #include <lunaix/mm/mmio.h>
5
6 #include <hal/pci.h>
7
8 #include "16x50.h"
9
10 #define PCI_DEVICE_16x50_UART    0x070000
11
12 LOG_MODULE("16x50-pci")
13
14 static DEFINE_LLIST(pci_ports);
15
16 static void
17 uart_msi_irq_handler(const struct hart_state* hstate)
18 {
19     int vector;
20     struct uart16550* uart;
21     
22     vector = hart_vector_stamp(hstate);
23     uart = (struct uart16550*)isrm_get_payload(hstate);
24
25     assert(uart);
26     uart_handle_irq(vector, uart);
27 }
28
29 static void
30 uart_intx_irq_handler(const struct hart_state* hstate)
31 {
32     int vector = hart_vector_stamp(hstate);
33     uart_handle_irq_overlap(vector, &pci_ports);
34 }
35
36 static int
37 pci16550_init(struct device_def* def)
38 {
39     return pci_bind_definition_all(pcidev_def(def));
40 }
41
42 static bool
43 pci16650_check_compat(struct pci_device_def* def, 
44                       struct pci_device* pcidev)
45 {
46     unsigned int classid;
47     classid = pci_device_class(pcidev);
48
49     return (classid & 0xffff00) == PCI_DEVICE_16x50_UART;
50 }
51
52 static int
53 pci16650_binder(struct device_def* def, struct device* dev)
54 {
55     int irq;
56     struct pci_base_addr* bar;
57     struct pci_device* pcidev;
58     struct uart16550* uart;
59     struct serial_dev* sdev;
60     
61     pcidev = PCI_DEVICE(dev);
62
63     pci_reg_t cmd = 0;
64
65     for (int i = 0; i < PCI_BAR_COUNT; i++) 
66     {
67         cmd = 0;
68         pci_cmd_set_msi(&cmd);
69         
70         bar = pci_device_bar(pcidev, i);
71         if (bar->size == 0) {
72             continue;
73         }
74                 
75         if (!pci_bar_mmio_space(bar)) {
76             pci_cmd_set_pmio(&cmd);
77             pci_apply_command(pcidev, cmd);
78
79             uart = uart16x50_pmio_create(bar->start);
80         }
81         else {
82             pci_cmd_set_mmio(&cmd);
83             pci_apply_command(pcidev, cmd);
84
85             uart = uart16x50_mmio_create(bar->start, bar->size);
86         }
87
88         if (!uart) {
89             WARN("not accessible (BAR #%d)", i);
90             continue;
91         }
92
93         if (pci_capability_msi(pcidev)) {
94             irq = isrm_ivexalloc(uart_msi_irq_handler);
95             isrm_set_payload(irq, __ptr(uart));
96             pci_setup_msi(pcidev, irq);
97         }
98         else {
99             irq = pci_intr_irq(pcidev);
100             irq = isrm_bindirq(irq, uart_intx_irq_handler);
101         }
102
103         INFO("base: 0x%x (%s), irq=%d (%s)", 
104                 bar->start, 
105                 pci_bar_mmio_space(bar) ? "mmio" : "pmio",
106                 irq, 
107                 pci_capability_msi(pcidev) ? "msi" : "intx, re-routed");
108         
109         uart->iv = irq;
110
111         sdev = uart_create_serial(uart, &def->class, &pci_ports, "PCI");
112         pci_bind_instance(pcidev, sdev);
113     }
114
115     return 0;
116 }
117
118 static struct pci_device_def uart_pci_def = {
119     .devdef = { .class = DEVCLASS(DEVIF_PCI, DEVFN_CHAR, DEV_UART16550),
120                 .name = "16550 UART (PCI/MMIO)",
121                 .init = pci16550_init,
122                 .bind = pci16650_binder },
123     .test_compatibility = pci16650_check_compat
124 };
125 EXPORT_PCI_DEVICE(uart16550_pci, &uart_pci_def, load_onboot);