[PATCH] Add driver for SUNIX Mutli-I/O board

Morris Ku <[email protected]>
Newsgroups gmane.linux.parport
Message-ID <[email protected]>
hi,Kai-Heng,
Thanks for review, my replies are inline:

Signed-off-by: Morris Ku <[email protected]>

>at 5:58 PM, morrisku <[email protected]> wrote:
 
>> This driver support SUNIX multi-io driver.
>>
>> Signed-off-by: Morris Ku <[email protected]>
>> ---
>>  sunix_pci_multi_io.c | 409 +++++++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 409 insertions(+)
>>  create mode 100644 sunix_pci_multi_io.c
>>
>> diff --git a/sunix_pci_multi_io.c b/sunix_pci_multi_io.c

>Signed-off-by: Morris Ku <[email protected]>
>---
> sunix_pci_multi_io.c | 409 +++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 409 insertions(+)
> create mode 100644 sunix_pci_multi_io.c
>Where do I put this driver under? drivers/mfd?

i put the driver under /drivers/parport,
if you recommend that put driver under /driver/mfd,
i will send the a new patch.

>diff --git a/driver/parport/sunix_pci_multi_io.c b/driver/parport/sunix_pci_multi_io.c
>new file mode 100644
>index 0000000..c863985
>--- /dev/null
>+++ b/driver/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

>> new file mode 100644
>> index 0000000..c863985
>> --- /dev/null
>> +++ b/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);

>Have you tested hibernate (S4)?

i had tested suspend (s3) and hibernate (s4)

>Kai-Heng


>> +
>> +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
-- 
2.17.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.