[SERIAL] serial_txx9 driver update
* Use platform_device. * Fix and cleanup suspend/resume/initialization codes. Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp> Acked-by: Alan Cox <alan@redhat.com> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
parent
5ce704f877
commit
0970769ace
|
@ -38,6 +38,8 @@
|
|||
* Fix some spin_locks.
|
||||
* Do not call uart_add_one_port for absent ports.
|
||||
* 1.07 Use CONFIG_SERIAL_TXX9_NR_UARTS. Cleanup.
|
||||
* 1.08 Use platform_device.
|
||||
* Fix and cleanup suspend/resume/initialization codes.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_SERIAL_TXX9_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
|
||||
|
@ -50,7 +52,7 @@
|
|||
#include <linux/console.h>
|
||||
#include <linux/sysrq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
|
@ -60,7 +62,7 @@
|
|||
|
||||
#include <asm/io.h>
|
||||
|
||||
static char *serial_version = "1.07";
|
||||
static char *serial_version = "1.08";
|
||||
static char *serial_name = "TX39/49 Serial driver";
|
||||
|
||||
#define PASS_LIMIT 256
|
||||
|
@ -94,12 +96,7 @@ static char *serial_name = "TX39/49 Serial driver";
|
|||
|
||||
struct uart_txx9_port {
|
||||
struct uart_port port;
|
||||
|
||||
/*
|
||||
* We provide a per-port pm hook.
|
||||
*/
|
||||
void (*pm)(struct uart_port *port,
|
||||
unsigned int state, unsigned int old);
|
||||
/* No additional info for now */
|
||||
};
|
||||
|
||||
#define TXX9_REGION_SIZE 0x24
|
||||
|
@ -277,6 +274,31 @@ static void serial_txx9_enable_ms(struct uart_port *port)
|
|||
/* TXX9-SIO can not control DTR... */
|
||||
}
|
||||
|
||||
static void serial_txx9_initialize(struct uart_port *port)
|
||||
{
|
||||
struct uart_txx9_port *up = (struct uart_txx9_port *)port;
|
||||
unsigned int tmout = 10000;
|
||||
|
||||
sio_out(up, TXX9_SIFCR, TXX9_SIFCR_SWRST);
|
||||
/* TX4925 BUG WORKAROUND. Accessing SIOC register
|
||||
* immediately after soft reset causes bus error. */
|
||||
mmiowb();
|
||||
udelay(1);
|
||||
while ((sio_in(up, TXX9_SIFCR) & TXX9_SIFCR_SWRST) && --tmout)
|
||||
udelay(1);
|
||||
/* TX Int by FIFO Empty, RX Int by Receiving 1 char. */
|
||||
sio_set(up, TXX9_SIFCR,
|
||||
TXX9_SIFCR_TDIL_MAX | TXX9_SIFCR_RDIL_1);
|
||||
/* initial settings */
|
||||
sio_out(up, TXX9_SILCR,
|
||||
TXX9_SILCR_UMODE_8BIT | TXX9_SILCR_USBL_1BIT |
|
||||
((up->port.flags & UPF_TXX9_USE_SCLK) ?
|
||||
TXX9_SILCR_SCS_SCLK_BG : TXX9_SILCR_SCS_IMCLK_BG));
|
||||
sio_quot_set(up, uart_get_divisor(port, 9600));
|
||||
sio_out(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSTL_MAX /* 15 */);
|
||||
sio_out(up, TXX9_SIDICR, 0);
|
||||
}
|
||||
|
||||
static inline void
|
||||
receive_chars(struct uart_txx9_port *up, unsigned int *status)
|
||||
{
|
||||
|
@ -657,9 +679,8 @@ static void
|
|||
serial_txx9_pm(struct uart_port *port, unsigned int state,
|
||||
unsigned int oldstate)
|
||||
{
|
||||
struct uart_txx9_port *up = (struct uart_txx9_port *)port;
|
||||
if (up->pm)
|
||||
up->pm(port, state, oldstate);
|
||||
if (state == 0)
|
||||
serial_txx9_initialize(port);
|
||||
}
|
||||
|
||||
static int serial_txx9_request_resource(struct uart_txx9_port *up)
|
||||
|
@ -732,7 +753,6 @@ static int serial_txx9_request_port(struct uart_port *port)
|
|||
static void serial_txx9_config_port(struct uart_port *port, int uflags)
|
||||
{
|
||||
struct uart_txx9_port *up = (struct uart_txx9_port *)port;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
|
@ -749,30 +769,7 @@ static void serial_txx9_config_port(struct uart_port *port, int uflags)
|
|||
if (up->port.line == up->port.cons->index)
|
||||
return;
|
||||
#endif
|
||||
spin_lock_irqsave(&up->port.lock, flags);
|
||||
/*
|
||||
* Reset the UART.
|
||||
*/
|
||||
sio_out(up, TXX9_SIFCR, TXX9_SIFCR_SWRST);
|
||||
#ifdef CONFIG_CPU_TX49XX
|
||||
/* TX4925 BUG WORKAROUND. Accessing SIOC register
|
||||
* immediately after soft reset causes bus error. */
|
||||
iob();
|
||||
udelay(1);
|
||||
#endif
|
||||
while (sio_in(up, TXX9_SIFCR) & TXX9_SIFCR_SWRST)
|
||||
;
|
||||
/* TX Int by FIFO Empty, RX Int by Receiving 1 char. */
|
||||
sio_set(up, TXX9_SIFCR,
|
||||
TXX9_SIFCR_TDIL_MAX | TXX9_SIFCR_RDIL_1);
|
||||
/* initial settings */
|
||||
sio_out(up, TXX9_SILCR,
|
||||
TXX9_SILCR_UMODE_8BIT | TXX9_SILCR_USBL_1BIT |
|
||||
((up->port.flags & UPF_TXX9_USE_SCLK) ?
|
||||
TXX9_SILCR_SCS_SCLK_BG : TXX9_SILCR_SCS_IMCLK_BG));
|
||||
sio_quot_set(up, uart_get_divisor(port, 9600));
|
||||
sio_out(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSTL_MAX /* 15 */);
|
||||
spin_unlock_irqrestore(&up->port.lock, flags);
|
||||
serial_txx9_initialize(port);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -818,7 +815,8 @@ static struct uart_ops serial_txx9_pops = {
|
|||
|
||||
static struct uart_txx9_port serial_txx9_ports[UART_NR];
|
||||
|
||||
static void __init serial_txx9_register_ports(struct uart_driver *drv)
|
||||
static void __init serial_txx9_register_ports(struct uart_driver *drv,
|
||||
struct device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -827,6 +825,7 @@ static void __init serial_txx9_register_ports(struct uart_driver *drv)
|
|||
|
||||
up->port.line = i;
|
||||
up->port.ops = &serial_txx9_pops;
|
||||
up->port.dev = dev;
|
||||
if (up->port.iobase || up->port.mapbase)
|
||||
uart_add_one_port(drv, &up->port);
|
||||
}
|
||||
|
@ -898,7 +897,7 @@ serial_txx9_console_write(struct console *co, const char *s, unsigned int count)
|
|||
sio_out(up, TXX9_SIDICR, ier);
|
||||
}
|
||||
|
||||
static int serial_txx9_console_setup(struct console *co, char *options)
|
||||
static int __init serial_txx9_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct uart_port *port;
|
||||
struct uart_txx9_port *up;
|
||||
|
@ -919,17 +918,7 @@ static int serial_txx9_console_setup(struct console *co, char *options)
|
|||
if (!port->ops)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Disable UART interrupts, set DTR and RTS high
|
||||
* and set speed.
|
||||
*/
|
||||
sio_out(up, TXX9_SIDICR, 0);
|
||||
/* initial settings */
|
||||
sio_out(up, TXX9_SILCR,
|
||||
TXX9_SILCR_UMODE_8BIT | TXX9_SILCR_USBL_1BIT |
|
||||
((port->flags & UPF_TXX9_USE_SCLK) ?
|
||||
TXX9_SILCR_SCS_SCLK_BG : TXX9_SILCR_SCS_IMCLK_BG));
|
||||
sio_out(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSTL_MAX /* 15 */);
|
||||
serial_txx9_initialize(&up->port);
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
|
@ -981,31 +970,6 @@ int __init early_serial_txx9_setup(struct uart_port *port)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SERIAL_TXX9_PCI
|
||||
#ifdef CONFIG_PM
|
||||
/**
|
||||
* serial_txx9_suspend_port - suspend one serial port
|
||||
* @line: serial line number
|
||||
*
|
||||
* Suspend one serial port.
|
||||
*/
|
||||
static void serial_txx9_suspend_port(int line)
|
||||
{
|
||||
uart_suspend_port(&serial_txx9_reg, &serial_txx9_ports[line].port);
|
||||
}
|
||||
|
||||
/**
|
||||
* serial_txx9_resume_port - resume one serial port
|
||||
* @line: serial line number
|
||||
*
|
||||
* Resume one serial port.
|
||||
*/
|
||||
static void serial_txx9_resume_port(int line)
|
||||
{
|
||||
uart_resume_port(&serial_txx9_reg, &serial_txx9_ports[line].port);
|
||||
}
|
||||
#endif
|
||||
|
||||
static DEFINE_MUTEX(serial_txx9_mutex);
|
||||
|
||||
/**
|
||||
|
@ -1028,8 +992,18 @@ static int __devinit serial_txx9_register_port(struct uart_port *port)
|
|||
mutex_lock(&serial_txx9_mutex);
|
||||
for (i = 0; i < UART_NR; i++) {
|
||||
uart = &serial_txx9_ports[i];
|
||||
if (!(uart->port.iobase || uart->port.mapbase))
|
||||
if (uart_match_port(&uart->port, port)) {
|
||||
uart_remove_one_port(&serial_txx9_reg, &uart->port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == UART_NR) {
|
||||
/* Find unused port */
|
||||
for (i = 0; i < UART_NR; i++) {
|
||||
uart = &serial_txx9_ports[i];
|
||||
if (!(uart->port.iobase || uart->port.mapbase))
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < UART_NR) {
|
||||
uart->port.iobase = port->iobase;
|
||||
|
@ -1071,6 +1045,95 @@ static void __devexit serial_txx9_unregister_port(int line)
|
|||
mutex_unlock(&serial_txx9_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Register a set of serial devices attached to a platform device.
|
||||
*/
|
||||
static int __devinit serial_txx9_probe(struct platform_device *dev)
|
||||
{
|
||||
struct uart_port *p = dev->dev.platform_data;
|
||||
struct uart_port port;
|
||||
int ret, i;
|
||||
|
||||
memset(&port, 0, sizeof(struct uart_port));
|
||||
for (i = 0; p && p->uartclk != 0; p++, i++) {
|
||||
port.iobase = p->iobase;
|
||||
port.membase = p->membase;
|
||||
port.irq = p->irq;
|
||||
port.uartclk = p->uartclk;
|
||||
port.iotype = p->iotype;
|
||||
port.flags = p->flags;
|
||||
port.mapbase = p->mapbase;
|
||||
port.dev = &dev->dev;
|
||||
ret = serial_txx9_register_port(&port);
|
||||
if (ret < 0) {
|
||||
dev_err(&dev->dev, "unable to register port at index %d "
|
||||
"(IO%x MEM%lx IRQ%d): %d\n", i,
|
||||
p->iobase, p->mapbase, p->irq, ret);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove serial ports registered against a platform device.
|
||||
*/
|
||||
static int __devexit serial_txx9_remove(struct platform_device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < UART_NR; i++) {
|
||||
struct uart_txx9_port *up = &serial_txx9_ports[i];
|
||||
|
||||
if (up->port.dev == &dev->dev)
|
||||
serial_txx9_unregister_port(i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int serial_txx9_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < UART_NR; i++) {
|
||||
struct uart_txx9_port *up = &serial_txx9_ports[i];
|
||||
|
||||
if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev)
|
||||
uart_suspend_port(&serial_txx9_reg, &up->port);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial_txx9_resume(struct platform_device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < UART_NR; i++) {
|
||||
struct uart_txx9_port *up = &serial_txx9_ports[i];
|
||||
|
||||
if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev)
|
||||
uart_resume_port(&serial_txx9_reg, &up->port);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct platform_driver serial_txx9_plat_driver = {
|
||||
.probe = serial_txx9_probe,
|
||||
.remove = __devexit_p(serial_txx9_remove),
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = serial_txx9_suspend,
|
||||
.resume = serial_txx9_resume,
|
||||
#endif
|
||||
.driver = {
|
||||
.name = "serial_txx9",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
#ifdef ENABLE_SERIAL_TXX9_PCI
|
||||
/*
|
||||
* Probe one serial board. Unfortunately, there is no rhyme nor reason
|
||||
* to the arrangement of serial ports on a PCI card.
|
||||
|
@ -1097,20 +1160,22 @@ pciserial_txx9_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
|
|||
line = serial_txx9_register_port(&port);
|
||||
if (line < 0) {
|
||||
printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), line);
|
||||
pci_disable_device(dev);
|
||||
return line;
|
||||
}
|
||||
pci_set_drvdata(dev, (void *)(long)line);
|
||||
pci_set_drvdata(dev, &serial_txx9_ports[line]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devexit pciserial_txx9_remove_one(struct pci_dev *dev)
|
||||
{
|
||||
int line = (int)(long)pci_get_drvdata(dev);
|
||||
struct uart_txx9_port *up = pci_get_drvdata(dev);
|
||||
|
||||
pci_set_drvdata(dev, NULL);
|
||||
|
||||
if (line) {
|
||||
serial_txx9_unregister_port(line);
|
||||
if (up) {
|
||||
serial_txx9_unregister_port(up->port.line);
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
}
|
||||
|
@ -1118,10 +1183,10 @@ static void __devexit pciserial_txx9_remove_one(struct pci_dev *dev)
|
|||
#ifdef CONFIG_PM
|
||||
static int pciserial_txx9_suspend_one(struct pci_dev *dev, pm_message_t state)
|
||||
{
|
||||
int line = (int)(long)pci_get_drvdata(dev);
|
||||
struct uart_txx9_port *up = pci_get_drvdata(dev);
|
||||
|
||||
if (line)
|
||||
serial_txx9_suspend_port(line);
|
||||
if (up)
|
||||
uart_suspend_port(&serial_txx9_reg, &up->port);
|
||||
pci_save_state(dev);
|
||||
pci_set_power_state(dev, pci_choose_state(dev, state));
|
||||
return 0;
|
||||
|
@ -1129,15 +1194,12 @@ static int pciserial_txx9_suspend_one(struct pci_dev *dev, pm_message_t state)
|
|||
|
||||
static int pciserial_txx9_resume_one(struct pci_dev *dev)
|
||||
{
|
||||
int line = (int)(long)pci_get_drvdata(dev);
|
||||
struct uart_txx9_port *up = pci_get_drvdata(dev);
|
||||
|
||||
pci_set_power_state(dev, PCI_D0);
|
||||
pci_restore_state(dev);
|
||||
|
||||
if (line) {
|
||||
pci_enable_device(dev);
|
||||
serial_txx9_resume_port(line);
|
||||
}
|
||||
if (up)
|
||||
uart_resume_port(&serial_txx9_reg, &up->port);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -1161,6 +1223,8 @@ static struct pci_driver serial_txx9_pci_driver = {
|
|||
MODULE_DEVICE_TABLE(pci, serial_txx9_pci_tbl);
|
||||
#endif /* ENABLE_SERIAL_TXX9_PCI */
|
||||
|
||||
static struct platform_device *serial_txx9_plat_devs;
|
||||
|
||||
static int __init serial_txx9_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
@ -1168,13 +1232,39 @@ static int __init serial_txx9_init(void)
|
|||
printk(KERN_INFO "%s version %s\n", serial_name, serial_version);
|
||||
|
||||
ret = uart_register_driver(&serial_txx9_reg);
|
||||
if (ret >= 0) {
|
||||
serial_txx9_register_ports(&serial_txx9_reg);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
serial_txx9_plat_devs = platform_device_alloc("serial_txx9", -1);
|
||||
if (!serial_txx9_plat_devs) {
|
||||
ret = -ENOMEM;
|
||||
goto unreg_uart_drv;
|
||||
}
|
||||
|
||||
ret = platform_device_add(serial_txx9_plat_devs);
|
||||
if (ret)
|
||||
goto put_dev;
|
||||
|
||||
serial_txx9_register_ports(&serial_txx9_reg,
|
||||
&serial_txx9_plat_devs->dev);
|
||||
|
||||
ret = platform_driver_register(&serial_txx9_plat_driver);
|
||||
if (ret)
|
||||
goto del_dev;
|
||||
|
||||
#ifdef ENABLE_SERIAL_TXX9_PCI
|
||||
ret = pci_register_driver(&serial_txx9_pci_driver);
|
||||
ret = pci_register_driver(&serial_txx9_pci_driver);
|
||||
#endif
|
||||
}
|
||||
if (ret == 0)
|
||||
goto out;
|
||||
|
||||
del_dev:
|
||||
platform_device_del(serial_txx9_plat_devs);
|
||||
put_dev:
|
||||
platform_device_put(serial_txx9_plat_devs);
|
||||
unreg_uart_drv:
|
||||
uart_unregister_driver(&serial_txx9_reg);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1185,6 +1275,8 @@ static void __exit serial_txx9_exit(void)
|
|||
#ifdef ENABLE_SERIAL_TXX9_PCI
|
||||
pci_unregister_driver(&serial_txx9_pci_driver);
|
||||
#endif
|
||||
platform_driver_unregister(&serial_txx9_plat_driver);
|
||||
platform_device_unregister(serial_txx9_plat_devs);
|
||||
for (i = 0; i < UART_NR; i++) {
|
||||
struct uart_txx9_port *up = &serial_txx9_ports[i];
|
||||
if (up->port.iobase || up->port.mapbase)
|
||||
|
|
Loading…
Reference in a new issue