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 | #include <linux/bitfield.h> |
17 | |
18 | #include "dw-edma-core.h" |
19 | |
20 | #define DW_PCIE_VSEC_DMA_ID 0x6 |
21 | #define DW_PCIE_VSEC_DMA_BAR GENMASK(10, 8) |
22 | #define DW_PCIE_VSEC_DMA_MAP GENMASK(2, 0) |
23 | #define DW_PCIE_VSEC_DMA_WR_CH GENMASK(9, 0) |
24 | #define DW_PCIE_VSEC_DMA_RD_CH GENMASK(25, 16) |
25 | |
26 | #define DW_BLOCK(a, b, c) \ |
27 | { \ |
28 | .bar = a, \ |
29 | .off = b, \ |
30 | .sz = c, \ |
31 | }, |
32 | |
33 | struct dw_edma_block { |
34 | enum pci_barno bar; |
35 | off_t off; |
36 | size_t sz; |
37 | }; |
38 | |
39 | struct dw_edma_pcie_data { |
40 | /* eDMA registers location */ |
41 | struct dw_edma_block rg; |
42 | /* eDMA memory linked list location */ |
43 | struct dw_edma_block ll_wr[EDMA_MAX_WR_CH]; |
44 | struct dw_edma_block ll_rd[EDMA_MAX_RD_CH]; |
45 | /* eDMA memory data location */ |
46 | struct dw_edma_block dt_wr[EDMA_MAX_WR_CH]; |
47 | struct dw_edma_block dt_rd[EDMA_MAX_RD_CH]; |
48 | /* Other */ |
49 | enum dw_edma_map_format mf; |
50 | u8 irqs; |
51 | u16 wr_ch_cnt; |
52 | u16 rd_ch_cnt; |
53 | }; |
54 | |
55 | static const struct dw_edma_pcie_data snps_edda_data = { |
56 | /* eDMA registers location */ |
57 | .rg.bar = BAR_0, |
58 | .rg.off = 0x00001000, /* 4 Kbytes */ |
59 | .rg.sz = 0x00002000, /* 8 Kbytes */ |
60 | /* eDMA memory linked list location */ |
61 | .ll_wr = { |
62 | /* Channel 0 - BAR 2, offset 0 Mbytes, size 2 Kbytes */ |
63 | DW_BLOCK(BAR_2, 0x00000000, 0x00000800) |
64 | /* Channel 1 - BAR 2, offset 2 Mbytes, size 2 Kbytes */ |
65 | DW_BLOCK(BAR_2, 0x00200000, 0x00000800) |
66 | }, |
67 | .ll_rd = { |
68 | /* Channel 0 - BAR 2, offset 4 Mbytes, size 2 Kbytes */ |
69 | DW_BLOCK(BAR_2, 0x00400000, 0x00000800) |
70 | /* Channel 1 - BAR 2, offset 6 Mbytes, size 2 Kbytes */ |
71 | DW_BLOCK(BAR_2, 0x00600000, 0x00000800) |
72 | }, |
73 | /* eDMA memory data location */ |
74 | .dt_wr = { |
75 | /* Channel 0 - BAR 2, offset 8 Mbytes, size 2 Kbytes */ |
76 | DW_BLOCK(BAR_2, 0x00800000, 0x00000800) |
77 | /* Channel 1 - BAR 2, offset 9 Mbytes, size 2 Kbytes */ |
78 | DW_BLOCK(BAR_2, 0x00900000, 0x00000800) |
79 | }, |
80 | .dt_rd = { |
81 | /* Channel 0 - BAR 2, offset 10 Mbytes, size 2 Kbytes */ |
82 | DW_BLOCK(BAR_2, 0x00a00000, 0x00000800) |
83 | /* Channel 1 - BAR 2, offset 11 Mbytes, size 2 Kbytes */ |
84 | DW_BLOCK(BAR_2, 0x00b00000, 0x00000800) |
85 | }, |
86 | /* Other */ |
87 | .mf = EDMA_MF_EDMA_UNROLL, |
88 | .irqs = 1, |
89 | .wr_ch_cnt = 2, |
90 | .rd_ch_cnt = 2, |
91 | }; |
92 | |
93 | static int dw_edma_pcie_irq_vector(struct device *dev, unsigned int nr) |
94 | { |
95 | return pci_irq_vector(to_pci_dev(dev), nr); |
96 | } |
97 | |
98 | static u64 dw_edma_pcie_address(struct device *dev, phys_addr_t cpu_addr) |
99 | { |
100 | struct pci_dev *pdev = to_pci_dev(dev); |
101 | struct pci_bus_region region; |
102 | struct resource res = { |
103 | .flags = IORESOURCE_MEM, |
104 | .start = cpu_addr, |
105 | .end = cpu_addr, |
106 | }; |
107 | |
108 | pcibios_resource_to_bus(bus: pdev->bus, region: ®ion, res: &res); |
109 | return region.start; |
110 | } |
111 | |
112 | static const struct dw_edma_plat_ops dw_edma_pcie_plat_ops = { |
113 | .irq_vector = dw_edma_pcie_irq_vector, |
114 | .pci_address = dw_edma_pcie_address, |
115 | }; |
116 | |
117 | static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev, |
118 | struct dw_edma_pcie_data *pdata) |
119 | { |
120 | u32 val, map; |
121 | u16 vsec; |
122 | u64 off; |
123 | |
124 | vsec = pci_find_vsec_capability(dev: pdev, PCI_VENDOR_ID_SYNOPSYS, |
125 | DW_PCIE_VSEC_DMA_ID); |
126 | if (!vsec) |
127 | return; |
128 | |
129 | pci_read_config_dword(dev: pdev, where: vsec + PCI_VNDR_HEADER, val: &val); |
130 | if (PCI_VNDR_HEADER_REV(val) != 0x00 || |
131 | PCI_VNDR_HEADER_LEN(val) != 0x18) |
132 | return; |
133 | |
134 | pci_dbg(pdev, "Detected PCIe Vendor-Specific Extended Capability DMA\n" ); |
135 | pci_read_config_dword(dev: pdev, where: vsec + 0x8, val: &val); |
136 | map = FIELD_GET(DW_PCIE_VSEC_DMA_MAP, val); |
137 | if (map != EDMA_MF_EDMA_LEGACY && |
138 | map != EDMA_MF_EDMA_UNROLL && |
139 | map != EDMA_MF_HDMA_COMPAT) |
140 | return; |
141 | |
142 | pdata->mf = map; |
143 | pdata->rg.bar = FIELD_GET(DW_PCIE_VSEC_DMA_BAR, val); |
144 | |
145 | pci_read_config_dword(dev: pdev, where: vsec + 0xc, val: &val); |
146 | pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt, |
147 | FIELD_GET(DW_PCIE_VSEC_DMA_WR_CH, val)); |
148 | pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt, |
149 | FIELD_GET(DW_PCIE_VSEC_DMA_RD_CH, val)); |
150 | |
151 | pci_read_config_dword(dev: pdev, where: vsec + 0x14, val: &val); |
152 | off = val; |
153 | pci_read_config_dword(dev: pdev, where: vsec + 0x10, val: &val); |
154 | off <<= 32; |
155 | off |= val; |
156 | pdata->rg.off = off; |
157 | } |
158 | |
159 | static int dw_edma_pcie_probe(struct pci_dev *pdev, |
160 | const struct pci_device_id *pid) |
161 | { |
162 | struct dw_edma_pcie_data *pdata = (void *)pid->driver_data; |
163 | struct dw_edma_pcie_data vsec_data; |
164 | struct device *dev = &pdev->dev; |
165 | struct dw_edma_chip *chip; |
166 | int err, nr_irqs; |
167 | int i, mask; |
168 | |
169 | /* Enable PCI device */ |
170 | err = pcim_enable_device(pdev); |
171 | if (err) { |
172 | pci_err(pdev, "enabling device failed\n" ); |
173 | return err; |
174 | } |
175 | |
176 | memcpy(&vsec_data, pdata, sizeof(struct dw_edma_pcie_data)); |
177 | |
178 | /* |
179 | * Tries to find if exists a PCIe Vendor-Specific Extended Capability |
180 | * for the DMA, if one exists, then reconfigures it. |
181 | */ |
182 | dw_edma_pcie_get_vsec_dma_data(pdev, pdata: &vsec_data); |
183 | |
184 | /* Mapping PCI BAR regions */ |
185 | mask = BIT(vsec_data.rg.bar); |
186 | for (i = 0; i < vsec_data.wr_ch_cnt; i++) { |
187 | mask |= BIT(vsec_data.ll_wr[i].bar); |
188 | mask |= BIT(vsec_data.dt_wr[i].bar); |
189 | } |
190 | for (i = 0; i < vsec_data.rd_ch_cnt; i++) { |
191 | mask |= BIT(vsec_data.ll_rd[i].bar); |
192 | mask |= BIT(vsec_data.dt_rd[i].bar); |
193 | } |
194 | err = pcim_iomap_regions(pdev, mask, name: pci_name(pdev)); |
195 | if (err) { |
196 | pci_err(pdev, "eDMA BAR I/O remapping failed\n" ); |
197 | return err; |
198 | } |
199 | |
200 | pci_set_master(dev: pdev); |
201 | |
202 | /* DMA configuration */ |
203 | err = dma_set_mask_and_coherent(dev: &pdev->dev, DMA_BIT_MASK(64)); |
204 | if (err) { |
205 | pci_err(pdev, "DMA mask 64 set failed\n" ); |
206 | return err; |
207 | } |
208 | |
209 | /* Data structure allocation */ |
210 | chip = devm_kzalloc(dev, size: sizeof(*chip), GFP_KERNEL); |
211 | if (!chip) |
212 | return -ENOMEM; |
213 | |
214 | /* IRQs allocation */ |
215 | nr_irqs = pci_alloc_irq_vectors(dev: pdev, min_vecs: 1, max_vecs: vsec_data.irqs, |
216 | PCI_IRQ_MSI | PCI_IRQ_MSIX); |
217 | if (nr_irqs < 1) { |
218 | pci_err(pdev, "fail to alloc IRQ vector (number of IRQs=%u)\n" , |
219 | nr_irqs); |
220 | return -EPERM; |
221 | } |
222 | |
223 | /* Data structure initialization */ |
224 | chip->dev = dev; |
225 | |
226 | chip->mf = vsec_data.mf; |
227 | chip->nr_irqs = nr_irqs; |
228 | chip->ops = &dw_edma_pcie_plat_ops; |
229 | |
230 | chip->ll_wr_cnt = vsec_data.wr_ch_cnt; |
231 | chip->ll_rd_cnt = vsec_data.rd_ch_cnt; |
232 | |
233 | chip->reg_base = pcim_iomap_table(pdev)[vsec_data.rg.bar]; |
234 | if (!chip->reg_base) |
235 | return -ENOMEM; |
236 | |
237 | for (i = 0; i < chip->ll_wr_cnt; i++) { |
238 | struct dw_edma_region *ll_region = &chip->ll_region_wr[i]; |
239 | struct dw_edma_region *dt_region = &chip->dt_region_wr[i]; |
240 | struct dw_edma_block *ll_block = &vsec_data.ll_wr[i]; |
241 | struct dw_edma_block *dt_block = &vsec_data.dt_wr[i]; |
242 | |
243 | ll_region->vaddr.io = pcim_iomap_table(pdev)[ll_block->bar]; |
244 | if (!ll_region->vaddr.io) |
245 | return -ENOMEM; |
246 | |
247 | ll_region->vaddr.io += ll_block->off; |
248 | ll_region->paddr = pci_bus_address(pdev, bar: ll_block->bar); |
249 | ll_region->paddr += ll_block->off; |
250 | ll_region->sz = ll_block->sz; |
251 | |
252 | dt_region->vaddr.io = pcim_iomap_table(pdev)[dt_block->bar]; |
253 | if (!dt_region->vaddr.io) |
254 | return -ENOMEM; |
255 | |
256 | dt_region->vaddr.io += dt_block->off; |
257 | dt_region->paddr = pci_bus_address(pdev, bar: dt_block->bar); |
258 | dt_region->paddr += dt_block->off; |
259 | dt_region->sz = dt_block->sz; |
260 | } |
261 | |
262 | for (i = 0; i < chip->ll_rd_cnt; i++) { |
263 | struct dw_edma_region *ll_region = &chip->ll_region_rd[i]; |
264 | struct dw_edma_region *dt_region = &chip->dt_region_rd[i]; |
265 | struct dw_edma_block *ll_block = &vsec_data.ll_rd[i]; |
266 | struct dw_edma_block *dt_block = &vsec_data.dt_rd[i]; |
267 | |
268 | ll_region->vaddr.io = pcim_iomap_table(pdev)[ll_block->bar]; |
269 | if (!ll_region->vaddr.io) |
270 | return -ENOMEM; |
271 | |
272 | ll_region->vaddr.io += ll_block->off; |
273 | ll_region->paddr = pci_bus_address(pdev, bar: ll_block->bar); |
274 | ll_region->paddr += ll_block->off; |
275 | ll_region->sz = ll_block->sz; |
276 | |
277 | dt_region->vaddr.io = pcim_iomap_table(pdev)[dt_block->bar]; |
278 | if (!dt_region->vaddr.io) |
279 | return -ENOMEM; |
280 | |
281 | dt_region->vaddr.io += dt_block->off; |
282 | dt_region->paddr = pci_bus_address(pdev, bar: dt_block->bar); |
283 | dt_region->paddr += dt_block->off; |
284 | dt_region->sz = dt_block->sz; |
285 | } |
286 | |
287 | /* Debug info */ |
288 | if (chip->mf == EDMA_MF_EDMA_LEGACY) |
289 | pci_dbg(pdev, "Version:\teDMA Port Logic (0x%x)\n" , chip->mf); |
290 | else if (chip->mf == EDMA_MF_EDMA_UNROLL) |
291 | pci_dbg(pdev, "Version:\teDMA Unroll (0x%x)\n" , chip->mf); |
292 | else if (chip->mf == EDMA_MF_HDMA_COMPAT) |
293 | pci_dbg(pdev, "Version:\tHDMA Compatible (0x%x)\n" , chip->mf); |
294 | else |
295 | pci_dbg(pdev, "Version:\tUnknown (0x%x)\n" , chip->mf); |
296 | |
297 | pci_dbg(pdev, "Registers:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p)\n" , |
298 | vsec_data.rg.bar, vsec_data.rg.off, vsec_data.rg.sz, |
299 | chip->reg_base); |
300 | |
301 | |
302 | for (i = 0; i < chip->ll_wr_cnt; i++) { |
303 | pci_dbg(pdev, "L. List:\tWRITE CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n" , |
304 | i, vsec_data.ll_wr[i].bar, |
305 | vsec_data.ll_wr[i].off, chip->ll_region_wr[i].sz, |
306 | chip->ll_region_wr[i].vaddr.io, &chip->ll_region_wr[i].paddr); |
307 | |
308 | pci_dbg(pdev, "Data:\tWRITE CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n" , |
309 | i, vsec_data.dt_wr[i].bar, |
310 | vsec_data.dt_wr[i].off, chip->dt_region_wr[i].sz, |
311 | chip->dt_region_wr[i].vaddr.io, &chip->dt_region_wr[i].paddr); |
312 | } |
313 | |
314 | for (i = 0; i < chip->ll_rd_cnt; i++) { |
315 | pci_dbg(pdev, "L. List:\tREAD CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n" , |
316 | i, vsec_data.ll_rd[i].bar, |
317 | vsec_data.ll_rd[i].off, chip->ll_region_rd[i].sz, |
318 | chip->ll_region_rd[i].vaddr.io, &chip->ll_region_rd[i].paddr); |
319 | |
320 | pci_dbg(pdev, "Data:\tREAD CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n" , |
321 | i, vsec_data.dt_rd[i].bar, |
322 | vsec_data.dt_rd[i].off, chip->dt_region_rd[i].sz, |
323 | chip->dt_region_rd[i].vaddr.io, &chip->dt_region_rd[i].paddr); |
324 | } |
325 | |
326 | pci_dbg(pdev, "Nr. IRQs:\t%u\n" , chip->nr_irqs); |
327 | |
328 | /* Validating if PCI interrupts were enabled */ |
329 | if (!pci_dev_msi_enabled(pci_dev: pdev)) { |
330 | pci_err(pdev, "enable interrupt failed\n" ); |
331 | return -EPERM; |
332 | } |
333 | |
334 | /* Starting eDMA driver */ |
335 | err = dw_edma_probe(chip); |
336 | if (err) { |
337 | pci_err(pdev, "eDMA probe failed\n" ); |
338 | return err; |
339 | } |
340 | |
341 | /* Saving data structure reference */ |
342 | pci_set_drvdata(pdev, data: chip); |
343 | |
344 | return 0; |
345 | } |
346 | |
347 | static void dw_edma_pcie_remove(struct pci_dev *pdev) |
348 | { |
349 | struct dw_edma_chip *chip = pci_get_drvdata(pdev); |
350 | int err; |
351 | |
352 | /* Stopping eDMA driver */ |
353 | err = dw_edma_remove(chip); |
354 | if (err) |
355 | pci_warn(pdev, "can't remove device properly: %d\n" , err); |
356 | |
357 | /* Freeing IRQs */ |
358 | pci_free_irq_vectors(dev: pdev); |
359 | } |
360 | |
361 | static const struct pci_device_id dw_edma_pcie_id_table[] = { |
362 | { PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_data) }, |
363 | { } |
364 | }; |
365 | MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table); |
366 | |
367 | static struct pci_driver dw_edma_pcie_driver = { |
368 | .name = "dw-edma-pcie" , |
369 | .id_table = dw_edma_pcie_id_table, |
370 | .probe = dw_edma_pcie_probe, |
371 | .remove = dw_edma_pcie_remove, |
372 | }; |
373 | |
374 | module_pci_driver(dw_edma_pcie_driver); |
375 | |
376 | MODULE_LICENSE("GPL v2" ); |
377 | MODULE_DESCRIPTION("Synopsys DesignWare eDMA PCIe driver" ); |
378 | MODULE_AUTHOR("Gustavo Pimentel <gustavo.pimentel@synopsys.com>" ); |
379 | |