Unit testing framework and devicetree framework refactoring (#50)
[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
17 static void
18 uart_msi_irq_handler(const struct hart_state* hstate)
19 {
20     int vector;
21     struct uart16550* uart;
22     
23     vector = hart_vector_stamp(hstate);
24     uart = (struct uart16550*)isrm_get_payload(hstate);
25
26     assert(uart);
27     uart_handle_irq(vector, uart);
28 }
29
30 static void
31 uart_intx_irq_handler(const struct hart_state* hstate)
32 {
33     int vector = hart_vector_stamp(hstate);
34     uart_handle_irq_overlap(vector, &pci_ports);
35 }
36
37 static bool
38 pci16x50_check_compat(struct pci_probe* probe)
39 {
40     unsigned int classid;
41     classid = pci_device_class(probe);
42
43     return (classid & 0xffff00) == PCI_DEVICE_16x50_UART;
44 }
45
46 int
47 pci16x50_pci_register(struct device_def* def)
48 {
49     return !pci_register_driver(def, pci16x50_check_compat);
50 }
51
52 int
53 pci16x50_pci_create(struct device_def* def, morph_t* obj)
54 {
55     struct pci_base_addr* bar;
56     struct pci_probe* probe;
57     struct uart16550* uart;
58     struct serial_dev* sdev;
59     msi_vector_t msiv;
60     
61     probe = changeling_reveal(obj, pci_probe_morpher);
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(probe, i);
71         if (bar->size == 0) {
72             continue;
73         }
74
75         if (!pci_bar_mmio_space(bar)) {
76 #ifdef CONFIG_PCI_PMIO
77             pci_cmd_set_pmio(&cmd);
78             pci_apply_command(probe, cmd);
79
80             uart = uart16x50_pmio_create(bar->start);
81 #else
82             WARN("plaform configured to not support pmio access.");
83             continue;
84 #endif
85         } else 
86         {
87             pci_cmd_set_mmio(&cmd);
88             pci_apply_command(probe, cmd);
89
90             uart = uart16x50_mmio_create(bar->start, bar->size);
91         }
92
93         if (!uart) {
94             WARN("not accessible (BAR #%d)", i);
95             continue;
96         }
97
98         if (!pci_capability_msi(probe)) {
99             WARN("failed to fallback to legacy INTx: not supported.");
100             continue;
101         }
102
103         sdev = uart_create_serial(uart, &def->class, &pci_ports, "PCI");
104
105         msiv = pci_msi_setup_simple(probe, uart_msi_irq_handler);
106         isrm_set_payload(msi_vect(msiv), __ptr(uart));
107
108         INFO("base: 0x%x (%s), irq=%d (%s)", 
109                 bar->start, 
110                 pci_bar_mmio_space(bar) ? "mmio" : "pmio",
111                 msi_vect(msiv), 
112                 pci_capability_msi(probe) ? "msi" : "intx, re-routed");
113         
114         uart->iv = msi_vect(msiv);
115
116         pci_bind_instance(probe, sdev->dev);
117     }
118
119     return 0;
120 }