Unifying External Interrupt System (#51)
[lunaix-os.git] / lunaix-os / hal / char / uart / 16x50_pci.c
1 #include <lunaix/device.h>
2 #include <lunaix/syslog.h>
3 #include <lunaix/mm/mmio.h>
4
5 #include <hal/pci.h>
6
7 #include "16x50.h"
8
9 #define PCI_DEVICE_16x50_UART    0x070000
10
11 LOG_MODULE("16x50-pci")
12
13 static DEFINE_LLIST(pci_ports);
14
15
16 static void
17 uart_msi_irq_handler(irq_t irq, const struct hart_state* hstate)
18 {
19     struct uart16550* uart;
20     
21     uart = irq_payload(irq, struct uart16550);
22
23     assert(uart);
24     uart_handle_irq(irq, uart);
25 }
26
27 static bool
28 pci16x50_check_compat(struct pci_probe* probe)
29 {
30     unsigned int classid;
31     classid = pci_device_class(probe);
32
33     return (classid & 0xffff00) == PCI_DEVICE_16x50_UART;
34 }
35
36 int
37 pci16x50_pci_register(struct device_def* def)
38 {
39     return !pci_register_driver(def, pci16x50_check_compat);
40 }
41
42 int
43 pci16x50_pci_create(struct device_def* def, morph_t* obj)
44 {
45     struct pci_base_addr* bar;
46     struct pci_probe* probe;
47     struct uart16550* uart;
48     struct serial_dev* sdev;
49     irq_t irq;
50     
51     probe = changeling_reveal(obj, pci_probe_morpher);
52
53     pci_reg_t cmd = 0;
54
55     for (int i = 0; i < PCI_BAR_COUNT; i++) 
56     {
57         cmd = 0;
58         pci_cmd_set_msi(&cmd);
59         
60         bar = pci_device_bar(probe, i);
61         if (bar->size == 0) {
62             continue;
63         }
64
65         if (!pci_bar_mmio_space(bar)) {
66 #ifdef CONFIG_PCI_PMIO
67             pci_cmd_set_pmio(&cmd);
68             pci_apply_command(probe, cmd);
69
70             uart = uart16x50_pmio_create(bar->start);
71 #else
72             WARN("plaform configured to not support pmio access.");
73             continue;
74 #endif
75         } else 
76         {
77             pci_cmd_set_mmio(&cmd);
78             pci_apply_command(probe, cmd);
79
80             uart = uart16x50_mmio_create(bar->start, bar->size);
81         }
82
83         if (!uart) {
84             WARN("not accessible (BAR #%d)", i);
85             continue;
86         }
87
88         if (!pci_capability_msi(probe)) {
89             WARN("failed to fallback to legacy INTx: not supported.");
90             continue;
91         }
92
93         sdev = uart_create_serial(uart, &def->class, &pci_ports, "PCI");
94
95         irq = pci_declare_msi_irq(uart_msi_irq_handler, probe);
96         irq_set_payload(irq, uart);
97         pci_assign_msi(probe, irq, NULL);
98
99         INFO("base: 0x%x (%s), %s", 
100                 bar->start, 
101                 pci_bar_mmio_space(bar) ? "mmio" : "pmio",
102                 pci_capability_msi(probe) ? "msi" : "intx, re-routed");
103         
104         uart->irq = irq;
105
106         pci_bind_instance(probe, sdev->dev);
107     }
108
109     return 0;
110 }