[PATCH] Add driver for SUNIX Multi-I/O board
morrisku <[email protected]>
| Newsgroups | gmane.linux.parport |
|---|---|
| Message-ID | <[email protected]> |
This patch add support for SUNIX Multi-I/O board. Signed-off-by: morrisku <[email protected]> --- parport/Kconfig | 8 + parport/Makefile | 1 + parport/sunix_pci_multi_io.c | 409 +++++++++++++++++++++++++++++++++++ 3 files changed, 418 insertions(+) create mode 100644 parport/sunix_pci_multi_io.c diff --git a/parport/Kconfig b/parport/Kconfig index 24189c33..b54ad2c1 100644 --- a/parport/Kconfig +++ b/parport/Kconfig @@ -151,6 +151,14 @@ config PARPORT_AX88796 The driver is not dependent on the AX88796 network driver, and should not interfere with the networking functions of the chip. +config SUNIX_PCI_MULTI_IO + tristate "SUNIX pci Multi-IO cards" + depends on PARPORT_PC && SERIAL_8250 + help + This adds support for SUNIX multi-IO cards that have parallel and + serial ports. You should say Y or M here. If you say M, the module + will be called sunix_pci_multi_io. + config PARPORT_1284 bool "IEEE 1284 transfer modes" help diff --git a/parport/Makefile b/parport/Makefile index 4e1baed5..774f8d76 100644 --- a/parport/Makefile +++ b/parport/Makefile @@ -20,5 +20,6 @@ obj-$(CONFIG_PARPORT_SUNBPP) += parport_sunbpp.o obj-$(CONFIG_PARPORT_GSC) += parport_gsc.o obj-$(CONFIG_PARPORT_AX88796) += parport_ax88796.o obj-$(CONFIG_PARPORT_IP32) += parport_ip32.o +obj-$(CONFIG_SUNIX_PCI_MULTI_IO) += sunix_pci_multi_io.o diff --git a/parport/sunix_pci_multi_io.c b/parport/sunix_pci_multi_io.c new file mode 100644 index 00000000..c863985f --- /dev/null +++ b/parport/sunix_pci_multi_io.c @@ -0,0 +1,409 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for common PCI multi-I/O cards (which is most of them) + * + * Copyright (C) 2001 Tim Waugh <[email protected]> + * + * 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 of the License, or (at your option) any later version. + * + * + * Driver for SUNIX PCI multi-I/O board + * Based on + * drivers/tty/parport/parport_serial.c + * drivers/tty/serial/8250/8250_pci.c, by Linus Torvalds, + * Theodore Ts'o. + * + * 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 of the License. + * + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/parport.h> +#include <linux/parport_pc.h> +#include <linux/8250_pci.h> +#include <linux/serial_8250.h> + +#define PCI_VENDOR_ID_SUNIX 0x1fd4 +#define PCI_DEVICE_ID_SUNIX_1999 0x1999 + +struct sunix_pci_board { + kernel_ulong_t driver_data; + struct pci_dev *pdev; + unsigned int num_ports; + int line[0]; +}; + +enum parport_pc_pci_cards { + sunix_4008a = 0, + sunix_5069a, + sunix_5079a, + sunix_5099a, +}; + +static struct sunix_pci_board sunix_pci_boards[] = { + [sunix_4008a] = {.num_ports = 0}, + [sunix_5069a] = {.num_ports = 1}, + [sunix_5079a] = {.num_ports = 2}, + [sunix_5099a] = {.num_ports = 4}, +}; + +struct sunix_parport_pc_pci { + int numports; + struct { + int lo; + int hi; + } addr[4]; + + int (*preinit_hook)(struct pci_dev *pdev, + struct sunix_parport_pc_pci *card, int autoirq, int autodma); + + void (*postinit_hook)(struct pci_dev *pdev, + struct sunix_parport_pc_pci *card, int failed); +}; + +static struct sunix_parport_pc_pci cards[] = { + /* sunix_4008a */ { 1, { { 1, 2 }, } }, + /* sunix_5069a */ { 1, { { 1, 2 }, } }, + /* sunix_5079a */ { 1, { { 1, 2 }, } }, + /* sunix_5099a */ { 1, { { 1, 2 }, } }, +}; + +struct parport_serial_private { + struct serial_private *serial; + int num_par; + struct parport *port[PARPORT_MAX]; + struct sunix_parport_pc_pci par; + + kernel_ulong_t driver_data; + struct pci_dev *pdev; + unsigned int num_ports; + int line[0]; + +}; + + +static struct pci_device_id sunix_parport_serial_pci_tbl[] = { + /* PCI cards */ + { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, + 0x0100, 0, 0, sunix_4008a }, + { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, + 0x0101, 0, 0, sunix_5069a }, + { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, + 0x0102, 0, 0, sunix_5079a }, + { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, PCI_VENDOR_ID_SUNIX, + 0x0104, 0, 0, sunix_5099a }, + { 0, } /* terminate list */ +}; +MODULE_DEVICE_TABLE(pci, sunix_parport_serial_pci_tbl); + +struct parport_serial_private *snx_init_ports(struct pci_dev *pdev, + kernel_ulong_t driver_data) +{ + struct uart_8250_port uart; + struct parport_serial_private *priv = pci_get_drvdata(pdev); + unsigned int num_ports; + int i, bar, offset; + + num_ports = sunix_pci_boards[driver_data].num_ports; + + priv->driver_data = driver_data; + priv->pdev = pdev; + priv->num_ports = num_ports; + + memset(&uart, 0, sizeof(uart)); + uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | + UPF_SHARE_IRQ | UPF_FIXED_TYPE; + uart.port.uartclk = 921600 * 16; + uart.port.irq = pdev->irq; + uart.port.dev = &pdev->dev; + uart.port.type = PORT_16C950; + uart.port.fifosize = 128; + + for (i = 0; i < num_ports; i++) { + + switch (i) { + case 0: + bar = 0; offset = 0; break; + case 1: + bar = 0; offset = 8; break; + case 2: + bar = 0; offset = 16; break; + case 3: + bar = 0; offset = 24; break; + case 4: + bar = 1; offset = 0; break; + case 5: + bar = 1; offset = 8; break; + case 6: + bar = 1; offset = 16; break; + case 7: + bar = 1; offset = 24; break; + case 8: + bar = 1; offset = 64; break; + case 9: + bar = 1; offset = 72; break; + case 10: + bar = 1; offset = 80; break; + case 11: + bar = 1; offset = 88; break; + case 12: + bar = 1; offset = 128; break; + case 13: + bar = 1; offset = 136; break; + case 14: + bar = 1; offset = 144; break; + case 15: + bar = 1; offset = 152; break; + } + + uart.port.iotype = UPIO_PORT; + uart.port.iobase = pci_resource_start(pdev, bar) + offset; + uart.port.mapbase = 0; + uart.port.membase = NULL; + uart.port.regshift = 0; + + priv->line[i] = serial8250_register_8250_port(&uart); + + if (priv->line[i] < 0) { + printk(KERN_INFO "sunix_pci_serial : Couldn't register serial port %s: %d\n", + pci_name(pdev), priv->line[i]); + break; + } + } + + return priv; +} + +static int serial_register(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct parport_serial_private *priv; + int rc; + + if (ent->driver_data >= ARRAY_SIZE(sunix_pci_boards)) { + printk(KERN_INFO "sunix_pci_serial : Invalid driver_data:%ld\n", + ent->driver_data); + return -EINVAL; + } + + pci_save_state(pdev); + + priv = snx_init_ports(pdev, ent->driver_data); + if (IS_ERR(priv)) { + rc = PTR_ERR(priv); + return rc; + } + + return 0; +} + +static int parport_register(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct sunix_parport_pc_pci *card; + struct parport_serial_private *priv = pci_get_drvdata(dev); + int n, success = 0; + + priv->par = cards[id->driver_data]; + card = &priv->par; + + if (card->preinit_hook && + card->preinit_hook(dev, card, PARPORT_IRQ_NONE, PARPORT_DMA_NONE)) + return -ENODEV; + + for (n = 0; n < card->numports; n++) { + struct parport *port; + int lo = card->addr[n].lo; + int hi = card->addr[n].hi; + unsigned long io_lo, io_hi; + int irq; + + if (priv->num_par == ARRAY_SIZE(priv->port)) { + printk(KERN_INFO "parport_serial: %s:", pci_name(dev)); + printk(KERN_INFO "only %zu parallel ports supported", + ARRAY_SIZE(priv->port)); + printk(KERN_INFO "%d reported)\n", card->numports); + break; + } + + io_lo = pci_resource_start(dev, lo); + io_hi = 0; + + if ((hi >= 0) && (hi <= 6)) + io_hi = pci_resource_start(dev, hi); + else if (hi > 6) + io_lo += hi; + + irq = dev->irq; + if (irq == IRQ_NONE) { + dev_dbg(&dev->dev, + "PCI parallel port detected 1: I/O at %#lx(%#lx)\n", + io_lo, io_hi); + irq = PARPORT_IRQ_NONE; + } else { + dev_dbg(&dev->dev, + "PCI parallel port detected 2: I/O at %#lx(%#lx), IRQ %d\n", + io_lo, io_hi, irq); + } + port = parport_pc_probe_port(io_lo, io_hi, irq, + PARPORT_DMA_NONE, &dev->dev, IRQF_SHARED); + + if (port) { + priv->port[priv->num_par++] = port; + success = 1; + } + } + + if (card->postinit_hook) + card->postinit_hook(dev, card, !success); + + return 0; +} + +static int sunix_parport_serial_pci_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct parport_serial_private *priv; + int err; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + pci_set_drvdata(dev, priv); + + err = pci_enable_device(dev); + + if (err) { + kfree(priv); + return err; + } + + if (parport_register(dev, id)) { + kfree(priv); + return -ENODEV; + } + + if (serial_register(dev, id)) { + int i; + + for (i = 0; i < priv->num_par; i++) + parport_pc_unregister_port(priv->port[i]); + + kfree(priv); + return -ENODEV; + } + + return 0; +} + +void snx_detach_ports(struct parport_serial_private *board) +{ + int i; + + for (i = 0; i < board->num_ports; i++) + serial8250_unregister_port(board->line[i]); +} + +void snx_remove_ports(struct parport_serial_private *board) +{ + snx_detach_ports(board); +} + +static void sunix_parport_serial_pci_remove(struct pci_dev *dev) +{ + int i; + struct parport_serial_private *priv = pci_get_drvdata(dev); + + snx_remove_ports(priv); + + for (i = 0; i < priv->num_par; i++) + parport_pc_unregister_port(priv->port[i]); + + kfree(priv); +} + +void snx_suspend_ports(struct parport_serial_private *board) +{ + int i; + + for (i = 0; i < board->num_ports; i++) { + + if (board->line[i] >= 0) + serial8250_suspend_port(board->line[i]); + } +} + +void snx_resume_ports(struct parport_serial_private *board) +{ + + int i; + + for (i = 0; i < board->num_ports; i++) { + + if (board->line[i] >= 0) + serial8250_resume_port(board->line[i]); + } +} + +#ifdef CONFIG_PM_SLEEP +static int snx_suspend_one(struct device *dev) +{ + + struct pci_dev *pdev = to_pci_dev(dev); + struct parport_serial_private *board = pci_get_drvdata(pdev); + + if (board) + snx_suspend_ports(board); + + return 0; +} + + +static int snx_resume_one(struct device *dev) +{ + + struct pci_dev *pdev = to_pci_dev(dev); + struct parport_serial_private *board = pci_get_drvdata(pdev); + int err; + + if (board) { + err = pci_enable_device(pdev); + + if (err) + printk(KERN_INFO "sunix_pci_serial : Unable to re-enable ports\n"); + + snx_resume_ports(board); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(sunix_pci_serial_pm_ops, + snx_suspend_one, snx_resume_one); + +static struct pci_driver sunix_multi_io_driver = { + .name = "sunix_multi_io", + .id_table = sunix_parport_serial_pci_tbl, + .probe = sunix_parport_serial_pci_probe, + .remove = sunix_parport_serial_pci_remove, + .driver = { + .pm = &sunix_pci_serial_pm_ops, + }, +}; + +module_pci_driver(sunix_multi_io_driver); + +MODULE_AUTHOR("SUNIX Co., Ltd.<[email protected]>"); +MODULE_DESCRIPTION("SUNIX PCI multi-I/O board driver"); +MODULE_LICENSE("GPL"); -- 2.17.1