瀏覽代碼

[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: Russell King <rmk+kernel@arm.linux.org.uk>
Russell King 20 年之前
父節點
當前提交
241fc4367b
共有 2 個文件被更改,包括 159 次插入112 次删除
  1. 121 112
      drivers/serial/8250_pci.c
  2. 38 0
      include/linux/8250_pci.h

+ 121 - 112
drivers/serial/8250_pci.c

@@ -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);
+		quirk->exit(priv->dev);
+
+	kfree(priv);
+}
+EXPORT_SYMBOL_GPL(pciserial_remove_ports);
+
+void pciserial_suspend_ports(struct serial_private *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;
+}
 
-	kfree(priv);
+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) {
-		int i;
+	if (priv)
+		pciserial_suspend_ports(priv);
 
-		for (i = 0; i < priv->nr; i++)
-			serial8250_suspend_port(priv->line[i]);
-	}
 	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
include/linux/8250_pci.h

@@ -1,2 +1,40 @@
+/*
+ * 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);