Commit 241fc436 authored by Russell King's avatar Russell King Committed by Russell King
Browse files

[SERIAL] Expose 8250_pci setup/removal/suspend/resume functions



Re-jig the setup/removal/suspend/resume of 8250 pci ports so that they
know slightly less about how they're attached to a PCI device.  Expose
this as the new interface for registering PCI serial ports, as well as
the pciserial_board structure and associated flag definitions.

Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 70db3d91
Loading
Loading
Loading
Loading
+121 −112
Original line number Diff line number Diff line
@@ -33,38 +33,6 @@

#undef SERIAL_DEBUG_PCI

/*
 * Definitions for PCI support.
 */
#define FL_BASE_MASK		0x0007
#define FL_BASE0		0x0000
#define FL_BASE1		0x0001
#define FL_BASE2		0x0002
#define FL_BASE3		0x0003
#define FL_BASE4		0x0004
#define FL_GET_BASE(x)		(x & FL_BASE_MASK)

/* Use successive BARs (PCI base address registers),
   else use offset into some specified BAR */
#define FL_BASE_BARS		0x0008

/* do not assign an irq */
#define FL_NOIRQ		0x0080

/* Use the Base address register size to cap number of ports */
#define FL_REGION_SZ_CAP	0x0100

struct pciserial_board {
	unsigned int flags;
	unsigned int num_ports;
	unsigned int base_baud;
	unsigned int uart_offset;
	unsigned int reg_shift;
	unsigned int first_offset;
};

struct serial_private;

/*
 * init function returns:
 *  > 0 - number of ports
@@ -1528,60 +1496,14 @@ serial_pci_matches(struct pciserial_board *board,
	    board->first_offset == guessed->first_offset;
}

/*
 * Probe one serial board.  Unfortunately, there is no rhyme nor reason
 * to the arrangement of serial ports on a PCI card.
 */
static int __devinit
pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
struct serial_private *
pciserial_init_ports(struct pci_dev *dev, struct pciserial_board *board)
{
	struct uart_port serial_port;
	struct serial_private *priv;
	struct pciserial_board *board, tmp;
	struct pci_serial_quirk *quirk;
	int rc, nr_ports, i;

	if (ent->driver_data >= ARRAY_SIZE(pci_boards)) {
		printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n",
			ent->driver_data);
		return -EINVAL;
	}

	board = &pci_boards[ent->driver_data];

	rc = pci_enable_device(dev);
	if (rc)
		return rc;

	if (ent->driver_data == pbn_default) {
		/*
		 * Use a copy of the pci_board entry for this;
		 * avoid changing entries in the table.
		 */
		memcpy(&tmp, board, sizeof(struct pciserial_board));
		board = &tmp;

		/*
		 * We matched one of our class entries.  Try to
		 * determine the parameters of this board.
		 */
		rc = serial_pci_guess_board(dev, board);
		if (rc)
			goto disable;
	} else {
		/*
		 * We matched an explicit entry.  If we are able to
		 * detect this boards settings with our heuristic,
		 * then we no longer need this entry.
		 */
		memcpy(&tmp, &pci_boards[pbn_default],
		       sizeof(struct pciserial_board));
		rc = serial_pci_guess_board(dev, &tmp);
		if (rc == 0 && serial_pci_matches(board, &tmp))
			moan_device("Redundant entry in serial pci_table.",
				    dev);
	}

	nr_ports = board->num_ports;

	/*
@@ -1598,8 +1520,10 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
	 */
	if (quirk->init) {
		rc = quirk->init(dev);
		if (rc < 0)
			goto disable;
		if (rc < 0) {
			priv = ERR_PTR(rc);
			goto err_out;
		}
		if (rc)
			nr_ports = rc;
	}
@@ -1608,8 +1532,8 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
		       sizeof(unsigned int) * nr_ports,
		       GFP_KERNEL);
	if (!priv) {
		rc = -ENOMEM;
		goto deinit;
		priv = ERR_PTR(-ENOMEM);
		goto err_deinit;
	}

	memset(priv, 0, sizeof(struct serial_private) +
@@ -1617,7 +1541,6 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent)

	priv->dev = dev;
	priv->quirk = quirk;
	pci_set_drvdata(dev, priv);

	memset(&serial_port, 0, sizeof(struct uart_port));
	serial_port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
@@ -1643,24 +1566,21 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent)

	priv->nr = i;

	return 0;
	return priv;

 deinit:
 err_deinit:
	if (quirk->exit)
		quirk->exit(dev);
 disable:
	pci_disable_device(dev);
	return rc;
 err_out:
	return priv;
}
EXPORT_SYMBOL_GPL(pciserial_init_ports);

static void __devexit pciserial_remove_one(struct pci_dev *dev)
void pciserial_remove_ports(struct serial_private *priv)
{
	struct serial_private *priv = pci_get_drvdata(dev);
	struct pci_serial_quirk *quirk;
	int i;

	pci_set_drvdata(dev, NULL);

	for (i = 0; i < priv->nr; i++)
		serial8250_unregister_port(priv->line[i]);

@@ -1673,25 +1593,123 @@ static void __devexit pciserial_remove_one(struct pci_dev *dev)
	/*
	 * Find the exit quirks.
	 */
	quirk = find_quirk(dev);
	quirk = find_quirk(priv->dev);
	if (quirk->exit)
		quirk->exit(dev);

	pci_disable_device(dev);
		quirk->exit(priv->dev);

	kfree(priv);
}
EXPORT_SYMBOL_GPL(pciserial_remove_ports);

static int pciserial_suspend_one(struct pci_dev *dev, pm_message_t state)
void pciserial_suspend_ports(struct serial_private *priv)
{
	struct serial_private *priv = pci_get_drvdata(dev);

	if (priv) {
	int i;

	for (i = 0; i < priv->nr; i++)
		if (priv->line[i] >= 0)
			serial8250_suspend_port(priv->line[i]);
}
EXPORT_SYMBOL_GPL(pciserial_suspend_ports);

void pciserial_resume_ports(struct serial_private *priv)
{
	int i;

	/*
	 * Ensure that the board is correctly configured.
	 */
	if (priv->quirk->init)
		priv->quirk->init(priv->dev);

	for (i = 0; i < priv->nr; i++)
		if (priv->line[i] >= 0)
			serial8250_resume_port(priv->line[i]);
}
EXPORT_SYMBOL_GPL(pciserial_resume_ports);

/*
 * Probe one serial board.  Unfortunately, there is no rhyme nor reason
 * to the arrangement of serial ports on a PCI card.
 */
static int __devinit
pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
{
	struct serial_private *priv;
	struct pciserial_board *board, tmp;
	int rc;

	if (ent->driver_data >= ARRAY_SIZE(pci_boards)) {
		printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n",
			ent->driver_data);
		return -EINVAL;
	}

	board = &pci_boards[ent->driver_data];

	rc = pci_enable_device(dev);
	if (rc)
		return rc;

	if (ent->driver_data == pbn_default) {
		/*
		 * Use a copy of the pci_board entry for this;
		 * avoid changing entries in the table.
		 */
		memcpy(&tmp, board, sizeof(struct pciserial_board));
		board = &tmp;

		/*
		 * We matched one of our class entries.  Try to
		 * determine the parameters of this board.
		 */
		rc = serial_pci_guess_board(dev, board);
		if (rc)
			goto disable;
	} else {
		/*
		 * We matched an explicit entry.  If we are able to
		 * detect this boards settings with our heuristic,
		 * then we no longer need this entry.
		 */
		memcpy(&tmp, &pci_boards[pbn_default],
		       sizeof(struct pciserial_board));
		rc = serial_pci_guess_board(dev, &tmp);
		if (rc == 0 && serial_pci_matches(board, &tmp))
			moan_device("Redundant entry in serial pci_table.",
				    dev);
	}

	priv = pciserial_init_ports(dev, board);
	if (!IS_ERR(priv)) {
		pci_set_drvdata(dev, priv);
		return 0;
	}

	rc = PTR_ERR(priv);

 disable:
	pci_disable_device(dev);
	return rc;
}

static void __devexit pciserial_remove_one(struct pci_dev *dev)
{
	struct serial_private *priv = pci_get_drvdata(dev);

	pci_set_drvdata(dev, NULL);

	pciserial_remove_ports(priv);

	pci_disable_device(dev);
}

static int pciserial_suspend_one(struct pci_dev *dev, pm_message_t state)
{
	struct serial_private *priv = pci_get_drvdata(dev);

	if (priv)
		pciserial_suspend_ports(priv);

	pci_save_state(dev);
	pci_set_power_state(dev, pci_choose_state(dev, state));
	return 0;
@@ -1705,21 +1723,12 @@ static int pciserial_resume_one(struct pci_dev *dev)
	pci_restore_state(dev);

	if (priv) {
		int i;

		/*
		 * The device may have been disabled.  Re-enable it.
		 */
		pci_enable_device(dev);

		/*
		 * Ensure that the board is correctly configured.
		 */
		if (priv->quirk->init)
			priv->quirk->init(dev);

		for (i = 0; i < priv->nr; i++)
			serial8250_resume_port(priv->line[i]);
		pciserial_resume_ports(priv);
	}
	return 0;
}
+38 −0
Original line number Diff line number Diff line
/*
 * Definitions for PCI support.
 */
#define FL_BASE_MASK		0x0007
#define FL_BASE0		0x0000
#define FL_BASE1		0x0001
#define FL_BASE2		0x0002
#define FL_BASE3		0x0003
#define FL_BASE4		0x0004
#define FL_GET_BASE(x)		(x & FL_BASE_MASK)

/* Use successive BARs (PCI base address registers),
   else use offset into some specified BAR */
#define FL_BASE_BARS		0x0008

/* do not assign an irq */
#define FL_NOIRQ		0x0080

/* Use the Base address register size to cap number of ports */
#define FL_REGION_SZ_CAP	0x0100

struct pciserial_board {
	unsigned int flags;
	unsigned int num_ports;
	unsigned int base_baud;
	unsigned int uart_offset;
	unsigned int reg_shift;
	unsigned int first_offset;
};

struct serial_private;

struct serial_private *
pciserial_init_ports(struct pci_dev *dev, struct pciserial_board *board);
void pciserial_remove_ports(struct serial_private *priv);
void pciserial_suspend_ports(struct serial_private *priv);
void pciserial_resume_ports(struct serial_private *priv);

int pci_siig10x_fn(struct pci_dev *dev, int enable);
int pci_siig20x_fn(struct pci_dev *dev, int enable);