diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 511f22489754..30f73ab2491f 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -46,10 +46,12 @@
 #include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/poll.h>
+#include <linux/proc_fs.h>
 #include <linux/bitops.h>
 #include <linux/file.h>
 #include <linux/uaccess.h>
 #include <linux/module.h>
+#include <linux/stringify.h>
 #include <linux/timer.h>
 #include <linux/tty_flip.h>
 #include <linux/tty_driver.h>
@@ -187,7 +189,7 @@ struct gsm_dlci {
 	void (*prev_data)(struct gsm_dlci *dlci, const u8 *data, int len);
 	struct net_device *net; /* network interface, if created */
 
-	/* Statistics (not currently exposed) */
+	/* Statistics (exposed via proc file) */
 	u64 tx;			/* Data bytes sent on this DLCI */
 	u64 rx;			/* Data bytes received on this DLCI */
 };
@@ -272,6 +274,7 @@ enum gsm_mux_state {
 
 struct gsm_mux {
 	struct tty_struct *tty;		/* The tty our ldisc is bound to */
+	struct proc_dir_entry *proc;	/* Associated proc fs entry */
 	spinlock_t lock;
 	struct mutex mutex;
 	unsigned int num;
@@ -339,7 +342,7 @@ struct gsm_mux {
 	bool wait_config;	/* Wait for configuration by ioctl before DLCI open */
 	u32 keep_alive;		/* Control channel keep-alive in 10ms */
 
-	/* Statistics (not currently exposed) */
+	/* Statistics (exposed via proc file) */
 	unsigned long bad_fcs;
 	unsigned long malformed;
 	unsigned long io_error;
@@ -357,6 +360,7 @@ struct gsm_mux {
 #define MAX_MUX		4			/* 256 minors */
 static struct gsm_mux *gsm_mux[MAX_MUX];	/* GSM muxes */
 static DEFINE_SPINLOCK(gsm_mux_lock);
+static DEFINE_MUTEX(gsm_mux_mutex);
 
 static struct tty_driver *gsm_tty_driver;
 
@@ -3079,6 +3083,153 @@ static void gsm_error(struct gsm_mux *gsm)
 	gsm->io_error++;
 }
 
+/*
+ * Entry to the proc file system in tty/ldisc/n_gsm/
+ */
+
+static struct proc_dir_entry *proc_gsm;
+
+/**
+ * gsm_proc_print_flag	-	check and print termios flag
+ * @m: output handle
+ * @flags: termios flags
+ * @val: flag to check and print
+ * @str: string representation of val
+ * @first: does not need separator?
+ */
+static bool gsm_proc_print_flag(struct seq_file *m, unsigned short flags,
+				int val, const char *str, bool first)
+{
+	if ((flags & val) == 0)
+		return first;
+	if (!first)
+		seq_putc(m, '|');
+	seq_puts(m, str);
+	return false;
+}
+
+#define gsm_proc_print_flag_str(m, flags, val) \
+	(first = gsm_proc_print_flag((m), (flags), (val), __stringify(val), first))
+
+/**
+ * gsm_proc_show	-	output proc file
+ * @m: output handle
+ * @v: result from start
+ *
+ * Handles the output of /proc/tty/ldisc/n_gsm/mux%d
+ */
+static int gsm_proc_show(struct seq_file *m, void *v)
+{
+	struct gsm_mux *gsm = m->private;
+	struct gsm_dlci *dlci = NULL;
+	unsigned short flags;
+	int i;
+	bool first = true;
+	const char *state, *ftype;
+
+	if (!gsm)
+		return -ENODEV;
+
+	/* The proc file may get removed in gsm_cleanup_mux() if the connection
+	 * was closed. Early out here to avoid a deadlock.
+	 */
+	if (!mutex_trylock(&gsm_mux_mutex))
+		return -EBUSY;
+	if (!mutex_trylock(&gsm->mutex)) {
+		mutex_unlock(&gsm_mux_mutex);
+		return -EBUSY;
+	}
+
+	seq_printf(m, "tty:%s flags:", tty_name(gsm->tty));
+	flags = gsm->tty->termios.c_iflag;
+	gsm_proc_print_flag_str(m, flags, IGNBRK);
+	gsm_proc_print_flag_str(m, flags, IXON);
+	gsm_proc_print_flag_str(m, flags, IXOFF);
+	flags = gsm->tty->termios.c_cflag;
+	gsm_proc_print_flag_str(m, flags, CLOCAL);
+	gsm_proc_print_flag_str(m, flags, CRTSCTS);
+	seq_putc(m, '\n');
+
+	seq_printf(m, "initiator:%d mode:%d mru:%u mtu:%u t1:%d t2:%d t3:%d",
+		   gsm->initiator, gsm->encoding, gsm->mru, gsm->mtu, gsm->t1,
+		   gsm->t2, gsm->t3);
+	seq_printf(m, " n2:%d k:%d wc:%d ka:%u", gsm->n2, gsm->k,
+		   gsm->wait_config ? 1 : 0, gsm->keep_alive);
+	if (gsm->dead)
+		seq_puts(m, " DEAD");
+	seq_putc(m, '\n');
+
+	seq_printf(m, "bad_fcs:%lu malformed:%lu io_error:%lu open_error:%lu",
+		   gsm->bad_fcs, gsm->malformed, gsm->io_error,
+		   gsm->open_error);
+	seq_printf(m, " bad_size:%lu unsupported:%lu\n\n", gsm->bad_size,
+		   gsm->unsupported);
+
+	for (i = 0; i < NUM_DLCI; i++) {
+		dlci = gsm->dlci[i];
+		if (!dlci)
+			continue;
+		switch (dlci->state) {
+		case DLCI_CLOSED:
+			state = "CLOSED";
+			break;
+		case DLCI_WAITING_CONFIG:
+			state = "WAITING_CONFIG";
+			break;
+		case DLCI_CONFIGURE:
+			state = "CONFIGURE";
+			break;
+		case DLCI_OPENING:
+			state = "OPENING";
+			break;
+		case DLCI_OPEN:
+			state = "OPEN";
+			break;
+		case DLCI_CLOSING:
+			state = "CLOSING";
+			break;
+		default:
+			state = "???";
+			break;
+		}
+		switch (dlci->ftype) {
+		case UI:
+			ftype = "UI";
+			break;
+		case UIH:
+			ftype = "UIH";
+			break;
+		default:
+			ftype = "???";
+			break;
+		}
+		seq_printf(m, "dlci:%d state:%s cl:%d prio:%d i:%s k:%d mtu:%u",
+			   i, state, dlci->adaption, dlci->prio, ftype, dlci->k,
+			   dlci->mtu);
+		seq_printf(m, " tx:%llu rx:%llu", dlci->tx, dlci->rx);
+		if (dlci->dead)
+			seq_puts(m, " DEAD");
+		seq_putc(m, '\n');
+	}
+
+	mutex_unlock(&gsm->mutex);
+	mutex_unlock(&gsm_mux_mutex);
+
+	return 0;
+}
+
+static int gsm_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, gsm_proc_show, pde_data(inode));
+}
+
+static const struct proc_ops gsm_proc_ops = {
+	.proc_open    = gsm_proc_open,
+	.proc_read    = seq_read,
+	.proc_lseek   = seq_lseek,
+	.proc_release = single_release,
+};
+
 /**
  *	gsm_cleanup_mux		-	generic GSM protocol cleanup
  *	@gsm: our mux
@@ -3131,6 +3282,9 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc)
 	list_for_each_entry_safe(txq, ntxq, &gsm->tx_data_list, list)
 		kfree(txq);
 	INIT_LIST_HEAD(&gsm->tx_data_list);
+
+	if (gsm->proc)
+		proc_remove(gsm->proc);
 }
 
 /**
@@ -3145,6 +3299,7 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc)
 static int gsm_activate_mux(struct gsm_mux *gsm)
 {
 	struct gsm_dlci *dlci;
+	char pbuf[8];
 	int ret;
 
 	dlci = gsm_dlci_alloc(gsm, 0);
@@ -3162,6 +3317,9 @@ static int gsm_activate_mux(struct gsm_mux *gsm)
 
 	gsm->has_devices = true;
 	gsm->dead = false;		/* Tty opens are now permissible */
+
+	if (proc_gsm && snprintf(pbuf, sizeof(pbuf), "mux%u", gsm->num) > 0)
+		gsm->proc = proc_create_data(pbuf, 0444, proc_gsm, &gsm_proc_ops, gsm);
 	return 0;
 }
 
@@ -3596,6 +3754,8 @@ static void gsmld_close(struct tty_struct *tty)
 {
 	struct gsm_mux *gsm = tty->disc_data;
 
+	mutex_lock(&gsm_mux_mutex);
+
 	/* The ldisc locks and closes the port before calling our close. This
 	 * means we have no way to do a proper disconnect. We will not bother
 	 * to do one.
@@ -3607,6 +3767,8 @@ static void gsmld_close(struct tty_struct *tty)
 	gsmld_flush_buffer(tty);
 	/* Do other clean up here */
 	mux_put(gsm);
+
+	mutex_unlock(&gsm_mux_mutex);
 }
 
 /**
@@ -3622,14 +3784,20 @@ static void gsmld_close(struct tty_struct *tty)
 static int gsmld_open(struct tty_struct *tty)
 {
 	struct gsm_mux *gsm;
+	int ret = 0;
 
-	if (tty->ops->write == NULL)
-		return -EINVAL;
+	mutex_lock(&gsm_mux_mutex);
+	if (tty->ops->write == NULL) {
+		ret = -EINVAL;
+		goto err_unlock;
+	}
 
 	/* Attach our ldisc data */
 	gsm = gsm_alloc_mux();
-	if (gsm == NULL)
-		return -ENOMEM;
+	if (gsm == NULL) {
+		ret = -ENOMEM;
+		goto err_unlock;
+	}
 
 	tty->disc_data = gsm;
 	tty->receive_room = 65536;
@@ -3645,7 +3813,9 @@ static int gsmld_open(struct tty_struct *tty)
 	else
 		gsm->receive = gsm1_receive;
 
-	return 0;
+err_unlock:
+	mutex_unlock(&gsm_mux_mutex);
+	return ret;
 }
 
 /**
@@ -3769,17 +3939,24 @@ static int gsmld_ioctl(struct tty_struct *tty, unsigned int cmd,
 	struct gsm_mux *gsm = tty->disc_data;
 	unsigned int base, addr;
 	struct gsm_dlci *dlci;
+	int ret = 0;
 
 	switch (cmd) {
 	case GSMIOC_GETCONF:
 		gsm_copy_config_values(gsm, &c);
+		mutex_lock(&gsm_mux_mutex);
 		if (copy_to_user((void __user *)arg, &c, sizeof(c)))
-			return -EFAULT;
-		return 0;
+			ret = -EFAULT;
+		mutex_unlock(&gsm_mux_mutex);
+		break;
 	case GSMIOC_SETCONF:
+		mutex_lock(&gsm_mux_mutex);
 		if (copy_from_user(&c, (void __user *)arg, sizeof(c)))
-			return -EFAULT;
-		return gsm_config(gsm, &c);
+			ret = -EFAULT;
+		else
+			ret = gsm_config(gsm, &c);
+		mutex_unlock(&gsm_mux_mutex);
+		break;
 	case GSMIOC_GETFIRST:
 		base = mux_num_to_base(gsm);
 		return put_user(base + 1, (__u32 __user *)arg);
@@ -3824,6 +4001,7 @@ static int gsmld_ioctl(struct tty_struct *tty, unsigned int cmd,
 	default:
 		return n_tty_ioctl_helper(tty, cmd, arg);
 	}
+	return ret;
 }
 
 /*
@@ -4497,7 +4675,9 @@ static void gsmtty_cleanup(struct tty_struct *tty)
 
 	dlci_put(dlci);
 	dlci_put(gsm->dlci[0]);
+	mutex_lock(&gsm_mux_mutex);
 	mux_put(gsm);
+	mutex_unlock(&gsm_mux_mutex);
 }
 
 /* Virtual ttys for the demux */
@@ -4556,6 +4736,9 @@ static int __init gsm_init(void)
 		status = -EBUSY;
 		goto err_put_driver;
 	}
+
+	proc_gsm = proc_mkdir_mode("tty/ldisc/n_gsm", 0555, NULL);
+
 	pr_debug("gsm_init: loaded as %d,%d.\n",
 			gsm_tty_driver->major, gsm_tty_driver->minor_start);
 	return 0;
@@ -4569,6 +4752,8 @@ static int __init gsm_init(void)
 static void __exit gsm_exit(void)
 {
 	tty_unregister_ldisc(&tty_ldisc_packet);
+	if (proc_gsm)
+		proc_remove(proc_gsm);
 	tty_unregister_driver(gsm_tty_driver);
 	tty_driver_kref_put(gsm_tty_driver);
 }
