From 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 Mon Sep 17 00:00:00 2001 From: Yunhong Jiang Date: Tue, 4 Aug 2015 12:17:53 -0700 Subject: Add the rt linux 4.1.3-rt3 as base Import the rt linux 4.1.3-rt3 as OPNFV kvm base. It's from git://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-rt-devel.git linux-4.1.y-rt and the base is: commit 0917f823c59692d751951bf5ea699a2d1e2f26a2 Author: Sebastian Andrzej Siewior Date: Sat Jul 25 12:13:34 2015 +0200 Prepare v4.1.3-rt3 Signed-off-by: Sebastian Andrzej Siewior We lose all the git history this way and it's not good. We should apply another opnfv project repo in future. Change-Id: I87543d81c9df70d99c5001fbdf646b202c19f423 Signed-off-by: Yunhong Jiang --- kernel/drivers/staging/dgnc/dgnc_driver.c | 729 ++++++++++++++++++++++++++++++ 1 file changed, 729 insertions(+) create mode 100644 kernel/drivers/staging/dgnc/dgnc_driver.c (limited to 'kernel/drivers/staging/dgnc/dgnc_driver.c') diff --git a/kernel/drivers/staging/dgnc/dgnc_driver.c b/kernel/drivers/staging/dgnc/dgnc_driver.c new file mode 100644 index 000000000..805dc617e --- /dev/null +++ b/kernel/drivers/staging/dgnc/dgnc_driver.c @@ -0,0 +1,729 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + */ + + +#include +#include +#include +#include +#include +#include "dgnc_driver.h" +#include "dgnc_pci.h" +#include "dgnc_mgmt.h" +#include "dgnc_tty.h" +#include "dgnc_cls.h" +#include "dgnc_neo.h" +#include "dgnc_sysfs.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Digi International, http://www.digi.com"); +MODULE_DESCRIPTION("Driver for the Digi International Neo and Classic PCI based product line"); +MODULE_SUPPORTED_DEVICE("dgnc"); + +/************************************************************************** + * + * protos for this file + * + */ +static int dgnc_start(void); +static int dgnc_finalize_board_init(struct dgnc_board *brd); +static void dgnc_init_globals(void); +static int dgnc_found_board(struct pci_dev *pdev, int id); +static void dgnc_cleanup_board(struct dgnc_board *brd); +static void dgnc_poll_handler(ulong dummy); +static int dgnc_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent); +static void dgnc_do_remap(struct dgnc_board *brd); + +/* + * File operations permitted on Control/Management major. + */ +static const struct file_operations dgnc_BoardFops = { + .owner = THIS_MODULE, + .unlocked_ioctl = dgnc_mgmt_ioctl, + .open = dgnc_mgmt_open, + .release = dgnc_mgmt_close +}; + + +/* + * Globals + */ +uint dgnc_NumBoards; +struct dgnc_board *dgnc_Board[MAXBOARDS]; +DEFINE_SPINLOCK(dgnc_global_lock); +uint dgnc_Major; +int dgnc_poll_tick = 20; /* Poll interval - 20 ms */ + +/* + * Static vars. + */ +static struct class *dgnc_class; + +/* + * Poller stuff + */ +static DEFINE_SPINLOCK(dgnc_poll_lock); /* Poll scheduling lock */ +static ulong dgnc_poll_time; /* Time of next poll */ +static uint dgnc_poll_stop; /* Used to tell poller to stop */ +static struct timer_list dgnc_poll_timer; + + +static const struct pci_device_id dgnc_pci_tbl[] = { + {PCI_DEVICE(DIGI_VID, PCI_DEVICE_CLASSIC_4_DID), .driver_data = 0}, + {PCI_DEVICE(DIGI_VID, PCI_DEVICE_CLASSIC_4_422_DID), .driver_data = 1}, + {PCI_DEVICE(DIGI_VID, PCI_DEVICE_CLASSIC_8_DID), .driver_data = 2}, + {PCI_DEVICE(DIGI_VID, PCI_DEVICE_CLASSIC_8_422_DID), .driver_data = 3}, + {0,} +}; +MODULE_DEVICE_TABLE(pci, dgnc_pci_tbl); + +struct board_id { + unsigned char *name; + uint maxports; + unsigned int is_pci_express; +}; + +static struct board_id dgnc_Ids[] = { + { PCI_DEVICE_CLASSIC_4_PCI_NAME, 4, 0 }, + { PCI_DEVICE_CLASSIC_4_422_PCI_NAME, 4, 0 }, + { PCI_DEVICE_CLASSIC_8_PCI_NAME, 8, 0 }, + { PCI_DEVICE_CLASSIC_8_422_PCI_NAME, 8, 0 }, + { PCI_DEVICE_NEO_4_PCI_NAME, 4, 0 }, + { PCI_DEVICE_NEO_8_PCI_NAME, 8, 0 }, + { PCI_DEVICE_NEO_2DB9_PCI_NAME, 2, 0 }, + { PCI_DEVICE_NEO_2DB9PRI_PCI_NAME, 2, 0 }, + { PCI_DEVICE_NEO_2RJ45_PCI_NAME, 2, 0 }, + { PCI_DEVICE_NEO_2RJ45PRI_PCI_NAME, 2, 0 }, + { PCI_DEVICE_NEO_1_422_PCI_NAME, 1, 0 }, + { PCI_DEVICE_NEO_1_422_485_PCI_NAME, 1, 0 }, + { PCI_DEVICE_NEO_2_422_485_PCI_NAME, 2, 0 }, + { PCI_DEVICE_NEO_EXPRESS_8_PCI_NAME, 8, 1 }, + { PCI_DEVICE_NEO_EXPRESS_4_PCI_NAME, 4, 1 }, + { PCI_DEVICE_NEO_EXPRESS_4RJ45_PCI_NAME, 4, 1 }, + { PCI_DEVICE_NEO_EXPRESS_8RJ45_PCI_NAME, 8, 1 }, + { NULL, 0, 0 } +}; + +static struct pci_driver dgnc_driver = { + .name = "dgnc", + .probe = dgnc_init_one, + .id_table = dgnc_pci_tbl, +}; + +/************************************************************************ + * + * Driver load/unload functions + * + ************************************************************************/ + +/* + * dgnc_cleanup_module() + * + * Module unload. This is where it all ends. + */ +static void dgnc_cleanup_module(void) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&dgnc_poll_lock, flags); + dgnc_poll_stop = 1; + spin_unlock_irqrestore(&dgnc_poll_lock, flags); + + /* Turn off poller right away. */ + del_timer_sync(&dgnc_poll_timer); + + dgnc_remove_driver_sysfiles(&dgnc_driver); + + device_destroy(dgnc_class, MKDEV(dgnc_Major, 0)); + class_destroy(dgnc_class); + unregister_chrdev(dgnc_Major, "dgnc"); + + for (i = 0; i < dgnc_NumBoards; ++i) { + dgnc_remove_ports_sysfiles(dgnc_Board[i]); + dgnc_tty_uninit(dgnc_Board[i]); + dgnc_cleanup_board(dgnc_Board[i]); + } + + dgnc_tty_post_uninit(); + + if (dgnc_NumBoards) + pci_unregister_driver(&dgnc_driver); +} + +/* + * init_module() + * + * Module load. This is where it all starts. + */ +static int __init dgnc_init_module(void) +{ + int rc = 0; + + /* + * Initialize global stuff + */ + rc = dgnc_start(); + + if (rc < 0) + return rc; + + /* + * Find and configure all the cards + */ + rc = pci_register_driver(&dgnc_driver); + + /* + * If something went wrong in the scan, bail out of driver. + */ + if (rc < 0) { + /* Only unregister if it was actually registered. */ + if (dgnc_NumBoards) + pci_unregister_driver(&dgnc_driver); + else + pr_warn("WARNING: dgnc driver load failed. No Digi Neo or Classic boards found.\n"); + + dgnc_cleanup_module(); + } else { + dgnc_create_driver_sysfiles(&dgnc_driver); + } + + return rc; +} + +module_init(dgnc_init_module); +module_exit(dgnc_cleanup_module); + +/* + * Start of driver. + */ +static int dgnc_start(void) +{ + int rc = 0; + unsigned long flags; + struct device *dev; + + /* make sure that the globals are init'd before we do anything else */ + dgnc_init_globals(); + + /* + * Register our base character device into the kernel. + * This allows the download daemon to connect to the downld device + * before any of the boards are init'ed. + * + * Register management/dpa devices + */ + rc = register_chrdev(0, "dgnc", &dgnc_BoardFops); + if (rc < 0) { + pr_err(DRVSTR ": Can't register dgnc driver device (%d)\n", rc); + return rc; + } + dgnc_Major = rc; + + dgnc_class = class_create(THIS_MODULE, "dgnc_mgmt"); + if (IS_ERR(dgnc_class)) { + rc = PTR_ERR(dgnc_class); + pr_err(DRVSTR ": Can't create dgnc_mgmt class (%d)\n", rc); + goto failed_class; + } + + dev = device_create(dgnc_class, NULL, + MKDEV(dgnc_Major, 0), + NULL, "dgnc_mgmt"); + if (IS_ERR(dev)) { + rc = PTR_ERR(dev); + pr_err(DRVSTR ": Can't create device (%d)\n", rc); + goto failed_device; + } + + /* + * Init any global tty stuff. + */ + rc = dgnc_tty_preinit(); + + if (rc < 0) { + pr_err(DRVSTR ": tty preinit - not enough memory (%d)\n", rc); + goto failed_tty; + } + + /* Start the poller */ + spin_lock_irqsave(&dgnc_poll_lock, flags); + setup_timer(&dgnc_poll_timer, dgnc_poll_handler, 0); + dgnc_poll_time = jiffies + dgnc_jiffies_from_ms(dgnc_poll_tick); + dgnc_poll_timer.expires = dgnc_poll_time; + spin_unlock_irqrestore(&dgnc_poll_lock, flags); + + add_timer(&dgnc_poll_timer); + + return 0; + +failed_tty: + device_destroy(dgnc_class, MKDEV(dgnc_Major, 0)); +failed_device: + class_destroy(dgnc_class); +failed_class: + unregister_chrdev(dgnc_Major, "dgnc"); + return rc; +} + +/* returns count (>= 0), or negative on error */ +static int dgnc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int rc; + + /* wake up and enable device */ + rc = pci_enable_device(pdev); + + if (rc < 0) { + rc = -EIO; + } else { + rc = dgnc_found_board(pdev, ent->driver_data); + if (rc == 0) + dgnc_NumBoards++; + } + return rc; +} + +/* + * dgnc_cleanup_board() + * + * Free all the memory associated with a board + */ +static void dgnc_cleanup_board(struct dgnc_board *brd) +{ + int i = 0; + + if (!brd || brd->magic != DGNC_BOARD_MAGIC) + return; + + switch (brd->device) { + case PCI_DEVICE_CLASSIC_4_DID: + case PCI_DEVICE_CLASSIC_8_DID: + case PCI_DEVICE_CLASSIC_4_422_DID: + case PCI_DEVICE_CLASSIC_8_422_DID: + + /* Tell card not to interrupt anymore. */ + outb(0, brd->iobase + 0x4c); + break; + + default: + break; + } + + if (brd->irq) + free_irq(brd->irq, brd); + + tasklet_kill(&brd->helper_tasklet); + + if (brd->re_map_membase) { + iounmap(brd->re_map_membase); + brd->re_map_membase = NULL; + } + + if (brd->msgbuf_head) { + unsigned long flags; + + spin_lock_irqsave(&dgnc_global_lock, flags); + brd->msgbuf = NULL; + dev_dbg(&brd->pdev->dev, "%s\n", brd->msgbuf_head); + kfree(brd->msgbuf_head); + brd->msgbuf_head = NULL; + spin_unlock_irqrestore(&dgnc_global_lock, flags); + } + + /* Free all allocated channels structs */ + for (i = 0; i < MAXPORTS ; i++) { + if (brd->channels[i]) { + kfree(brd->channels[i]->ch_rqueue); + kfree(brd->channels[i]->ch_equeue); + kfree(brd->channels[i]->ch_wqueue); + kfree(brd->channels[i]); + brd->channels[i] = NULL; + } + } + + kfree(brd->flipbuf); + + dgnc_Board[brd->boardnum] = NULL; + + kfree(brd); +} + + +/* + * dgnc_found_board() + * + * A board has been found, init it. + */ +static int dgnc_found_board(struct pci_dev *pdev, int id) +{ + struct dgnc_board *brd; + unsigned int pci_irq; + int i = 0; + int rc = 0; + unsigned long flags; + + /* get the board structure and prep it */ + dgnc_Board[dgnc_NumBoards] = kzalloc(sizeof(*brd), GFP_KERNEL); + brd = dgnc_Board[dgnc_NumBoards]; + + if (!brd) + return -ENOMEM; + + /* make a temporary message buffer for the boot messages */ + brd->msgbuf_head = kcalloc(8192, sizeof(u8), GFP_KERNEL); + brd->msgbuf = brd->msgbuf_head; + + if (!brd->msgbuf) { + kfree(brd); + return -ENOMEM; + } + + /* store the info for the board we've found */ + brd->magic = DGNC_BOARD_MAGIC; + brd->boardnum = dgnc_NumBoards; + brd->vendor = dgnc_pci_tbl[id].vendor; + brd->device = dgnc_pci_tbl[id].device; + brd->pdev = pdev; + brd->pci_bus = pdev->bus->number; + brd->pci_slot = PCI_SLOT(pdev->devfn); + brd->name = dgnc_Ids[id].name; + brd->maxports = dgnc_Ids[id].maxports; + if (dgnc_Ids[i].is_pci_express) + brd->bd_flags |= BD_IS_PCI_EXPRESS; + brd->dpastatus = BD_NOFEP; + init_waitqueue_head(&brd->state_wait); + + spin_lock_init(&brd->bd_lock); + spin_lock_init(&brd->bd_intr_lock); + + brd->state = BOARD_FOUND; + + for (i = 0; i < MAXPORTS; i++) + brd->channels[i] = NULL; + + /* store which card & revision we have */ + pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &brd->subvendor); + pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &brd->subdevice); + pci_read_config_byte(pdev, PCI_REVISION_ID, &brd->rev); + + pci_irq = pdev->irq; + brd->irq = pci_irq; + + + switch (brd->device) { + + case PCI_DEVICE_CLASSIC_4_DID: + case PCI_DEVICE_CLASSIC_8_DID: + case PCI_DEVICE_CLASSIC_4_422_DID: + case PCI_DEVICE_CLASSIC_8_422_DID: + + brd->dpatype = T_CLASSIC | T_PCIBUS; + + /* + * For PCI ClassicBoards + * PCI Local Address (i.e. "resource" number) space + * 0 PLX Memory Mapped Config + * 1 PLX I/O Mapped Config + * 2 I/O Mapped UARTs and Status + * 3 Memory Mapped VPD + * 4 Memory Mapped UARTs and Status + */ + + + /* get the PCI Base Address Registers */ + brd->membase = pci_resource_start(pdev, 4); + + if (!brd->membase) { + dev_err(&brd->pdev->dev, + "Card has no PCI IO resources, failing.\n"); + return -ENODEV; + } + + brd->membase_end = pci_resource_end(pdev, 4); + + if (brd->membase & 1) + brd->membase &= ~3; + else + brd->membase &= ~15; + + brd->iobase = pci_resource_start(pdev, 1); + brd->iobase_end = pci_resource_end(pdev, 1); + brd->iobase = ((unsigned int) (brd->iobase)) & 0xFFFE; + + /* Assign the board_ops struct */ + brd->bd_ops = &dgnc_cls_ops; + + brd->bd_uart_offset = 0x8; + brd->bd_dividend = 921600; + + dgnc_do_remap(brd); + + /* Get and store the board VPD, if it exists */ + brd->bd_ops->vpd(brd); + + /* + * Enable Local Interrupt 1 (0x1), + * Local Interrupt 1 Polarity Active high (0x2), + * Enable PCI interrupt (0x40) + */ + outb(0x43, brd->iobase + 0x4c); + + break; + + + case PCI_DEVICE_NEO_4_DID: + case PCI_DEVICE_NEO_8_DID: + case PCI_DEVICE_NEO_2DB9_DID: + case PCI_DEVICE_NEO_2DB9PRI_DID: + case PCI_DEVICE_NEO_2RJ45_DID: + case PCI_DEVICE_NEO_2RJ45PRI_DID: + case PCI_DEVICE_NEO_1_422_DID: + case PCI_DEVICE_NEO_1_422_485_DID: + case PCI_DEVICE_NEO_2_422_485_DID: + case PCI_DEVICE_NEO_EXPRESS_8_DID: + case PCI_DEVICE_NEO_EXPRESS_4_DID: + case PCI_DEVICE_NEO_EXPRESS_4RJ45_DID: + case PCI_DEVICE_NEO_EXPRESS_8RJ45_DID: + + /* + * This chip is set up 100% when we get to it. + * No need to enable global interrupts or anything. + */ + if (brd->bd_flags & BD_IS_PCI_EXPRESS) + brd->dpatype = T_NEO_EXPRESS | T_PCIBUS; + else + brd->dpatype = T_NEO | T_PCIBUS; + + /* get the PCI Base Address Registers */ + brd->membase = pci_resource_start(pdev, 0); + brd->membase_end = pci_resource_end(pdev, 0); + + if (brd->membase & 1) + brd->membase &= ~3; + else + brd->membase &= ~15; + + /* Assign the board_ops struct */ + brd->bd_ops = &dgnc_neo_ops; + + brd->bd_uart_offset = 0x200; + brd->bd_dividend = 921600; + + dgnc_do_remap(brd); + + if (brd->re_map_membase) { + + /* Read and store the dvid after remapping */ + brd->dvid = readb(brd->re_map_membase + 0x8D); + + /* Get and store the board VPD, if it exists */ + brd->bd_ops->vpd(brd); + } + break; + + default: + dev_err(&brd->pdev->dev, + "Didn't find any compatible Neo/Classic PCI boards.\n"); + return -ENXIO; + + } + + /* + * Do tty device initialization. + */ + + rc = dgnc_tty_register(brd); + if (rc < 0) { + pr_err(DRVSTR ": Can't register tty devices (%d)\n", rc); + goto failed; + } + + rc = dgnc_finalize_board_init(brd); + if (rc < 0) { + pr_err(DRVSTR ": Can't finalize board init (%d)\n", rc); + goto failed; + } + + rc = dgnc_tty_init(brd); + if (rc < 0) { + pr_err(DRVSTR ": Can't init tty devices (%d)\n", rc); + goto failed; + } + + brd->state = BOARD_READY; + brd->dpastatus = BD_RUNNING; + + dgnc_create_ports_sysfiles(brd); + + /* init our poll helper tasklet */ + tasklet_init(&brd->helper_tasklet, + brd->bd_ops->tasklet, + (unsigned long) brd); + + spin_lock_irqsave(&dgnc_global_lock, flags); + brd->msgbuf = NULL; + dev_dbg(&brd->pdev->dev, "%s\n", brd->msgbuf_head); + kfree(brd->msgbuf_head); + brd->msgbuf_head = NULL; + spin_unlock_irqrestore(&dgnc_global_lock, flags); + + /* + * allocate flip buffer for board. + * + * Okay to malloc with GFP_KERNEL, we are not at interrupt + * context, and there are no locks held. + */ + brd->flipbuf = kzalloc(MYFLIPLEN, GFP_KERNEL); + + wake_up_interruptible(&brd->state_wait); + + return 0; + +failed: + dgnc_tty_uninit(brd); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + + return -ENXIO; + +} + + +static int dgnc_finalize_board_init(struct dgnc_board *brd) +{ + int rc = 0; + + if (!brd || brd->magic != DGNC_BOARD_MAGIC) + return -ENODEV; + + if (brd->irq) { + rc = request_irq(brd->irq, brd->bd_ops->intr, + IRQF_SHARED, "DGNC", brd); + + if (rc) { + dev_err(&brd->pdev->dev, + "Failed to hook IRQ %d\n", brd->irq); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + rc = -ENODEV; + } + } + return rc; +} + +/* + * Remap PCI memory. + */ +static void dgnc_do_remap(struct dgnc_board *brd) +{ + + if (!brd || brd->magic != DGNC_BOARD_MAGIC) + return; + + brd->re_map_membase = ioremap(brd->membase, 0x1000); +} + + +/***************************************************************************** +* +* Function: +* +* dgnc_poll_handler +* +* Author: +* +* Scott H Kilau +* +* Parameters: +* +* dummy -- ignored +* +* Return Values: +* +* none +* +* Description: +* +* As each timer expires, it determines (a) whether the "transmit" +* waiter needs to be woken up, and (b) whether the poller needs to +* be rescheduled. +* +******************************************************************************/ + +static void dgnc_poll_handler(ulong dummy) +{ + struct dgnc_board *brd; + unsigned long flags; + int i; + unsigned long new_time; + + /* Go thru each board, kicking off a tasklet for each if needed */ + for (i = 0; i < dgnc_NumBoards; i++) { + brd = dgnc_Board[i]; + + spin_lock_irqsave(&brd->bd_lock, flags); + + /* If board is in a failed state don't schedule a tasklet */ + if (brd->state == BOARD_FAILED) { + spin_unlock_irqrestore(&brd->bd_lock, flags); + continue; + } + + /* Schedule a poll helper task */ + tasklet_schedule(&brd->helper_tasklet); + + spin_unlock_irqrestore(&brd->bd_lock, flags); + } + + /* + * Schedule ourself back at the nominal wakeup interval. + */ + spin_lock_irqsave(&dgnc_poll_lock, flags); + dgnc_poll_time += dgnc_jiffies_from_ms(dgnc_poll_tick); + + new_time = dgnc_poll_time - jiffies; + + if ((ulong) new_time >= 2 * dgnc_poll_tick) + dgnc_poll_time = jiffies + dgnc_jiffies_from_ms(dgnc_poll_tick); + + setup_timer(&dgnc_poll_timer, dgnc_poll_handler, 0); + dgnc_poll_timer.expires = dgnc_poll_time; + spin_unlock_irqrestore(&dgnc_poll_lock, flags); + + if (!dgnc_poll_stop) + add_timer(&dgnc_poll_timer); +} + +/* + * dgnc_init_globals() + * + * This is where we initialize the globals from the static insmod + * configuration variables. These are declared near the head of + * this file. + */ +static void dgnc_init_globals(void) +{ + int i = 0; + + dgnc_NumBoards = 0; + + for (i = 0; i < MAXBOARDS; i++) + dgnc_Board[i] = NULL; + + init_timer(&dgnc_poll_timer); +} + -- cgit 1.2.3-korg