From 7808edcd306f22aeb23775d34e70b7fa2f58b852 Mon Sep 17 00:00:00 2001 From: Nicos Gollan Date: Thu, 5 May 2011 21:00:37 +0200 Subject: Basic support for Moschip 9900 family I/O chips Add I/O based support for serial and parallel ports of the following chips: Vendor: Moschip (0x9710) Parts (device IDs) * 9900 (0x9900) * 9904 (0x9904 * 9901 (0x9912, also sold as 9912) * 9922 (0x9922) On all chips but the 9900, a single port is provided per PCI subdevice (subvendor-ID 0xA000, subdevice-IDs 0x1000 for serial, 0x2000 for parallel with proper class codes). In cascading configurations, the 9900 provides two devices per subdevice, with subvendor-ID 0xA000 and subdevice-IDs 0x30ps where p is the number of parallel ports and s the number of serial ports. Basic testing was only done on the serial part of a 9912 to the point where it can be used for a serial kernel console, and advanced features are completely untested. It is possible to reduce functionality of the chips by adding a configuration EEPROM, and the datasheet [1] is inconsistent w.r.t subdevices in the 4s+2s1p and 2s1p+4s configurations. The subdevice-ID 0x3012 should likely read 0x3011 with a serial port in function 3, which would be consistent with the BAR layouts. For now, the drivers ignore subdevices with ID 0x1000 and no class code. The parallel ports are integrated in parport_serial even for purely parallel parts to reduce the footprint of the patch. [1] http://www.moschip.com/data/products/MCS9900/MCS9900_Datasheet.pdf Signed-off-by: Nicos Gollan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250_pci.c | 104 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250_pci.c b/drivers/tty/serial/8250_pci.c index 4b4968a294b..0b255cef57e 100644 --- a/drivers/tty/serial/8250_pci.c +++ b/drivers/tty/serial/8250_pci.c @@ -56,6 +56,9 @@ struct serial_private { int line[0]; }; +static int pci_default_setup(struct serial_private*, + const struct pciserial_board*, struct uart_port*, int); + static void moan_device(const char *str, struct pci_dev *dev) { printk(KERN_WARNING @@ -752,6 +755,62 @@ pci_ni8430_setup(struct serial_private *priv, return setup_port(priv, port, bar, offset, board->reg_shift); } +static int pci_netmos_9900_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int bar; + + if ((priv->dev->subsystem_device & 0xff00) == 0x3000) { + /* netmos apparently orders BARs by datasheet layout, so serial + * ports get BARs 0 and 3 (or 1 and 4 for memmapped) + */ + bar = 3 * idx; + + return setup_port(priv, port, bar, 0, board->reg_shift); + } else { + return pci_default_setup(priv, board, port, idx); + } +} + +/* the 99xx series comes with a range of device IDs and a variety + * of capabilities: + * + * 9900 has varying capabilities and can cascade to sub-controllers + * (cascading should be purely internal) + * 9904 is hardwired with 4 serial ports + * 9912 and 9922 are hardwired with 2 serial ports + */ +static int pci_netmos_9900_numports(struct pci_dev *dev) +{ + unsigned int c = dev->class; + unsigned int pi; + unsigned short sub_serports; + + pi = (c & 0xff); + + if (pi == 2) { + return 1; + } else if ((pi == 0) && + (dev->device == PCI_DEVICE_ID_NETMOS_9900)) { + /* two possibilities: 0x30ps encodes number of parallel and + * serial ports, or 0x1000 indicates *something*. This is not + * immediately obvious, since the 2s1p+4s configuration seems + * to offer all functionality on functions 0..2, while still + * advertising the same function 3 as the 4s+2s1p config. + */ + sub_serports = dev->subsystem_device & 0xf; + if (sub_serports > 0) { + return sub_serports; + } else { + printk(KERN_NOTICE "NetMos/Mostech serial driver ignoring port on ambiguous config.\n"); + return 0; + } + } + + moan_device("unknown NetMos/Mostech program interface", dev); + return 0; +} static int pci_netmos_init(struct pci_dev *dev) { @@ -761,12 +820,28 @@ static int pci_netmos_init(struct pci_dev *dev) if ((dev->device == PCI_DEVICE_ID_NETMOS_9901) || (dev->device == PCI_DEVICE_ID_NETMOS_9865)) return 0; + if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM && dev->subsystem_device == 0x0299) return 0; + switch (dev->device) { /* FALLTHROUGH on all */ + case PCI_DEVICE_ID_NETMOS_9904: + case PCI_DEVICE_ID_NETMOS_9912: + case PCI_DEVICE_ID_NETMOS_9922: + case PCI_DEVICE_ID_NETMOS_9900: + num_serial = pci_netmos_9900_numports(dev); + break; + + default: + if (num_serial == 0 ) { + moan_device("unknown NetMos/Mostech device", dev); + } + } + if (num_serial == 0) return -ENODEV; + return num_serial; } @@ -1417,7 +1492,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .init = pci_netmos_init, - .setup = pci_default_setup, + .setup = pci_netmos_9900_setup, }, /* * For Oxford Semiconductor Tornado based devices @@ -1644,6 +1719,7 @@ enum pci_board_num_t { pbn_ADDIDATA_PCIe_8_3906250, pbn_ce4100_1_115200, pbn_omegapci, + pbn_NETMOS9900_2s_115200, }; /* @@ -2345,6 +2421,11 @@ static struct pciserial_board pci_boards[] __devinitdata = { .base_baud = 115200, .uart_offset = 0x200, }, + [pbn_NETMOS9900_2s_115200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 115200, + }, }; static const struct pci_device_id softmodem_blacklist[] = { @@ -3826,6 +3907,27 @@ static struct pci_device_id serial_pci_tbl[] = { 0xA000, 0x1000, 0, 0, pbn_b0_1_115200 }, + /* the 9901 is a rebranded 9912 */ + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9922, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9904, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900, + 0xA000, 0x3002, + 0, 0, pbn_NETMOS9900_2s_115200 }, + /* * Best Connectivity PCI Multi I/O cards */ -- cgit v1.2.3-18-g5258 From 5bf8f501e05930364b345ed8710c5b1a13207134 Mon Sep 17 00:00:00 2001 From: Frédéric Brière Date: Sun, 29 May 2011 15:08:03 -0400 Subject: serial: 8250_pci: add .probe member to struct pci_serial_quirk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function, if present, is called early on by the 8250_pci probe; it can be used to reject devices meant for parport_serial. (The .init function cannot be used for this purpose, as it is also called by parport_serial.) Signed-off-by: Frédéric Brière Acked-by: Alan Cox Cc: linux-serial@vger.kernel.org Cc: linux-parport@lists.infradead.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250_pci.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250_pci.c b/drivers/tty/serial/8250_pci.c index 0b255cef57e..9b119fe9257 100644 --- a/drivers/tty/serial/8250_pci.c +++ b/drivers/tty/serial/8250_pci.c @@ -39,6 +39,7 @@ struct pci_serial_quirk { u32 device; u32 subvendor; u32 subdevice; + int (*probe)(struct pci_dev *dev); int (*init)(struct pci_dev *dev); int (*setup)(struct serial_private *, const struct pciserial_board *, @@ -2662,11 +2663,19 @@ EXPORT_SYMBOL_GPL(pciserial_resume_ports); static int __devinit pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) { + struct pci_serial_quirk *quirk; struct serial_private *priv; const struct pciserial_board *board; struct pciserial_board tmp; int rc; + quirk = find_quirk(dev); + if (quirk->probe) { + rc = quirk->probe(dev); + if (rc) + return rc; + } + if (ent->driver_data >= ARRAY_SIZE(pci_boards)) { printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n", ent->driver_data); -- cgit v1.2.3-18-g5258 From b9b24558f7d36c550b5cf0b550a8926f8c03cdbd Mon Sep 17 00:00:00 2001 From: Frédéric Brière Date: Sun, 29 May 2011 15:08:04 -0400 Subject: parport/serial: add support for Timedia/SUNIX cards to parport_serial MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Timedia/SUNIX PCI cards with both serial and parallel ports are currently supported by 8250_pci and parport_pc individually. Moving that support into parport_serial allows using both types of ports at the same time. This was successfully tested with a SUNIX 4079T. Signed-off-by: Frédéric Brière Acked-by: Alan Cox Cc: linux-serial@vger.kernel.org Cc: linux-parport@lists.infradead.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250_pci.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250_pci.c b/drivers/tty/serial/8250_pci.c index 9b119fe9257..e1d4668f86a 100644 --- a/drivers/tty/serial/8250_pci.c +++ b/drivers/tty/serial/8250_pci.c @@ -575,6 +575,28 @@ static const struct timedia_struct { { 8, timedia_eight_port } }; +/* + * There are nearly 70 different Timedia/SUNIX PCI serial devices. Instead of + * listing them individually, this driver merely grabs them all with + * PCI_ANY_ID. Some of these devices, however, also feature a parallel port, + * and should be left free to be claimed by parport_serial instead. + */ +static int pci_timedia_probe(struct pci_dev *dev) +{ + /* + * Check the third digit of the subdevice ID + * (0,2,3,5,6: serial only -- 7,8,9: serial + parallel) + */ + if ((dev->subsystem_device & 0x00f0) >= 0x70) { + dev_info(&dev->dev, + "ignoring Timedia subdevice %04x for parport_serial\n", + dev->subsystem_device); + return -ENODEV; + } + + return 0; +} + static int pci_timedia_init(struct pci_dev *dev) { const unsigned short *ids; @@ -1463,6 +1485,7 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .device = PCI_DEVICE_ID_TIMEDIA_1889, .subvendor = PCI_VENDOR_ID_TIMEDIA, .subdevice = PCI_ANY_ID, + .probe = pci_timedia_probe, .init = pci_timedia_init, .setup = pci_timedia_setup, }, -- cgit v1.2.3-18-g5258 From 0e2adc06843a9b5a28af4ca5f796240297907897 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 26 May 2011 10:41:17 +0200 Subject: serial/pch: use global div helper instead of creating a private one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/pch_uart.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index f2cb7503fcb..ae28250b0be 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -14,6 +14,7 @@ *along with this program; if not, write to the Free Software *Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ +#include #include #include #include @@ -137,8 +138,6 @@ enum { #define PCH_UART_DLL 0x00 #define PCH_UART_DLM 0x01 -#define DIV_ROUND(a, b) (((a) + ((b)/2)) / (b)) - #define PCH_UART_IID_RLS (PCH_UART_IIR_REI) #define PCH_UART_IID_RDR (PCH_UART_IIR_RRI) #define PCH_UART_IID_RDR_TO (PCH_UART_IIR_RRI | PCH_UART_IIR_TOI) @@ -316,7 +315,7 @@ static int pch_uart_hal_set_line(struct eg20t_port *priv, int baud, unsigned int dll, dlm, lcr; int div; - div = DIV_ROUND(priv->base_baud / 16, baud); + div = DIV_ROUND_CLOSEST(priv->base_baud / 16, baud); if (div < 0 || USHRT_MAX <= div) { dev_err(priv->port.dev, "Invalid Baud(div=0x%x)\n", div); return -EINVAL; -- cgit v1.2.3-18-g5258 From 2807190b69f60ce4a04a9c7c523c9bce8cb62b2e Mon Sep 17 00:00:00 2001 From: Michael Reed Date: Tue, 31 May 2011 12:06:28 -0500 Subject: 8250_pci Add EEH support to the 8250 driver for IBM/Digi PCIe 2-port Adapter The purpose of the patch is to add EEH support to the 8250_PCI driver for the IBM/Digi PCIE 2port Async EIA-232 Adapter that uses a PLX chipset on the PPC platforrm. Basic support for this adapter was recently added https://lkml.org/lkml/2011/5/11/341 This patch was created against the linux-next kernel Cc: Greg Kroah-Hartman Cc: Breno Leitao Cc: Scott Kilau Signed-off-by: Michael Reed Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250_pci.c | 47 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250_pci.c b/drivers/tty/serial/8250_pci.c index e1d4668f86a..ae2188ce067 100644 --- a/drivers/tty/serial/8250_pci.c +++ b/drivers/tty/serial/8250_pci.c @@ -2708,6 +2708,7 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) board = &pci_boards[ent->driver_data]; rc = pci_enable_device(dev); + pci_save_state(dev); if (rc) return rc; @@ -4002,6 +4003,51 @@ static struct pci_device_id serial_pci_tbl[] = { { 0, } }; +static pci_ers_result_t serial8250_io_error_detected(struct pci_dev *dev, + pci_channel_state_t state) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + if (state == pci_channel_io_perm_failure) + return PCI_ERS_RESULT_DISCONNECT; + + if (priv) + pciserial_suspend_ports(priv); + + pci_disable_device(dev); + + return PCI_ERS_RESULT_NEED_RESET; +} + +static pci_ers_result_t serial8250_io_slot_reset(struct pci_dev *dev) +{ + int rc; + + rc = pci_enable_device(dev); + + if (rc) + return PCI_ERS_RESULT_DISCONNECT; + + pci_restore_state(dev); + pci_save_state(dev); + + return PCI_ERS_RESULT_RECOVERED; +} + +static void serial8250_io_resume(struct pci_dev *dev) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + if (priv) + pciserial_resume_ports(priv); +} + +static struct pci_error_handlers serial8250_err_handler = { + .error_detected = serial8250_io_error_detected, + .slot_reset = serial8250_io_slot_reset, + .resume = serial8250_io_resume, +}; + static struct pci_driver serial_pci_driver = { .name = "serial", .probe = pciserial_init_one, @@ -4011,6 +4057,7 @@ static struct pci_driver serial_pci_driver = { .resume = pciserial_resume_one, #endif .id_table = serial_pci_tbl, + .err_handler = &serial8250_err_handler, }; static int __init serial8250_pci_init(void) -- cgit v1.2.3-18-g5258 From e7328ae1848966181a7ac47e8ae6cddbd2cf55f3 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sun, 5 Jun 2011 22:51:49 +0200 Subject: serial: 8250, increase PASS_LIMIT With virtual machines like qemu, it's pretty common to see "too much work for irq4" messages nowadays. This happens when a bunch of output is printed on the emulated serial console. This is caused by too low PASS_LIMIT. When ISR loops more than the limit, it spits the message. I've been using a kernel with doubled the limit and I couldn't see no problems. Maybe it's time to get rid of the message now? Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index b40f7b90c81..f11df87fa24 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -81,7 +81,7 @@ static unsigned int skip_txen_test; /* force skip of txen test at init time */ #define DEBUG_INTR(fmt...) do { } while (0) #endif -#define PASS_LIMIT 256 +#define PASS_LIMIT 512 #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) -- cgit v1.2.3-18-g5258 From d50f6dcaf22a3234a65ae4f6087173e66b7fff56 Mon Sep 17 00:00:00 2001 From: Russ Gorby Date: Tue, 14 Jun 2011 13:35:32 -0700 Subject: tty: n_gsm: expose gsmtty device nodes at ldisc open time The n_gsm driver being an ldisc, does not provide a convenient method e.g. udev to create the tty device nodes automatically when the ldisc is opened. The TTY device nodes are now created via calls to tty_register_device from the ldisc open. Signed-off-by: Russ Gorby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 09e8c7d53af..b288ff6cb81 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -169,6 +169,7 @@ struct gsm_control { struct gsm_mux { struct tty_struct *tty; /* The tty our ldisc is bound to */ spinlock_t lock; + unsigned int num; /* Events on the GSM channel */ wait_queue_head_t event; @@ -250,6 +251,8 @@ struct gsm_mux { static struct gsm_mux *gsm_mux[MAX_MUX]; /* GSM muxes */ static spinlock_t gsm_mux_lock; +static struct tty_driver *gsm_tty_driver; + /* * This section of the driver logic implements the GSM encodings * both the basic and the 'advanced'. Reliable transport is not @@ -1996,6 +1999,7 @@ int gsm_activate_mux(struct gsm_mux *gsm) spin_lock(&gsm_mux_lock); for (i = 0; i < MAX_MUX; i++) { if (gsm_mux[i] == NULL) { + gsm->num = i; gsm_mux[i] = gsm; break; } @@ -2101,13 +2105,20 @@ static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len) static int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) { - int ret; + int ret, i; + int base = gsm->num << 6; /* Base for this MUX */ gsm->tty = tty_kref_get(tty); gsm->output = gsmld_output; ret = gsm_activate_mux(gsm); if (ret != 0) tty_kref_put(gsm->tty); + else { + /* Don't register device 0 - this is the control channel and not + a usable tty interface */ + for (i = 1; i < NUM_DLCI; i++) + tty_register_device(gsm_tty_driver, base + i, NULL); + } return ret; } @@ -2122,7 +2133,12 @@ static int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) { + int i; + int base = gsm->num << 6; /* Base for this MUX */ + WARN_ON(tty != gsm->tty); + for (i = 1; i < NUM_DLCI; i++) + tty_unregister_device(gsm_tty_driver, base + i); gsm_cleanup_mux(gsm); tty_kref_put(gsm->tty); gsm->tty = NULL; @@ -2712,7 +2728,6 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state) return gsmtty_modem_update(dlci, encode); } -static struct tty_driver *gsm_tty_driver; /* Virtual ttys for the demux */ static const struct tty_operations gsmtty_ops = { -- cgit v1.2.3-18-g5258 From bcd5abe28f40cc6a935d3339cde27976f6be3f1a Mon Sep 17 00:00:00 2001 From: Russ Gorby Date: Thu, 16 Jun 2011 14:20:12 -0700 Subject: tty: n_gsm: Add raw-ip support This patch adds the ability to open a network data connection over a mux virtual tty channel. This is for modems that support data connections with raw IP frames instead of PPP. On high speed data connections this eliminates a significant amount of PPP overhead. To use this interface, the application must first tell the modem to open a network connection on a virtual tty. Once that has been accomplished, the app will issue an IOCTL on that virtual tty to create the network interface. The IOCTL will return the index of the interface created. The two IOCTL commands are: ioctl( fd, GSMIOC_ENABLE_NET ); ioctl( fd, GSMIOC_DISABLE_NET ); Signed-off-by: Russ Gorby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 279 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 270 insertions(+), 9 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index b288ff6cb81..9a13e510dae 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -58,6 +58,10 @@ #include #include #include +#include +#include +#include +#include #include static int debug; @@ -77,8 +81,24 @@ module_param(debug, int, 0600); * Semi-arbitrary buffer size limits. 0710 is normally run with 32-64 byte * limits so this is plenty */ -#define MAX_MRU 512 -#define MAX_MTU 512 +#define MAX_MRU 1500 +#define MAX_MTU 1500 +#define GSM_NET_TX_TIMEOUT (HZ*10) + +/** + * struct gsm_mux_net - network interface + * @struct gsm_dlci* dlci + * @struct net_device_stats stats; + * + * Created when net interface is initialized. + **/ +struct gsm_mux_net { + struct kref ref; + struct gsm_dlci *dlci; + struct net_device_stats stats; +}; + +#define STATS(net) (((struct gsm_mux_net *)netdev_priv(net))->stats) /* * Each block of data we have queued to go out is in the form of @@ -113,6 +133,7 @@ struct gsm_dlci { #define DLCI_OPENING 1 /* Sending SABM not seen UA */ #define DLCI_OPEN 2 /* SABM/UA complete */ #define DLCI_CLOSING 3 /* Sending DISC not seen UA/DM */ + struct mutex mutex; /* Link layer */ spinlock_t lock; /* Protects the internal state */ @@ -123,6 +144,7 @@ struct gsm_dlci { struct kfifo *fifo; /* Queue fifo for the DLCI */ struct kfifo _fifo; /* For new fifo API porting only */ int adaption; /* Adaption layer in use */ + int prev_adaption; u32 modem_rx; /* Our incoming virtual modem lines */ u32 modem_tx; /* Our outgoing modem lines */ int dead; /* Refuse re-open */ @@ -134,6 +156,8 @@ struct gsm_dlci { struct sk_buff_head skb_list; /* Queued frames */ /* Data handling callback */ void (*data)(struct gsm_dlci *dlci, u8 *data, int len); + void (*prev_data)(struct gsm_dlci *dlci, u8 *data, int len); + struct net_device *net; /* network interface, if created */ }; /* DLCI 0, 62/63 are special or reseved see gsmtty_open */ @@ -880,8 +904,10 @@ static int gsm_dlci_data_output_framed(struct gsm_mux *gsm, } memcpy(dp, skb_pull(dlci->skb, len), len); __gsm_data_queue(dlci, msg); - if (last) + if (last) { + kfree_skb(dlci->skb); dlci->skb = NULL; + } return size; } @@ -914,7 +940,7 @@ static void gsm_dlci_data_sweep(struct gsm_mux *gsm) i++; continue; } - if (dlci->adaption < 3) + if (dlci->adaption < 3 && !dlci->net) len = gsm_dlci_data_output(gsm, dlci); else len = gsm_dlci_data_output_framed(gsm, dlci); @@ -941,9 +967,12 @@ static void gsm_dlci_data_kick(struct gsm_dlci *dlci) spin_lock_irqsave(&dlci->gsm->tx_lock, flags); /* If we have nothing running then we need to fire up */ - if (dlci->gsm->tx_bytes == 0) - gsm_dlci_data_output(dlci->gsm, dlci); - else if (dlci->gsm->tx_bytes < TX_THRESH_LO) + if (dlci->gsm->tx_bytes == 0) { + if (dlci->net) + gsm_dlci_data_output_framed(dlci->gsm, dlci); + else + gsm_dlci_data_output(dlci->gsm, dlci); + } else if (dlci->gsm->tx_bytes < TX_THRESH_LO) gsm_dlci_data_sweep(dlci->gsm); spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags); } @@ -1577,6 +1606,7 @@ static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr) if (dlci == NULL) return NULL; spin_lock_init(&dlci->lock); + mutex_init(&dlci->mutex); dlci->fifo = &dlci->_fifo; if (kfifo_alloc(&dlci->_fifo, 4096, GFP_KERNEL) < 0) { kfree(dlci); @@ -2059,7 +2089,6 @@ struct gsm_mux *gsm_alloc_mux(void) gsm->t2 = T2; gsm->n2 = N2; gsm->ftype = UIH; - gsm->initiator = 0; gsm->adaption = 1; gsm->encoding = 1; gsm->mru = 64; /* Default to encoding 1 so these should be 64 */ @@ -2478,6 +2507,210 @@ static int gsmld_ioctl(struct tty_struct *tty, struct file *file, } } +/* + * Network interface + * + */ + +static int gsm_mux_net_open(struct net_device *net) +{ + pr_debug("%s called\n", __func__); + netif_start_queue(net); + return 0; +} + +static int gsm_mux_net_close(struct net_device *net) +{ + netif_stop_queue(net); + return 0; +} + +static struct net_device_stats *gsm_mux_net_get_stats(struct net_device *net) +{ + return &((struct gsm_mux_net *)netdev_priv(net))->stats; +} +static void dlci_net_free(struct gsm_dlci *dlci) +{ + if (!dlci->net) { + WARN_ON(1); + return; + } + dlci->adaption = dlci->prev_adaption; + dlci->data = dlci->prev_data; + free_netdev(dlci->net); + dlci->net = NULL; +} +static void net_free(struct kref *ref) +{ + struct gsm_mux_net *mux_net; + struct gsm_dlci *dlci; + + mux_net = container_of(ref, struct gsm_mux_net, ref); + dlci = mux_net->dlci; + + if (dlci->net) { + unregister_netdev(dlci->net); + dlci_net_free(dlci); + } +} + +static int gsm_mux_net_start_xmit(struct sk_buff *skb, + struct net_device *net) +{ + struct gsm_mux_net *mux_net = (struct gsm_mux_net *)netdev_priv(net); + struct gsm_dlci *dlci = mux_net->dlci; + kref_get(&mux_net->ref); + + skb_queue_head(&dlci->skb_list, skb); + STATS(net).tx_packets++; + STATS(net).tx_bytes += skb->len; + gsm_dlci_data_kick(dlci); + /* And tell the kernel when the last transmit started. */ + net->trans_start = jiffies; + kref_put(&mux_net->ref, net_free); + return NETDEV_TX_OK; +} + +/* called when a packet did not ack after watchdogtimeout */ +static void gsm_mux_net_tx_timeout(struct net_device *net) +{ + /* Tell syslog we are hosed. */ + dev_dbg(&net->dev, "Tx timed out.\n"); + + /* Update statistics */ + STATS(net).tx_errors++; +} + +static void gsm_mux_rx_netchar(struct gsm_dlci *dlci, + unsigned char *in_buf, int size) +{ + struct net_device *net = dlci->net; + struct sk_buff *skb; + struct gsm_mux_net *mux_net = (struct gsm_mux_net *)netdev_priv(net); + kref_get(&mux_net->ref); + + /* Allocate an sk_buff */ + skb = dev_alloc_skb(size + NET_IP_ALIGN); + if (!skb) { + /* We got no receive buffer. */ + STATS(net).rx_dropped++; + kref_put(&mux_net->ref, net_free); + return; + } + skb_reserve(skb, NET_IP_ALIGN); + memcpy(skb_put(skb, size), in_buf, size); + + skb->dev = net; + skb->protocol = __constant_htons(ETH_P_IP); + + /* Ship it off to the kernel */ + netif_rx(skb); + + /* update out statistics */ + STATS(net).rx_packets++; + STATS(net).rx_bytes += size; + kref_put(&mux_net->ref, net_free); + return; +} + +int gsm_change_mtu(struct net_device *net, int new_mtu) +{ + struct gsm_mux_net *mux_net = (struct gsm_mux_net *)netdev_priv(net); + if ((new_mtu < 8) || (new_mtu > mux_net->dlci->gsm->mtu)) + return -EINVAL; + net->mtu = new_mtu; + return 0; +} + +static void gsm_mux_net_init(struct net_device *net) +{ + static const struct net_device_ops gsm_netdev_ops = { + .ndo_open = gsm_mux_net_open, + .ndo_stop = gsm_mux_net_close, + .ndo_start_xmit = gsm_mux_net_start_xmit, + .ndo_tx_timeout = gsm_mux_net_tx_timeout, + .ndo_get_stats = gsm_mux_net_get_stats, + .ndo_change_mtu = gsm_change_mtu, + }; + + net->netdev_ops = &gsm_netdev_ops; + + /* fill in the other fields */ + net->watchdog_timeo = GSM_NET_TX_TIMEOUT; + net->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + net->type = ARPHRD_NONE; + net->tx_queue_len = 10; +} + + +/* caller holds the dlci mutex */ +static void gsm_destroy_network(struct gsm_dlci *dlci) +{ + struct gsm_mux_net *mux_net; + + pr_debug("destroy network interface"); + if (!dlci->net) + return; + mux_net = (struct gsm_mux_net *)netdev_priv(dlci->net); + kref_put(&mux_net->ref, net_free); +} + + +/* caller holds the dlci mutex */ +static int gsm_create_network(struct gsm_dlci *dlci, struct gsm_netconfig *nc) +{ + char *netname; + int retval = 0; + struct net_device *net; + struct gsm_mux_net *mux_net; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Already in a non tty mode */ + if (dlci->adaption > 2) + return -EBUSY; + + if (nc->protocol != htons(ETH_P_IP)) + return -EPROTONOSUPPORT; + + if (nc->adaption != 3 && nc->adaption != 4) + return -EPROTONOSUPPORT; + + pr_debug("create network interface"); + + netname = "gsm%d"; + if (nc->if_name[0] != '\0') + netname = nc->if_name; + net = alloc_netdev(sizeof(struct gsm_mux_net), + netname, + gsm_mux_net_init); + if (!net) { + pr_err("alloc_netdev failed"); + return -ENOMEM; + } + net->mtu = dlci->gsm->mtu; + mux_net = (struct gsm_mux_net *)netdev_priv(net); + mux_net->dlci = dlci; + kref_init(&mux_net->ref); + strncpy(nc->if_name, net->name, IFNAMSIZ); /* return net name */ + + /* reconfigure dlci for network */ + dlci->prev_adaption = dlci->adaption; + dlci->prev_data = dlci->data; + dlci->adaption = nc->adaption; + dlci->data = gsm_mux_rx_netchar; + dlci->net = net; + + pr_debug("register netdev"); + retval = register_netdev(net); + if (retval) { + pr_err("network register fail %d\n", retval); + dlci_net_free(dlci); + return retval; + } + return net->ifindex; /* return network index */ +} /* Line discipline for real tty */ struct tty_ldisc_ops tty_ldisc_packet = { @@ -2598,6 +2831,9 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp) struct gsm_dlci *dlci = tty->driver_data; if (dlci == NULL) return; + mutex_lock(&dlci->mutex); + gsm_destroy_network(dlci); + mutex_unlock(&dlci->mutex); if (tty_port_close_start(&dlci->port, tty, filp) == 0) return; gsm_dlci_begin_close(dlci); @@ -2679,7 +2915,32 @@ static int gsmtty_tiocmset(struct tty_struct *tty, static int gsmtty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { - return -ENOIOCTLCMD; + struct gsm_dlci *dlci = tty->driver_data; + struct gsm_netconfig nc; + int index; + + switch (cmd) { + case GSMIOC_ENABLE_NET: + if (copy_from_user(&nc, (void __user *)arg, sizeof(nc))) + return -EFAULT; + nc.if_name[IFNAMSIZ-1] = '\0'; + /* return net interface index or error code */ + mutex_lock(&dlci->mutex); + index = gsm_create_network(dlci, &nc); + mutex_unlock(&dlci->mutex); + if (copy_to_user((void __user *)arg, &nc, sizeof(nc))) + return -EFAULT; + return index; + case GSMIOC_DISABLE_NET: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + mutex_lock(&dlci->mutex); + gsm_destroy_network(dlci); + mutex_unlock(&dlci->mutex); + return 0; + default: + return -ENOIOCTLCMD; + } } static void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old) -- cgit v1.2.3-18-g5258 From 6ab8fba7fcb012a42d686abd33555b2215071415 Mon Sep 17 00:00:00 2001 From: Russ Gorby Date: Thu, 16 Jun 2011 14:20:13 -0700 Subject: tty: n_gsm: Added refcount usage to gsm_mux and gsm_dlci structs The gsm_mux is created/destroyed when ldisc is opened/closed but clients of the MUX channel devices (gsmttyN) may access this structure as long as the TTYs are open. For the open, the ldisc open is guaranteed to preceed the TTY open, but the close has no such guaranteed ordering. As a result, the gsm_mux can be freed in the ldisc close before being accessed by one of the TTY clients. This can happen if the ldisc is removed while there are open, active MUX channels. A similar situation exists for DLCI-0, it is basically a resource shared by MUX and DLCI , and should not be freed while they can be accessed To avoid this, gsm_mux and dlcis now have a reference counter ldisc open takes a reference on the mux and all the dlcis gsmtty_open takes a reference on the mux, dlci0 and its specific dlci. Dropping the last reference initiates the actual free. Signed-off-by: Russ Gorby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 111 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 91 insertions(+), 20 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 9a13e510dae..a38114b01fe 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -133,6 +133,7 @@ struct gsm_dlci { #define DLCI_OPENING 1 /* Sending SABM not seen UA */ #define DLCI_OPEN 2 /* SABM/UA complete */ #define DLCI_CLOSING 3 /* Sending DISC not seen UA/DM */ + struct kref ref; /* freed from port or mux close */ struct mutex mutex; /* Link layer */ @@ -194,6 +195,7 @@ struct gsm_mux { struct tty_struct *tty; /* The tty our ldisc is bound to */ spinlock_t lock; unsigned int num; + struct kref ref; /* Events on the GSM channel */ wait_queue_head_t event; @@ -1606,6 +1608,7 @@ static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr) if (dlci == NULL) return NULL; spin_lock_init(&dlci->lock); + kref_init(&dlci->ref); mutex_init(&dlci->mutex); dlci->fifo = &dlci->_fifo; if (kfifo_alloc(&dlci->_fifo, 4096, GFP_KERNEL) < 0) { @@ -1632,26 +1635,52 @@ static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr) } /** - * gsm_dlci_free - release DLCI + * gsm_dlci_free - free DLCI + * @dlci: DLCI to free + * + * Free up a DLCI. + * + * Can sleep. + */ +static void gsm_dlci_free(struct kref *ref) +{ + struct gsm_dlci *dlci = container_of(ref, struct gsm_dlci, ref); + + del_timer_sync(&dlci->t1); + dlci->gsm->dlci[dlci->addr] = NULL; + kfifo_free(dlci->fifo); + while ((dlci->skb = skb_dequeue(&dlci->skb_list))) + kfree_skb(dlci->skb); + kfree(dlci); +} + +static inline void dlci_get(struct gsm_dlci *dlci) +{ + kref_get(&dlci->ref); +} + +static inline void dlci_put(struct gsm_dlci *dlci) +{ + kref_put(&dlci->ref, gsm_dlci_free); +} + +/** + * gsm_dlci_release - release DLCI * @dlci: DLCI to destroy * - * Free up a DLCI. Currently to keep the lifetime rules sane we only - * clean up DLCI objects when the MUX closes rather than as the port - * is closed down on both the tty and mux levels. + * Release a DLCI. Actual free is deferred until either + * mux is closed or tty is closed - whichever is last. * * Can sleep. */ -static void gsm_dlci_free(struct gsm_dlci *dlci) +static void gsm_dlci_release(struct gsm_dlci *dlci) { struct tty_struct *tty = tty_port_tty_get(&dlci->port); if (tty) { tty_vhangup(tty); tty_kref_put(tty); } - del_timer_sync(&dlci->t1); - dlci->gsm->dlci[dlci->addr] = NULL; - kfifo_free(dlci->fifo); - kfree(dlci); + dlci_put(dlci); } /* @@ -1989,7 +2018,7 @@ void gsm_cleanup_mux(struct gsm_mux *gsm) /* Free up any link layer users */ for (i = 0; i < NUM_DLCI; i++) if (gsm->dlci[i]) - gsm_dlci_free(gsm->dlci[i]); + gsm_dlci_release(gsm->dlci[i]); /* Now wipe the queues */ for (txq = gsm->tx_head; txq != NULL; txq = gsm->tx_head) { gsm->tx_head = txq->next; @@ -2050,8 +2079,7 @@ EXPORT_SYMBOL_GPL(gsm_activate_mux); * gsm_free_mux - free up a mux * @mux: mux to free * - * Dispose of allocated resources for a dead mux. No refcounting - * at present so the mux must be truly dead. + * Dispose of allocated resources for a dead mux */ void gsm_free_mux(struct gsm_mux *gsm) { @@ -2061,6 +2089,28 @@ void gsm_free_mux(struct gsm_mux *gsm) } EXPORT_SYMBOL_GPL(gsm_free_mux); +/** + * gsm_free_muxr - free up a mux + * @mux: mux to free + * + * Dispose of allocated resources for a dead mux + */ +static void gsm_free_muxr(struct kref *ref) +{ + struct gsm_mux *gsm = container_of(ref, struct gsm_mux, ref); + gsm_free_mux(gsm); +} + +static inline void mux_get(struct gsm_mux *gsm) +{ + kref_get(&gsm->ref); +} + +static inline void mux_put(struct gsm_mux *gsm) +{ + kref_put(&gsm->ref, gsm_free_muxr); +} + /** * gsm_alloc_mux - allocate a mux * @@ -2084,6 +2134,7 @@ struct gsm_mux *gsm_alloc_mux(void) return NULL; } spin_lock_init(&gsm->lock); + kref_init(&gsm->ref); gsm->t1 = T1; gsm->t2 = T2; @@ -2255,7 +2306,7 @@ static void gsmld_close(struct tty_struct *tty) gsmld_flush_buffer(tty); /* Do other clean up here */ - gsm_free_mux(gsm); + mux_put(gsm); } /** @@ -2554,12 +2605,22 @@ static void net_free(struct kref *ref) } } +static inline void muxnet_get(struct gsm_mux_net *mux_net) +{ + kref_get(&mux_net->ref); +} + +static inline void muxnet_put(struct gsm_mux_net *mux_net) +{ + kref_put(&mux_net->ref, net_free); +} + static int gsm_mux_net_start_xmit(struct sk_buff *skb, struct net_device *net) { struct gsm_mux_net *mux_net = (struct gsm_mux_net *)netdev_priv(net); struct gsm_dlci *dlci = mux_net->dlci; - kref_get(&mux_net->ref); + muxnet_get(mux_net); skb_queue_head(&dlci->skb_list, skb); STATS(net).tx_packets++; @@ -2567,7 +2628,7 @@ static int gsm_mux_net_start_xmit(struct sk_buff *skb, gsm_dlci_data_kick(dlci); /* And tell the kernel when the last transmit started. */ net->trans_start = jiffies; - kref_put(&mux_net->ref, net_free); + muxnet_put(mux_net); return NETDEV_TX_OK; } @@ -2587,14 +2648,14 @@ static void gsm_mux_rx_netchar(struct gsm_dlci *dlci, struct net_device *net = dlci->net; struct sk_buff *skb; struct gsm_mux_net *mux_net = (struct gsm_mux_net *)netdev_priv(net); - kref_get(&mux_net->ref); + muxnet_get(mux_net); /* Allocate an sk_buff */ skb = dev_alloc_skb(size + NET_IP_ALIGN); if (!skb) { /* We got no receive buffer. */ STATS(net).rx_dropped++; - kref_put(&mux_net->ref, net_free); + muxnet_put(mux_net); return; } skb_reserve(skb, NET_IP_ALIGN); @@ -2609,7 +2670,7 @@ static void gsm_mux_rx_netchar(struct gsm_dlci *dlci, /* update out statistics */ STATS(net).rx_packets++; STATS(net).rx_bytes += size; - kref_put(&mux_net->ref, net_free); + muxnet_put(mux_net); return; } @@ -2652,7 +2713,7 @@ static void gsm_destroy_network(struct gsm_dlci *dlci) if (!dlci->net) return; mux_net = (struct gsm_mux_net *)netdev_priv(dlci->net); - kref_put(&mux_net->ref, net_free); + muxnet_put(mux_net); } @@ -2814,6 +2875,9 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp) port = &dlci->port; port->count++; tty->driver_data = dlci; + dlci_get(dlci); + dlci_get(dlci->gsm->dlci[0]); + mux_get(dlci->gsm); tty_port_tty_set(port, tty); dlci->modem_rx = 0; @@ -2829,16 +2893,23 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp) static void gsmtty_close(struct tty_struct *tty, struct file *filp) { struct gsm_dlci *dlci = tty->driver_data; + struct gsm_mux *gsm; + if (dlci == NULL) return; mutex_lock(&dlci->mutex); gsm_destroy_network(dlci); mutex_unlock(&dlci->mutex); + gsm = dlci->gsm; if (tty_port_close_start(&dlci->port, tty, filp) == 0) - return; + goto out; gsm_dlci_begin_close(dlci); tty_port_close_end(&dlci->port, tty); tty_port_tty_set(&dlci->port, NULL); +out: + dlci_put(dlci); + dlci_put(gsm->dlci[0]); + mux_put(gsm); } static void gsmtty_hangup(struct tty_struct *tty) -- cgit v1.2.3-18-g5258 From 5a3c6b251d587715f8b87a50216e4c085c655777 Mon Sep 17 00:00:00 2001 From: Manuel Zerpies Date: Thu, 16 Jun 2011 14:07:22 +0200 Subject: drivers/tty: use printk_ratelimited() instead of printk_ratelimit() Since the printk_ratelimit() shouldn't be used anymore (see comment in include/linux/printk.h), replace it with printk_ratelimited(). Signed-off-by: Manuel Zerpies Signed-off-by: Greg Kroah-Hartman --- drivers/tty/moxa.c | 5 +++-- drivers/tty/mxser.c | 4 ++-- drivers/tty/tty_io.c | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index ba679ce0a77..d15a071b1a5 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -242,8 +243,8 @@ static void moxa_wait_finish(void __iomem *ofsAddr) while (readw(ofsAddr + FuncCode) != 0) if (time_after(jiffies, end)) return; - if (readw(ofsAddr + FuncCode) != 0 && printk_ratelimit()) - printk(KERN_WARNING "moxa function expired\n"); + if (readw(ofsAddr + FuncCode) != 0) + printk_ratelimited(KERN_WARNING "moxa function expired\n"); } static void moxafunc(void __iomem *ofsAddr, u16 cmd, u16 arg) diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index d188f378684..7fc8c02fea6 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -1490,8 +1491,7 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) switch (cmd) { case MOXA_GET_MAJOR: - if (printk_ratelimit()) - printk(KERN_WARNING "mxser: '%s' uses deprecated ioctl " + printk_ratelimited(KERN_WARNING "mxser: '%s' uses deprecated ioctl " "%x (GET_MAJOR), fix your userspace\n", current->comm, cmd); return put_user(ttymajor, (int __user *)argp); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 6556f7452ba..150e4f747c7 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -94,6 +94,7 @@ #include #include #include +#include #include #include @@ -1420,8 +1421,7 @@ err_module_put: /* call the tty release_tty routine to clean out this slot */ err_release_tty: - if (printk_ratelimit()) - printk(KERN_INFO "tty_init_dev: ldisc open failed, " + printk_ratelimited(KERN_INFO "tty_init_dev: ldisc open failed, " "clearing slot %d\n", idx); release_tty(tty, idx); return ERR_PTR(retval); -- cgit v1.2.3-18-g5258 From 7c365bac4437887382d53be2f735b34634d68c3b Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Thu, 16 Jun 2011 18:31:42 -0400 Subject: serial: bfin_5xx: fix off-by-one with resource size This doesn't cause any real bugs, but it should still be fixed. Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/bfin_5xx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/bfin_5xx.c b/drivers/tty/serial/bfin_5xx.c index 9b1ff2b6bb3..957135a1974 100644 --- a/drivers/tty/serial/bfin_5xx.c +++ b/drivers/tty/serial/bfin_5xx.c @@ -1304,8 +1304,7 @@ static int bfin_serial_probe(struct platform_device *pdev) goto out_error_free_peripherals; } - uart->port.membase = ioremap(res->start, - res->end - res->start); + uart->port.membase = ioremap(res->start, resource_size(res)); if (!uart->port.membase) { dev_err(&pdev->dev, "Cannot map uart IO\n"); ret = -ENXIO; -- cgit v1.2.3-18-g5258 From 5568181f188ae9485a0cdbea5ea48f63d186a298 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 17 Jun 2011 09:45:07 -0600 Subject: tty/serial: Fix XSCALE serial ports, e.g. ce4100 Commit 4539c24fe4f92c09ee668ef959d3e8180df619b9 "tty/serial: Add explicit PORT_TEGRA type" introduced separate flags describing the need for IER bits UUE and RTOIE. Both bits are required for the XSCALE port type. While that patch updated uart_config[] as required, the auto-probing code wasn't updated to set the RTOIE flag when an XSCALE port type was detected. This caused such ports to stop working. This patch rectifies that. Reported-by: Sebastian Andrzej Siewior Tested-by: Sebastian Andrzej Siewior Signed-off-by: Stephen Warren Cc: stable [3.0] Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index f11df87fa24..f1422a64676 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -1107,7 +1107,7 @@ static void autoconfig_16550a(struct uart_8250_port *up) */ DEBUG_AUTOCONF("Xscale "); up->port.type = PORT_XSCALE; - up->capabilities |= UART_CAP_UUE; + up->capabilities |= UART_CAP_UUE | UART_CAP_RTOIE; return; } } else { -- cgit v1.2.3-18-g5258 From 40eb0de5ba7e88e30349f336a38cf22016b8c07f Mon Sep 17 00:00:00 2001 From: Jongpill Lee Date: Wed, 22 Jun 2011 17:50:25 +0900 Subject: tty: s5pv210: Add delay loop on fifo reset function for UART This patch addes delay loop on fifo reset function for UART. On high speed freq, it needs delay function when fifo reset. If not, system will hang by this uart reset problem when resuming from suspend mode. Signed-off-by: Jongpill Lee Signed-off-by: Jaecheol Lee Signed-off-by: Kukjin Kim Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/s5pv210.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/s5pv210.c b/drivers/tty/serial/s5pv210.c index fb2619f93d8..dce6cb3dbad 100644 --- a/drivers/tty/serial/s5pv210.c +++ b/drivers/tty/serial/s5pv210.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -83,6 +84,9 @@ static int s5pv210_serial_resetport(struct uart_port *port, wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); wr_regl(port, S3C2410_UFCON, cfg->ufcon); + /* It is need to delay When reset FIFO register */ + udelay(1); + return 0; } -- cgit v1.2.3-18-g5258 From f2934c3c519b58f84082652447e62ab4a76c53ec Mon Sep 17 00:00:00 2001 From: William Douglas Date: Thu, 23 Jun 2011 13:38:36 +0100 Subject: mrst_max3110: Change max missing message priority. Change print message to notice instead of error to clean up non critcal messages showing on startup. The MAX3111 not being present is a normal path for end user systems. Signed-off-by: William Douglas [rebased on 3.0, switched to dev_dbg()] Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mrst_max3110.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index 1bd28450ca4..3be1b0dd6f2 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -823,7 +823,7 @@ static int __devinit serial_m3110_probe(struct spi_device *spi) res = RC_TAG; ret = max3110_write_then_read(max, (u8 *)&res, (u8 *)&res, 2, 0); if (ret < 0 || res == 0 || res == 0xffff) { - printk(KERN_ERR "MAX3111 deemed not present (conf reg %04x)", + dev_dbg(&spi->dev, "MAX3111 deemed not present (conf reg %04x)", res); ret = -ENODEV; goto err_get_page; -- cgit v1.2.3-18-g5258 From 9c00c6e7f9137b669e691219ea871eeb2c67c3d0 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 23 Jun 2011 13:39:00 +0100 Subject: serial: mrst_max3110: initialize waitqueue earlier The driver went to initialize its waitqueue at the start of the main processing thread. However, it is possible that this thread is not scheduled on a CPU before the write function is called which leads to a following error: BUG: spinlock bad magic on CPU#1, swapper/1 lock: f5f3ebdc, .magic: 00000000, .owner: /-1, .owner_cpu: 0 Pid: 1, comm: swapper Not tainted 3.0.0-rc2+ #67 Call Trace: [] spin_bug+0xa3/0xf0 [] do_raw_spin_lock+0x7d/0x150 [] ? init_idle+0x8d/0x20c [] _raw_spin_lock_irqsave+0x4e/0x60 [] ? __wake_up+0x1b/0x50 [] __wake_up+0x1b/0x50 [] ? uart_console_write+0x4c/0x60 [] ? serial_m3110_enable_ms+0x10/0x10 [] serial_m3110_con_write+0x55/0x60 [] __call_console_drivers+0x75/0x90 [] _call_console_drivers+0x49/0x80 [] console_unlock+0xca/0x1f0 [] vprintk+0x18f/0x4f0 [] ? trace_hardirqs_on+0xb/0x10 [] printk+0x18/0x1a [] register_console+0x2e0/0x350 [] uart_add_one_port+0x33e/0x3d0 [] ? trace_hardirqs_on+0xb/0x10 [] ? try_to_wake_up+0x18b/0x250 [] serial_m3110_probe+0x1c2/0x1df [] ? serial_m3110_suspend+0x40/0x40 [] spi_drv_probe+0x17/0x20 ... We fix this by initializing the waitqueue before the main thread is created. Signed-off-by: Mika Westerberg Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mrst_max3110.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index 3be1b0dd6f2..a764bf99743 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -421,7 +421,6 @@ static int max3110_main_thread(void *_max) int ret = 0; struct circ_buf *xmit = &max->con_xmit; - init_waitqueue_head(wq); pr_info(PR_FMT "start main thread\n"); do { @@ -838,6 +837,8 @@ static int __devinit serial_m3110_probe(struct spi_device *spi) max->con_xmit.head = 0; max->con_xmit.tail = 0; + init_waitqueue_head(&max->wq); + max->main_thread = kthread_run(max3110_main_thread, max, "max3110_main"); if (IS_ERR(max->main_thread)) { -- cgit v1.2.3-18-g5258 From f2eb3cdf14457fccb14ae8c4d7d7cee088cd3957 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Mon, 27 Jun 2011 14:26:56 +0100 Subject: SERIAL: SC26xx: Fix link error. Kconfig allows enabling console support for the SC26xx driver even when it's configured as a module resulting in a: ERROR: "uart_console_device" [drivers/tty/serial/sc26xx.ko] undefined! modpost error since the driver was merged in eea63e0e8a60d00485b47fb6e75d9aa2566b989b [SC26XX: New serial driver for SC2681 uarts] in 2.6.25. Fixed by only allowing console support to be enabled if the driver is builtin. Signed-off-by: Ralf Baechle Cc: linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: linux-mips@linux-mips.org Cc: stable Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 636144cea93..b3692e6e3c1 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1419,7 +1419,7 @@ config SERIAL_SC26XX config SERIAL_SC26XX_CONSOLE bool "Console on SC2681/SC2692 serial port" - depends on SERIAL_SC26XX + depends on SERIAL_SC26XX=y select SERIAL_CORE_CONSOLE help Support for Console on SC2681/SC2692 serial ports. -- cgit v1.2.3-18-g5258 From aef7fe5274af3e608e0f3eaf0fea6d3c78ffb92e Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Wed, 29 Jun 2011 15:28:24 +0900 Subject: tty: serial: samsung.c remove legacy PM code. This patch "modernize" tty/serial/samsung.c to use non-legacy code for suspend/resume. Signed-off-by: MyungJoo Ham Signed-off-by: KyungMin Park Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index f66f6482930..34b8afe5f47 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1194,12 +1194,10 @@ int __devexit s3c24xx_serial_remove(struct platform_device *dev) EXPORT_SYMBOL_GPL(s3c24xx_serial_remove); /* UART power management code */ - -#ifdef CONFIG_PM - -static int s3c24xx_serial_suspend(struct platform_device *dev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int s3c24xx_serial_suspend(struct device *dev) { - struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); + struct uart_port *port = s3c24xx_dev_to_port(dev); if (port) uart_suspend_port(&s3c24xx_uart_drv, port); @@ -1207,9 +1205,9 @@ static int s3c24xx_serial_suspend(struct platform_device *dev, pm_message_t stat return 0; } -static int s3c24xx_serial_resume(struct platform_device *dev) +static int s3c24xx_serial_resume(struct device *dev) { - struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); + struct uart_port *port = s3c24xx_dev_to_port(dev); struct s3c24xx_uart_port *ourport = to_ourport(port); if (port) { @@ -1222,17 +1220,20 @@ static int s3c24xx_serial_resume(struct platform_device *dev) return 0; } -#endif + +static const struct dev_pm_ops s3c24xx_serial_pm_ops = { + .suspend = s3c24xx_serial_suspend, + .resume = s3c24xx_serial_resume, +}; +#else /* !CONFIG_PM_SLEEP */ +#define s3c24xx_serial_pm_ops NULL +#endif /* CONFIG_PM_SLEEP */ int s3c24xx_serial_init(struct platform_driver *drv, struct s3c24xx_uart_info *info) { dbg("s3c24xx_serial_init(%p,%p)\n", drv, info); - -#ifdef CONFIG_PM - drv->suspend = s3c24xx_serial_suspend; - drv->resume = s3c24xx_serial_resume; -#endif + drv->driver.pm = &s3c24xx_serial_pm_ops; return platform_driver_register(drv); } -- cgit v1.2.3-18-g5258 From 7b292b4bf9a9d6098440d85616d6ca4c608b8304 Mon Sep 17 00:00:00 2001 From: Andrew McGregor Date: Mon, 13 Jun 2011 11:31:31 +1200 Subject: tty: fix "IRQ45: nobody cared" Unthrottling the TTY during close ends up enabling interrupts on a device not on the active list, which will never have the interrupts cleared. Doctor, it hurts when I do this. >>> On 6/2/2011 at 01:56 AM, in message <20110601145608.3e586e16@bob.linux.org.uk>, Alan Cox wrote: > On Wed, 01 Jun 2011 10:34:07 +1200 > "andrew mcgregor" wrote: > > The LKML message > > http://kerneltrap.org/mailarchive/linux-kernel/2010/2/25/4541847 from > > February doesn't seem to have been resolved since. We struck the > > issue, and the patch below (against 2.6.32) fixes it. Should I > > supply a patch against 3.0.0rc? > > I think that would be sensible. I don't actually see how you hit it as > the IRQ ought to be masked by then but it's certainly wrong for n_tty > to be calling into check_unthrottle at that point. > > So yes please send a patch with a suitable Signed-off-by: line to > linux-serial and cc GregKH as well. > > Alan Signed-off-by: Andrew McGregor Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 0ad32888091..1ddc5d7c959 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -185,7 +185,6 @@ static void reset_buffer_flags(struct tty_struct *tty) tty->canon_head = tty->canon_data = tty->erasing = 0; memset(&tty->read_flags, 0, sizeof tty->read_flags); n_tty_set_room(tty); - check_unthrottle(tty); } /** @@ -1587,6 +1586,7 @@ static int n_tty_open(struct tty_struct *tty) return -ENOMEM; } reset_buffer_flags(tty); + tty_unthrottle(tty); tty->column = 0; n_tty_set_termios(tty, NULL); tty->minimum_to_wake = 1; -- cgit v1.2.3-18-g5258 From e463595fd9c752fa4bf06b47df93ef9ade3c7cf0 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Mon, 4 Jul 2011 08:58:31 +0200 Subject: pch_uart: Add MSI support Signed-off-by: Alexander Stein Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/pch_uart.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index ae28250b0be..35cb9af893b 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -1426,6 +1426,8 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, goto init_port_hal_free; } + pci_enable_msi(pdev); + iobase = pci_resource_start(pdev, 0); mapbase = pci_resource_start(pdev, 1); priv->mapbase = mapbase; @@ -1482,6 +1484,8 @@ static void pch_uart_pci_remove(struct pci_dev *pdev) struct eg20t_port *priv; priv = (struct eg20t_port *)pci_get_drvdata(pdev); + + pci_disable_msi(pdev); pch_uart_exit_port(priv); pci_disable_device(pdev); kfree(priv); @@ -1565,6 +1569,7 @@ static int __devinit pch_uart_pci_probe(struct pci_dev *pdev, return ret; probe_disable_device: + pci_disable_msi(pdev); pci_disable_device(pdev); probe_error: return ret; -- cgit v1.2.3-18-g5258 From bff52fd458a1bca06266449b0ab8a43e9a50d240 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Mon, 4 Jul 2011 15:35:51 +0200 Subject: pch_uart: add missing comment about OKI ML7223 Signed-off-by: Alexander Stein Reviewed-by: Jesper Juhl Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/pch_uart.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 35cb9af893b..03736828530 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -45,6 +45,7 @@ enum { /* Set the max number of UART port * Intel EG20T PCH: 4 port * OKI SEMICONDUCTOR ML7213 IOH: 3 port + * OKI SEMICONDUCTOR ML7223 IOH: 2 port */ #define PCH_UART_NR 4 -- cgit v1.2.3-18-g5258 From f086ced17191fa0c5712539d2b680eae3dc972a1 Mon Sep 17 00:00:00 2001 From: "Du, Alek" Date: Thu, 7 Jul 2011 15:16:48 +0100 Subject: n_gsm: fix the wrong FCS handling FCS could be GSM0_SOF, so will break state machine... [This byte isn't quoted in any way so a SOF here doesn't imply an error occurred.] Signed-off-by: Alek Du Signed-off-by: Alan Cox Cc: stable [3.0] [Trivial but best backported once its in 3.1rc I think] Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index a38114b01fe..14522ee8a91 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -1871,10 +1871,6 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c) break; case GSM_FCS: /* FCS follows the packet */ gsm->received_fcs = c; - if (c == GSM0_SOF) { - gsm->state = GSM_SEARCH; - break; - } gsm_queue(gsm); gsm->state = GSM_SSOF; break; -- cgit v1.2.3-18-g5258 From def90f4239f094f3846c108c1c41a4cd55c33e8e Mon Sep 17 00:00:00 2001 From: Shreshtha Kumar Sahu Date: Thu, 9 Jun 2011 22:56:32 +0200 Subject: amba pl011: workaround for uart registers lockup This workaround aims to break the deadlock situation which raises during continuous transfer of data for long duration over uart with hardware flow control. It is observed that CTS interrupt cannot be cleared in uart interrupt register (ICR). Hence further transfer over uart gets blocked. It is seen that during such deadlock condition ICR don't get cleared even on multiple write. This leads pass_counter to decrease and finally reach zero. This can be taken as trigger point to run this UART_BT_WA. Workaround backups the register configuration, does soft reset of UART using BIT-0 of PRCC_K_SOFTRST_SET/CLEAR registers and restores the registers. This patch also provides support for uart init and exit function calls if present. Signed-off-by: Shreshtha Kumar Sahu Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 123 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 8dc0541feec..f5f6831b0a6 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include @@ -65,6 +66,30 @@ #define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE) #define UART_DUMMY_DR_RX (1 << 16) + +#define UART_WA_SAVE_NR 14 + +static void pl011_lockup_wa(unsigned long data); +static const u32 uart_wa_reg[UART_WA_SAVE_NR] = { + ST_UART011_DMAWM, + ST_UART011_TIMEOUT, + ST_UART011_LCRH_RX, + UART011_IBRD, + UART011_FBRD, + ST_UART011_LCRH_TX, + UART011_IFLS, + ST_UART011_XFCR, + ST_UART011_XON1, + ST_UART011_XON2, + ST_UART011_XOFF1, + ST_UART011_XOFF2, + UART011_CR, + UART011_IMSC +}; + +static u32 uart_wa_regdata[UART_WA_SAVE_NR]; +static DECLARE_TASKLET(pl011_lockup_tlet, pl011_lockup_wa, 0); + /* There is by now at least one vendor with differing details, so handle it */ struct vendor_data { unsigned int ifls; @@ -72,6 +97,7 @@ struct vendor_data { unsigned int lcrh_tx; unsigned int lcrh_rx; bool oversampling; + bool interrupt_may_hang; /* vendor-specific */ bool dma_threshold; }; @@ -90,9 +116,12 @@ static struct vendor_data vendor_st = { .lcrh_tx = ST_UART011_LCRH_TX, .lcrh_rx = ST_UART011_LCRH_RX, .oversampling = true, + .interrupt_may_hang = true, .dma_threshold = true, }; +static struct uart_amba_port *amba_ports[UART_NR]; + /* Deals with DMA transactions */ struct pl011_sgbuf { @@ -132,6 +161,7 @@ struct uart_amba_port { unsigned int lcrh_rx; /* vendor-specific */ bool autorts; char type[12]; + bool interrupt_may_hang; /* vendor-specific */ #ifdef CONFIG_DMA_ENGINE /* DMA stuff */ bool using_tx_dma; @@ -1008,6 +1038,68 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap) #endif +/* + * pl011_lockup_wa + * This workaround aims to break the deadlock situation + * when after long transfer over uart in hardware flow + * control, uart interrupt registers cannot be cleared. + * Hence uart transfer gets blocked. + * + * It is seen that during such deadlock condition ICR + * don't get cleared even on multiple write. This leads + * pass_counter to decrease and finally reach zero. This + * can be taken as trigger point to run this UART_BT_WA. + * + */ +static void pl011_lockup_wa(unsigned long data) +{ + struct uart_amba_port *uap = amba_ports[0]; + void __iomem *base = uap->port.membase; + struct circ_buf *xmit = &uap->port.state->xmit; + struct tty_struct *tty = uap->port.state->port.tty; + int buf_empty_retries = 200; + int loop; + + /* Stop HCI layer from submitting data for tx */ + tty->hw_stopped = 1; + while (!uart_circ_empty(xmit)) { + if (buf_empty_retries-- == 0) + break; + udelay(100); + } + + /* Backup registers */ + for (loop = 0; loop < UART_WA_SAVE_NR; loop++) + uart_wa_regdata[loop] = readl(base + uart_wa_reg[loop]); + + /* Disable UART so that FIFO data is flushed out */ + writew(0x00, uap->port.membase + UART011_CR); + + /* Soft reset UART module */ + if (uap->port.dev->platform_data) { + struct amba_pl011_data *plat; + + plat = uap->port.dev->platform_data; + if (plat->reset) + plat->reset(); + } + + /* Restore registers */ + for (loop = 0; loop < UART_WA_SAVE_NR; loop++) + writew(uart_wa_regdata[loop] , + uap->port.membase + uart_wa_reg[loop]); + + /* Initialise the old status of the modem signals */ + uap->old_status = readw(uap->port.membase + UART01x_FR) & + UART01x_FR_MODEM_ANY; + + if (readl(base + UART011_MIS) & 0x2) + printk(KERN_EMERG "UART_BT_WA: ***FAILED***\n"); + + /* Start Tx/Rx */ + tty->hw_stopped = 0; +} + static void pl011_stop_tx(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; @@ -1158,8 +1250,11 @@ static irqreturn_t pl011_int(int irq, void *dev_id) if (status & UART011_TXIS) pl011_tx_chars(uap); - if (pass_counter-- == 0) + if (pass_counter-- == 0) { + if (uap->interrupt_may_hang) + tasklet_schedule(&pl011_lockup_tlet); break; + } status = readw(uap->port.membase + UART011_MIS); } while (status != 0); @@ -1339,6 +1434,14 @@ static int pl011_startup(struct uart_port *port) writew(uap->im, uap->port.membase + UART011_IMSC); spin_unlock_irq(&uap->port.lock); + if (uap->port.dev->platform_data) { + struct amba_pl011_data *plat; + + plat = uap->port.dev->platform_data; + if (plat->init) + plat->init(); + } + return 0; clk_dis: @@ -1394,6 +1497,15 @@ static void pl011_shutdown(struct uart_port *port) * Shut down the clock producer */ clk_disable(uap->clk); + + if (uap->port.dev->platform_data) { + struct amba_pl011_data *plat; + + plat = uap->port.dev->platform_data; + if (plat->exit) + plat->exit(); + } + } static void @@ -1700,6 +1812,14 @@ static int __init pl011_console_setup(struct console *co, char *options) if (!uap) return -ENODEV; + if (uap->port.dev->platform_data) { + struct amba_pl011_data *plat; + + plat = uap->port.dev->platform_data; + if (plat->init) + plat->init(); + } + uap->port.uartclk = clk_get_rate(uap->clk); if (options) @@ -1774,6 +1894,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) uap->lcrh_rx = vendor->lcrh_rx; uap->lcrh_tx = vendor->lcrh_tx; uap->fifosize = vendor->fifosize; + uap->interrupt_may_hang = vendor->interrupt_may_hang; uap->port.dev = &dev->dev; uap->port.mapbase = dev->res.start; uap->port.membase = base; -- cgit v1.2.3-18-g5258