提交 241fc436 编写于 作者: R Russell King 提交者: Russell King

[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: NRussell King <rmk+kernel@arm.linux.org.uk>
上级 70db3d91
......@@ -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;
}
......
/*
* 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);
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册