p@2550: /** p@2550: Network Driver for Beckhoff CCAT communication controller p@2550: Copyright (C) 2014 Beckhoff Automation GmbH p@2550: Author: Patrick Bruenn p@2550: p@2550: This program is free software; you can redistribute it and/or modify p@2550: it under the terms of the GNU General Public License as published by p@2550: the Free Software Foundation; either version 2 of the License, or p@2550: (at your option) any later version. p@2550: p@2550: This program is distributed in the hope that it will be useful, p@2550: but WITHOUT ANY WARRANTY; without even the implied warranty of p@2550: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the p@2550: GNU General Public License for more details. p@2550: p@2550: You should have received a copy of the GNU General Public License along p@2550: with this program; if not, write to the Free Software Foundation, Inc., p@2550: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. p@2550: */ p@2550: p@2550: #include p@2550: #include p@2550: #include p@2550: #include p@2550: #include "module.h" p@2550: #include "netdev.h" p@2550: #include "update.h" p@2550: p@2550: MODULE_DESCRIPTION(DRV_DESCRIPTION); p@2550: MODULE_AUTHOR("Patrick Bruenn "); p@2550: MODULE_LICENSE("GPL"); p@2550: MODULE_VERSION(DRV_VERSION); p@2550: p@2550: static void ccat_bar_free(struct ccat_bar *bar) p@2550: { p@2550: if (bar->ioaddr) { p@2550: const struct ccat_bar tmp = *bar; p@2550: memset(bar, 0, sizeof(*bar)); p@2550: iounmap(tmp.ioaddr); p@2550: release_mem_region(tmp.start, tmp.len); p@2550: } else { p@2550: pr_warn("%s(): %p was already done.\n", __FUNCTION__, bar); p@2550: } p@2550: } p@2550: p@2550: /** p@2550: * ccat_bar_init() - Initialize a CCAT pci bar p@2550: * @bar object which should be initialized p@2550: * @index 0 and 2 are valid for CCAT, meaning pci bar0 or pci bar2 p@2550: * @pdev the pci device as which the CCAT was recognized before p@2550: * p@2550: * Reading PCI config space; request and map memory region. p@2550: */ p@2550: static int ccat_bar_init(struct ccat_bar *bar, size_t index, p@2550: struct pci_dev *pdev) p@2550: { p@2550: struct resource *res; p@2567: p@2550: bar->start = pci_resource_start(pdev, index); p@2550: bar->end = pci_resource_end(pdev, index); p@2550: bar->len = pci_resource_len(pdev, index); p@2550: bar->flags = pci_resource_flags(pdev, index); p@2550: if (!(IORESOURCE_MEM & bar->flags)) { p@2567: pr_info("bar%llu is no mem_region -> abort.\n", (u64) index); p@2550: return -EIO; p@2550: } p@2550: p@2565: res = request_mem_region(bar->start, bar->len, KBUILD_MODNAME); p@2550: if (!res) { p@2550: pr_info("allocate mem_region failed.\n"); p@2550: return -EIO; p@2550: } p@2567: pr_debug("bar%llu at [%lx,%lx] len=%lu res: %p.\n", (u64) index, p@2550: bar->start, bar->end, bar->len, res); p@2550: p@2550: bar->ioaddr = ioremap(bar->start, bar->len); p@2550: if (!bar->ioaddr) { p@2567: pr_info("bar%llu ioremap failed.\n", (u64) index); p@2550: release_mem_region(bar->start, bar->len); p@2550: return -EIO; p@2550: } p@2567: pr_debug("bar%llu I/O mem mapped to %p.\n", (u64) index, bar->ioaddr); p@2550: return 0; p@2550: } p@2550: p@2550: void ccat_dma_free(struct ccat_dma *const dma) p@2550: { p@2550: const struct ccat_dma tmp = *dma; p@2567: p@2550: free_dma(dma->channel); p@2550: memset(dma, 0, sizeof(*dma)); p@2550: dma_free_coherent(tmp.dev, tmp.size, tmp.virt, tmp.phys); p@2550: } p@2550: p@2550: /** p@2550: * ccat_dma_init() - Initialize CCAT and host memory for DMA transfer p@2550: * @dma object for management data which will be initialized p@2550: * @channel number of the DMA channel p@2550: * @ioaddr of the pci bar2 configspace used to calculate the address of the pci dma configuration p@2550: * @dev which should be configured for DMA p@2550: */ p@2550: int ccat_dma_init(struct ccat_dma *const dma, size_t channel, p@2550: void __iomem * const ioaddr, struct device *const dev) p@2550: { p@2550: void *frame; p@2567: u64 addr; p@2567: u32 translateAddr; p@2567: u32 memTranslate; p@2567: u32 memSize; p@2567: u32 data = 0xffffffff; p@2567: u32 offset = (sizeof(u64) * channel) + 0x1000; p@2550: p@2550: dma->channel = channel; p@2550: dma->dev = dev; p@2550: p@2550: /* calculate size and alignments */ p@2550: iowrite32(data, ioaddr + offset); p@2550: wmb(); p@2550: data = ioread32(ioaddr + offset); p@2550: memTranslate = data & 0xfffffffc; p@2550: memSize = (~memTranslate) + 1; p@2550: dma->size = 2 * memSize - PAGE_SIZE; p@2550: dma->virt = dma_zalloc_coherent(dev, dma->size, &dma->phys, GFP_KERNEL); p@2550: if (!dma->virt || !dma->phys) { p@2567: pr_info("init DMA%llu memory failed.\n", (u64) channel); p@2550: return -1; p@2550: } p@2550: p@2565: if (request_dma(channel, KBUILD_MODNAME)) { p@2567: pr_info("request dma channel %llu failed\n", (u64) channel); p@2550: ccat_dma_free(dma); p@2550: return -1; p@2550: } p@2550: p@2550: translateAddr = (dma->phys + memSize - PAGE_SIZE) & memTranslate; p@2550: addr = translateAddr; p@2550: memcpy_toio(ioaddr + offset, &addr, sizeof(addr)); p@2550: frame = dma->virt + translateAddr - dma->phys; p@2550: pr_debug p@2550: ("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", p@2567: (u64) channel, dma->virt, (u64) (dma->phys), addr, p@2550: ioread32(ioaddr + offset + 4), ioread32(ioaddr + offset), p@2567: memTranslate, (u64) dma->size); p@2550: return 0; p@2550: } p@2550: p@2550: /** p@2550: * Initialize all available CCAT functions. p@2550: * p@2550: * Return: count of failed functions p@2550: */ p@2550: static int ccat_functions_init(struct ccat_device *const ccatdev) p@2550: { p@2569: static const size_t block_size = sizeof(struct ccat_info_block); p@2569: void __iomem *addr = ccatdev->bar[0].ioaddr; /** first block is the CCAT information block entry */ p@2569: const u8 num_func = ioread8(addr + 4); /** number of CCAT function blocks is at offset 0x4 */ p@2569: const void __iomem *end = addr + (block_size * num_func); p@2569: int status = 0; /** count init function failures */ p@2550: p@2550: while (addr < end) { p@2567: const u8 type = ioread16(addr); p@2550: switch (type) { p@2550: case CCATINFO_NOTUSED: p@2550: break; p@2550: case CCATINFO_EPCS_PROM: p@2550: pr_info("Found: CCAT update(EPCS_PROM) -> init()\n"); p@2550: ccatdev->update = ccat_update_init(ccatdev, addr); p@2550: status += (NULL == ccatdev->update); p@2550: break; p@2550: case CCATINFO_ETHERCAT_MASTER_DMA: p@2550: pr_info("Found: ETHERCAT_MASTER_DMA -> init()\n"); p@2550: ccatdev->ethdev = ccat_eth_init(ccatdev, addr); p@2550: status += (NULL == ccatdev->ethdev); p@2550: break; p@2550: default: p@2550: pr_info("Found: 0x%04x not supported\n", type); p@2550: break; p@2550: } p@2569: addr += block_size; p@2550: } p@2550: return status; p@2550: } p@2550: p@2550: /** p@2550: * Destroy all previously initialized CCAT functions p@2550: */ p@2550: static void ccat_functions_remove(struct ccat_device *const ccatdev) p@2550: { p@2550: if (!ccatdev->ethdev) { p@2550: pr_warn("%s(): 'ethdev' was not initialized.\n", __FUNCTION__); p@2550: } else { p@2550: struct ccat_eth_priv *const ethdev = ccatdev->ethdev; p@2550: ccatdev->ethdev = NULL; p@2550: ccat_eth_remove(ethdev); p@2550: } p@2550: if (!ccatdev->update) { p@2550: pr_warn("%s(): 'update' was not initialized.\n", __FUNCTION__); p@2550: } else { p@2550: struct ccat_update *const update = ccatdev->update; p@2550: ccatdev->update = NULL; p@2550: ccat_update_remove(update); p@2550: } p@2550: } p@2550: p@2550: static int ccat_probe(struct pci_dev *pdev, const struct pci_device_id *id) p@2550: { p@2550: int status; p@2550: u8 revision; p@2550: struct ccat_device *ccatdev = kmalloc(sizeof(*ccatdev), GFP_KERNEL); p@2567: p@2550: if (!ccatdev) { p@2550: pr_err("%s() out of memory.\n", __FUNCTION__); p@2550: return -ENOMEM; p@2550: } p@2550: memset(ccatdev, 0, sizeof(*ccatdev)); p@2550: ccatdev->pdev = pdev; p@2550: pci_set_drvdata(pdev, ccatdev); p@2550: p@2550: status = pci_enable_device_mem(pdev); p@2550: if (status) { p@2550: pr_info("enable %s failed: %d\n", pdev->dev.kobj.name, status); p@2550: return status; p@2550: } p@2550: p@2550: status = pci_read_config_byte(pdev, PCI_REVISION_ID, &revision); p@2550: if (status) { p@2550: pr_warn("read CCAT pci revision failed with %d\n", status); p@2550: return status; p@2550: } p@2550: p@2569: if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64))) { p@2550: pr_debug("64 bit DMA supported, pci rev: %u\n", revision); p@2569: } else if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32))) { p@2550: pr_debug("32 bit DMA supported, pci rev: %u\n", revision); p@2550: } else { p@2550: pr_warn("No suitable DMA available, pci rev: %u\n", revision); p@2550: } p@2550: p@2550: if (ccat_bar_init(&ccatdev->bar[0], 0, pdev)) { p@2550: pr_warn("initialization of bar0 failed.\n"); p@2550: return -EIO; p@2550: } p@2550: p@2550: if (ccat_bar_init(&ccatdev->bar[2], 2, pdev)) { p@2550: pr_warn("initialization of bar2 failed.\n"); p@2550: return -EIO; p@2550: } p@2550: p@2550: pci_set_master(pdev); p@2550: if (ccat_functions_init(ccatdev)) { p@2550: pr_warn("some functions couldn't be initialized\n"); p@2550: } p@2550: return 0; p@2550: } p@2550: p@2550: static void ccat_remove(struct pci_dev *pdev) p@2550: { p@2550: struct ccat_device *ccatdev = pci_get_drvdata(pdev); p@2567: p@2550: if (ccatdev) { p@2550: ccat_functions_remove(ccatdev); p@2550: ccat_bar_free(&ccatdev->bar[2]); p@2550: ccat_bar_free(&ccatdev->bar[0]); p@2550: pci_disable_device(pdev); p@2550: pci_set_drvdata(pdev, NULL); p@2550: kfree(ccatdev); p@2550: } p@2550: pr_debug("%s() done.\n", __FUNCTION__); p@2550: } p@2550: p@2550: #define PCI_DEVICE_ID_BECKHOFF_CCAT 0x5000 p@2550: #define PCI_VENDOR_ID_BECKHOFF 0x15EC p@2550: p@2550: static const struct pci_device_id pci_ids[] = { p@2550: {PCI_DEVICE(PCI_VENDOR_ID_BECKHOFF, PCI_DEVICE_ID_BECKHOFF_CCAT)}, p@2550: {0,}, p@2550: }; p@2550: fp@2564: #if 0 /* prevent auto-loading */ p@2550: MODULE_DEVICE_TABLE(pci, pci_ids); fp@2564: #endif p@2550: p@2550: static struct pci_driver pci_driver = { p@2565: .name = KBUILD_MODNAME, p@2550: .id_table = pci_ids, p@2550: .probe = ccat_probe, p@2550: .remove = ccat_remove, p@2550: }; p@2550: p@2569: static void __exit ccat_exit_module(void) p@2550: { p@2550: pci_unregister_driver(&pci_driver); p@2550: } p@2550: p@2569: static int __init ccat_init_module(void) p@2569: { p@2550: pr_info("%s, %s\n", DRV_DESCRIPTION, DRV_VERSION); p@2550: return pci_register_driver(&pci_driver); p@2550: } p@2550: p@2550: module_exit(ccat_exit_module); p@2550: module_init(ccat_init_module);