Commit | Line | Data |
---|---|---|
41aaff2a GP |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates. | |
4 | * Synopsys DesignWare eDMA PCIe driver | |
5 | * | |
6 | * Author: Gustavo Pimentel <gustavo.pimentel@synopsys.com> | |
7 | */ | |
8 | ||
9 | #include <linux/kernel.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/pci.h> | |
12 | #include <linux/device.h> | |
13 | #include <linux/dma/edma.h> | |
14 | #include <linux/pci-epf.h> | |
15 | #include <linux/msi.h> | |
16 | ||
17 | #include "dw-edma-core.h" | |
18 | ||
19 | struct dw_edma_pcie_data { | |
20 | /* eDMA registers location */ | |
21 | enum pci_barno rg_bar; | |
22 | off_t rg_off; | |
23 | size_t rg_sz; | |
24 | /* eDMA memory linked list location */ | |
25 | enum pci_barno ll_bar; | |
26 | off_t ll_off; | |
27 | size_t ll_sz; | |
28 | /* eDMA memory data location */ | |
29 | enum pci_barno dt_bar; | |
30 | off_t dt_off; | |
31 | size_t dt_sz; | |
32 | /* Other */ | |
33 | u32 version; | |
34 | enum dw_edma_mode mode; | |
35 | u8 irqs; | |
36 | }; | |
37 | ||
38 | static const struct dw_edma_pcie_data snps_edda_data = { | |
39 | /* eDMA registers location */ | |
40 | .rg_bar = BAR_0, | |
41 | .rg_off = 0x00001000, /* 4 Kbytes */ | |
42 | .rg_sz = 0x00002000, /* 8 Kbytes */ | |
43 | /* eDMA memory linked list location */ | |
44 | .ll_bar = BAR_2, | |
45 | .ll_off = 0x00000000, /* 0 Kbytes */ | |
46 | .ll_sz = 0x00800000, /* 8 Mbytes */ | |
47 | /* eDMA memory data location */ | |
48 | .dt_bar = BAR_2, | |
49 | .dt_off = 0x00800000, /* 8 Mbytes */ | |
50 | .dt_sz = 0x03800000, /* 56 Mbytes */ | |
51 | /* Other */ | |
52 | .version = 0, | |
53 | .mode = EDMA_MODE_UNROLL, | |
54 | .irqs = 1, | |
55 | }; | |
56 | ||
57 | static int dw_edma_pcie_probe(struct pci_dev *pdev, | |
58 | const struct pci_device_id *pid) | |
59 | { | |
60 | const struct dw_edma_pcie_data *pdata = (void *)pid->driver_data; | |
61 | struct device *dev = &pdev->dev; | |
62 | struct dw_edma_chip *chip; | |
63 | int err, nr_irqs; | |
64 | struct dw_edma *dw; | |
65 | ||
66 | /* Enable PCI device */ | |
67 | err = pcim_enable_device(pdev); | |
68 | if (err) { | |
69 | pci_err(pdev, "enabling device failed\n"); | |
70 | return err; | |
71 | } | |
72 | ||
73 | /* Mapping PCI BAR regions */ | |
74 | err = pcim_iomap_regions(pdev, BIT(pdata->rg_bar) | | |
75 | BIT(pdata->ll_bar) | | |
76 | BIT(pdata->dt_bar), | |
77 | pci_name(pdev)); | |
78 | if (err) { | |
79 | pci_err(pdev, "eDMA BAR I/O remapping failed\n"); | |
80 | return err; | |
81 | } | |
82 | ||
83 | pci_set_master(pdev); | |
84 | ||
85 | /* DMA configuration */ | |
86 | err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); | |
87 | if (!err) { | |
88 | err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); | |
89 | if (err) { | |
90 | pci_err(pdev, "consistent DMA mask 64 set failed\n"); | |
91 | return err; | |
92 | } | |
93 | } else { | |
94 | pci_err(pdev, "DMA mask 64 set failed\n"); | |
95 | ||
96 | err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); | |
97 | if (err) { | |
98 | pci_err(pdev, "DMA mask 32 set failed\n"); | |
99 | return err; | |
100 | } | |
101 | ||
102 | err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); | |
103 | if (err) { | |
104 | pci_err(pdev, "consistent DMA mask 32 set failed\n"); | |
105 | return err; | |
106 | } | |
107 | } | |
108 | ||
109 | /* Data structure allocation */ | |
110 | chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); | |
111 | if (!chip) | |
112 | return -ENOMEM; | |
113 | ||
114 | dw = devm_kzalloc(dev, sizeof(*dw), GFP_KERNEL); | |
115 | if (!dw) | |
116 | return -ENOMEM; | |
117 | ||
118 | /* IRQs allocation */ | |
119 | nr_irqs = pci_alloc_irq_vectors(pdev, 1, pdata->irqs, | |
120 | PCI_IRQ_MSI | PCI_IRQ_MSIX); | |
121 | if (nr_irqs < 1) { | |
122 | pci_err(pdev, "fail to alloc IRQ vector (number of IRQs=%u)\n", | |
123 | nr_irqs); | |
124 | return -EPERM; | |
125 | } | |
126 | ||
127 | /* Data structure initialization */ | |
128 | chip->dw = dw; | |
129 | chip->dev = dev; | |
130 | chip->id = pdev->devfn; | |
131 | chip->irq = pdev->irq; | |
132 | ||
756c3ef9 | 133 | dw->rg_region.vaddr = pcim_iomap_table(pdev)[pdata->rg_bar]; |
41aaff2a GP |
134 | dw->rg_region.vaddr += pdata->rg_off; |
135 | dw->rg_region.paddr = pdev->resource[pdata->rg_bar].start; | |
136 | dw->rg_region.paddr += pdata->rg_off; | |
137 | dw->rg_region.sz = pdata->rg_sz; | |
138 | ||
756c3ef9 | 139 | dw->ll_region.vaddr = pcim_iomap_table(pdev)[pdata->ll_bar]; |
41aaff2a GP |
140 | dw->ll_region.vaddr += pdata->ll_off; |
141 | dw->ll_region.paddr = pdev->resource[pdata->ll_bar].start; | |
142 | dw->ll_region.paddr += pdata->ll_off; | |
143 | dw->ll_region.sz = pdata->ll_sz; | |
144 | ||
756c3ef9 | 145 | dw->dt_region.vaddr = pcim_iomap_table(pdev)[pdata->dt_bar]; |
41aaff2a GP |
146 | dw->dt_region.vaddr += pdata->dt_off; |
147 | dw->dt_region.paddr = pdev->resource[pdata->dt_bar].start; | |
148 | dw->dt_region.paddr += pdata->dt_off; | |
149 | dw->dt_region.sz = pdata->dt_sz; | |
150 | ||
151 | dw->version = pdata->version; | |
152 | dw->mode = pdata->mode; | |
153 | dw->nr_irqs = nr_irqs; | |
154 | ||
155 | /* Debug info */ | |
156 | pci_dbg(pdev, "Version:\t%u\n", dw->version); | |
157 | ||
158 | pci_dbg(pdev, "Mode:\t%s\n", | |
159 | dw->mode == EDMA_MODE_LEGACY ? "Legacy" : "Unroll"); | |
160 | ||
756c3ef9 | 161 | pci_dbg(pdev, "Registers:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", |
41aaff2a | 162 | pdata->rg_bar, pdata->rg_off, pdata->rg_sz, |
756c3ef9 | 163 | dw->rg_region.vaddr, &dw->rg_region.paddr); |
41aaff2a | 164 | |
756c3ef9 | 165 | pci_dbg(pdev, "L. List:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", |
41aaff2a | 166 | pdata->ll_bar, pdata->ll_off, pdata->ll_sz, |
756c3ef9 | 167 | dw->ll_region.vaddr, &dw->ll_region.paddr); |
41aaff2a | 168 | |
756c3ef9 | 169 | pci_dbg(pdev, "Data:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", |
41aaff2a | 170 | pdata->dt_bar, pdata->dt_off, pdata->dt_sz, |
756c3ef9 | 171 | dw->dt_region.vaddr, &dw->dt_region.paddr); |
41aaff2a GP |
172 | |
173 | pci_dbg(pdev, "Nr. IRQs:\t%u\n", dw->nr_irqs); | |
174 | ||
175 | /* Validating if PCI interrupts were enabled */ | |
176 | if (!pci_dev_msi_enabled(pdev)) { | |
177 | pci_err(pdev, "enable interrupt failed\n"); | |
178 | return -EPERM; | |
179 | } | |
180 | ||
181 | dw->irq = devm_kcalloc(dev, nr_irqs, sizeof(*dw->irq), GFP_KERNEL); | |
182 | if (!dw->irq) | |
183 | return -ENOMEM; | |
184 | ||
185 | /* Starting eDMA driver */ | |
186 | err = dw_edma_probe(chip); | |
187 | if (err) { | |
188 | pci_err(pdev, "eDMA probe failed\n"); | |
189 | return err; | |
190 | } | |
191 | ||
192 | /* Saving data structure reference */ | |
193 | pci_set_drvdata(pdev, chip); | |
194 | ||
195 | return 0; | |
196 | } | |
197 | ||
198 | static void dw_edma_pcie_remove(struct pci_dev *pdev) | |
199 | { | |
200 | struct dw_edma_chip *chip = pci_get_drvdata(pdev); | |
201 | int err; | |
202 | ||
203 | /* Stopping eDMA driver */ | |
204 | err = dw_edma_remove(chip); | |
205 | if (err) | |
206 | pci_warn(pdev, "can't remove device properly: %d\n", err); | |
207 | ||
208 | /* Freeing IRQs */ | |
209 | pci_free_irq_vectors(pdev); | |
210 | } | |
211 | ||
212 | static const struct pci_device_id dw_edma_pcie_id_table[] = { | |
213 | { PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_data) }, | |
214 | { } | |
215 | }; | |
216 | MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table); | |
217 | ||
218 | static struct pci_driver dw_edma_pcie_driver = { | |
219 | .name = "dw-edma-pcie", | |
220 | .id_table = dw_edma_pcie_id_table, | |
221 | .probe = dw_edma_pcie_probe, | |
222 | .remove = dw_edma_pcie_remove, | |
223 | }; | |
224 | ||
225 | module_pci_driver(dw_edma_pcie_driver); | |
226 | ||
227 | MODULE_LICENSE("GPL v2"); | |
228 | MODULE_DESCRIPTION("Synopsys DesignWare eDMA PCIe driver"); | |
229 | MODULE_AUTHOR("Gustavo Pimentel <gustavo.pimentel@synopsys.com>"); |