1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2023-02-25     GuEe-GUI     the first version
9  */
10 
11 #include <rtthread.h>
12 #include <rtdevice.h>
13 
14 #define NVME_REG_BAR 0
15 
16 struct pci_nvme_quirk
17 {
18     const struct rt_nvme_ops *ops;
19 };
20 
21 struct pci_nvme_controller
22 {
23     struct rt_nvme_controller parent;
24     const struct pci_nvme_quirk *quirk;
25 
26     rt_bool_t is_msi;
27     struct rt_pci_msix_entry msix_entries[RT_USING_NVME_QUEUE];
28 };
29 
30 static const struct rt_nvme_ops pci_nvme_std_ops =
31 {
32     .name = "PCI",
33 };
34 
pci_nvme_probe(struct rt_pci_device * pdev)35 static rt_err_t pci_nvme_probe(struct rt_pci_device *pdev)
36 {
37     rt_err_t err;
38     rt_ssize_t msi_nr;
39     struct rt_nvme_controller *nvme;
40     struct pci_nvme_controller *pci_nvme = rt_calloc(1, sizeof(*pci_nvme));
41     const struct pci_nvme_quirk *quirk = pdev->id->data;
42 
43     if (!pci_nvme)
44     {
45         return -RT_ENOMEM;
46     }
47 
48     pci_nvme->quirk = quirk;
49     nvme = &pci_nvme->parent;
50     nvme->dev = &pdev->parent;
51     nvme->regs = rt_pci_iomap(pdev, NVME_REG_BAR);
52 
53     if (!nvme->regs)
54     {
55         err = -RT_EIO;
56         goto _fail;
57     }
58 
59     nvme->ops = quirk && quirk->ops ? quirk->ops : &pci_nvme_std_ops;
60 
61     if ((msi_nr = rt_pci_msix_vector_count(pdev)) <= 0)
62     {
63         msi_nr = rt_pci_msi_vector_count(pdev);
64     }
65     if (msi_nr > 0)
66     {
67         nvme->irqs_nr = RT_ARRAY_SIZE(pci_nvme->msix_entries);
68         nvme->irqs_nr = rt_min_t(rt_size_t, msi_nr, nvme->irqs_nr);
69     }
70 
71     if (nvme->irqs_nr > 0)
72     {
73         rt_pci_msix_entry_index_linear(pci_nvme->msix_entries, nvme->irqs_nr);
74 
75         if (rt_pci_msix_enable(pdev, pci_nvme->msix_entries, nvme->irqs_nr) > 0)
76         {
77             pci_nvme->is_msi = RT_TRUE;
78 
79             for (int i = 0; i < nvme->irqs_nr; ++i)
80             {
81                 nvme->irqs[i] = pci_nvme->msix_entries[i].irq;
82             }
83         }
84     }
85 
86     if (!pci_nvme->is_msi)
87     {
88         nvme->irqs_nr = 1;
89         nvme->irqs[0] = pdev->irq;
90         rt_pci_irq_unmask(pdev);
91     }
92 
93     rt_pci_set_master(pdev);
94 
95     if ((err = rt_nvme_controller_register(nvme)))
96     {
97         goto _disable;
98     }
99 
100     pdev->parent.user_data = pci_nvme;
101 
102     return RT_EOK;
103 
104 _disable:
105     if (pci_nvme->is_msi)
106     {
107         rt_pci_msix_disable(pdev);
108     }
109     else
110     {
111         rt_pci_irq_mask(pdev);
112     }
113     rt_pci_clear_master(pdev);
114     rt_iounmap(nvme->regs);
115 
116 _fail:
117     rt_free(pci_nvme);
118 
119     return err;
120 }
121 
pci_nvme_remove(struct rt_pci_device * pdev)122 static rt_err_t pci_nvme_remove(struct rt_pci_device *pdev)
123 {
124     struct rt_nvme_controller *nvme;
125     struct pci_nvme_controller *pci_nvme = pdev->parent.user_data;
126 
127     nvme = &pci_nvme->parent;
128 
129     rt_nvme_controller_unregister(nvme);
130 
131     if (pci_nvme->is_msi)
132     {
133         rt_pci_msix_disable(pdev);
134     }
135     else
136     {
137         /* INTx is shared, don't mask all */
138         rt_hw_interrupt_umask(pdev->irq);
139         rt_pci_irq_mask(pdev);
140     }
141 
142     rt_pci_clear_master(pdev);
143 
144     rt_iounmap(nvme->regs);
145     rt_free(pci_nvme);
146 
147     return RT_EOK;
148 }
149 
pci_nvme_shutdown(struct rt_pci_device * pdev)150 static rt_err_t pci_nvme_shutdown(struct rt_pci_device *pdev)
151 {
152     return pci_nvme_remove(pdev);
153 }
154 
155 static const struct rt_pci_device_id pci_nvme_ids[] =
156 {
157     { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_REDHAT, 0x0010) },
158     { RT_PCI_DEVICE_CLASS(PCIS_STORAGE_EXPRESS, ~0) },
159     { /* sentinel */ }
160 };
161 
162 static struct rt_pci_driver pci_nvme_driver =
163 {
164     .name = "nvme-pci",
165 
166     .ids = pci_nvme_ids,
167     .probe = pci_nvme_probe,
168     .remove = pci_nvme_remove,
169     .shutdown = pci_nvme_shutdown,
170 };
171 RT_PCI_DRIVER_EXPORT(pci_nvme_driver);
172