p@2550: /** p@2550: Network Driver for Beckhoff CCAT communication controller p@2636: Copyright (C) 2014-2015 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@2636: #include fp@2580: #include p@2550: #include "module.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@2636: #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,12,27)) p@2636: /* p@2636: * Set both the DMA mask and the coherent DMA mask to the same thing. p@2636: * Note that we don't check the return value from dma_set_coherent_mask() p@2636: * as the DMA API guarantees that the coherent DMA mask can be set to p@2636: * the same or smaller than the streaming DMA mask. p@2636: */ p@2636: static inline int dma_set_mask_and_coherent(struct device *dev, u64 mask) p@2636: { p@2636: int rc = dma_set_mask(dev, mask); p@2636: if (rc == 0) p@2636: dma_set_coherent_mask(dev, mask); p@2636: return rc; p@2636: } p@2636: #endif p@2550: p@2550: /** p@2636: * configure the drivers capabilities here p@2550: */ p@2636: static const struct ccat_driver *const drivers[] = { p@2636: #ifdef CONFIG_PCI p@2636: ð_dma_driver, /* load Ethernet MAC/EtherCAT Master driver with DMA support from netdev.c */ p@2636: #endif p@2636: ð_eim_driver, /* load Ethernet MAC/EtherCAT Master driver without DMA support from */ p@2636: &gpio_driver, /* load GPIO driver from gpio.c */ p@2636: &sram_driver, /* load SRAM driver from sram.c */ p@2636: &update_driver, /* load Update driver from update.c */ p@2636: }; p@2636: p@2636: static int __init ccat_class_init(struct ccat_class *base) p@2636: { p@2636: if (1 == atomic_inc_return(&base->instances)) { p@2636: if (alloc_chrdev_region p@2636: (&base->dev, 0, base->count, KBUILD_MODNAME)) { p@2636: pr_warn("alloc_chrdev_region() for '%s' failed\n", p@2636: base->name); p@2636: return -1; p@2636: } p@2636: p@2636: base->class = class_create(THIS_MODULE, base->name); p@2636: if (!base->class) { p@2636: pr_warn("Create device class '%s' failed\n", p@2636: base->name); p@2636: unregister_chrdev_region(base->dev, base->count); p@2636: return -1; p@2636: } p@2636: } p@2636: return 0; p@2636: } p@2636: p@2636: static void ccat_class_exit(struct ccat_class *base) p@2636: { p@2636: if (!atomic_dec_return(&base->instances)) { p@2636: class_destroy(base->class); p@2636: unregister_chrdev_region(base->dev, base->count); p@2636: } p@2636: } p@2636: p@2636: static void free_ccat_cdev(struct ccat_cdev *ccdev) p@2636: { p@2636: ccat_class_exit(ccdev->class); p@2636: ccdev->dev = 0; p@2636: } p@2636: p@2636: static struct ccat_cdev *alloc_ccat_cdev(struct ccat_class *base) p@2636: { p@2636: int i = 0; p@2636: p@2636: ccat_class_init(base); p@2636: for (i = 0; i < base->count; ++i) { p@2636: if (base->devices[i].dev == 0) { p@2636: base->devices[i].dev = MKDEV(MAJOR(base->dev), i); p@2636: return &base->devices[i]; p@2636: } p@2636: } p@2636: pr_warn("exceeding max. number of '%s' devices (%d)\n", p@2636: base->class->name, base->count); p@2636: atomic_dec_return(&base->instances); p@2636: return NULL; p@2636: } p@2636: p@2636: static int ccat_cdev_init(struct cdev *cdev, dev_t dev, struct class *class, p@2636: struct file_operations *fops) p@2636: { p@2636: if (!device_create p@2636: (class, NULL, dev, NULL, "%s%d", class->name, MINOR(dev))) { p@2636: pr_warn("device_create() failed\n"); p@2550: return -1; p@2550: } p@2550: p@2636: cdev_init(cdev, fops); p@2636: cdev->owner = fops->owner; p@2636: if (cdev_add(cdev, dev, 1)) { p@2636: pr_warn("add update device failed\n"); p@2636: device_destroy(class, dev); p@2550: return -1; p@2550: } p@2550: p@2636: pr_info("registered %s%d.\n", class->name, MINOR(dev)); p@2636: return 0; p@2636: } p@2636: p@2636: int ccat_cdev_open(struct inode *const i, struct file *const f) p@2636: { p@2636: struct ccat_cdev *ccdev = p@2636: container_of(i->i_cdev, struct ccat_cdev, cdev); p@2636: struct cdev_buffer *buf; p@2636: p@2636: if (!atomic_dec_and_test(&ccdev->in_use)) { p@2636: atomic_inc(&ccdev->in_use); p@2636: return -EBUSY; p@2636: } p@2636: p@2636: buf = kzalloc(sizeof(*buf) + ccdev->iosize, GFP_KERNEL); p@2636: if (!buf) { p@2636: atomic_inc(&ccdev->in_use); p@2636: return -ENOMEM; p@2636: } p@2636: p@2636: buf->ccdev = ccdev; p@2636: f->private_data = buf; p@2636: return 0; p@2636: } p@2636: p@2636: int ccat_cdev_probe(struct ccat_function *func, struct ccat_class *cdev_class, p@2636: size_t iosize) p@2636: { p@2636: struct ccat_cdev *const ccdev = alloc_ccat_cdev(cdev_class); p@2636: if (!ccdev) { p@2636: return -ENOMEM; p@2636: } p@2636: p@2636: ccdev->ioaddr = func->ccat->bar_0 + func->info.addr; p@2636: ccdev->iosize = iosize; p@2636: atomic_set(&ccdev->in_use, 1); p@2636: p@2636: if (ccat_cdev_init p@2636: (&ccdev->cdev, ccdev->dev, cdev_class->class, &cdev_class->fops)) { p@2636: pr_warn("ccat_cdev_probe() failed\n"); p@2636: free_ccat_cdev(ccdev); p@2636: return -1; p@2636: } p@2636: ccdev->class = cdev_class; p@2636: func->private_data = ccdev; p@2636: return 0; p@2636: } p@2636: p@2636: int ccat_cdev_release(struct inode *const i, struct file *const f) p@2636: { p@2636: const struct cdev_buffer *const buf = f->private_data; p@2636: struct ccat_cdev *const ccdev = buf->ccdev; p@2636: p@2636: kfree(f->private_data); p@2636: atomic_inc(&ccdev->in_use); p@2636: return 0; p@2636: } p@2636: p@2636: void ccat_cdev_remove(struct ccat_function *func) p@2636: { p@2636: struct ccat_cdev *const ccdev = func->private_data; p@2636: p@2636: cdev_del(&ccdev->cdev); p@2636: device_destroy(ccdev->class->class, ccdev->dev); p@2636: free_ccat_cdev(ccdev); p@2636: } p@2636: p@2636: static const struct ccat_driver *ccat_function_connect(struct ccat_function p@2636: *const func) p@2636: { p@2636: int i; p@2636: p@2636: for (i = 0; i < ARRAY_SIZE(drivers); ++i) { p@2636: if (func->info.type == drivers[i]->type) { p@2636: return drivers[i]->probe(func) ? NULL : drivers[i]; p@2636: } p@2636: } p@2636: return NULL; 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@2636: struct ccat_function *next = kzalloc(sizeof(*next), GFP_KERNEL); p@2636: void __iomem *addr = ccatdev->bar_0; /** 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@2636: p@2636: INIT_LIST_HEAD(&ccatdev->functions); p@2636: for (; addr < end && next; addr += block_size) { p@2636: memcpy_fromio(&next->info, addr, sizeof(next->info)); p@2636: if (CCATINFO_NOTUSED != next->info.type) { p@2636: next->ccat = ccatdev; p@2636: next->drv = ccat_function_connect(next); p@2636: if (next->drv) { p@2636: list_add(&next->list, &ccatdev->functions); p@2636: next = kzalloc(sizeof(*next), GFP_KERNEL); p@2636: } p@2636: } p@2636: } p@2636: kfree(next); p@2636: return list_empty(&ccatdev->functions); p@2550: } p@2550: p@2550: /** p@2550: * Destroy all previously initialized CCAT functions p@2550: */ p@2636: static void ccat_functions_remove(struct ccat_device *const dev) p@2636: { p@2636: struct ccat_function *func; p@2636: struct ccat_function *tmp; p@2636: list_for_each_entry_safe(func, tmp, &dev->functions, list) { p@2636: if (func->drv) { p@2636: func->drv->remove(func); p@2636: func->drv = NULL; p@2636: } p@2636: list_del(&func->list); p@2636: kfree(func); p@2636: } p@2636: } p@2636: p@2636: #ifdef CONFIG_PCI p@2636: static int ccat_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) p@2636: { p@2636: struct ccat_device *ccatdev; p@2638: u8 rev; p@2550: int status; p@2636: p@2636: ccatdev = devm_kzalloc(&pdev->dev, sizeof(*ccatdev), GFP_KERNEL); p@2550: if (!ccatdev) { p@2550: pr_err("%s() out of memory.\n", __FUNCTION__); p@2550: return -ENOMEM; p@2550: } 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@2638: pr_err("enable %s failed: %d\n", pdev->dev.kobj.name, status); p@2638: return status; p@2638: } p@2638: p@2638: status = pci_read_config_byte(pdev, PCI_REVISION_ID, &rev); p@2550: if (status) { p@2638: pr_err("read CCAT pci revision failed with %d\n", status); p@2638: goto disable_device; p@2638: } p@2638: p@2638: status = pci_request_regions(pdev, KBUILD_MODNAME); p@2638: if (status) { p@2638: pr_err("allocate mem_regions failed.\n"); p@2638: goto disable_device; p@2638: } p@2638: p@2638: status = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); p@2638: if (status) { p@2638: status = p@2638: dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); p@2638: if (status) { p@2638: pr_err("No suitable DMA available, pci rev: %u\n", rev); p@2638: goto release_regions; p@2638: } p@2638: pr_debug("32 bit DMA supported, pci rev: %u\n", rev); p@2550: } else { p@2638: pr_debug("64 bit DMA supported, pci rev: %u\n", rev); p@2638: } p@2638: p@2638: ccatdev->bar_0 = pci_iomap(pdev, 0, 0); p@2638: if (!ccatdev->bar_0) { p@2638: pr_err("initialization of bar0 failed.\n"); p@2636: status = -EIO; p@2638: goto release_regions; p@2638: } p@2638: p@2638: ccatdev->bar_2 = pci_iomap(pdev, 2, 0); p@2638: if (!ccatdev->bar_2) { p@2636: pr_warn("initialization of optional bar2 failed.\n"); 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@2638: p@2638: release_regions: p@2638: pci_release_regions(pdev); p@2638: disable_device: p@2636: pci_disable_device(pdev); p@2636: return status; p@2636: } p@2636: p@2636: static void ccat_pci_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@2636: if (ccatdev->bar_2) p@2636: pci_iounmap(pdev, ccatdev->bar_2); p@2636: pci_iounmap(pdev, ccatdev->bar_0); p@2636: pci_release_regions(pdev); p@2550: pci_disable_device(pdev); p@2636: } 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@2640: /* prevent auto-loading. */ fp@2640: /* MODULE_DEVICE_TABLE(pci, pci_ids); */ p@2636: p@2636: static struct pci_driver ccat_pci_driver = { p@2565: .name = KBUILD_MODNAME, p@2550: .id_table = pci_ids, p@2636: .probe = ccat_pci_probe, p@2636: .remove = ccat_pci_remove, p@2550: }; p@2550: p@2636: module_pci_driver(ccat_pci_driver); p@2636: p@2636: #else /* #ifdef CONFIG_PCI */ p@2636: p@2636: static int ccat_eim_probe(struct platform_device *pdev) p@2636: { p@2636: struct ccat_device *ccatdev; p@2636: p@2636: ccatdev = devm_kzalloc(&pdev->dev, sizeof(*ccatdev), GFP_KERNEL); p@2636: if (!ccatdev) { p@2636: pr_err("%s() out of memory.\n", __FUNCTION__); p@2636: return -ENOMEM; p@2636: } p@2636: ccatdev->pdev = pdev; p@2636: platform_set_drvdata(pdev, ccatdev); p@2636: p@2636: if (!request_mem_region(0xf0000000, 0x02000000, pdev->name)) { p@2636: pr_warn("request mem region failed.\n"); p@2636: return -EIO; p@2636: } p@2636: p@2636: if (!(ccatdev->bar_0 = ioremap(0xf0000000, 0x02000000))) { p@2636: pr_warn("initialization of bar0 failed.\n"); p@2636: return -EIO; p@2636: } p@2636: p@2636: ccatdev->bar_2 = NULL; p@2636: p@2636: if (ccat_functions_init(ccatdev)) { p@2636: pr_warn("some functions couldn't be initialized\n"); p@2636: } p@2636: return 0; p@2636: } p@2636: p@2636: static int ccat_eim_remove(struct platform_device *pdev) p@2636: { p@2636: struct ccat_device *ccatdev = platform_get_drvdata(pdev); p@2636: p@2636: if (ccatdev) { p@2636: ccat_functions_remove(ccatdev); p@2636: iounmap(ccatdev->bar_0); p@2636: release_mem_region(0xf0000000, 0x02000000); p@2636: } p@2636: return 0; p@2636: } p@2636: p@2636: static const struct of_device_id bhf_eim_ccat_ids[] = { p@2636: {.compatible = "bhf,emi-ccat",}, p@2636: {} p@2636: }; p@2636: fp@2640: /* prevent auto-loading. */ fp@2640: /* MODULE_DEVICE_TABLE(of, bhf_eim_ccat_ids); */ p@2636: p@2636: static struct platform_driver ccat_eim_driver = { p@2636: .driver = { p@2636: .name = KBUILD_MODNAME, p@2636: .of_match_table = bhf_eim_ccat_ids, p@2636: }, p@2636: .probe = ccat_eim_probe, p@2636: .remove = ccat_eim_remove, p@2636: }; p@2636: p@2636: module_platform_driver(ccat_eim_driver); p@2636: #endif /* #ifdef CONFIG_PCI */