|
1 /** |
|
2 Network Driver for Beckhoff CCAT communication controller |
|
3 Copyright (C) 2014 Beckhoff Automation GmbH |
|
4 Author: Patrick Bruenn <p.bruenn@beckhoff.com> |
|
5 |
|
6 This program is free software; you can redistribute it and/or modify |
|
7 it under the terms of the GNU General Public License as published by |
|
8 the Free Software Foundation; either version 2 of the License, or |
|
9 (at your option) any later version. |
|
10 |
|
11 This program is distributed in the hope that it will be useful, |
|
12 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 GNU General Public License for more details. |
|
15 |
|
16 You should have received a copy of the GNU General Public License along |
|
17 with this program; if not, write to the Free Software Foundation, Inc., |
|
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|
19 */ |
|
20 |
|
21 #include <asm/dma.h> |
|
22 #include <linux/etherdevice.h> |
|
23 #include <linux/module.h> |
|
24 #include <linux/netdevice.h> |
|
25 #include "compat.h" |
|
26 #include "module.h" |
|
27 #include "netdev.h" |
|
28 #include "update.h" |
|
29 |
|
30 MODULE_DESCRIPTION(DRV_DESCRIPTION); |
|
31 MODULE_AUTHOR("Patrick Bruenn <p.bruenn@beckhoff.com>"); |
|
32 MODULE_LICENSE("GPL"); |
|
33 MODULE_VERSION(DRV_VERSION); |
|
34 |
|
35 static void ccat_bar_free(struct ccat_bar *bar) |
|
36 { |
|
37 if (bar->ioaddr) { |
|
38 const struct ccat_bar tmp = *bar; |
|
39 memset(bar, 0, sizeof(*bar)); |
|
40 iounmap(tmp.ioaddr); |
|
41 release_mem_region(tmp.start, tmp.len); |
|
42 } else { |
|
43 pr_warn("%s(): %p was already done.\n", __FUNCTION__, bar); |
|
44 } |
|
45 } |
|
46 |
|
47 /** |
|
48 * ccat_bar_init() - Initialize a CCAT pci bar |
|
49 * @bar object which should be initialized |
|
50 * @index 0 and 2 are valid for CCAT, meaning pci bar0 or pci bar2 |
|
51 * @pdev the pci device as which the CCAT was recognized before |
|
52 * |
|
53 * Reading PCI config space; request and map memory region. |
|
54 */ |
|
55 static int ccat_bar_init(struct ccat_bar *bar, size_t index, |
|
56 struct pci_dev *pdev) |
|
57 { |
|
58 struct resource *res; |
|
59 bar->start = pci_resource_start(pdev, index); |
|
60 bar->end = pci_resource_end(pdev, index); |
|
61 bar->len = pci_resource_len(pdev, index); |
|
62 bar->flags = pci_resource_flags(pdev, index); |
|
63 if (!(IORESOURCE_MEM & bar->flags)) { |
|
64 pr_info("bar%llu is no mem_region -> abort.\n", |
|
65 (uint64_t) index); |
|
66 return -EIO; |
|
67 } |
|
68 |
|
69 res = request_mem_region(bar->start, bar->len, DRV_NAME); |
|
70 if (!res) { |
|
71 pr_info("allocate mem_region failed.\n"); |
|
72 return -EIO; |
|
73 } |
|
74 pr_debug("bar%llu at [%lx,%lx] len=%lu res: %p.\n", (uint64_t) index, |
|
75 bar->start, bar->end, bar->len, res); |
|
76 |
|
77 bar->ioaddr = ioremap(bar->start, bar->len); |
|
78 if (!bar->ioaddr) { |
|
79 pr_info("bar%llu ioremap failed.\n", (uint64_t) index); |
|
80 release_mem_region(bar->start, bar->len); |
|
81 return -EIO; |
|
82 } |
|
83 pr_debug("bar%llu I/O mem mapped to %p.\n", (uint64_t) index, |
|
84 bar->ioaddr); |
|
85 return 0; |
|
86 } |
|
87 |
|
88 void ccat_dma_free(struct ccat_dma *const dma) |
|
89 { |
|
90 const struct ccat_dma tmp = *dma; |
|
91 free_dma(dma->channel); |
|
92 memset(dma, 0, sizeof(*dma)); |
|
93 dma_free_coherent(tmp.dev, tmp.size, tmp.virt, tmp.phys); |
|
94 } |
|
95 |
|
96 /** |
|
97 * ccat_dma_init() - Initialize CCAT and host memory for DMA transfer |
|
98 * @dma object for management data which will be initialized |
|
99 * @channel number of the DMA channel |
|
100 * @ioaddr of the pci bar2 configspace used to calculate the address of the pci dma configuration |
|
101 * @dev which should be configured for DMA |
|
102 */ |
|
103 int ccat_dma_init(struct ccat_dma *const dma, size_t channel, |
|
104 void __iomem * const ioaddr, struct device *const dev) |
|
105 { |
|
106 void *frame; |
|
107 uint64_t addr; |
|
108 uint32_t translateAddr; |
|
109 uint32_t memTranslate; |
|
110 uint32_t memSize; |
|
111 uint32_t data = 0xffffffff; |
|
112 uint32_t offset = (sizeof(uint64_t) * channel) + 0x1000; |
|
113 |
|
114 dma->channel = channel; |
|
115 dma->dev = dev; |
|
116 |
|
117 /* calculate size and alignments */ |
|
118 iowrite32(data, ioaddr + offset); |
|
119 wmb(); |
|
120 data = ioread32(ioaddr + offset); |
|
121 memTranslate = data & 0xfffffffc; |
|
122 memSize = (~memTranslate) + 1; |
|
123 dma->size = 2 * memSize - PAGE_SIZE; |
|
124 dma->virt = dma_zalloc_coherent(dev, dma->size, &dma->phys, GFP_KERNEL); |
|
125 if (!dma->virt || !dma->phys) { |
|
126 pr_info("init DMA%llu memory failed.\n", (uint64_t) channel); |
|
127 return -1; |
|
128 } |
|
129 |
|
130 if (request_dma(channel, DRV_NAME)) { |
|
131 pr_info("request dma channel %llu failed\n", |
|
132 (uint64_t) channel); |
|
133 ccat_dma_free(dma); |
|
134 return -1; |
|
135 } |
|
136 |
|
137 translateAddr = (dma->phys + memSize - PAGE_SIZE) & memTranslate; |
|
138 addr = translateAddr; |
|
139 memcpy_toio(ioaddr + offset, &addr, sizeof(addr)); |
|
140 frame = dma->virt + translateAddr - dma->phys; |
|
141 pr_debug |
|
142 ("DMA%llu mem initialized\n virt: 0x%p\n phys: 0x%llx\n translated: 0x%llx\n pci addr: 0x%08x%x\n memTranslate: 0x%x\n size: %llu bytes.\n", |
|
143 (uint64_t) channel, dma->virt, (uint64_t) (dma->phys), addr, |
|
144 ioread32(ioaddr + offset + 4), ioread32(ioaddr + offset), |
|
145 memTranslate, (uint64_t) dma->size); |
|
146 return 0; |
|
147 } |
|
148 |
|
149 /** |
|
150 * Initialize all available CCAT functions. |
|
151 * |
|
152 * Return: count of failed functions |
|
153 */ |
|
154 static int ccat_functions_init(struct ccat_device *const ccatdev) |
|
155 { |
|
156 /* read CCatInfoBlock.nMaxEntries from ccat */ |
|
157 const uint8_t num_func = ioread8(ccatdev->bar[0].ioaddr + 4); |
|
158 void __iomem *addr = ccatdev->bar[0].ioaddr; |
|
159 const void __iomem *end = addr + (sizeof(CCatInfoBlock) * num_func); |
|
160 int status = 0; //count init function failures |
|
161 |
|
162 while (addr < end) { |
|
163 const uint8_t type = ioread16(addr); |
|
164 switch (type) { |
|
165 case CCATINFO_NOTUSED: |
|
166 break; |
|
167 case CCATINFO_EPCS_PROM: |
|
168 pr_info("Found: CCAT update(EPCS_PROM) -> init()\n"); |
|
169 ccatdev->update = ccat_update_init(ccatdev, addr); |
|
170 status += (NULL == ccatdev->update); |
|
171 break; |
|
172 case CCATINFO_ETHERCAT_MASTER_DMA: |
|
173 pr_info("Found: ETHERCAT_MASTER_DMA -> init()\n"); |
|
174 ccatdev->ethdev = ccat_eth_init(ccatdev, addr); |
|
175 status += (NULL == ccatdev->ethdev); |
|
176 break; |
|
177 default: |
|
178 pr_info("Found: 0x%04x not supported\n", type); |
|
179 break; |
|
180 } |
|
181 addr += sizeof(CCatInfoBlock); |
|
182 } |
|
183 return status; |
|
184 } |
|
185 |
|
186 /** |
|
187 * Destroy all previously initialized CCAT functions |
|
188 */ |
|
189 static void ccat_functions_remove(struct ccat_device *const ccatdev) |
|
190 { |
|
191 if (!ccatdev->ethdev) { |
|
192 pr_warn("%s(): 'ethdev' was not initialized.\n", __FUNCTION__); |
|
193 } else { |
|
194 struct ccat_eth_priv *const ethdev = ccatdev->ethdev; |
|
195 ccatdev->ethdev = NULL; |
|
196 ccat_eth_remove(ethdev); |
|
197 } |
|
198 if (!ccatdev->update) { |
|
199 pr_warn("%s(): 'update' was not initialized.\n", __FUNCTION__); |
|
200 } else { |
|
201 struct ccat_update *const update = ccatdev->update; |
|
202 ccatdev->update = NULL; |
|
203 ccat_update_remove(update); |
|
204 } |
|
205 } |
|
206 |
|
207 static int ccat_probe(struct pci_dev *pdev, const struct pci_device_id *id) |
|
208 { |
|
209 int status; |
|
210 u8 revision; |
|
211 struct ccat_device *ccatdev = kmalloc(sizeof(*ccatdev), GFP_KERNEL); |
|
212 if (!ccatdev) { |
|
213 pr_err("%s() out of memory.\n", __FUNCTION__); |
|
214 return -ENOMEM; |
|
215 } |
|
216 memset(ccatdev, 0, sizeof(*ccatdev)); |
|
217 ccatdev->pdev = pdev; |
|
218 pci_set_drvdata(pdev, ccatdev); |
|
219 |
|
220 status = pci_enable_device_mem(pdev); |
|
221 if (status) { |
|
222 pr_info("enable %s failed: %d\n", pdev->dev.kobj.name, status); |
|
223 return status; |
|
224 } |
|
225 |
|
226 status = pci_read_config_byte(pdev, PCI_REVISION_ID, &revision); |
|
227 if (status) { |
|
228 pr_warn("read CCAT pci revision failed with %d\n", status); |
|
229 return status; |
|
230 } |
|
231 |
|
232 /* FIXME upgrade to a newer kernel to get support of dma_set_mask_and_coherent() |
|
233 * (!dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(64))) { |
|
234 */ |
|
235 if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(64))) { |
|
236 pr_debug("64 bit DMA supported, pci rev: %u\n", revision); |
|
237 /*} else if (!dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32))) { */ |
|
238 } else if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) { |
|
239 pr_debug("32 bit DMA supported, pci rev: %u\n", revision); |
|
240 } else { |
|
241 pr_warn("No suitable DMA available, pci rev: %u\n", revision); |
|
242 } |
|
243 |
|
244 if (ccat_bar_init(&ccatdev->bar[0], 0, pdev)) { |
|
245 pr_warn("initialization of bar0 failed.\n"); |
|
246 return -EIO; |
|
247 } |
|
248 |
|
249 if (ccat_bar_init(&ccatdev->bar[2], 2, pdev)) { |
|
250 pr_warn("initialization of bar2 failed.\n"); |
|
251 return -EIO; |
|
252 } |
|
253 |
|
254 pci_set_master(pdev); |
|
255 if (ccat_functions_init(ccatdev)) { |
|
256 pr_warn("some functions couldn't be initialized\n"); |
|
257 } |
|
258 return 0; |
|
259 } |
|
260 |
|
261 static void ccat_remove(struct pci_dev *pdev) |
|
262 { |
|
263 struct ccat_device *ccatdev = pci_get_drvdata(pdev); |
|
264 if (ccatdev) { |
|
265 ccat_functions_remove(ccatdev); |
|
266 ccat_bar_free(&ccatdev->bar[2]); |
|
267 ccat_bar_free(&ccatdev->bar[0]); |
|
268 pci_disable_device(pdev); |
|
269 pci_set_drvdata(pdev, NULL); |
|
270 kfree(ccatdev); |
|
271 } |
|
272 pr_debug("%s() done.\n", __FUNCTION__); |
|
273 } |
|
274 |
|
275 #define PCI_DEVICE_ID_BECKHOFF_CCAT 0x5000 |
|
276 #define PCI_VENDOR_ID_BECKHOFF 0x15EC |
|
277 |
|
278 static const struct pci_device_id pci_ids[] = { |
|
279 {PCI_DEVICE(PCI_VENDOR_ID_BECKHOFF, PCI_DEVICE_ID_BECKHOFF_CCAT)}, |
|
280 {0,}, |
|
281 }; |
|
282 |
|
283 MODULE_DEVICE_TABLE(pci, pci_ids); |
|
284 |
|
285 static struct pci_driver pci_driver = { |
|
286 .name = DRV_NAME, |
|
287 .id_table = pci_ids, |
|
288 .probe = ccat_probe, |
|
289 .remove = ccat_remove, |
|
290 }; |
|
291 |
|
292 static void ccat_exit_module(void) |
|
293 { |
|
294 pci_unregister_driver(&pci_driver); |
|
295 } |
|
296 |
|
297 static int ccat_init_module(void) |
|
298 { |
|
299 BUILD_BUG_ON(offsetof(struct ccat_eth_frame, data) != |
|
300 CCAT_DMA_FRAME_HEADER_LENGTH); |
|
301 pr_info("%s, %s\n", DRV_DESCRIPTION, DRV_VERSION); |
|
302 return pci_register_driver(&pci_driver); |
|
303 } |
|
304 |
|
305 module_exit(ccat_exit_module); |
|
306 module_init(ccat_init_module); |