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 */
fp@2684: #ifdef CONFIG_GPIO
p@2636: &gpio_driver, /* load GPIO driver from gpio.c */
fp@2684: #endif
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 */