Commit 0970769a authored by Atsushi Nemoto's avatar Atsushi Nemoto Committed by Ralf Baechle

[SERIAL] serial_txx9 driver update

 * Use platform_device.
 * Fix and cleanup suspend/resume/initialization codes.
Signed-off-by: default avatarAtsushi Nemoto <anemo@mba.ocn.ne.jp>
Acked-by: default avatarAlan Cox <alan@redhat.com>
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 5ce704f8
...@@ -38,6 +38,8 @@ ...@@ -38,6 +38,8 @@
* Fix some spin_locks. * Fix some spin_locks.
* Do not call uart_add_one_port for absent ports. * Do not call uart_add_one_port for absent ports.
* 1.07 Use CONFIG_SERIAL_TXX9_NR_UARTS. Cleanup. * 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) #if defined(CONFIG_SERIAL_TXX9_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
...@@ -50,7 +52,7 @@ ...@@ -50,7 +52,7 @@
#include <linux/console.h> #include <linux/console.h>
#include <linux/sysrq.h> #include <linux/sysrq.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/device.h> #include <linux/platform_device.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/tty.h> #include <linux/tty.h>
#include <linux/tty_flip.h> #include <linux/tty_flip.h>
...@@ -60,7 +62,7 @@ ...@@ -60,7 +62,7 @@
#include <asm/io.h> #include <asm/io.h>
static char *serial_version = "1.07"; static char *serial_version = "1.08";
static char *serial_name = "TX39/49 Serial driver"; static char *serial_name = "TX39/49 Serial driver";
#define PASS_LIMIT 256 #define PASS_LIMIT 256
...@@ -94,12 +96,7 @@ static char *serial_name = "TX39/49 Serial driver"; ...@@ -94,12 +96,7 @@ static char *serial_name = "TX39/49 Serial driver";
struct uart_txx9_port { struct uart_txx9_port {
struct uart_port port; struct uart_port port;
/* No additional info for now */
/*
* We provide a per-port pm hook.
*/
void (*pm)(struct uart_port *port,
unsigned int state, unsigned int old);
}; };
#define TXX9_REGION_SIZE 0x24 #define TXX9_REGION_SIZE 0x24
...@@ -277,6 +274,31 @@ static void serial_txx9_enable_ms(struct uart_port *port) ...@@ -277,6 +274,31 @@ static void serial_txx9_enable_ms(struct uart_port *port)
/* TXX9-SIO can not control DTR... */ /* 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 static inline void
receive_chars(struct uart_txx9_port *up, unsigned int *status) receive_chars(struct uart_txx9_port *up, unsigned int *status)
{ {
...@@ -657,9 +679,8 @@ static void ...@@ -657,9 +679,8 @@ static void
serial_txx9_pm(struct uart_port *port, unsigned int state, serial_txx9_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate) unsigned int oldstate)
{ {
struct uart_txx9_port *up = (struct uart_txx9_port *)port; if (state == 0)
if (up->pm) serial_txx9_initialize(port);
up->pm(port, state, oldstate);
} }
static int serial_txx9_request_resource(struct uart_txx9_port *up) 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) ...@@ -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) static void serial_txx9_config_port(struct uart_port *port, int uflags)
{ {
struct uart_txx9_port *up = (struct uart_txx9_port *)port; struct uart_txx9_port *up = (struct uart_txx9_port *)port;
unsigned long flags;
int ret; int ret;
/* /*
...@@ -749,30 +769,7 @@ static void serial_txx9_config_port(struct uart_port *port, int uflags) ...@@ -749,30 +769,7 @@ static void serial_txx9_config_port(struct uart_port *port, int uflags)
if (up->port.line == up->port.cons->index) if (up->port.line == up->port.cons->index)
return; return;
#endif #endif
spin_lock_irqsave(&up->port.lock, flags); serial_txx9_initialize(port);
/*
* 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);
} }
static int static int
...@@ -818,7 +815,8 @@ static struct uart_ops serial_txx9_pops = { ...@@ -818,7 +815,8 @@ static struct uart_ops serial_txx9_pops = {
static struct uart_txx9_port serial_txx9_ports[UART_NR]; 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; int i;
...@@ -827,6 +825,7 @@ static void __init serial_txx9_register_ports(struct uart_driver *drv) ...@@ -827,6 +825,7 @@ static void __init serial_txx9_register_ports(struct uart_driver *drv)
up->port.line = i; up->port.line = i;
up->port.ops = &serial_txx9_pops; up->port.ops = &serial_txx9_pops;
up->port.dev = dev;
if (up->port.iobase || up->port.mapbase) if (up->port.iobase || up->port.mapbase)
uart_add_one_port(drv, &up->port); uart_add_one_port(drv, &up->port);
} }
...@@ -898,7 +897,7 @@ serial_txx9_console_write(struct console *co, const char *s, unsigned int count) ...@@ -898,7 +897,7 @@ serial_txx9_console_write(struct console *co, const char *s, unsigned int count)
sio_out(up, TXX9_SIDICR, ier); 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_port *port;
struct uart_txx9_port *up; struct uart_txx9_port *up;
...@@ -919,17 +918,7 @@ static int serial_txx9_console_setup(struct console *co, char *options) ...@@ -919,17 +918,7 @@ static int serial_txx9_console_setup(struct console *co, char *options)
if (!port->ops) if (!port->ops)
return -ENODEV; return -ENODEV;
/* serial_txx9_initialize(&up->port);
* 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 */);
if (options) if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow); uart_parse_options(options, &baud, &parity, &bits, &flow);
...@@ -981,31 +970,6 @@ int __init early_serial_txx9_setup(struct uart_port *port) ...@@ -981,31 +970,6 @@ int __init early_serial_txx9_setup(struct uart_port *port)
return 0; 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); static DEFINE_MUTEX(serial_txx9_mutex);
/** /**
...@@ -1026,11 +990,21 @@ static int __devinit serial_txx9_register_port(struct uart_port *port) ...@@ -1026,11 +990,21 @@ static int __devinit serial_txx9_register_port(struct uart_port *port)
int ret = -ENOSPC; int ret = -ENOSPC;
mutex_lock(&serial_txx9_mutex); mutex_lock(&serial_txx9_mutex);
for (i = 0; i < UART_NR; i++) {
uart = &serial_txx9_ports[i];
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++) { for (i = 0; i < UART_NR; i++) {
uart = &serial_txx9_ports[i]; uart = &serial_txx9_ports[i];
if (!(uart->port.iobase || uart->port.mapbase)) if (!(uart->port.iobase || uart->port.mapbase))
break; break;
} }
}
if (i < UART_NR) { if (i < UART_NR) {
uart->port.iobase = port->iobase; uart->port.iobase = port->iobase;
uart->port.membase = port->membase; uart->port.membase = port->membase;
...@@ -1071,6 +1045,95 @@ static void __devexit serial_txx9_unregister_port(int line) ...@@ -1071,6 +1045,95 @@ static void __devexit serial_txx9_unregister_port(int line)
mutex_unlock(&serial_txx9_mutex); 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 * Probe one serial board. Unfortunately, there is no rhyme nor reason
* to the arrangement of serial ports on a PCI card. * 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) ...@@ -1097,20 +1160,22 @@ pciserial_txx9_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
line = serial_txx9_register_port(&port); line = serial_txx9_register_port(&port);
if (line < 0) { if (line < 0) {
printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), line); 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; return 0;
} }
static void __devexit pciserial_txx9_remove_one(struct pci_dev *dev) 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); pci_set_drvdata(dev, NULL);
if (line) { if (up) {
serial_txx9_unregister_port(line); serial_txx9_unregister_port(up->port.line);
pci_disable_device(dev); pci_disable_device(dev);
} }
} }
...@@ -1118,10 +1183,10 @@ static void __devexit pciserial_txx9_remove_one(struct pci_dev *dev) ...@@ -1118,10 +1183,10 @@ static void __devexit pciserial_txx9_remove_one(struct pci_dev *dev)
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int pciserial_txx9_suspend_one(struct pci_dev *dev, pm_message_t state) 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) if (up)
serial_txx9_suspend_port(line); uart_suspend_port(&serial_txx9_reg, &up->port);
pci_save_state(dev); pci_save_state(dev);
pci_set_power_state(dev, pci_choose_state(dev, state)); pci_set_power_state(dev, pci_choose_state(dev, state));
return 0; return 0;
...@@ -1129,15 +1194,12 @@ static int pciserial_txx9_suspend_one(struct pci_dev *dev, pm_message_t state) ...@@ -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) 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_set_power_state(dev, PCI_D0);
pci_restore_state(dev); pci_restore_state(dev);
if (up)
if (line) { uart_resume_port(&serial_txx9_reg, &up->port);
pci_enable_device(dev);
serial_txx9_resume_port(line);
}
return 0; return 0;
} }
#endif #endif
...@@ -1161,6 +1223,8 @@ static struct pci_driver serial_txx9_pci_driver = { ...@@ -1161,6 +1223,8 @@ static struct pci_driver serial_txx9_pci_driver = {
MODULE_DEVICE_TABLE(pci, serial_txx9_pci_tbl); MODULE_DEVICE_TABLE(pci, serial_txx9_pci_tbl);
#endif /* ENABLE_SERIAL_TXX9_PCI */ #endif /* ENABLE_SERIAL_TXX9_PCI */
static struct platform_device *serial_txx9_plat_devs;
static int __init serial_txx9_init(void) static int __init serial_txx9_init(void)
{ {
int ret; int ret;
...@@ -1168,13 +1232,39 @@ static int __init serial_txx9_init(void) ...@@ -1168,13 +1232,39 @@ static int __init serial_txx9_init(void)
printk(KERN_INFO "%s version %s\n", serial_name, serial_version); printk(KERN_INFO "%s version %s\n", serial_name, serial_version);
ret = uart_register_driver(&serial_txx9_reg); ret = uart_register_driver(&serial_txx9_reg);
if (ret >= 0) { if (ret)
serial_txx9_register_ports(&serial_txx9_reg); 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 #ifdef ENABLE_SERIAL_TXX9_PCI
ret = pci_register_driver(&serial_txx9_pci_driver); ret = pci_register_driver(&serial_txx9_pci_driver);
#endif #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; return ret;
} }
...@@ -1185,6 +1275,8 @@ static void __exit serial_txx9_exit(void) ...@@ -1185,6 +1275,8 @@ static void __exit serial_txx9_exit(void)
#ifdef ENABLE_SERIAL_TXX9_PCI #ifdef ENABLE_SERIAL_TXX9_PCI
pci_unregister_driver(&serial_txx9_pci_driver); pci_unregister_driver(&serial_txx9_pci_driver);
#endif #endif
platform_driver_unregister(&serial_txx9_plat_driver);
platform_device_unregister(serial_txx9_plat_devs);
for (i = 0; i < UART_NR; i++) { for (i = 0; i < UART_NR; i++) {
struct uart_txx9_port *up = &serial_txx9_ports[i]; struct uart_txx9_port *up = &serial_txx9_ports[i];
if (up->port.iobase || up->port.mapbase) if (up->port.iobase || up->port.mapbase)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment