linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem
@ 2020-04-21 23:27 Tony Lindgren
  2020-04-21 23:27 ` [PATCH 1/4] tty: n_gsm: Add support for serdev drivers Tony Lindgren
                   ` (4 more replies)
  0 siblings, 5 replies; 19+ messages in thread
From: Tony Lindgren @ 2020-04-21 23:27 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rob Herring
  Cc: Alan Cox, Lee Jones, Jiri Slaby, Johan Hovold, Merlijn Wajer,
	Pavel Machek, Peter Hurley, Sebastian Reichel, linux-serial,
	devicetree, linux-kernel, linux-omap

Hi all,

Here's v4 set of n_gsm serdev support patches, and the related protocol
driver for the modem found on Motorola Mapphone phones and tablets
like droid4.

This series only adds basic character device support for the serdev
driver. Other serdev consumer drivers for specific devices will be
posted separately.

The patches are against v5.6-rc series.

Regards,

Tony


Changes since v5:
- Cosmetic fixes for issues noted by Pavel

Changes since v4:
- Use drivers/tty/serdev/protocol directory for the driver instead of
  drivers/mfd as discussed on the lists for v3 set of patches
- Fix remove to call kfree only after removing device from the list

Changes since v3:
- Update list of folks in Cc, looks like I sent v3 only to Lee and lkml
- Init privdata before motmdm_register_dlci calls gsm_serdev_register_dlci
- Update binding based on Rob's comments for license and "allOf"

Changes since v2:
- Drop useless send_command indirection, use static motmdm_send_command

Changes since v1:

- Simplified usage and got rid of few pointless inline functions
- Added consumer MFD driver, devicetree binding, and dts changes


Tony Lindgren (4):
  tty: n_gsm: Add support for serdev drivers
  serdev: ngsm-motmdm: Add Motorola TS 27.010 serdev modem driver for
    droid4
  dt-bindings: serdev: motmdm: Add binding for motorola-mdm
  ARM: dts: omap4-droid4: Enable basic modem support

 .../serdev/motorola,mapphone-mdm6600.yaml     |   34 +
 .../boot/dts/motorola-mapphone-common.dtsi    |    6 +
 drivers/tty/n_gsm.c                           |  372 +++++
 drivers/tty/serdev/Kconfig                    |    2 +
 drivers/tty/serdev/Makefile                   |    2 +
 drivers/tty/serdev/protocol/Kconfig           |   14 +
 drivers/tty/serdev/protocol/Makefile          |    3 +
 .../tty/serdev/protocol/serdev-ngsm-motmdm.c  | 1191 +++++++++++++++++
 include/linux/serdev-gsm.h                    |  168 +++
 9 files changed, 1792 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/serdev/motorola,mapphone-mdm6600.yaml
 create mode 100644 drivers/tty/serdev/protocol/Kconfig
 create mode 100644 drivers/tty/serdev/protocol/Makefile
 create mode 100644 drivers/tty/serdev/protocol/serdev-ngsm-motmdm.c
 create mode 100644 include/linux/serdev-gsm.h

-- 
2.26.2

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 1/4] tty: n_gsm: Add support for serdev drivers
  2020-04-21 23:27 [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem Tony Lindgren
@ 2020-04-21 23:27 ` Tony Lindgren
  2020-04-21 23:27 ` [PATCH 2/4] serdev: ngsm-motmdm: Add Motorola TS 27.010 serdev modem driver for droid4 Tony Lindgren
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 19+ messages in thread
From: Tony Lindgren @ 2020-04-21 23:27 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rob Herring
  Cc: Alan Cox, Lee Jones, Jiri Slaby, Johan Hovold, Merlijn Wajer,
	Pavel Machek, Peter Hurley, Sebastian Reichel, linux-serial,
	devicetree, linux-kernel, linux-omap

We can make use of serdev drivers to do simple device drivers for
TS 27.010 chanels, and we can handle vendor specific protocols on top
of TS 27.010 with serdev drivers.

So far this has been tested with Motorola droid4 where there is a custom
packet numbering protocol on top of TS 27.010 for the MDM6600 modem.

I initially though about adding the serdev support into a separate file,
but that will take some refactoring of n_gsm.c. And I'd like to have
things working first. Then later on we might want to consider splitting
n_gsm.c into three pieces for core, tty and serdev parts. And then maybe
the serdev related parts can be just moved to live under something like
drivers/tty/serdev/protocol/ngsm.c.

Reviewed-by: Pavel Machek <pavel@ucw.cz>
Tested-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Tony Lindgren <tony@atomide.com>
---
 drivers/tty/n_gsm.c        | 372 +++++++++++++++++++++++++++++++++++++
 include/linux/serdev-gsm.h | 168 +++++++++++++++++
 2 files changed, 540 insertions(+)
 create mode 100644 include/linux/serdev-gsm.h

diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -39,6 +39,7 @@
 #include <linux/file.h>
 #include <linux/uaccess.h>
 #include <linux/module.h>
+#include <linux/serdev.h>
 #include <linux/timer.h>
 #include <linux/tty_flip.h>
 #include <linux/tty_driver.h>
@@ -50,6 +51,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/gsmmux.h>
+#include <linux/serdev-gsm.h>
 
 static int debug;
 module_param(debug, int, 0600);
@@ -150,6 +152,7 @@ struct gsm_dlci {
 	/* Data handling callback */
 	void (*data)(struct gsm_dlci *dlci, const u8 *data, int len);
 	void (*prev_data)(struct gsm_dlci *dlci, const u8 *data, int len);
+	struct gsm_serdev_dlci *ops; /* serdev dlci ops, if used */
 	struct net_device *net; /* network interface, if created */
 };
 
@@ -198,6 +201,7 @@ enum gsm_mux_state {
  */
 
 struct gsm_mux {
+	struct gsm_serdev *gsd;		/* Serial device bus data */
 	struct tty_struct *tty;		/* The tty our ldisc is bound to */
 	spinlock_t lock;
 	struct mutex mutex;
@@ -2346,6 +2350,374 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
 	return 0;
 }
 
+#ifdef CONFIG_SERIAL_DEV_BUS
+
+/**
+ * gsm_serdev_get_config - read ts 27.010 config
+ * @gsd:	serdev-gsm instance
+ * @c:		ts 27.010 config data
+ *
+ * See gsm_copy_config_values() for more information.
+ */
+int gsm_serdev_get_config(struct gsm_serdev *gsd, struct gsm_config *c)
+{
+	struct gsm_mux *gsm;
+
+	if (!gsd || !gsd->gsm)
+		return -ENODEV;
+
+	gsm = gsd->gsm;
+
+	if (!c)
+		return -EINVAL;
+
+	gsm_copy_config_values(gsm, c);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gsm_serdev_get_config);
+
+/**
+ * gsm_serdev_set_config - set ts 27.010 config
+ * @gsd:	serdev-gsm instance
+ * @c:		ts 27.010 config data
+ *
+ * See gsm_config() for more information.
+ */
+int gsm_serdev_set_config(struct gsm_serdev *gsd, struct gsm_config *c)
+{
+	struct gsm_mux *gsm;
+
+	if (!gsd || !gsd->serdev || !gsd->gsm)
+		return -ENODEV;
+
+	gsm = gsd->gsm;
+
+	if (!c)
+		return -EINVAL;
+
+	return gsm_config(gsm, c);
+}
+EXPORT_SYMBOL_GPL(gsm_serdev_set_config);
+
+static struct gsm_dlci *gsd_dlci_get(struct gsm_serdev *gsd, int line,
+				     bool allocate)
+{
+	struct gsm_mux *gsm;
+	struct gsm_dlci *dlci;
+
+	if (!gsd || !gsd->gsm)
+		return ERR_PTR(-ENODEV);
+
+	gsm = gsd->gsm;
+
+	if (line < 1 || line >= 63)
+		return ERR_PTR(-EINVAL);
+
+	mutex_lock(&gsm->mutex);
+
+	if (gsm->dlci[line]) {
+		dlci = gsm->dlci[line];
+		goto unlock;
+	} else if (!allocate) {
+		dlci = ERR_PTR(-ENODEV);
+		goto unlock;
+	}
+
+	dlci = gsm_dlci_alloc(gsm, line);
+	if (!dlci) {
+		gsm = ERR_PTR(-ENOMEM);
+		goto unlock;
+	}
+
+	gsm->dlci[line] = dlci;
+
+unlock:
+	mutex_unlock(&gsm->mutex);
+
+	return dlci;
+}
+
+static void gsd_dlci_data(struct gsm_dlci *dlci, const u8 *buf, int len)
+{
+	struct gsm_mux *gsm = dlci->gsm;
+	struct gsm_serdev *gsd = gsm->gsd;
+
+	if (!gsd || !dlci->ops)
+		return;
+
+	switch (dlci->adaption) {
+	case 0:
+	case 1:
+		if (dlci->ops->receive_buf)
+			dlci->ops->receive_buf(dlci->ops, buf, len);
+		break;
+	default:
+		pr_warn("dlci%i adaption %i not yet implemented\n",
+			dlci->addr, dlci->adaption);
+		break;
+	}
+}
+
+/**
+ * gsm_serdev_write - write data to a ts 27.010 channel
+ * @gsd:	serdev-gsm instance
+ * @ops:	channel ops
+ * @buf:	write buffer
+ * @len:	buffer length
+ */
+int gsm_serdev_write(struct gsm_serdev *gsd, struct gsm_serdev_dlci *ops,
+		     const u8 *buf, int len)
+{
+	struct gsm_mux *gsm;
+	struct gsm_dlci *dlci;
+	struct gsm_msg *msg;
+	int h, size, total_size = 0;
+	u8 *dp;
+
+	if (!gsd || !gsd->gsm)
+		return -ENODEV;
+
+	gsm = gsd->gsm;
+
+	dlci = gsd_dlci_get(gsd, ops->line, false);
+	if (IS_ERR(dlci))
+		return PTR_ERR(dlci);
+
+	h = dlci->adaption - 1;
+
+	if (len > gsm->mtu)
+		len = gsm->mtu;
+
+	size = len + h;
+
+	msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype);
+	if (!msg)
+		return -ENOMEM;
+
+	dp = msg->data;
+	switch (dlci->adaption) {
+	case 1:
+		break;
+	case 2:
+		*dp++ = gsm_encode_modem(dlci);
+		break;
+	}
+	memcpy(dp, buf, len);
+	gsm_data_queue(dlci, msg);
+	total_size += size;
+
+	return total_size;
+}
+EXPORT_SYMBOL_GPL(gsm_serdev_write);
+
+/**
+ * gsm_serdev_data_kick - indicate more data can be transmitted
+ * @gsd:	serdev-gsm instance
+ *
+ * See gsm_data_kick() for more information.
+ */
+void gsm_serdev_data_kick(struct gsm_serdev *gsd)
+{
+	struct gsm_mux *gsm;
+	unsigned long flags;
+
+	if (!gsd || !gsd->gsm)
+		return;
+
+	gsm = gsd->gsm;
+
+	spin_lock_irqsave(&gsm->tx_lock, flags);
+	gsm_data_kick(gsm);
+	spin_unlock_irqrestore(&gsm->tx_lock, flags);
+}
+EXPORT_SYMBOL_GPL(gsm_serdev_data_kick);
+
+/**
+ * gsm_serdev_register_dlci - register a ts 27.010 channel
+ * @gsd:	serdev-gsm instance
+ * @ops:	channel ops
+ */
+int gsm_serdev_register_dlci(struct gsm_serdev *gsd,
+			     struct gsm_serdev_dlci *ops)
+{
+	struct gsm_dlci *dlci;
+	struct gsm_mux *gsm;
+	int retries;
+
+	if (!gsd || !gsd->gsm || !gsd->serdev)
+		return -ENODEV;
+
+	gsm = gsd->gsm;
+
+	if (!ops || !ops->line || !ops->receive_buf)
+		return -EINVAL;
+
+	dlci = gsd_dlci_get(gsd, ops->line, true);
+	if (IS_ERR(dlci))
+		return PTR_ERR(dlci);
+
+	if (dlci->state == DLCI_OPENING || dlci->state == DLCI_OPEN ||
+	    dlci->state == DLCI_CLOSING)
+		return -EBUSY;
+
+	mutex_lock(&dlci->mutex);
+	dlci->ops = ops;
+	dlci->modem_rx = 0;
+	dlci->prev_data = dlci->data;
+	dlci->data = gsd_dlci_data;
+	mutex_unlock(&dlci->mutex);
+
+	gsm_dlci_begin_open(dlci);
+
+	/*
+	 * Allow some time for dlci to move to DLCI_OPEN state. Otherwise
+	 * the serdev consumer driver can start sending data too early during
+	 * the setup, and the response will be missed by gms_queue() if we
+	 * still have DLCI_CLOSED state.
+	 */
+	for (retries = 10; retries > 0; retries--) {
+		if (dlci->state == DLCI_OPEN)
+			break;
+		msleep(100);
+	}
+
+	if (!retries)
+		dev_dbg(&gsd->serdev->dev, "dlci%i not currently active\n",
+			dlci->addr);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gsm_serdev_register_dlci);
+
+/**
+ * gsm_serdev_unregister_dlci - unregister a ts 27.010 channel
+ * @gsd:	serdev-gsm instance
+ * @ops:	channel ops
+ */
+void gsm_serdev_unregister_dlci(struct gsm_serdev *gsd,
+				struct gsm_serdev_dlci *ops)
+{
+	struct gsm_mux *gsm;
+	struct gsm_dlci *dlci;
+
+	if (!gsd || !gsd->gsm || !gsd->serdev)
+		return;
+
+	gsm = gsd->gsm;
+
+	if (!ops || !ops->line)
+		return;
+
+	dlci = gsd_dlci_get(gsd, ops->line, false);
+	if (IS_ERR(dlci))
+		return;
+
+	mutex_lock(&dlci->mutex);
+	gsm_destroy_network(dlci);
+	dlci->data = dlci->prev_data;
+	dlci->ops = NULL;
+	mutex_unlock(&dlci->mutex);
+
+	gsm_dlci_begin_close(dlci);
+}
+EXPORT_SYMBOL_GPL(gsm_serdev_unregister_dlci);
+
+static int gsm_serdev_output(struct gsm_mux *gsm, u8 *data, int len)
+{
+	struct gsm_serdev *gsd = gsm->gsd;
+	struct serdev_device *serdev = gsm->gsd->serdev;
+	bool asleep;
+
+	asleep = atomic_read(&gsd->asleep);
+	if (asleep)
+		return -ENOSPC;
+
+	if (gsm->gsd->output)
+		return gsm->gsd->output(gsm->gsd, data, len);
+	else
+		return serdev_device_write_buf(serdev, data, len);
+}
+
+static int gsd_receive_buf(struct serdev_device *serdev, const u8 *data,
+			   size_t count)
+{
+	struct gsm_serdev *gsd = serdev_device_get_drvdata(serdev);
+	struct gsm_mux *gsm;
+	const unsigned char *dp;
+	int i;
+
+	if (WARN_ON(!gsd))
+		return 0;
+
+	gsm = gsd->gsm;
+
+	if (debug & 4)
+		print_hex_dump_bytes("gsd_receive_buf: ",
+				     DUMP_PREFIX_OFFSET,
+				     data, count);
+
+	for (i = count, dp = data; i; i--, dp++)
+		gsm->receive(gsm, *dp);
+
+	return count;
+}
+
+static void gsd_write_wakeup(struct serdev_device *serdev)
+{
+	serdev_device_write_wakeup(serdev);
+}
+
+static struct serdev_device_ops gsd_client_ops = {
+	.receive_buf = gsd_receive_buf,
+	.write_wakeup = gsd_write_wakeup,
+};
+
+int gsm_serdev_register_device(struct gsm_serdev *gsd)
+{
+	struct gsm_mux *gsm;
+	int error;
+
+	if (WARN(!gsd || !gsd->serdev || !gsd->output,
+		 "serdev and output must be initialized\n"))
+		return -EINVAL;
+
+	serdev_device_set_client_ops(gsd->serdev, &gsd_client_ops);
+
+	gsm = gsm_alloc_mux();
+	if (!gsm)
+		return -ENOMEM;
+
+	gsm->encoding = 1;
+	gsm->tty = NULL;
+	gsm->gsd = gsd;
+	gsm->output = gsm_serdev_output;
+	atomic_set(&gsd->asleep, 0);
+	gsd->gsm = gsm;
+	mux_get(gsd->gsm);
+
+	error = gsm_activate_mux(gsd->gsm);
+	if (error) {
+		gsm_cleanup_mux(gsd->gsm);
+		mux_put(gsd->gsm);
+
+		return error;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gsm_serdev_register_device);
+
+void gsm_serdev_unregister_device(struct gsm_serdev *gsd)
+{
+	gsm_cleanup_mux(gsd->gsm);
+	mux_put(gsd->gsm);
+	gsd->gsm = NULL;
+}
+EXPORT_SYMBOL_GPL(gsm_serdev_unregister_device);
+
+#endif	/* CONFIG_SERIAL_DEV_BUS */
+
 /**
  *	gsmld_output		-	write to link
  *	@gsm: our mux
diff --git a/include/linux/serdev-gsm.h b/include/linux/serdev-gsm.h
new file mode 100644
--- /dev/null
+++ b/include/linux/serdev-gsm.h
@@ -0,0 +1,168 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _LINUX_SERDEV_GSM_H
+#define _LINUX_SERDEV_GSM_H
+
+#include <linux/device.h>
+#include <linux/serdev.h>
+#include <linux/types.h>
+
+struct gsm_serdev_dlci;
+struct gsm_config;
+
+/**
+ * struct gsm_serdev - serdev-gsm instance
+ * @serdev:		serdev instance
+ * @gsm:		ts 27.010 n_gsm instance
+ * @asleep:		device is in idle state
+ * @drvdata:		serdev-gsm consumer driver data
+ * @output:		read data from ts 27.010 channel
+ *
+ * Currently only serdev and output must be initialized, the rest are
+ * are initialized by gsm_serdev_register_dlci().
+ */
+struct gsm_serdev {
+	struct serdev_device *serdev;
+	struct gsm_mux *gsm;
+	atomic_t asleep;
+	void *drvdata;
+	int (*output)(struct gsm_serdev *gsd, u8 *data, int len);
+};
+
+/**
+ * struct gsm_serdev_dlci - serdev-gsm ts 27.010 channel data
+ * @line:		ts 27.010 channel, control channel 0 is not available
+ * @receive_buf:	function to handle data received for the channel
+ */
+struct gsm_serdev_dlci {
+	int line;
+	int (*receive_buf)(struct gsm_serdev_dlci *ops,
+			   const unsigned char *buf,
+			   size_t len);
+};
+
+#if IS_ENABLED(CONFIG_N_GSM) && IS_ENABLED(CONFIG_SERIAL_DEV_BUS)
+
+int gsm_serdev_register_device(struct gsm_serdev *gsd);
+void gsm_serdev_unregister_device(struct gsm_serdev *gsd);
+
+static inline void *gsm_serdev_get_drvdata(struct device *dev)
+{
+	struct serdev_device *serdev = to_serdev_device(dev);
+	struct gsm_serdev *gsd = serdev_device_get_drvdata(serdev);
+
+	if (gsd)
+		return gsd->drvdata;
+
+	return NULL;
+}
+
+static inline void gsm_serdev_set_drvdata(struct device *dev, void *drvdata)
+{
+	struct serdev_device *serdev = to_serdev_device(dev);
+	struct gsm_serdev *gsd = serdev_device_get_drvdata(serdev);
+
+	if (gsd)
+		gsd->drvdata = drvdata;
+}
+
+static inline void gsm_serdev_suspend(struct gsm_serdev *gsd)
+{
+	if (!gsd)
+		return;
+
+	atomic_set(&gsd->asleep, 1);
+}
+
+static inline int gsm_serdev_resume(struct gsm_serdev *gsd)
+{
+	if (!gsd)
+		return -EINVAL;
+
+	atomic_set(&gsd->asleep, 0);
+
+	return 0;
+}
+
+extern int gsm_serdev_get_config(struct gsm_serdev *gsd, struct gsm_config *c);
+extern int gsm_serdev_set_config(struct gsm_serdev *gsd, struct gsm_config *c);
+extern int gsm_serdev_register_dlci(struct gsm_serdev *gsd,
+				    struct gsm_serdev_dlci *ops);
+extern void gsm_serdev_unregister_dlci(struct gsm_serdev *gsd,
+				       struct gsm_serdev_dlci *ops);
+extern int gsm_serdev_write(struct gsm_serdev *gsd,
+			    struct gsm_serdev_dlci *ops,
+			    const u8 *buf, int len);
+extern void gsm_serdev_data_kick(struct gsm_serdev *gsd);
+
+#else	/* CONFIG_SERIAL_DEV_BUS */
+
+static inline
+int gsm_serdev_register_device(struct gsm_serdev *gsd)
+{
+	return -ENODEV;
+}
+
+static inline
+void gsm_serdev_unregister_device(struct gsm_serdev *gsd)
+{
+}
+
+static inline void *gsm_serdev_get_drvdata(struct device *dev)
+{
+	return NULL;
+}
+
+static inline
+void gsm_serdev_set_drvdata(struct device *dev, void *drvdata)
+{
+}
+
+static inline void gsm_serdev_suspend(struct gsm_serdev *gsd)
+{
+}
+
+static inline int gsm_serdev_resume(struct gsm_serdev *gsd)
+{
+	return -ENODEV;
+}
+
+static inline
+int gsm_serdev_get_config(struct gsm_serdev *gsd, struct gsm_config *c)
+{
+	return -ENODEV;
+}
+
+static inline
+int gsm_serdev_set_config(struct gsm_serdev *gsd, struct gsm_config *c)
+{
+	return -ENODEV;
+}
+
+static inline
+int gsm_serdev_register_dlci(struct gsm_serdev *gsd,
+			     struct gsm_serdev_dlci *ops)
+{
+	return -ENODEV;
+}
+
+static inline
+void gsm_serdev_unregister_dlci(struct gsm_serdev *gsd,
+				struct gsm_serdev_dlci *ops)
+{
+}
+
+static inline
+int gsm_serdev_write(struct gsm_serdev *gsd, struct gsm_serdev_dlci *ops,
+		     const u8 *buf, int len)
+{
+	return -ENODEV;
+}
+
+static inline
+void gsm_serdev_data_kick(struct gsm_serdev *gsd)
+{
+}
+
+#endif	/* CONFIG_N_GSM && CONFIG_SERIAL_DEV_BUS */
+#endif	/* _LINUX_SERDEV_GSM_H */
-- 
2.26.2

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 2/4] serdev: ngsm-motmdm: Add Motorola TS 27.010 serdev modem driver for droid4
  2020-04-21 23:27 [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem Tony Lindgren
  2020-04-21 23:27 ` [PATCH 1/4] tty: n_gsm: Add support for serdev drivers Tony Lindgren
@ 2020-04-21 23:27 ` Tony Lindgren
  2020-04-21 23:27 ` [PATCH 3/4] dt-bindings: serdev: motmdm: Add binding for motorola-mdm Tony Lindgren
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 19+ messages in thread
From: Tony Lindgren @ 2020-04-21 23:27 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rob Herring
  Cc: Alan Cox, Lee Jones, Jiri Slaby, Johan Hovold, Merlijn Wajer,
	Pavel Machek, Peter Hurley, Sebastian Reichel, linux-serial,
	devicetree, linux-kernel, linux-omap

Many Motorola phones are controlling the modem using a custom variant
of TS 27.010 serial line discipline. Devices on these modems have a
dedicated TS 27.010 channel for features like audio mixer, GNSS, voice
modem, SIM card reader and so on.

This serdev protocol driver allows using various devices on the modem.
In order to do that, we need to take care of the following three things:

1. Provide /dev/motmdm* character devices for apps to use for talking
   to the various devices on the modem

2. Handle Motorola custom protocol over TS 27.010 to make the channels
   usable for userspace

3. Coordinate PM runtime with the USB PHY because of shared GPIO pins
   with the USB PHY

With this patch, folks with droid4 can place a voice call with just:

$ printf "ATD%s,0\r" "${phone_number}" > /dev/motmdm1
D:OK
~+CIEV=1,1,0
...
$ printf "ATH\r" > /dev/motmdm1
H:OK

Also SMS can be sent with this patch using /dev/motmdm3 for sending,
and /dev/motmdm9 for receiving messages, and /dev/motmdm10 can be
used for SIM access.

Note that the audio mixer needs additional patches though. I will be
sending those as a separate series of patches.

Reviewed-by: Pavel Machek <pavel@ucw.cz>
Tested-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Tony Lindgren <tony@atomide.com>
---
 drivers/tty/serdev/Kconfig                    |    2 +
 drivers/tty/serdev/Makefile                   |    2 +
 drivers/tty/serdev/protocol/Kconfig           |   14 +
 drivers/tty/serdev/protocol/Makefile          |    3 +
 .../tty/serdev/protocol/serdev-ngsm-motmdm.c  | 1191 +++++++++++++++++
 5 files changed, 1212 insertions(+)
 create mode 100644 drivers/tty/serdev/protocol/Kconfig
 create mode 100644 drivers/tty/serdev/protocol/Makefile
 create mode 100644 drivers/tty/serdev/protocol/serdev-ngsm-motmdm.c

diff --git a/drivers/tty/serdev/Kconfig b/drivers/tty/serdev/Kconfig
--- a/drivers/tty/serdev/Kconfig
+++ b/drivers/tty/serdev/Kconfig
@@ -22,4 +22,6 @@ config SERIAL_DEV_CTRL_TTYPORT
 	depends on SERIAL_DEV_BUS != m
 	default y
 
+source "drivers/tty/serdev/protocol/Kconfig"
+
 endif
diff --git a/drivers/tty/serdev/Makefile b/drivers/tty/serdev/Makefile
--- a/drivers/tty/serdev/Makefile
+++ b/drivers/tty/serdev/Makefile
@@ -4,3 +4,5 @@ serdev-objs := core.o
 obj-$(CONFIG_SERIAL_DEV_BUS) += serdev.o
 
 obj-$(CONFIG_SERIAL_DEV_CTRL_TTYPORT) += serdev-ttyport.o
+
+obj-y += protocol/
diff --git a/drivers/tty/serdev/protocol/Kconfig b/drivers/tty/serdev/protocol/Kconfig
new file mode 100644
--- /dev/null
+++ b/drivers/tty/serdev/protocol/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Serial bus device driver protocol specific configuration
+#
+
+config SERIAL_DEV_NGSM_MOTMDM
+	tristate "Motorola Modem TS 27.010 Serdev Protocol Driver"
+	depends on N_GSM && PHY_MAPPHONE_MDM6600
+	help
+	  Select this for Motorola modems using TS 27.010 serial line
+	  discipline such as MDM6600 modem found on Motorola Mapphone
+	  devices like Droid4.
+
+	  If unsure, say N.
diff --git a/drivers/tty/serdev/protocol/Makefile b/drivers/tty/serdev/protocol/Makefile
new file mode 100644
--- /dev/null
+++ b/drivers/tty/serdev/protocol/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_SERIAL_DEV_NGSM_MOTMDM) += serdev-ngsm-motmdm.o
diff --git a/drivers/tty/serdev/protocol/serdev-ngsm-motmdm.c b/drivers/tty/serdev/protocol/serdev-ngsm-motmdm.c
new file mode 100644
--- /dev/null
+++ b/drivers/tty/serdev/protocol/serdev-ngsm-motmdm.c
@@ -0,0 +1,1191 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Motorola TS 27.010 serial line discipline serdev driver
+ * Copyright (C) 2018 - 2020 Tony Lindgren <tony@atomide.com>
+ */
+
+#include <linux/cdev.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/kfifo.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/serdev.h>
+#include <linux/serdev-gsm.h>
+
+#include <linux/mfd/core.h>
+#include <linux/phy/phy.h>
+
+#include <uapi/linux/gsmmux.h>
+
+#define MOTMDM_C_N2		3	/* TS27.010 default value */
+#define MOTMDM_DLCI_MIN		1
+#define MOTMDM_DLCI_MAX		12
+#define MOTMDM_DLCI_MASK	0x1ffe	/* DLCI from 1 to 12 */
+#define MOTMDM_ID_LEN		5	/* U + unsigned short */
+#define MOTMDM_CMD_LEN(x)	(MOTMDM_ID_LEN + (x) + 1)
+#define MOTMDM_WRITE_BUF_SIZE	1024
+#define MOTMDM_READ_FIFO_SIZE	4096
+
+struct motmdm_cfg {
+	unsigned long cdevmask;
+	unsigned int aggressive_pm:1;
+	int modem_dlci;
+	int codec_dlci;
+};
+
+struct motmdm {
+	struct device *dev;
+	struct phy *phy;
+	struct gsm_serdev gsd;
+	const struct motmdm_cfg *cfg;
+	struct class *class;
+	struct list_head dlcis;
+	struct list_head cdevs;
+	dev_t dev_id;
+	u16 cmdid;
+};
+
+struct motmdm_response {
+	struct list_head node;
+	u16 id;
+	const unsigned char *cmd;
+	size_t cmdlen;
+	size_t reslen;
+	unsigned char *buf;
+	size_t len;
+	unsigned int handled:1;
+};
+
+enum motmdm_dlci_nr {
+	MOTMDM_DLCI1 = 1,
+	MOTMDM_DLCI2,
+	MOTMDM_DLCI3,
+	MOTMDM_DLCI4,
+	MOTMDM_DLCI5,
+	MOTMDM_DLCI6,
+	MOTMDM_DLCI7,
+	MOTMDM_DLCI8,
+	MOTMDM_DLCI9,
+	MOTMDM_DLCI10,
+	MOTMDM_DLCI11,
+	MOTMDM_DLCI12,
+	MOTMDM_DLCI13,
+	MOTMDM_DLCI14,
+	MOTMDM_DLCI15,
+};
+
+struct motmdm_dlci {
+	struct gsm_serdev_dlci gsm_dlci;
+	struct list_head node;
+	wait_queue_head_t read_queue;
+	struct kfifo read_fifo;
+	int line;
+	u16 id;
+	struct list_head list;
+	void *privdata;		/* Do not use, internal data */
+	void *drvdata;		/* Available for consumer drivers */
+};
+
+struct motmdm_cdev {
+	struct motmdm *ddata;
+	struct list_head node;
+	struct motmdm_dlci *dlci;
+	struct device *dev;
+	struct cdev cdev;
+	struct rw_semaphore rwsem;
+	unsigned int count;
+	unsigned int disconnected:1;
+	struct mutex read_mutex;	/* char dev write lock */
+	struct mutex write_mutex;	/* char dev read lock */
+	char *write_buf;
+	size_t write_buf_sz;
+	size_t write_offset;
+};
+
+static const char * const motmdm_driver_name = "motmdm";
+
+static int motmdm_runtime_suspend(struct device *dev)
+{
+	struct motmdm *ddata = gsm_serdev_get_drvdata(dev);
+	int err;
+
+	if (IS_ERR_OR_NULL(ddata->phy))
+		return 0;
+
+	err = phy_pm_runtime_put(ddata->phy);
+	if (err < 0)
+		dev_warn(dev, "%s: phy_pm_runtime_put: %i\n",
+			 __func__, err);
+
+	gsm_serdev_resume(&ddata->gsd);
+
+	return 0;
+}
+
+static int motmdm_runtime_resume(struct device *dev)
+{
+	struct motmdm *ddata = gsm_serdev_get_drvdata(dev);
+	int err;
+
+	if (IS_ERR_OR_NULL(ddata->phy))
+		return 0;
+
+	err = phy_pm_runtime_get_sync(ddata->phy);
+	if (err < 0) {
+		dev_warn(dev, "%s: phy_pm_runtime_get: %i\n",
+			 __func__, err);
+
+		return err;
+	}
+
+	err = gsm_serdev_resume(&ddata->gsd);
+	if (err < 0) {
+		phy_pm_runtime_put(ddata->phy);
+
+		return err;
+	}
+
+	gsm_serdev_data_kick(&ddata->gsd);
+
+	return 0;
+}
+
+static const struct dev_pm_ops motmdm_pm_ops = {
+	SET_RUNTIME_PM_OPS(motmdm_runtime_suspend,
+			   motmdm_runtime_resume,
+			   NULL)
+};
+
+/*
+ * Motorola MDM6600 devices have GPIO wake pins shared between the USB PHY and
+ * the TS 27.010 interface. So for PM, we need to use the phy_pm_runtime
+ * related calls. Otherwise the modem won't respond to anything on the UART
+ * and will never idle either.
+ */
+static int motmdm_init_phy(struct device *dev)
+{
+	struct motmdm *ddata = gsm_serdev_get_drvdata(dev);
+	int err;
+
+	ddata->phy = devm_of_phy_get(dev, dev->of_node, NULL);
+	if (IS_ERR(ddata->phy)) {
+		err = PTR_ERR(ddata->phy);
+		if (err != -EPROBE_DEFER)
+			dev_err(dev, "%s: no phy: %i\n", __func__, err);
+
+		return err;
+	}
+
+	return 0;
+}
+
+/*
+ * Motorola MDM6600 devices add a custom packet numbering layer on top of the
+ * TS 27.010 DLCI channels. This is a layering violation as all the devices
+ * are on dedicated channels already. For some reason the packet numbering is
+ * not specific to each DLCI.. It is for all the DLCI instead. As both ends
+ * can increase packet IDs, conflicts are guaranteed but seem to be harmless.
+ * Who knows, maybe the packet IDs were added by frustrated developers to
+ * debug buggy modem code reponding on a wrong DLCI. Valid packet numbers are
+ * from 0000 to 9999 decimal. We just parse the modem sent packet number to
+ * match the response to sent commands and don't use modem sent packet
+ * numbers for new command packets we send out.
+ */
+static int motmdm_read_packet_id(struct gsm_serdev_dlci *gsm_dlci,
+				 const unsigned char *buf,
+				 size_t len)
+{
+	struct motmdm_dlci *mot_dlci =
+		container_of(gsm_dlci, struct motmdm_dlci,
+			     gsm_dlci);
+	struct motmdm *ddata = mot_dlci->privdata;
+	unsigned char tmp[MOTMDM_ID_LEN];
+	int err;
+	u16 id;
+
+	if (WARN(!ddata, "%s no ddata?\n", __func__))
+		return 0;
+
+	if (len < MOTMDM_ID_LEN || buf[0] != 'U')
+		return -ECOMM;
+
+	snprintf(tmp, MOTMDM_ID_LEN, "%s", buf + 1);
+	err = kstrtou16(tmp, 10, &id);
+	if (err)
+		return -ECOMM;
+
+	return id;
+}
+
+/*
+ * For new packets, we just use jiffies based numbering and let the modem
+ * deal with any possible numbering conflicts across the DLCI.
+ */
+static int motmdm_new_packet_id(void)
+{
+	return jiffies % 10000;
+}
+
+/* Fix line breaks for apps if needed and feed kfifo */
+static int motmdm_dlci_feed_kfifo(struct motmdm_dlci *mot_dlci,
+				  const unsigned char *buf,
+				  size_t len)
+{
+	size_t newlen = len;
+	int err;
+
+	if (len && buf[len - 1] == '\n') {
+		if (len > 1 && buf[len - 2] != '\r')
+			newlen--;
+		else if (len == 1)
+			newlen--;
+	}
+
+	err = kfifo_in(&mot_dlci->read_fifo, buf, newlen);
+	if (err != newlen)
+		return -ENOSPC;
+
+	if (newlen != len) {
+		err = kfifo_in(&mot_dlci->read_fifo, "\r\n", 2);
+		if (err != 2)
+			err = -ENOSPC;
+		else
+			newlen += err;
+	}
+
+	return newlen;
+}
+
+/*
+ * Helper to parse the command response from modem
+ */
+static int motmdm_dlci_handle_command(struct motmdm_dlci *mot_dlci, int id,
+				      const unsigned char *buf, size_t len)
+{
+	struct motmdm_response *resp = NULL;
+	struct list_head *pos, *q;
+	int resp_start;
+
+	list_for_each_safe(pos, q, &mot_dlci->list) {
+		resp = list_entry(pos, struct motmdm_response, node);
+		if (resp->id == id)
+			break;
+	}
+
+	if (!resp || !resp->buf)
+		return -ENODEV;
+
+	/* Firmware leaves out AT from the commands */
+	resp_start = resp->cmdlen - 2;
+	if (len < resp_start)
+		return -EPIPE;
+
+	/* Only some firmware messages start with ':' */
+	if (buf[resp_start] == ':')
+		resp_start++;
+
+	resp->reslen = min3(len - resp_start, resp->len, len);
+	strncpy(resp->buf, buf + resp_start, resp->reslen);
+
+	/* Leave out trailing line break if there */
+	if (resp->reslen > 1 && resp->buf[resp->reslen - 1] == '\n') {
+		resp->buf[resp->reslen - 1] = '\0';
+		resp->reslen--;
+	}
+
+	resp->handled = true;
+
+	return 0;
+}
+
+/*
+ * Read handling for Motorola custom layering on top of TS 27.010
+ */
+static int motmdm_dlci_receive_buf(struct gsm_serdev_dlci *gsm_dlci,
+				   const unsigned char *buf,
+				   size_t len)
+{
+	struct motmdm_dlci *mot_dlci =
+		container_of(gsm_dlci, struct motmdm_dlci,
+			     gsm_dlci);
+	const unsigned char *msg;
+	size_t msglen;
+	int id, err;
+
+	if (len < (MOTMDM_ID_LEN + 1) || buf[0] != 'U')
+		return 0;
+
+	id = motmdm_read_packet_id(gsm_dlci, buf, len);
+	if (id < 0)
+		return 0;
+
+	/* Strip out Motorola custom packet numbering */
+	msg = buf + MOTMDM_ID_LEN;
+	msglen = len - MOTMDM_ID_LEN;
+
+	motmdm_dlci_handle_command(mot_dlci, id, msg, msglen);
+
+	if (kfifo_initialized(&mot_dlci->read_fifo)) {
+		err = motmdm_dlci_feed_kfifo(mot_dlci, msg, msglen);
+		if (err < 0)
+			goto err_kfifo;
+	}
+
+	err = msglen;
+
+	wake_up(&mot_dlci->read_queue);
+
+err_kfifo:
+
+	return err;
+}
+
+/*
+ * Write handling for Motorola custom layering on top of TS 27.010
+ */
+static int motmdm_write(struct device *dev, struct motmdm_dlci *mot_dlci,
+			int cmdid, const unsigned char *buf, size_t count)
+{
+	struct motmdm *ddata;
+	struct gsm_serdev *gsd;
+	int err, cmdlen;
+	char *cmd;
+
+	if (!dev || !mot_dlci || !buf || !count)
+		return -EINVAL;
+
+	ddata = gsm_serdev_get_drvdata(dev);
+	if (!ddata)
+		return -ENODEV;
+
+	gsd = &ddata->gsd;
+
+	err = pm_runtime_get_sync(dev);
+	if ((err != -EINPROGRESS) && err < 0) {
+		pm_runtime_put_noidle(dev);
+
+		return err;
+	}
+
+	cmdlen = MOTMDM_CMD_LEN(count);
+	cmd = kmalloc(cmdlen, GFP_KERNEL);
+	if (!cmd)
+		return -ENOMEM;
+
+	switch (cmdid) {
+	case -ENOENT:
+		/* No ID number, just U for continuation messages */
+		snprintf(cmd, cmdlen, "U%s\r", buf);
+		break;
+	case 0 ... 9999:
+		/* Valid ID */
+		mot_dlci->id = cmdid;
+		snprintf(cmd, cmdlen, "U%04i%s\r", mot_dlci->id, buf);
+		break;
+	default:
+		/* Assign ID */
+		mot_dlci->id = motmdm_new_packet_id();
+		snprintf(cmd, cmdlen, "U%04i%s\r", mot_dlci->id, buf);
+		break;
+	}
+
+	err = gsm_serdev_write(gsd, &mot_dlci->gsm_dlci, cmd, cmdlen);
+	if (err == cmdlen)
+		err = count;
+
+	kfree(cmd);
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
+
+	return err;
+}
+
+/*
+ * Helper for child device drivers to send a command to a DLCI and wait
+ * for result with a matching packet ID.
+ */
+static int motmdm_send_command(struct device *dev,
+			       struct motmdm_dlci *mot_dlci,
+			       unsigned long timeout_ms,
+			       const unsigned char *cmd, size_t cmdlen,
+			       unsigned char *rsp, size_t rsplen)
+{
+	struct motmdm_response *resp, *tmp;
+	struct list_head *pos, *q;
+	unsigned char *delim;
+	int err;
+
+	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+	if (!resp)
+		return -ENOMEM;
+
+	memset(rsp, 0, rsplen);
+
+	resp->cmd = cmd;
+	resp->cmdlen = cmdlen;
+	resp->buf = rsp;
+	resp->len = rsplen;
+	resp->id = motmdm_new_packet_id();
+
+	/* Firmware will return only the command without value */
+	if (cmd[cmdlen - 1] != '=') {
+		delim = strchr(cmd, '=');
+		if (delim)
+			resp->cmdlen -= strlen(delim);
+	}
+
+	list_add_tail(&resp->node, &mot_dlci->list);
+
+	err = motmdm_write(dev, mot_dlci, resp->id, cmd, cmdlen);
+	if (err < 0)
+		goto unregister;
+
+	err = wait_event_timeout(mot_dlci->read_queue, resp->handled,
+				 msecs_to_jiffies(timeout_ms));
+	if (err < 0)
+		goto unregister;
+
+	if (err == 0) {
+		err = -ETIMEDOUT;
+		goto unregister;
+	}
+
+	dev_dbg(dev, "%s: %s got %s\n", __func__, cmd, resp->buf);
+
+	err = resp->reslen;
+
+unregister:
+	list_for_each_safe(pos, q, &mot_dlci->list) {
+		tmp = list_entry(pos, struct motmdm_response, node);
+		if (tmp->id == resp->id)
+			list_del(pos);
+	}
+
+	kfree(resp);
+
+	return err;
+}
+
+static int motmdm_register_dlci(struct device *dev,
+				struct motmdm_dlci *mot_dlci)
+{
+	struct motmdm *ddata;
+	struct gsm_serdev *gsd;
+	struct gsm_serdev_dlci *gsm_dlci;
+	int err;
+
+	if (!dev || !mot_dlci || !mot_dlci->line)
+		return -EINVAL;
+
+	err = pm_runtime_get_sync(dev);
+	if ((err != -EINPROGRESS) && err < 0) {
+		pm_runtime_put_noidle(dev);
+
+		return err;
+	}
+
+	ddata = gsm_serdev_get_drvdata(dev);
+	gsd = &ddata->gsd;
+	INIT_LIST_HEAD(&mot_dlci->list);
+	init_waitqueue_head(&mot_dlci->read_queue);
+	mot_dlci->privdata = ddata;
+
+	gsm_dlci = &mot_dlci->gsm_dlci;
+	gsm_dlci->line = mot_dlci->line;
+	gsm_dlci->receive_buf = motmdm_dlci_receive_buf;
+
+	err = gsm_serdev_register_dlci(gsd, gsm_dlci);
+	if (err) {
+		dev_warn(dev, "error registering dlci%i: %i\n",
+			 mot_dlci->line, err);
+		kfifo_free(&mot_dlci->read_fifo);
+		memset(gsm_dlci, 0, sizeof(*gsm_dlci));
+		goto out_idle;
+	}
+
+	list_add_tail(&mot_dlci->node, &ddata->dlcis);
+
+out_idle:
+	pm_runtime_put(dev);
+
+	return err;
+}
+
+static void motmdm_unregister_dlci(struct device *dev,
+				   struct motmdm_dlci *mot_dlci)
+{
+	struct motmdm *ddata;
+	struct gsm_serdev *gsd;
+	struct gsm_serdev_dlci *gsm_dlci;
+	struct list_head *pos, *q;
+	struct motmdm_dlci *tmp;
+	int err;
+
+	if (!dev || !mot_dlci || !mot_dlci->line)
+		return;
+
+	err = pm_runtime_get_sync(dev);
+	if ((err != -EINPROGRESS) && err < 0) {
+		pm_runtime_put_noidle(dev);
+		return;
+	}
+
+	ddata = gsm_serdev_get_drvdata(dev);
+
+	list_for_each_safe(pos, q, &ddata->dlcis) {
+		tmp = list_entry(pos, struct motmdm_dlci, node);
+		if (tmp == mot_dlci)
+			list_del(pos);
+	}
+
+	gsd = &ddata->gsd;
+	gsm_dlci = &mot_dlci->gsm_dlci;
+	gsm_serdev_unregister_dlci(gsd, gsm_dlci);
+	gsm_dlci->receive_buf = NULL;
+	mot_dlci->privdata = NULL;
+
+	pm_runtime_put(dev);
+}
+
+/*
+ * Character devices for DLCI channels with no serdev drivers
+ */
+static int motmdm_cdev_open(struct inode *inode, struct file *file)
+{
+	struct motmdm_cdev *cdata;
+	int ret = 0;
+
+	cdata = container_of(inode->i_cdev, struct motmdm_cdev, cdev);
+	get_device(cdata->dev);
+	nonseekable_open(inode, file);
+	file->private_data = cdata;
+
+	down_write(&cdata->rwsem);
+	if (cdata->disconnected)
+		ret = -ENODEV;
+	up_write(&cdata->rwsem);
+
+	if (ret)
+		put_device(cdata->dev);
+
+	return ret;
+}
+
+static int motmdm_cdev_release(struct inode *inode, struct file *file)
+{
+	struct motmdm_cdev *cdata = file->private_data;
+
+	down_write(&cdata->rwsem);
+	up_write(&cdata->rwsem);
+	put_device(cdata->dev);
+
+	return 0;
+}
+
+static ssize_t motmdm_cdev_read(struct file *file, char __user *buf,
+				size_t count, loff_t *pos)
+{
+	struct motmdm_cdev *cdata = file->private_data;
+	struct motmdm_dlci *mot_dlci = cdata->dlci;
+	unsigned int copied;
+	int err;
+
+	mutex_lock(&cdata->read_mutex);
+	while (kfifo_is_empty(&mot_dlci->read_fifo)) {
+		mutex_unlock(&cdata->read_mutex);
+
+		if (cdata->disconnected)
+			return 0;
+
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		err = wait_event_interruptible(mot_dlci->read_queue,
+				cdata->disconnected ||
+				!kfifo_is_empty(&mot_dlci->read_fifo));
+		if (err)
+			return -ERESTARTSYS;
+
+		mutex_lock(&cdata->read_mutex);
+	}
+
+	err = kfifo_to_user(&mot_dlci->read_fifo, buf, count, &copied);
+	if (err == 0)
+		err = copied;
+
+	mutex_unlock(&cdata->read_mutex);
+
+	return err;
+}
+
+static ssize_t motmdm_cdev_write_packet(struct motmdm_cdev *cdata, int cmdid)
+{
+	struct motmdm_dlci *mot_dlci = cdata->dlci;
+	struct motmdm *ddata = mot_dlci->privdata;
+
+	return motmdm_write(ddata->dev, mot_dlci, cmdid,
+			    cdata->write_buf, cdata->write_offset - 1);
+}
+
+static ssize_t motmdm_cdev_write(struct file *file, const char __user *buf,
+				 size_t count, loff_t *pos)
+{
+	struct motmdm_cdev *cdata = file->private_data;
+	size_t written = 0;
+	int err, flag = -ENOMSG;
+
+	if (cdata->disconnected)
+		return -EIO;
+
+	if (!count)
+		return 0;
+
+	err = mutex_lock_interruptible(&cdata->write_mutex);
+	if (err)
+		return -ERESTARTSYS;
+
+	for (;;) {
+		size_t n;
+		bool packet = false;
+
+		n = min(count, cdata->write_buf_sz -
+			cdata->write_offset - count);
+		if (copy_from_user(cdata->write_buf + cdata->write_offset,
+				   buf, n)) {
+			err = -EFAULT;
+			goto out_unlock;
+		}
+
+		cdata->write_offset += n;
+		cdata->write_buf[cdata->write_offset] = '\0';
+		if (cdata->write_offset) {
+			u8 last = cdata->write_buf[cdata->write_offset - 1];
+
+			switch (last) {
+			case 0x1a:	/* Continuation packets end with ^Z */
+				flag = -ENOENT;
+				/* Fallthrough */
+			case '\n':
+			case '\r':
+				packet = true;
+				break;
+			default:
+				break;
+			}
+		}
+
+		down_read(&cdata->rwsem);
+
+		if (cdata->disconnected) {
+			err = -EIO;
+			goto err_write;
+		}
+
+		if (packet) {
+			err = motmdm_cdev_write_packet(cdata, flag);
+			if (err < 0)
+				goto err_write;
+
+			cdata->write_offset = 0;
+		}
+
+		err = n;
+
+err_write:
+		up_read(&cdata->rwsem);
+
+		if (err < 0)
+			break;
+
+		written += err;
+		buf += err;
+
+		if (written == count)
+			break;
+	}
+
+	if (written)
+		err = written;
+
+out_unlock:
+	mutex_unlock(&cdata->write_mutex);
+
+	return err;
+}
+
+static __poll_t motmdm_cdev_poll(struct file *file, poll_table *wait)
+{
+	struct motmdm_cdev *cdata = file->private_data;
+	struct motmdm_dlci *mot_dlci = cdata->dlci;
+	__poll_t mask = 0;
+
+	poll_wait(file, &mot_dlci->read_queue, wait);
+
+	if (!kfifo_is_empty(&mot_dlci->read_fifo))
+		mask |= EPOLLIN | EPOLLRDNORM;
+	if (cdata->write_offset < cdata->write_buf_sz)
+		mask |= EPOLLOUT | EPOLLWRNORM;
+	if (cdata->disconnected)
+		mask |= EPOLLHUP;
+
+	return mask;
+}
+
+static const struct file_operations motmdm_fops = {
+	.owner		= THIS_MODULE,
+	.open		= motmdm_cdev_open,
+	.release	= motmdm_cdev_release,
+	.read		= motmdm_cdev_read,
+	.write		= motmdm_cdev_write,
+	.poll		= motmdm_cdev_poll,
+	.llseek		= no_llseek,
+};
+
+static int motmdm_cdev_init_one(struct motmdm *ddata, int index)
+{
+	struct motmdm_cdev *cdata;
+	struct motmdm_dlci *mot_dlci;
+	int err;
+
+	mot_dlci = kzalloc(sizeof(*mot_dlci), GFP_KERNEL);
+	if (!mot_dlci)
+		return -ENOMEM;
+
+	mot_dlci->drvdata = ddata;
+	mot_dlci->line = index;
+
+	err = kfifo_alloc(&mot_dlci->read_fifo, MOTMDM_READ_FIFO_SIZE,
+			  GFP_KERNEL);
+	if (err)
+		goto err_free_dlci;
+
+	cdata = kzalloc(sizeof(*cdata), GFP_KERNEL);
+	if (!cdata)
+		goto err_free_kfifo;
+
+	cdata->dlci = mot_dlci;
+	init_rwsem(&cdata->rwsem);
+	mutex_init(&cdata->read_mutex);
+	mutex_init(&cdata->write_mutex);
+	cdata->write_buf_sz = MOTMDM_WRITE_BUF_SIZE;
+	cdata->write_buf = kzalloc(cdata->write_buf_sz, GFP_KERNEL);
+	if (!cdata->write_buf)
+		goto err_free_cdata;
+
+	err = motmdm_register_dlci(ddata->dev, cdata->dlci);
+	if (err)
+		goto err_free_write_buf;
+
+	cdata->dev = device_create(ddata->class, ddata->dev,
+				   MKDEV(MAJOR(ddata->dev_id), index),
+				   mot_dlci, "%s%i", motmdm_driver_name,
+				   index);
+	if (IS_ERR(cdata->dev)) {
+		err = PTR_ERR(cdata->dev);
+		goto err_unregister;
+	}
+
+	cdev_init(&cdata->cdev, &motmdm_fops);
+	err = cdev_add(&cdata->cdev, MKDEV(MAJOR(ddata->dev_id), index),
+		       MOTMDM_DLCI_MAX);
+	if (err)
+		goto err_device_destroy;
+
+	list_add_tail(&cdata->node, &ddata->cdevs);
+
+	return 0;
+
+err_device_destroy:
+	device_destroy(ddata->class, MKDEV(MAJOR(ddata->dev_id), index));
+err_unregister:
+	motmdm_unregister_dlci(ddata->dev, cdata->dlci);
+err_free_write_buf:
+	kfree(cdata->write_buf);
+err_free_cdata:
+	kfree(cdata);
+err_free_kfifo:
+	kfifo_free(&mot_dlci->read_fifo);
+err_free_dlci:
+	kfree(mot_dlci);
+
+	return err;
+}
+
+static void motmdm_cdev_cleanup(struct device *dev);
+
+static int motmdm_cdev_init(struct device *dev)
+{
+	struct motmdm *ddata = gsm_serdev_get_drvdata(dev);
+	int bit, err;
+
+	err = alloc_chrdev_region(&ddata->dev_id, 0, MOTMDM_DLCI_MAX,
+				  motmdm_driver_name);
+	if (err)
+		return err;
+
+	ddata->class = class_create(THIS_MODULE, motmdm_driver_name);
+	if (IS_ERR(ddata->class)) {
+		err = PTR_ERR(ddata->class);
+		motmdm_cdev_cleanup(dev);
+
+		return err;
+	}
+
+	for_each_set_bit(bit, &ddata->cfg->cdevmask, BITS_PER_LONG) {
+		err = motmdm_cdev_init_one(ddata, bit);
+		if (err) {
+			motmdm_cdev_cleanup(dev);
+
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static void motmdm_cdev_free_one(struct motmdm_cdev *cdata)
+{
+	struct motmdm_dlci *mot_dlci = cdata->dlci;
+	struct motmdm *ddata = mot_dlci->privdata;
+
+	down_write(&cdata->rwsem);
+	cdata->disconnected = true;
+	if (cdata->count)
+		wake_up(&mot_dlci->read_queue);
+	up_write(&cdata->rwsem);
+
+	cdev_del(&cdata->cdev);
+	device_destroy(ddata->class,
+		       MKDEV(MAJOR(ddata->dev_id), mot_dlci->line));
+	kfree(cdata->write_buf);
+
+	motmdm_unregister_dlci(ddata->dev, cdata->dlci);
+	kfifo_free(&mot_dlci->read_fifo);
+	kfree(cdata->dlci);
+	kfree(cdata);
+}
+
+static void motmdm_cdev_cleanup(struct device *dev)
+{
+	struct motmdm *ddata = gsm_serdev_get_drvdata(dev);
+	struct motmdm_cdev *cdata, *tmp;
+
+	list_for_each_entry_safe(cdata, tmp, &ddata->cdevs, node) {
+		list_del(&cdata->node);
+		motmdm_cdev_free_one(cdata);
+	}
+
+	class_destroy(ddata->class);
+
+	unregister_chrdev_region(ddata->dev_id, MOTMDM_DLCI_MAX);
+}
+
+static int motmdm_check_revision(struct device *dev)
+{
+	struct motmdm *ddata = gsm_serdev_get_drvdata(dev);
+	struct motmdm_dlci *mot_dlci;
+	const unsigned char *cmd = "AT+VERSION=";
+	unsigned char *buf;
+	int retries = 3, err;
+
+	mot_dlci = kzalloc(sizeof(*mot_dlci), GFP_KERNEL);
+	if (!mot_dlci)
+		return -ENOMEM;
+
+	mot_dlci->drvdata = ddata;
+	mot_dlci->line = MOTMDM_DLCI6;
+
+	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!buf) {
+		err = -ENOMEM;
+		goto free_dlci;
+	}
+
+	err = motmdm_register_dlci(dev, mot_dlci);
+	if (err)
+		goto free_buf;
+
+	while (retries--) {
+		err = motmdm_send_command(dev, mot_dlci, 1000, cmd, strlen(cmd),
+					  buf, PAGE_SIZE);
+		if (err >= 0) {
+			msleep(100);
+			break;
+		}
+
+		msleep(500);
+	}
+
+	if (err < 0) {
+		dev_err(dev, "Could not connect: %i\n", err);
+	} else if (!strncmp(buf, "ERROR", 5)) {
+		dev_err(dev, "Firmware error: %s\n", buf);
+		err = -ENODEV;
+	} else {
+		dev_info(dev, "Firmware: %s\n", buf);
+		err = 0;
+	}
+
+	motmdm_unregister_dlci(dev, mot_dlci);
+
+free_buf:
+	kfree(buf);
+free_dlci:
+	kfree(mot_dlci);
+
+	return err;
+}
+
+static int motmdm_set_config(struct device *dev, int retransmissions)
+{
+	struct motmdm *ddata = gsm_serdev_get_drvdata(dev);
+	struct gsm_serdev *gsd = &ddata->gsd;
+	struct gsm_config c;
+	int err;
+
+	err = gsm_serdev_get_config(gsd, &c);
+	if (err)
+		return err;
+
+	c.i = 1;		/* 1 = UIH, 2 = UI */
+	c.initiator = 1;
+	c.encapsulation = 0;	/* basic mode */
+	c.adaption = 1;
+	c.mru = 1024;		/* from android TS 27010 driver */
+	c.mtu = 1024;		/* from android TS 27010 driver */
+	c.t1 = 10;		/* ack timer, default 10ms */
+	c.t2 = 34;		/* response timer, default 34 */
+	c.n2 = retransmissions;	/* retransmissions, default 3 */
+
+	err = gsm_serdev_set_config(gsd, &c);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int motmdm_output(struct gsm_serdev *gsd, u8 *data, int len)
+{
+	struct serdev_device *serdev = gsd->serdev;
+	struct device *dev = &serdev->dev;
+	int err;
+
+	err = pm_runtime_get(dev);
+	if ((err != -EINPROGRESS) && err < 0) {
+		pm_runtime_put_noidle(dev);
+
+		return err;
+	}
+
+	serdev_device_write_buf(serdev, data, len);
+
+	pm_runtime_put(dev);
+
+	return len;
+}
+
+static const struct motmdm_cfg mapphone_mdm6600_data = {
+	.cdevmask = MOTMDM_DLCI_MASK & ~(BIT(MOTMDM_DLCI2) | BIT(MOTMDM_DLCI4)),
+	.aggressive_pm = true,
+	.modem_dlci = MOTMDM_DLCI1,
+	.codec_dlci = MOTMDM_DLCI2,
+};
+
+static const struct of_device_id motmdm_id_table[] = {
+	{
+		.compatible = "motorola,mapphone-mdm6600-serial",
+		.data = &mapphone_mdm6600_data,
+	},
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, motmdm_id_table);
+
+static const struct mfd_cell motmdm_mfd_devices[] = {
+	{
+		.name = "mot-mdm6600-codec",
+	},
+	{
+		.name = "gnss-mot-mdm6600",
+		.of_compatible = "motorola,mapphone-mdm6600-gnss",
+	},
+};
+
+static int motmdm_probe(struct serdev_device *serdev)
+{
+	struct device *dev = &serdev->dev;
+	const struct of_device_id *match;
+	struct gsm_serdev *gsd;
+	struct motmdm *ddata;
+	int err;
+
+	match = of_match_device(of_match_ptr(motmdm_id_table), dev);
+	if (!match)
+		return -ENODEV;
+
+	ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
+	if (!ddata)
+		return -ENOMEM;
+
+	ddata->dev = dev;
+	ddata->cfg = match->data;
+
+	INIT_LIST_HEAD(&ddata->dlcis);
+	INIT_LIST_HEAD(&ddata->cdevs);
+	gsd = &ddata->gsd;
+	gsd->serdev = serdev;
+	gsd->output = motmdm_output;
+	serdev_device_set_drvdata(serdev, gsd);
+	gsm_serdev_set_drvdata(dev, ddata);
+
+	err = motmdm_init_phy(dev);
+	if (err)
+		return err;
+
+	pm_runtime_set_autosuspend_delay(dev, 200);
+	pm_runtime_use_autosuspend(dev);
+	pm_runtime_enable(dev);
+	err = pm_runtime_get_sync(dev);
+	if (err < 0) {
+		pm_runtime_put_noidle(dev);
+
+		return err;
+	}
+
+	err = gsm_serdev_register_device(gsd);
+	if (err)
+		goto disable;
+
+	err = serdev_device_open(gsd->serdev);
+	if (err)
+		goto disable;
+
+	serdev_device_set_baudrate(gsd->serdev, 115200);
+	serdev_device_set_rts(gsd->serdev, true);
+	serdev_device_set_flow_control(gsd->serdev, true);
+
+	/*
+	 * Getting dlci0 connected quirk: We set initial retransmissions
+	 * value high to get n_gsm to send SABM packets. Then after about
+	 * three seconds we'll get a "reassembly overrun" error from firmware
+	 * on dlci0 followed by DM(P) packets and then we're connected in ADM
+	 * mode. Note we will set the retransmissions back to default value
+	 * later on.
+	 */
+	err = motmdm_set_config(dev, MOTMDM_C_N2 * 10);
+	if (err)
+		goto close;
+
+	msleep(3000);
+
+	err = motmdm_check_revision(dev);
+	if (err)
+		goto close;
+
+	msleep(500);
+
+	err = motmdm_cdev_init(dev);
+	if (err)
+		goto close;
+
+	pm_runtime_put_sync(dev);
+
+	err = devm_mfd_add_devices(dev, 0, motmdm_mfd_devices,
+				   ARRAY_SIZE(motmdm_mfd_devices),
+				   NULL, 0, NULL);
+	if (err)
+		goto cdev_cleanup;
+
+	/* Set initial retransmissions back to default value */
+	err = motmdm_set_config(dev, MOTMDM_C_N2);
+	if (err)
+		goto cdev_cleanup;
+
+	if (ddata->cfg->aggressive_pm) {
+		/*
+		 * Configure SoC 8250 device for 700 ms autosuspend delay,
+		 * values around 600 ms and shorter cause spurious wake-up
+		 * events at least on droid 4.
+		 */
+		pm_runtime_set_autosuspend_delay(serdev->ctrl->dev.parent, 700);
+
+		/*
+		 * Allow parent serdev device to idle when open, balanced in
+		 * remove
+		 */
+		pm_runtime_put(&serdev->ctrl->dev);
+
+		/*
+		 * Keep parent SoC 8250 device active during use because of the
+		 * OOB GPIO wake-up signaling shared with USB PHY.
+		 */
+		pm_suspend_ignore_children(&serdev->ctrl->dev, false);
+	}
+
+	return 0;
+
+cdev_cleanup:
+	motmdm_cdev_cleanup(dev);
+
+close:
+	serdev_device_close(serdev);
+
+disable:
+	pm_runtime_dont_use_autosuspend(dev);
+	pm_runtime_put_sync(dev);
+	pm_runtime_disable(dev);
+	gsm_serdev_unregister_device(gsd);
+
+	return err;
+}
+
+static void motmdm_remove(struct serdev_device *serdev)
+{
+	struct gsm_serdev *gsd = serdev_device_get_drvdata(serdev);
+	struct device *dev = &serdev->dev;
+	struct motmdm *ddata = gsm_serdev_get_drvdata(dev);
+	int err;
+
+	/* Balance the put done in probe for UART */
+	if (ddata->cfg->aggressive_pm)
+		pm_runtime_get(&serdev->ctrl->dev);
+
+	err = pm_runtime_get_sync(dev);
+	if (err < 0)
+		dev_warn(dev, "%s: PM runtime: %i\n", __func__, err);
+
+	motmdm_cdev_cleanup(dev);
+	serdev_device_close(serdev);
+	gsm_serdev_unregister_device(gsd);
+
+	pm_runtime_dont_use_autosuspend(dev);
+	pm_runtime_put_sync(dev);
+	pm_runtime_disable(dev);
+}
+
+static struct serdev_device_driver motmdm_driver = {
+	.driver = {
+		.name = "motmdm",
+		.of_match_table = of_match_ptr(motmdm_id_table),
+		.pm = &motmdm_pm_ops,
+	},
+	.probe = motmdm_probe,
+	.remove = motmdm_remove,
+};
+
+module_serdev_device_driver(motmdm_driver);
+
+MODULE_ALIAS("platform:motorola-mdm");
+MODULE_DESCRIPTION("Motorola Modem TS 27.010 serdev driver");
+MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.26.2

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 3/4] dt-bindings: serdev: motmdm: Add binding for motorola-mdm
  2020-04-21 23:27 [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem Tony Lindgren
  2020-04-21 23:27 ` [PATCH 1/4] tty: n_gsm: Add support for serdev drivers Tony Lindgren
  2020-04-21 23:27 ` [PATCH 2/4] serdev: ngsm-motmdm: Add Motorola TS 27.010 serdev modem driver for droid4 Tony Lindgren
@ 2020-04-21 23:27 ` Tony Lindgren
  2020-04-21 23:27 ` [PATCH 4/4] ARM: dts: omap4-droid4: Enable basic modem support Tony Lindgren
  2020-04-23 11:43 ` [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem Johan Hovold
  4 siblings, 0 replies; 19+ messages in thread
From: Tony Lindgren @ 2020-04-21 23:27 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rob Herring
  Cc: Alan Cox, Lee Jones, Jiri Slaby, Johan Hovold, Merlijn Wajer,
	Pavel Machek, Peter Hurley, Sebastian Reichel, linux-serial,
	devicetree, linux-kernel, linux-omap

Add a binding document for Motorola modems controllable by
TS 27.010 UART line discipline using serdev drivers.

Reviewed-by: Rob Herring <robh@kernel.org>
Reviewed-by: Pavel Machek <pavel@ucw.cz>
[tony@atomide.com: moved to live under bindings/serdev]
Signed-off-by: Tony Lindgren <tony@atomide.com>
---
 .../serdev/motorola,mapphone-mdm6600.yaml     | 34 +++++++++++++++++++
 1 file changed, 34 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/serdev/motorola,mapphone-mdm6600.yaml

diff --git a/Documentation/devicetree/bindings/serdev/motorola,mapphone-mdm6600.yaml b/Documentation/devicetree/bindings/serdev/motorola,mapphone-mdm6600.yaml
new file mode 100644
--- /dev/null
+++ b/Documentation/devicetree/bindings/serdev/motorola,mapphone-mdm6600.yaml
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/serdev/motorola,mapphone-mdm6600.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Motorola Mapphone MDM6600 Modem
+
+maintainers:
+  - Tony Lindgren <tony@atomide.com>
+
+properties:
+  compatible:
+    items:
+      - const: motorola,mapphone-mdm6600-serial
+
+  phys:
+    maxItems: 1
+
+  phy-names:
+    const: usb
+
+required:
+  - compatible
+  - phys
+  - phy-names
+
+examples:
+  - |
+    modem {
+        compatible = "motorola,mapphone-mdm6600-serial";
+        phys = <&fsusb1_phy>;
+        phy-names = "usb";
+    };
-- 
2.26.2

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 4/4] ARM: dts: omap4-droid4: Enable basic modem support
  2020-04-21 23:27 [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem Tony Lindgren
                   ` (2 preceding siblings ...)
  2020-04-21 23:27 ` [PATCH 3/4] dt-bindings: serdev: motmdm: Add binding for motorola-mdm Tony Lindgren
@ 2020-04-21 23:27 ` Tony Lindgren
  2020-04-23 11:43 ` [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem Johan Hovold
  4 siblings, 0 replies; 19+ messages in thread
From: Tony Lindgren @ 2020-04-21 23:27 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Rob Herring
  Cc: Alan Cox, Lee Jones, Jiri Slaby, Johan Hovold, Merlijn Wajer,
	Pavel Machek, Peter Hurley, Sebastian Reichel, linux-serial,
	devicetree, linux-kernel, linux-omap

This allows apps to use /dev/motmdm1 for voice call AT commands,
/dev/motmdm3 for sending SMS, and /dev/motmdm9 for reading SMS.

Voice call audio mixer and GNSS are not yet supported.

Reviewed-by: Pavel Machek <pavel@ucw.cz>
Tested-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Tony Lindgren <tony@atomide.com>
---
 arch/arm/boot/dts/motorola-mapphone-common.dtsi | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/motorola-mapphone-common.dtsi b/arch/arm/boot/dts/motorola-mapphone-common.dtsi
--- a/arch/arm/boot/dts/motorola-mapphone-common.dtsi
+++ b/arch/arm/boot/dts/motorola-mapphone-common.dtsi
@@ -698,6 +698,12 @@ &uart1 {
 	pinctrl-0 = <&uart1_pins>;
 	interrupts-extended = <&wakeupgen GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH
 			       &omap4_pmx_core 0xfc>;
+
+	modem {
+		compatible = "motorola,mapphone-mdm6600-serial";
+		phys = <&fsusb1_phy>;
+		phy-names = "usb";
+	};
 };
 
 &uart3 {
-- 
2.26.2

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem
  2020-04-21 23:27 [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem Tony Lindgren
                   ` (3 preceding siblings ...)
  2020-04-21 23:27 ` [PATCH 4/4] ARM: dts: omap4-droid4: Enable basic modem support Tony Lindgren
@ 2020-04-23 11:43 ` Johan Hovold
  2020-04-23 15:37   ` Tony Lindgren
  2020-04-24 21:50   ` Pavel Machek
  4 siblings, 2 replies; 19+ messages in thread
From: Johan Hovold @ 2020-04-23 11:43 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Greg Kroah-Hartman, Rob Herring, Alan Cox, Lee Jones, Jiri Slaby,
	Johan Hovold, Merlijn Wajer, Pavel Machek, Peter Hurley,
	Sebastian Reichel, linux-serial, devicetree, linux-kernel,
	linux-omap

Hi Tony,

On Tue, Apr 21, 2020 at 04:27:48PM -0700, Tony Lindgren wrote:
> Hi all,
> 
> Here's v4 set of n_gsm serdev support patches, and the related protocol
> driver for the modem found on Motorola Mapphone phones and tablets
> like droid4.
> 
> This series only adds basic character device support for the serdev
> driver. Other serdev consumer drivers for specific devices will be
> posted separately.

I'm still missing an architectural (design) overview here -- reviewer
time is a scarce resource.

I also suggested earlier that you include, at least as an RFC, one or
more of your child-device drivers so that we can see how this ends up
being used in the end (including an example devicetree).

Some high-level comments until then:

I'm not sure that a plain chardev interface for the mux channels is the
right interface. The n_gsm ldisc exposes tty devices and I think your
serdev adaptation should continue to do that.

On that note; you're not actually adding general TS 27.010 serdev
support, but rather some hooks and a custom driver and interface (mfd +
/dev/motmdmN) for one particular modem.

I'd rather see a generic implementation which can be used with other
modems and that continues to expose a /dev/gsmttyN interface to which we
could attach serdev clients instead (and not create a motmdm serdev
replica of sorts).

I know the location of this driver has been up for discussion already,
but drivers/tty/serdev/protocol still isn't right (e.g. we don't have an
drivers/i2c/protocol directory where we stuff random i2c client
drivers).

It's an mfd + custom chardev driver for a modem and related to n_gsm
(even more if you add generic serdev support). Currently, drivers/mfd or
drivers/misc appear to be better choices. Otherwise, n_gsm lives in
drivers/tty since it's a line discipline, but it could be moved to a new
drivers/modem if needed (cf. the bluetooth hci ldisc).

Last, it seems you've based the serdev-ngsm-motmdm.c chardev
implementation on a more or less verbatim copy of drivers/gnss/core.c.
I'd appreciate if you could mention that in the file header and
reproduce the copyright notice if you end up keeping that interface.

> Tony Lindgren (4):
>   tty: n_gsm: Add support for serdev drivers
>   serdev: ngsm-motmdm: Add Motorola TS 27.010 serdev modem driver for
>     droid4
>   dt-bindings: serdev: motmdm: Add binding for motorola-mdm
>   ARM: dts: omap4-droid4: Enable basic modem support
> 
>  .../serdev/motorola,mapphone-mdm6600.yaml     |   34 +
>  .../boot/dts/motorola-mapphone-common.dtsi    |    6 +
>  drivers/tty/n_gsm.c                           |  372 +++++
>  drivers/tty/serdev/Kconfig                    |    2 +
>  drivers/tty/serdev/Makefile                   |    2 +
>  drivers/tty/serdev/protocol/Kconfig           |   14 +
>  drivers/tty/serdev/protocol/Makefile          |    3 +
>  .../tty/serdev/protocol/serdev-ngsm-motmdm.c  | 1191 +++++++++++++++++
>  include/linux/serdev-gsm.h                    |  168 +++
>  9 files changed, 1792 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/serdev/motorola,mapphone-mdm6600.yaml
>  create mode 100644 drivers/tty/serdev/protocol/Kconfig
>  create mode 100644 drivers/tty/serdev/protocol/Makefile
>  create mode 100644 drivers/tty/serdev/protocol/serdev-ngsm-motmdm.c
>  create mode 100644 include/linux/serdev-gsm.h

Johan

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem
  2020-04-23 11:43 ` [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem Johan Hovold
@ 2020-04-23 15:37   ` Tony Lindgren
  2020-04-23 23:27     ` Tony Lindgren
  2020-05-28  8:24     ` Johan Hovold
  2020-04-24 21:50   ` Pavel Machek
  1 sibling, 2 replies; 19+ messages in thread
From: Tony Lindgren @ 2020-04-23 15:37 UTC (permalink / raw)
  To: Johan Hovold
  Cc: Greg Kroah-Hartman, Rob Herring, Alan Cox, Lee Jones, Jiri Slaby,
	Merlijn Wajer, Pavel Machek, Peter Hurley, Sebastian Reichel,
	linux-serial, devicetree, linux-kernel, linux-omap

* Johan Hovold <johan@kernel.org> [200423 11:44]:
> Hi Tony,
> 
> On Tue, Apr 21, 2020 at 04:27:48PM -0700, Tony Lindgren wrote:
> > Hi all,
> > 
> > Here's v4 set of n_gsm serdev support patches, and the related protocol
> > driver for the modem found on Motorola Mapphone phones and tablets
> > like droid4.
> > 
> > This series only adds basic character device support for the serdev
> > driver. Other serdev consumer drivers for specific devices will be
> > posted separately.
> 
> I'm still missing an architectural (design) overview here -- reviewer
> time is a scarce resource.

OK, so for this series it would be roughly:

1. Allow serdev drivers configure and use n_gsm for TS 27.010

2. Add support for a motorola modem with a custom protocol on
   top of TS 27.010 with only chardev support initially

3. Start adding TS 27.010 channel specific device drivers as
   regular Linux device drivers later on

> I also suggested earlier that you include, at least as an RFC, one or
> more of your child-device drivers so that we can see how this ends up
> being used in the end (including an example devicetree).

Well I left that out intentionally to keep the series smaller. But yeah,
I guess that part is really what people want. I'll add a basic GNSS
driver for the next version.

> Some high-level comments until then:
> 
> I'm not sure that a plain chardev interface for the mux channels is the
> right interface. The n_gsm ldisc exposes tty devices and I think your
> serdev adaptation should continue to do that.

That's what I started with, but was not happy at all because of the
custom motorola protocol layer on top of TS 27.010.

A generic n_gsm serdev driver should work easily though. Roughly, the
n_gsm serdev driver(s) wanting to use just the n_gsm tty devices would
just call the n_gsm config functions only, and configure no custom
callbacks. Maybe some flag is needed for n_gsm tty. This needs to
be checked again though as I have not done that for a while.

Then for the custom n_gsm serdev implementations, like this modem,
we have packet data from n_gsm. I don't think stuffing the packet data
back into tty devices helps for these cases.

> On that note; you're not actually adding general TS 27.010 serdev
> support, but rather some hooks and a custom driver and interface (mfd +
> /dev/motmdmN) for one particular modem.

A generic n_gsm serdev driver would just use a subset of these same
functions.

> I'd rather see a generic implementation which can be used with other
> modems and that continues to expose a /dev/gsmttyN interface to which we
> could attach serdev clients instead (and not create a motmdm serdev
> replica of sorts).

Yeah this should be doable quite easily actually without really any of
the motorola driver code. It's a separate driver though, and not
usable for this case because of the custom layer.

> I know the location of this driver has been up for discussion already,
> but drivers/tty/serdev/protocol still isn't right (e.g. we don't have an
> drivers/i2c/protocol directory where we stuff random i2c client
> drivers).

Argh, the location of driver again.. So we do have the custom motorola
layer to deal with on top of TS 27.010, but the custom handling is
contained within the driver. So maybe just drivers/serial for the
custom driver then.

> It's an mfd + custom chardev driver for a modem and related to n_gsm
> (even more if you add generic serdev support). Currently, drivers/mfd or
> drivers/misc appear to be better choices. Otherwise, n_gsm lives in
> drivers/tty since it's a line discipline, but it could be moved to a new
> drivers/modem if needed (cf. the bluetooth hci ldisc).

The n_gsm suport is not limited to modems only. Probably best to
not move it. And I'd avoid drivers/modem until we actually have three
similar use cases based on the late David "Mr.Bus" Brownell rule of
thumb :)

> Last, it seems you've based the serdev-ngsm-motmdm.c chardev
> implementation on a more or less verbatim copy of drivers/gnss/core.c.
> I'd appreciate if you could mention that in the file header and
> reproduce the copyright notice if you end up keeping that interface.

Oh yes indeed, thanks for pointing that out. I'll add it to the next
version. The chardev code is for sure based on drivers/gnss.

To explain my ignorance, I added the chardev support initially as an
experiment to see if I can handle the motorola packet layer better
that way compared to the n_gsm ttys and userspace handling. It ended
up working quite nicely, so I kept it but then I accidentally left
out references to the source. Sorry about that.

Regards,

Tony

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem
  2020-04-23 15:37   ` Tony Lindgren
@ 2020-04-23 23:27     ` Tony Lindgren
  2020-04-25 16:58       ` Tony Lindgren
  2020-05-28  8:24     ` Johan Hovold
  1 sibling, 1 reply; 19+ messages in thread
From: Tony Lindgren @ 2020-04-23 23:27 UTC (permalink / raw)
  To: Johan Hovold
  Cc: Greg Kroah-Hartman, Rob Herring, Alan Cox, Lee Jones, Jiri Slaby,
	Merlijn Wajer, Pavel Machek, Peter Hurley, Sebastian Reichel,
	linux-serial, devicetree, linux-kernel, linux-omap

* Tony Lindgren <tony@atomide.com> [200423 15:38]:
> * Johan Hovold <johan@kernel.org> [200423 11:44]:
> > I'd rather see a generic implementation which can be used with other
> > modems and that continues to expose a /dev/gsmttyN interface to which we
> > could attach serdev clients instead (and not create a motmdm serdev
> > replica of sorts).
> 
> Yeah this should be doable quite easily actually without really any of
> the motorola driver code. It's a separate driver though, and not
> usable for this case because of the custom layer.

Well here's a first take at the such a generic serdev-ngsm driver
that depends on patch 1/4 of this series. All this does is configure
a serial port for TS 27.010 adaption1, and brings up 16 /dev/gsmtty*
devices. So no custom options for the binding so far, and no support
for adding channel specific device drivers. But all that can be added
naturally later on.

I've briefly tested it with my droid4 instead of the custom driver
in this series to make sure things work with the following steps:

I added modem to arch/arm/boot/dts/motorola-mapphone-common.dtsi
under uart1:

	modem {
		compatible = "3gpp,ts27010-adaption1";
	};

And then I had to disable the shared gpio PM pin toggling for the
USB PHY to prevent the modem from idling:

# echo on > /sys/devices/platform/usb-phy@1/power/control

And then I was able to talk with the modem by prepending
custom packet numbering to the messages:

# cat /dev/gsmtty10 &
# printf "U1234AT+MSIM?\r" > /dev/gsmtty10
# U1234+MSIM=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

Hmm. Maybe we could just handle the droid4 PM needs with a
custom compatible here too. And deal with the motorola packet
layer somewhere.

Then the only difference to the custom driver in this series
really is that we would be using the /dev/gsmtty* tty devices
for userspace isntead of the custom /dev/motmdm* char devices.

Regards,

Tony

8< --------------------
 drivers/tty/n_gsm.c              |  65 ++++++++++++-
 drivers/tty/serdev/Kconfig       |  10 ++
 drivers/tty/serdev/Makefile      |   1 +
 drivers/tty/serdev/serdev-ngsm.c | 156 +++++++++++++++++++++++++++++++
 include/linux/serdev-gsm.h       |  11 +++
 5 files changed, 242 insertions(+), 1 deletion(-)
 create mode 100644 drivers/tty/serdev/serdev-ngsm.c

diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -2438,6 +2438,19 @@ static struct gsm_dlci *gsd_dlci_get(struct gsm_serdev *gsd, int line,
 	return dlci;
 }
 
+static int gsd_dlci_receive_buf(struct gsm_dlci *dlci,
+				const unsigned char *buf,
+				size_t len)
+{
+	struct tty_port *port;
+
+	port = &dlci->port;
+	tty_insert_flip_string(port, buf, len);
+	tty_flip_buffer_push(port);
+
+	return 0;
+}
+
 static void gsd_dlci_data(struct gsm_dlci *dlci, const u8 *buf, int len)
 {
 	struct gsm_mux *gsm = dlci->gsm;
@@ -2451,6 +2464,8 @@ static void gsd_dlci_data(struct gsm_dlci *dlci, const u8 *buf, int len)
 	case 1:
 		if (dlci->ops->receive_buf)
 			dlci->ops->receive_buf(dlci->ops, buf, len);
+		else
+			gsd_dlci_receive_buf(dlci, buf, len);
 		break;
 	default:
 		pr_warn("dlci%i adaption %i not yet implemented\n",
@@ -2550,7 +2565,7 @@ int gsm_serdev_register_dlci(struct gsm_serdev *gsd,
 
 	gsm = gsd->gsm;
 
-	if (!ops || !ops->line || !ops->receive_buf)
+	if (!ops || !ops->line)
 		return -EINVAL;
 
 	dlci = gsd_dlci_get(gsd, ops->line, true);
@@ -2673,6 +2688,54 @@ static struct serdev_device_ops gsd_client_ops = {
 	.write_wakeup = gsd_write_wakeup,
 };
 
+int gsm_serdev_register_tty_port(struct gsm_serdev *gsd, int line)
+{
+	struct gsm_serdev_dlci *ops;
+	unsigned int base;
+	int error;
+
+	if (line < 1)
+		return -EINVAL;
+
+	ops = kzalloc(sizeof(*ops), GFP_KERNEL);
+	if (!ops)
+		return -ENOMEM;
+
+	ops->line = line;
+
+	error = gsm_serdev_register_dlci(gsd, ops);
+	if (error) {
+		kfree(ops);
+
+		return error;
+	}
+
+	base = mux_num_to_base(gsd->gsm);
+	tty_register_device(gsm_tty_driver, base + ops->line, NULL);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gsm_serdev_register_tty_port);
+
+void gsm_serdev_unregister_tty_port(struct gsm_serdev *gsd, int line)
+{
+	struct gsm_dlci *dlci;
+	unsigned int base;
+
+	if (line < 1)
+		return;
+
+	dlci = gsd_dlci_get(gsd, line, false);
+	if (IS_ERR(dlci))
+		return;
+
+	base = mux_num_to_base(gsd->gsm);
+	tty_unregister_device(gsm_tty_driver, base + line);
+	gsm_serdev_unregister_dlci(gsd, dlci->ops);
+	kfree(dlci->ops);
+}
+EXPORT_SYMBOL_GPL(gsm_serdev_unregister_tty_port);
+
 int gsm_serdev_register_device(struct gsm_serdev *gsd)
 {
 	struct gsm_mux *gsm;
diff --git a/drivers/tty/serdev/Kconfig b/drivers/tty/serdev/Kconfig
--- a/drivers/tty/serdev/Kconfig
+++ b/drivers/tty/serdev/Kconfig
@@ -22,4 +22,14 @@ config SERIAL_DEV_CTRL_TTYPORT
 	depends on SERIAL_DEV_BUS != m
 	default y
 
+config SERIAL_DEV_N_GSM
+	tristate "Serial device TS 27.010 support"
+	help
+	  Select this if you want to use the TS 27.010 with a serial port with
+	  devices such as modems and GNSS devices.
+
+	  If unsure, say N.
+	depends on N_GSM
+	depends on SERIAL_DEV_CTRL_TTYPORT
+
 endif
diff --git a/drivers/tty/serdev/Makefile b/drivers/tty/serdev/Makefile
--- a/drivers/tty/serdev/Makefile
+++ b/drivers/tty/serdev/Makefile
@@ -4,3 +4,4 @@ serdev-objs := core.o
 obj-$(CONFIG_SERIAL_DEV_BUS) += serdev.o
 
 obj-$(CONFIG_SERIAL_DEV_CTRL_TTYPORT) += serdev-ttyport.o
+obj-$(CONFIG_SERIAL_DEV_N_GSM) += serdev-ngsm.o
diff --git a/drivers/tty/serdev/serdev-ngsm.c b/drivers/tty/serdev/serdev-ngsm.c
new file mode 100644
--- /dev/null
+++ b/drivers/tty/serdev/serdev-ngsm.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic TS 27.010 serial line discipline serdev driver
+ * Copyright (C) 2020 Tony Lindgren <tony@atomide.com>
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/serdev.h>
+#include <linux/serdev-gsm.h>
+
+#include <uapi/linux/gsmmux.h>
+
+#define TS27010_C_N2		3	/* TS 27.010 default value */
+#define TS27010_NUM_DLCI	16
+
+struct serdev_ngsm {
+	struct device *dev;
+	struct gsm_serdev gsd;
+};
+
+static int serdev_ngsm_set_config_adaption1(struct device *dev,
+					    int retransmissions)
+{
+	struct serdev_ngsm *ddata = gsm_serdev_get_drvdata(dev);
+	struct gsm_serdev *gsd = &ddata->gsd;
+	struct gsm_config c;
+	int err;
+
+	err = gsm_serdev_get_config(gsd, &c);
+	if (err)
+		return err;
+
+	c.i = 1;		/* 1 = UIH, 2 = UI */
+	c.initiator = 1;
+	c.encapsulation = 0;	/* basic mode */
+	c.adaption = 1;
+	c.mru = 1024;		/* from android TS 27010 driver */
+	c.mtu = 1024;		/* from android TS 27010 driver */
+	c.t1 = 10;		/* ack timer, default 10ms */
+	c.t2 = 34;		/* response timer, default 34 */
+	c.n2 = retransmissions;	/* retransmissions, default 3 */
+
+	err = gsm_serdev_set_config(gsd, &c);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int serdev_ngsm_output(struct gsm_serdev *gsd, u8 *data, int len)
+{
+	struct serdev_device *serdev = gsd->serdev;
+
+	serdev_device_write_buf(serdev, data, len);
+
+	return len;
+}
+
+static const struct of_device_id serdev_ngsm_id_table[] = {
+	{ .compatible = "3gpp,ts27010-adaption1", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, serdev_ngsm_id_table);
+
+static int serdev_ngsm_probe(struct serdev_device *serdev)
+{
+	struct device *dev = &serdev->dev;
+	const struct of_device_id *match;
+	struct gsm_serdev *gsd;
+	struct serdev_ngsm *ddata;
+	int err, i;
+
+	match = of_match_device(of_match_ptr(serdev_ngsm_id_table), dev);
+	if (!match)
+		return -ENODEV;
+
+	ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
+	if (!ddata)
+		return -ENOMEM;
+
+	ddata->dev = dev;
+
+	gsd = &ddata->gsd;
+	gsd->serdev = serdev;
+	gsd->output = serdev_ngsm_output;
+	serdev_device_set_drvdata(serdev, gsd);
+	gsm_serdev_set_drvdata(dev, ddata);
+
+	err = gsm_serdev_register_device(gsd);
+	if (err)
+		goto err_disable;
+
+	err = serdev_device_open(gsd->serdev);
+	if (err)
+		goto err_disable;
+
+	serdev_device_set_baudrate(gsd->serdev, 115200);
+	serdev_device_set_rts(gsd->serdev, true);
+	serdev_device_set_flow_control(gsd->serdev, true);
+
+	err = serdev_ngsm_set_config_adaption1(dev, TS27010_C_N2);
+	if (err)
+		goto err_close;
+
+	/* Start from dlci1 as dlci0 is reserved control channel */
+	for (i = 1; i < TS27010_NUM_DLCI; i++) {
+		err = gsm_serdev_register_tty_port(gsd, i);
+		if (err)
+			dev_warn(dev, "tty init failed for dlci%i: %i\n",
+				 i, err);
+	}
+
+	return 0;
+
+err_close:
+	serdev_device_close(serdev);
+
+err_disable:
+	gsm_serdev_unregister_device(gsd);
+
+	return err;
+}
+
+static void serdev_ngsm_remove(struct serdev_device *serdev)
+{
+	struct gsm_serdev *gsd = serdev_device_get_drvdata(serdev);
+	int i;
+
+	for (i = 1; i < TS27010_NUM_DLCI; i++)
+		gsm_serdev_unregister_tty_port(gsd, i);
+
+	serdev_device_close(serdev);
+	gsm_serdev_unregister_device(gsd);
+}
+
+static struct serdev_device_driver serdev_ngsm_driver = {
+	.driver = {
+		.name = "serdev_ngsm",
+		.of_match_table = of_match_ptr(serdev_ngsm_id_table),
+	},
+	.probe = serdev_ngsm_probe,
+	.remove = serdev_ngsm_remove,
+};
+
+module_serdev_device_driver(serdev_ngsm_driver);
+
+MODULE_DESCRIPTION("serdev n_gsm driver");
+MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/serdev-gsm.h b/include/linux/serdev-gsm.h
--- a/include/linux/serdev-gsm.h
+++ b/include/linux/serdev-gsm.h
@@ -45,6 +45,8 @@ struct gsm_serdev_dlci {
 
 int gsm_serdev_register_device(struct gsm_serdev *gsd);
 void gsm_serdev_unregister_device(struct gsm_serdev *gsd);
+int gsm_serdev_register_tty_port(struct gsm_serdev *gsd, int line);
+void gsm_serdev_unregister_tty_port(struct gsm_serdev *gsd, int line);
 
 static inline void *gsm_serdev_get_drvdata(struct device *dev)
 {
@@ -108,6 +110,15 @@ void gsm_serdev_unregister_device(struct gsm_serdev *gsd)
 {
 }
 
+int gsm_serdev_register_tty_port(struct gsm_serdev *gsd, int line);
+{
+	return -ENODEV;
+}
+
+void gsm_serdev_unregister_tty_port(struct gsm_serdev *gsd, int line);
+{
+}
+
 static inline void *gsm_serdev_get_drvdata(struct device *dev)
 {
 	return NULL;
-- 
2.26.2

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem
  2020-04-23 11:43 ` [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem Johan Hovold
  2020-04-23 15:37   ` Tony Lindgren
@ 2020-04-24 21:50   ` Pavel Machek
  2020-04-24 22:15     ` Tony Lindgren
  1 sibling, 1 reply; 19+ messages in thread
From: Pavel Machek @ 2020-04-24 21:50 UTC (permalink / raw)
  To: Johan Hovold
  Cc: Tony Lindgren, Greg Kroah-Hartman, Rob Herring, Alan Cox,
	Lee Jones, Jiri Slaby, Merlijn Wajer, Peter Hurley,
	Sebastian Reichel, linux-serial, devicetree, linux-kernel,
	linux-omap

[-- Attachment #1: Type: text/plain, Size: 1151 bytes --]

Hi!

> > Here's v4 set of n_gsm serdev support patches, and the related protocol
> > driver for the modem found on Motorola Mapphone phones and tablets
> > like droid4.
> > 
> > This series only adds basic character device support for the serdev
> > driver. Other serdev consumer drivers for specific devices will be
> > posted separately.
> 
> I'm still missing an architectural (design) overview here -- reviewer
> time is a scarce resource.
> 
> I also suggested earlier that you include, at least as an RFC, one or
> more of your child-device drivers so that we can see how this ends up
> being used in the end (including an example devicetree).

Note that this is useful on its own: we have ofonod running on the top
of this doing calls and SMSes.

Tony: I know you have drivers depending on this somewhere (audio
routing and GPS), but I can't find them. It is not droid4-pending-v5.6
AFAICT. Do you have a pointer / could you publish them somewhere?

Best regards,
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem
  2020-04-24 21:50   ` Pavel Machek
@ 2020-04-24 22:15     ` Tony Lindgren
  2020-04-26  7:27       ` Pavel Machek
  2020-04-26 20:07       ` Pavel Machek
  0 siblings, 2 replies; 19+ messages in thread
From: Tony Lindgren @ 2020-04-24 22:15 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Johan Hovold, Greg Kroah-Hartman, Rob Herring, Alan Cox,
	Lee Jones, Jiri Slaby, Merlijn Wajer, Peter Hurley,
	Sebastian Reichel, linux-serial, devicetree, linux-kernel,
	linux-omap

* Pavel Machek <pavel@denx.de> [200424 21:51]:
> Hi!
> 
> > > Here's v4 set of n_gsm serdev support patches, and the related protocol
> > > driver for the modem found on Motorola Mapphone phones and tablets
> > > like droid4.
> > > 
> > > This series only adds basic character device support for the serdev
> > > driver. Other serdev consumer drivers for specific devices will be
> > > posted separately.
> > 
> > I'm still missing an architectural (design) overview here -- reviewer
> > time is a scarce resource.
> > 
> > I also suggested earlier that you include, at least as an RFC, one or
> > more of your child-device drivers so that we can see how this ends up
> > being used in the end (including an example devicetree).
> 
> Note that this is useful on its own: we have ofonod running on the top
> of this doing calls and SMSes.

Yup.

> Tony: I know you have drivers depending on this somewhere (audio
> routing and GPS), but I can't find them. It is not droid4-pending-v5.6
> AFAICT. Do you have a pointer / could you publish them somewhere?

Hmm they should be there in droid4-pending-v5.6 branch [0]:

$ git log --abbrev=12 --pretty=format:"%h (\"%s\")" \
	v5.6..droid4-pending-v5.6 | grep -i -e gsm -e mot -e mdm
e09590a260a4 ("mfd: motmdm: Fix oops on unload of motorola-mdm")
f9252f9ff6bd ("mfd: motmdm: Revert bad list change")
d733dcaf4416 ("mfd: motmdm: Fix issue with receiving data before ddata is set")
452d2b5d4c95 ("n_gsm: Build fixes for make randconfig build")
6882b27ea92a ("phy: mapphone-mdm6600: Fix write timeouts with shorter GPIO toggle interval")
58ff58c4b520 ("mfd: motmdm: Add basic DTMF support")
e92b6f30e5ae ("ASoC: audio-graph-card: Add audio mixer for motorold mdm6600")
c2caea5767d5 ("gnss: mot-mdm6600: Add support for Motorola Mapphone MDM6600 modem")
a5f73b7b06f6 ("mfd: motmdm: Add Motorola TS 27.010 serdev driver for devices like droid4")
6c311d5aeb0a ("dt-bindings: mfd: motmdm: Add binding for motorola-mdm")
cd02274b920e ("tty: n_gsm: Add support for serdev drivers")
a73a48321c98 ("phy: mapphone-mdm6600: Fix timeouts by adding wake-up handling")

Regards,

Tony

[0] https://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap.git/log/?h=droid4-pending-v5.6

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem
  2020-04-23 23:27     ` Tony Lindgren
@ 2020-04-25 16:58       ` Tony Lindgren
  0 siblings, 0 replies; 19+ messages in thread
From: Tony Lindgren @ 2020-04-25 16:58 UTC (permalink / raw)
  To: Johan Hovold
  Cc: Greg Kroah-Hartman, Rob Herring, Alan Cox, Lee Jones, Jiri Slaby,
	Merlijn Wajer, Pavel Machek, Peter Hurley, Sebastian Reichel,
	linux-serial, devicetree, linux-kernel, linux-omap

* Tony Lindgren <tony@atomide.com> [200423 23:28]:
> * Tony Lindgren <tony@atomide.com> [200423 15:38]:
> > * Johan Hovold <johan@kernel.org> [200423 11:44]:
> > > I'd rather see a generic implementation which can be used with other
> > > modems and that continues to expose a /dev/gsmttyN interface to which we
> > > could attach serdev clients instead (and not create a motmdm serdev
> > > replica of sorts).
> > 
> > Yeah this should be doable quite easily actually without really any of
> > the motorola driver code. It's a separate driver though, and not
> > usable for this case because of the custom layer.
> 
> Well here's a first take at the such a generic serdev-ngsm driver
> that depends on patch 1/4 of this series. All this does is configure
> a serial port for TS 27.010 adaption1, and brings up 16 /dev/gsmtty*
> devices. So no custom options for the binding so far, and no support
> for adding channel specific device drivers. But all that can be added
> naturally later on.

Just to clarify the difference between the generic serdev-ngsm case
and this modem..

In the generic case, channel specific Linux device drivers can just
use gsm_serdev_write() directly for things like GNSS driver, SIM
file system, audio and whatever the modems might have on separate
channels.

This Motorola modem needs custom read and write functions because
of the custom Motorola packet format on top of TS 27.010. So for
example, the motmdm_write() function stuffs the packet header, and
then just calls gsm_serdev_write().

So for the generic channel specific drivers, the driver just needs
to be able to do something like:

Configure devicetree for the drivers/tty/serdev/serdev-ngsm.c
to bring up the TS 27.010 port based on the compatible and
whatever port specific properties we may want use.

And then have the channel specific device drivers do something
like this:

gsm_serdev_ngsm_register();	/* TBD to add to serdev-ngsm.c */
gsm_serdev_register_dlci();	/* Already patched for n_gsm.c */
gsm_serdev_write();		/* Already patched for n_gsm.c */
...
gsm_serdev_unregister_dlci();	/* Already patched for n_gsm.c */
gsm_serdev_ngsm_unregister();	/* TBD to add to serdev-ngsm.c */

So I think we can limit the code in the generic serdev-ngsm.c to bring
up the TS 27.010, and then allow whatever channel specific device
drivers to register with it. I guess that should do for the generic
API.

Anybody got better suggestions?

Regards,

Tony

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem
  2020-04-24 22:15     ` Tony Lindgren
@ 2020-04-26  7:27       ` Pavel Machek
  2020-04-26 20:07       ` Pavel Machek
  1 sibling, 0 replies; 19+ messages in thread
From: Pavel Machek @ 2020-04-26  7:27 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Johan Hovold, Greg Kroah-Hartman, Rob Herring, Alan Cox,
	Lee Jones, Jiri Slaby, Merlijn Wajer, Peter Hurley,
	Sebastian Reichel, linux-serial, devicetree, linux-kernel,
	linux-omap

[-- Attachment #1: Type: text/plain, Size: 7713 bytes --]

Hi!

> > > > Here's v4 set of n_gsm serdev support patches, and the related protocol
> > > > driver for the modem found on Motorola Mapphone phones and tablets
> > > > like droid4.
> > > > 
> > > > This series only adds basic character device support for the serdev
> > > > driver. Other serdev consumer drivers for specific devices will be
> > > > posted separately.
> > > 
> > > I'm still missing an architectural (design) overview here -- reviewer
> > > time is a scarce resource.
> > > 
> > > I also suggested earlier that you include, at least as an RFC, one or
> > > more of your child-device drivers so that we can see how this ends up
> > > being used in the end (including an example devicetree).
> > 
> > Note that this is useful on its own: we have ofonod running on the top
> > of this doing calls and SMSes.
> 
> Yup.
> 
> > Tony: I know you have drivers depending on this somewhere (audio
> > routing and GPS), but I can't find them. It is not droid4-pending-v5.6
> > AFAICT. Do you have a pointer / could you publish them somewhere?
> 
> Hmm they should be there in droid4-pending-v5.6 branch [0]:
> 
> $ git log --abbrev=12 --pretty=format:"%h (\"%s\")" \
> 	v5.6..droid4-pending-v5.6 | grep -i -e gsm -e mot -e mdm
> e09590a260a4 ("mfd: motmdm: Fix oops on unload of motorola-mdm")
> f9252f9ff6bd ("mfd: motmdm: Revert bad list change")
> d733dcaf4416 ("mfd: motmdm: Fix issue with receiving data before ddata is set")
> 452d2b5d4c95 ("n_gsm: Build fixes for make randconfig build")
> 6882b27ea92a ("phy: mapphone-mdm6600: Fix write timeouts with shorter GPIO toggle interval")
> 58ff58c4b520 ("mfd: motmdm: Add basic DTMF support")
> e92b6f30e5ae ("ASoC: audio-graph-card: Add audio mixer for motorold mdm6600")
> c2caea5767d5 ("gnss: mot-mdm6600: Add support for Motorola Mapphone MDM6600 modem")
> a5f73b7b06f6 ("mfd: motmdm: Add Motorola TS 27.010 serdev driver for devices like droid4")
> 6c311d5aeb0a ("dt-bindings: mfd: motmdm: Add binding for motorola-mdm")
> cd02274b920e ("tty: n_gsm: Add support for serdev drivers")
> a73a48321c98 ("phy: mapphone-mdm6600: Fix timeouts by adding wake-up handling")

Thanks for pointers, I was lost in all the git trees. Port is not
quite trivial :-(.

    I tried to port GPS and codec support over to new version of base
    patches, but I get oops when I try to open the GPS:

Best regards,
								Pavel
    
    [  182.877014] bfe0: 00000005 bed23af8 b6f279cf b6eb46f6 00070030
    bed23ea7 00000000 00000000
    [  182.901672] [<c06e7ba8>] (motmdm_gnss_open) from [<c06e71d4>]
    (gnss_open+0x7c/0xb0)
    [  182.901672] [<c06e71d4>] (gnss_open) from [<c0221fe0>]
    (chrdev_open+0x9c/0x194)
    [  182.901672] [<c0221fe0>] (chrdev_open) from [<c0219524>]
    (do_dentry_open+0x21c/0x3e8)
    [  182.901672] [<c0219524>] (do_dentry_open) from [<c022b424>]
    (path_openat+0x8b0/0xc1c)
    [  182.901672] [<c022b424>] (path_openat) from [<c022d0ac>]
    (do_filp_open+0x60/0xb4)
    [  182.940002] [<c022d0ac>] (do_filp_open) from [<c0219958>]
    (do_sys_openat2+0x1f0/0x2f4)
    [  182.940002] [<c0219958>] (do_sys_openat2) from [<c021ada8>]
    (do_sys_open+0x98:

diff --git a/drivers/gnss/Kconfig b/drivers/gnss/Kconfig
index 960178dfad47..13b91034e3ea 100644
--- a/drivers/gnss/Kconfig
+++ b/drivers/gnss/Kconfig
@@ -15,7 +15,6 @@ if GNSS
 
 config GNSS_MOTMDM
 	tristate "Motorola Modem TS 27.010 serdev GNSS receiver support"
-	depends on MFD_MOTMDM
 	---help---
 	  Say Y here if you have a Motorola modem using TS 27.010 line
 	  discipline for GNSS such as a Motorola Mapphone series device
diff --git a/drivers/tty/serdev/protocol/serdev-ngsm-motmdm.c b/drivers/tty/serdev/protocol/serdev-ngsm-motmdm.c
index 97eb349f5f13..f234a0cd2fb7 100644
--- a/drivers/tty/serdev/protocol/serdev-ngsm-motmdm.c
+++ b/drivers/tty/serdev/protocol/serdev-ngsm-motmdm.c
@@ -477,7 +477,7 @@ static int motmdm_send_command(struct device *dev,
 	return err;
 }
 
-static int motmdm_register_dlci(struct device *dev,
+int motmdm_register_dlci(struct device *dev,
 				struct motmdm_dlci *mot_dlci)
 {
 	struct motmdm *ddata;
@@ -522,7 +522,7 @@ static int motmdm_register_dlci(struct device *dev,
 	return err;
 }
 
-static void motmdm_unregister_dlci(struct device *dev,
+void motmdm_unregister_dlci(struct device *dev,
 				   struct motmdm_dlci *mot_dlci)
 {
 	struct motmdm *ddata;
diff --git a/include/linux/mfd/motorola-mdm.h b/include/linux/mfd/motorola-mdm.h
new file mode 100644
index 000000000000..aae61050cd34
--- /dev/null
+++ b/include/linux/mfd/motorola-mdm.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+struct gsm_serdev_dlci;
+struct kfifo;
+struct motmdm_response;
+
+enum motmdm_dlci_nr {
+	MOTMDM_DLCI1 = 1,
+	MOTMDM_DLCI2,
+	MOTMDM_DLCI3,
+	MOTMDM_DLCI4,
+	MOTMDM_DLCI5,
+	MOTMDM_DLCI6,
+	MOTMDM_DLCI7,
+	MOTMDM_DLCI8,
+	MOTMDM_DLCI9,
+	MOTMDM_DLCI10,
+	MOTMDM_DLCI11,
+	MOTMDM_DLCI12,
+	MOTMDM_DLCI13,
+	MOTMDM_DLCI14,
+	MOTMDM_DLCI15,
+};
+
+enum motmdm_state {
+	MOTMDM_STATE_IDLE = 0,
+	MOTMDM_STATE_DIAL = 1,
+	MOTMDM_STATE_ANSWERING = 2,
+	MOTMDM_STATE_CONNECTING = 3,
+	MOTMDM_STATE_INCOMING = 4,
+	MOTMDM_STATE_CONNECTED = 5,
+	MOTMDM_STATE_HANGING_UP = 6,
+	MOTMDM_STATE_DISCONNECTED = 7,
+};
+
+struct motmdm_dlci {
+	struct gsm_serdev_dlci gsm_dlci;
+	struct list_head node;
+	wait_queue_head_t read_queue;
+	struct kfifo read_fifo;
+	int line;
+	u16 id;
+	int (*send_command)(struct device *dev, struct motmdm_dlci *mot_dlci,
+			    unsigned long timeout_ms, const unsigned char *cmd,
+			    size_t cmdlen,
+			    unsigned char *rsp, size_t rsplen);
+	int (*handle_command)(struct motmdm_dlci *mot_dlci, int id,
+			      const unsigned char *buf, size_t len);
+	int (*receive_data)(struct motmdm_dlci *mot_dlci,
+			    const unsigned char *buf,
+			    size_t len);
+	int (*write)(struct device *dev, struct motmdm_dlci *mot_dlci,
+		     int cmdid, const unsigned char *buf, size_t count);
+	int (*notify)(struct motmdm_dlci *mot_dlci, enum motmdm_state);
+	struct list_head list;
+	void *privdata;		/* Do not use, internal data */
+	void *drvdata;		/* Available for consumer drivers */
+};
+
+int motmdm_register_dlci(struct device *dev, struct motmdm_dlci *mot_dlci);
+void motmdm_unregister_dlci(struct device *dev, struct motmdm_dlci *mot_dlci);
+
+static inline
+int motmdm_send_command(struct device *dev, struct motmdm_dlci *mot_dlci,
+			unsigned long timeout_ms, const unsigned char *cmd,
+			size_t cmdlen, unsigned char *rsp, size_t rsplen)
+{
+	if (mot_dlci && mot_dlci->send_command)
+		return mot_dlci->send_command(dev, mot_dlci,
+					      timeout_ms, cmd, cmdlen,
+					      rsp, rsplen);
+	else
+		return -EINVAL;
+}
+
+static inline
+int motmdm_write(struct device *dev, struct motmdm_dlci *mot_dlci,
+		 const unsigned char *buf, size_t count)
+{
+	if (mot_dlci && mot_dlci->write)
+		return mot_dlci->write(dev, mot_dlci, -1, buf, count);
+	else
+		return -EINVAL;
+}
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 3635fc0ab2a6..63d8d3e731ff 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -878,7 +878,6 @@ config SND_SOC_MAX9860
 
 config SND_SOC_MOTMDM
 	tristate "Motorola Modem TS 27.010 Voice Call Codec"
-	depends on MFD_MOTMDM
 	help
 	  Enable support for Motorola TS 27.010 line discipline serdev
 	  voice call codec driver for Motorola Mapphone series of devices

-- 
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem
  2020-04-24 22:15     ` Tony Lindgren
  2020-04-26  7:27       ` Pavel Machek
@ 2020-04-26 20:07       ` Pavel Machek
  2020-04-26 23:25         ` Tony Lindgren
  1 sibling, 1 reply; 19+ messages in thread
From: Pavel Machek @ 2020-04-26 20:07 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Pavel Machek, Johan Hovold, Greg Kroah-Hartman, Rob Herring,
	Alan Cox, Lee Jones, Jiri Slaby, Merlijn Wajer, Peter Hurley,
	Sebastian Reichel, linux-serial, devicetree, linux-kernel,
	linux-omap

[-- Attachment #1: Type: text/plain, Size: 8330 bytes --]

Hi!

> > > > Here's v4 set of n_gsm serdev support patches, and the related protocol
> > > > driver for the modem found on Motorola Mapphone phones and tablets
> > > > like droid4.
> > > > 
> > > > This series only adds basic character device support for the serdev
> > > > driver. Other serdev consumer drivers for specific devices will be
> > > > posted separately.
> > > 
> > > I'm still missing an architectural (design) overview here -- reviewer
> > > time is a scarce resource.
> > > 
> > > I also suggested earlier that you include, at least as an RFC, one or
> > > more of your child-device drivers so that we can see how this ends up
> > > being used in the end (including an example devicetree).
> > 
> > Note that this is useful on its own: we have ofonod running on the top
> > of this doing calls and SMSes.
> 
> Yup.
> 
> > Tony: I know you have drivers depending on this somewhere (audio
> > routing and GPS), but I can't find them. It is not droid4-pending-v5.6
> > AFAICT. Do you have a pointer / could you publish them somewhere?
> 
> Hmm they should be there in droid4-pending-v5.6 branch [0]:
> 
> $ git log --abbrev=12 --pretty=format:"%h (\"%s\")" \
> 	v5.6..droid4-pending-v5.6 | grep -i -e gsm -e mot -e mdm
> e09590a260a4 ("mfd: motmdm: Fix oops on unload of motorola-mdm")
> f9252f9ff6bd ("mfd: motmdm: Revert bad list change")
> d733dcaf4416 ("mfd: motmdm: Fix issue with receiving data before ddata is set")
> 452d2b5d4c95 ("n_gsm: Build fixes for make randconfig build")
> 6882b27ea92a ("phy: mapphone-mdm6600: Fix write timeouts with shorter GPIO toggle interval")
> 58ff58c4b520 ("mfd: motmdm: Add basic DTMF support")
> e92b6f30e5ae ("ASoC: audio-graph-card: Add audio mixer for motorold mdm6600")
> c2caea5767d5 ("gnss: mot-mdm6600: Add support for Motorola Mapphone MDM6600 modem")
> a5f73b7b06f6 ("mfd: motmdm: Add Motorola TS 27.010 serdev driver for devices like droid4")
> 6c311d5aeb0a ("dt-bindings: mfd: motmdm: Add binding for motorola-mdm")
> cd02274b920e ("tty: n_gsm: Add support for serdev drivers")
> a73a48321c98 ("phy: mapphone-mdm6600: Fix timeouts by adding wake-up handling")

Here's better try at getting gnss to work on top of linux-next... but
still no luck.

diff --git a/drivers/gnss/Kconfig b/drivers/gnss/Kconfig
index 960178dfad47..13b91034e3ea 100644
--- a/drivers/gnss/Kconfig
+++ b/drivers/gnss/Kconfig
@@ -15,7 +15,6 @@ if GNSS
 
 config GNSS_MOTMDM
 	tristate "Motorola Modem TS 27.010 serdev GNSS receiver support"
-	depends on MFD_MOTMDM
 	---help---
 	  Say Y here if you have a Motorola modem using TS 27.010 line
 	  discipline for GNSS such as a Motorola Mapphone series device
diff --git a/drivers/gnss/motmdm.c b/drivers/gnss/motmdm.c
index 5ec2fa25df6f..426e9da6bb12 100644
--- a/drivers/gnss/motmdm.c
+++ b/drivers/gnss/motmdm.c
@@ -21,6 +21,8 @@
 
 #include <linux/mfd/motorola-mdm.h>
 
+#define DEBUG
+
 #define MOTMDM_GNSS_TIMEOUT	1000
 #define MOTMDM_GNSS_RATE	1000
 
@@ -197,6 +199,8 @@ static int motmdm_gnss_receive_data(struct motmdm_dlci *mot_dlci,
 	if (len <= MOTMDM_GNSS_MPD_LEN)
 		return 0;
 
+	printk("gnss: Have %d bytes of data\n", len);
+
 	switch (buf[MOTMDM_GNSS_MPD_LEN]) {
 	case 'N':	/* UNNNN~+MPDNMEA=NN, */
 		msg = buf + MOTMDM_GNSS_NMEA_LEN;
diff --git a/drivers/tty/serdev/protocol/serdev-ngsm-motmdm.c b/drivers/tty/serdev/protocol/serdev-ngsm-motmdm.c
index 97eb349f5f13..15353e228b18 100644
--- a/drivers/tty/serdev/protocol/serdev-ngsm-motmdm.c
+++ b/drivers/tty/serdev/protocol/serdev-ngsm-motmdm.c
@@ -4,6 +4,7 @@
  * Copyright (C) 2018 - 2020 Tony Lindgren <tony@atomide.com>
  */
 
+#define DEBUG
 #include <linux/cdev.h>
 #include <linux/delay.h>
 #include <linux/device.h>
@@ -26,6 +27,8 @@
 #include <linux/mfd/core.h>
 #include <linux/phy/phy.h>
 
+#include <linux/mfd/motorola-mdm.h>
+
 #include <uapi/linux/gsmmux.h>
 
 #define MOTMDM_C_N2		3	/* TS27.010 default value */
@@ -67,36 +70,6 @@ struct motmdm_response {
 	unsigned int handled:1;
 };
 
-enum motmdm_dlci_nr {
-	MOTMDM_DLCI1 = 1,
-	MOTMDM_DLCI2,
-	MOTMDM_DLCI3,
-	MOTMDM_DLCI4,
-	MOTMDM_DLCI5,
-	MOTMDM_DLCI6,
-	MOTMDM_DLCI7,
-	MOTMDM_DLCI8,
-	MOTMDM_DLCI9,
-	MOTMDM_DLCI10,
-	MOTMDM_DLCI11,
-	MOTMDM_DLCI12,
-	MOTMDM_DLCI13,
-	MOTMDM_DLCI14,
-	MOTMDM_DLCI15,
-};
-
-struct motmdm_dlci {
-	struct gsm_serdev_dlci gsm_dlci;
-	struct list_head node;
-	wait_queue_head_t read_queue;
-	struct kfifo read_fifo;
-	int line;
-	u16 id;
-	struct list_head list;
-	void *privdata;		/* Do not use, internal data */
-	void *drvdata;		/* Available for consumer drivers */
-};
-
 struct motmdm_cdev {
 	struct motmdm *ddata;
 	struct list_head node;
@@ -283,6 +256,10 @@ static int motmdm_dlci_handle_command(struct motmdm_dlci *mot_dlci, int id,
 			break;
 	}
 
+	if (mot_dlci->receive_data) {
+		return (*mot_dlci->receive_data)(mot_dlci, buf, len);
+	}
+
 	if (!resp || !resp->buf)
 		return -ENODEV;
 
@@ -415,7 +392,7 @@ static int motmdm_write(struct device *dev, struct motmdm_dlci *mot_dlci,
  * Helper for child device drivers to send a command to a DLCI and wait
  * for result with a matching packet ID.
  */
-static int motmdm_send_command(struct device *dev,
+int motmdm_send_command(struct device *dev,
 			       struct motmdm_dlci *mot_dlci,
 			       unsigned long timeout_ms,
 			       const unsigned char *cmd, size_t cmdlen,
@@ -477,7 +454,7 @@ static int motmdm_send_command(struct device *dev,
 	return err;
 }
 
-static int motmdm_register_dlci(struct device *dev,
+int motmdm_register_dlci(struct device *dev,
 				struct motmdm_dlci *mot_dlci)
 {
 	struct motmdm *ddata;
@@ -522,7 +499,7 @@ static int motmdm_register_dlci(struct device *dev,
 	return err;
 }
 
-static void motmdm_unregister_dlci(struct device *dev,
+void motmdm_unregister_dlci(struct device *dev,
 				   struct motmdm_dlci *mot_dlci)
 {
 	struct motmdm *ddata;
diff --git a/include/linux/mfd/motorola-mdm.h b/include/linux/mfd/motorola-mdm.h
new file mode 100644
index 000000000000..70c52304ffff
--- /dev/null
+++ b/include/linux/mfd/motorola-mdm.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+struct gsm_serdev_dlci;
+struct kfifo;
+struct motmdm_response;
+
+enum motmdm_dlci_nr {
+	MOTMDM_DLCI1 = 1,
+	MOTMDM_DLCI2,
+	MOTMDM_DLCI3,
+	MOTMDM_DLCI4,
+	MOTMDM_DLCI5,
+	MOTMDM_DLCI6,
+	MOTMDM_DLCI7,
+	MOTMDM_DLCI8,
+	MOTMDM_DLCI9,
+	MOTMDM_DLCI10,
+	MOTMDM_DLCI11,
+	MOTMDM_DLCI12,
+	MOTMDM_DLCI13,
+	MOTMDM_DLCI14,
+	MOTMDM_DLCI15,
+};
+
+enum motmdm_state {
+	MOTMDM_STATE_IDLE = 0,
+	MOTMDM_STATE_DIAL = 1,
+	MOTMDM_STATE_ANSWERING = 2,
+	MOTMDM_STATE_CONNECTING = 3,
+	MOTMDM_STATE_INCOMING = 4,
+	MOTMDM_STATE_CONNECTED = 5,
+	MOTMDM_STATE_HANGING_UP = 6,
+	MOTMDM_STATE_DISCONNECTED = 7,
+};
+
+struct motmdm_dlci {
+	struct gsm_serdev_dlci gsm_dlci;
+	struct list_head node;
+	wait_queue_head_t read_queue;
+	struct kfifo read_fifo;
+	int line;
+	u16 id;
+	struct list_head list;
+	void *privdata;		/* Do not use, internal data */
+	void *drvdata;		/* Available for consumer drivers */
+	
+	int (*receive_data)(struct motmdm_dlci *mot_dlci,
+			    const unsigned char *buf,
+			    size_t len);
+};
+
+int motmdm_register_dlci(struct device *dev, struct motmdm_dlci *mot_dlci);
+void motmdm_unregister_dlci(struct device *dev, struct motmdm_dlci *mot_dlci);
+
+extern int motmdm_send_command(struct device *dev,
+			       struct motmdm_dlci *mot_dlci,
+			       unsigned long timeout_ms,
+			       const unsigned char *cmd, size_t cmdlen,
+			       unsigned char *rsp, size_t rsplen);
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 3635fc0ab2a6..63d8d3e731ff 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -878,7 +878,6 @@ config SND_SOC_MAX9860
 
 config SND_SOC_MOTMDM
 	tristate "Motorola Modem TS 27.010 Voice Call Codec"
-	depends on MFD_MOTMDM
 	help
 	  Enable support for Motorola TS 27.010 line discipline serdev
 	  voice call codec driver for Motorola Mapphone series of devices

-- 
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem
  2020-04-26 20:07       ` Pavel Machek
@ 2020-04-26 23:25         ` Tony Lindgren
  0 siblings, 0 replies; 19+ messages in thread
From: Tony Lindgren @ 2020-04-26 23:25 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Pavel Machek, Johan Hovold, Greg Kroah-Hartman, Rob Herring,
	Alan Cox, Lee Jones, Jiri Slaby, Merlijn Wajer, Peter Hurley,
	Sebastian Reichel, linux-serial, devicetree, linux-kernel,
	linux-omap

* Pavel Machek <pavel@ucw.cz> [200426 20:08]:
> Hi!
> 
> > > > > Here's v4 set of n_gsm serdev support patches, and the related protocol
> > > > > driver for the modem found on Motorola Mapphone phones and tablets
> > > > > like droid4.
> > > > > 
> > > > > This series only adds basic character device support for the serdev
> > > > > driver. Other serdev consumer drivers for specific devices will be
> > > > > posted separately.
> > > > 
> > > > I'm still missing an architectural (design) overview here -- reviewer
> > > > time is a scarce resource.
> > > > 
> > > > I also suggested earlier that you include, at least as an RFC, one or
> > > > more of your child-device drivers so that we can see how this ends up
> > > > being used in the end (including an example devicetree).
> > > 
> > > Note that this is useful on its own: we have ofonod running on the top
> > > of this doing calls and SMSes.
> > 
> > Yup.
> > 
> > > Tony: I know you have drivers depending on this somewhere (audio
> > > routing and GPS), but I can't find them. It is not droid4-pending-v5.6
> > > AFAICT. Do you have a pointer / could you publish them somewhere?
> > 
> > Hmm they should be there in droid4-pending-v5.6 branch [0]:
> > 
> > $ git log --abbrev=12 --pretty=format:"%h (\"%s\")" \
> > 	v5.6..droid4-pending-v5.6 | grep -i -e gsm -e mot -e mdm
> > e09590a260a4 ("mfd: motmdm: Fix oops on unload of motorola-mdm")
> > f9252f9ff6bd ("mfd: motmdm: Revert bad list change")
> > d733dcaf4416 ("mfd: motmdm: Fix issue with receiving data before ddata is set")
> > 452d2b5d4c95 ("n_gsm: Build fixes for make randconfig build")
> > 6882b27ea92a ("phy: mapphone-mdm6600: Fix write timeouts with shorter GPIO toggle interval")
> > 58ff58c4b520 ("mfd: motmdm: Add basic DTMF support")
> > e92b6f30e5ae ("ASoC: audio-graph-card: Add audio mixer for motorold mdm6600")
> > c2caea5767d5 ("gnss: mot-mdm6600: Add support for Motorola Mapphone MDM6600 modem")
> > a5f73b7b06f6 ("mfd: motmdm: Add Motorola TS 27.010 serdev driver for devices like droid4")
> > 6c311d5aeb0a ("dt-bindings: mfd: motmdm: Add binding for motorola-mdm")
> > cd02274b920e ("tty: n_gsm: Add support for serdev drivers")
> > a73a48321c98 ("phy: mapphone-mdm6600: Fix timeouts by adding wake-up handling")
> 
> Here's better try at getting gnss to work on top of linux-next... but
> still no luck.

Hmm that's about all it should take. Presumably you already tried
diffing the related files against droid4-pending-v5.6 branch.

I'm moving around things to work with a generic serdev-ngsm.c driver
so I'm inbetween patches right now and don't have it working either
probably for a few more days.

Regards,

Tony

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem
  2020-04-23 15:37   ` Tony Lindgren
  2020-04-23 23:27     ` Tony Lindgren
@ 2020-05-28  8:24     ` Johan Hovold
  2020-12-20 22:48       ` Pavel Machek
  1 sibling, 1 reply; 19+ messages in thread
From: Johan Hovold @ 2020-05-28  8:24 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Johan Hovold, Greg Kroah-Hartman, Rob Herring, Alan Cox,
	Lee Jones, Jiri Slaby, Merlijn Wajer, Pavel Machek, Peter Hurley,
	Sebastian Reichel, linux-serial, devicetree, linux-kernel,
	linux-omap

Hi Tony,

Sorry about the late reply on this.

On Thu, Apr 23, 2020 at 08:37:56AM -0700, Tony Lindgren wrote:
> * Johan Hovold <johan@kernel.org> [200423 11:44]:

> > I know the location of this driver has been up for discussion already,
> > but drivers/tty/serdev/protocol still isn't right (e.g. we don't have an
> > drivers/i2c/protocol directory where we stuff random i2c client
> > drivers).
> 
> Argh, the location of driver again.. So we do have the custom motorola
> layer to deal with on top of TS 27.010, but the custom handling is
> contained within the driver. So maybe just drivers/serial for the
> custom driver then.

Yeah, that should do for now; n_gsm is a serial driver (exposing tty
devices) after all.

> > Last, it seems you've based the serdev-ngsm-motmdm.c chardev
> > implementation on a more or less verbatim copy of drivers/gnss/core.c.
> > I'd appreciate if you could mention that in the file header and
> > reproduce the copyright notice if you end up keeping that interface.
> 
> Oh yes indeed, thanks for pointing that out. I'll add it to the next
> version. The chardev code is for sure based on drivers/gnss.
> 
> To explain my ignorance, I added the chardev support initially as an
> experiment to see if I can handle the motorola packet layer better
> that way compared to the n_gsm ttys and userspace handling. It ended
> up working quite nicely, so I kept it but then I accidentally left
> out references to the source. Sorry about that.

No worries.

Johan

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem
  2020-05-28  8:24     ` Johan Hovold
@ 2020-12-20 22:48       ` Pavel Machek
  2020-12-24  8:02         ` Tony Lindgren
  0 siblings, 1 reply; 19+ messages in thread
From: Pavel Machek @ 2020-12-20 22:48 UTC (permalink / raw)
  To: Johan Hovold, phone-devel
  Cc: Tony Lindgren, Greg Kroah-Hartman, Rob Herring, Alan Cox,
	Lee Jones, Jiri Slaby, Merlijn Wajer, Peter Hurley,
	Sebastian Reichel, linux-serial, devicetree, linux-kernel,
	linux-omap

[-- Attachment #1: Type: text/plain, Size: 38764 bytes --]

Hi!

> Sorry about the late reply on this.

I'm afraid I'll need some more answers in near future, but for now:

Tony, do you remember / can you figure out what gsmtty GPS is on? I
never used it on that interface, and I can't seem to figure it out.

My notes say:

/dev/motmdm1 -- basic support, calls, on/off                                    
/dev/motmdm3 -- send sms interface                                              
/dev/motmdm9 -- receive sms interface                                           

(and gsmtty numbering is same)

For now I converted gnss driver to use serdev interface, and n_gsm to
provide it... Not yet finished but I believe I'm walking in the right
direction.

Best regards,
								Pavel

diff --git a/arch/arm/boot/dts/motorola-mapphone-common.dtsi b/arch/arm/boot/dts/motorola-mapphone-common.dtsi
index f5e7ec8e1028..ce907aa40a28 100644
--- a/arch/arm/boot/dts/motorola-mapphone-common.dtsi
+++ b/arch/arm/boot/dts/motorola-mapphone-common.dtsi
@@ -761,9 +761,22 @@
 		};
 
 		gnss@4 {
-			compatible = "motorola,mapphone-mdm6600-gnss";
+			compatible = "disabled-old,motorola,mapphone-mdm6600-gnss";
 			reg = <4>;
 		};
+
+		port@1 {
+			compatible = "gsmmux,port";
+			reg = <1>;
+			subdev@1 {
+				 compatible = "motorola,mapphone-mdm6600-gnss";
+			};
+		};
+
+		port@3 {
+			compatible = "disabled,gsmmux,port";
+			reg = <3>;
+		};
 	};
 };
 
diff --git a/drivers/gnss/motmdm.c b/drivers/gnss/motmdm.c
index da1d44bed899..5a74bbcbc5de 100644
--- a/drivers/gnss/motmdm.c
+++ b/drivers/gnss/motmdm.c
@@ -3,11 +3,14 @@
  * Motorola Modem TS 27.010 serdev GNSS driver
  *
  * Copyright (C) 2018 - 2020 Tony Lindgren <tony@atomide.com>
+ * Copyright (C) 2020 Pavel Machek <pavel@ucw.cz>
  *
  * Based on drivers/gnss/sirf.c driver example:
  * Copyright (C) 2018 Johan Hovold <johan@kernel.org>
  */
 
+/* FIXME: see serial.c for good example..? */
+
 #include <linux/errno.h>
 #include <linux/gnss.h>
 #include <linux/init.h>
@@ -45,7 +48,7 @@ enum motmdm_gnss_status {
 struct motmdm_gnss_data {
 	struct gnss_device *gdev;
 	struct device *modem;
-	struct gsm_serdev_dlci dlci;
+	struct serdev_device *serdev;
 	struct delayed_work restart_work;
 	struct mutex mutex;	/* For modem commands */
 	ktime_t last_update;
@@ -76,6 +79,7 @@ int motmdm_gnss_send_command(struct motmdm_gnss_data *ddata,
 	unsigned char cmd[128];
 	int ret, cmdlen;
 
+	printk("send_command\n");
 	cmdlen = len + 5 + 1;
 	if (cmdlen > 128)
 		return -EINVAL;
@@ -84,23 +88,31 @@ int motmdm_gnss_send_command(struct motmdm_gnss_data *ddata,
 	memset(ddata->buf, 0, ddata->len);
 	ddata->parsed = false;
 	snprintf(cmd, cmdlen, "U%04li%s", jiffies % 10000, buf);
-	ret = serdev_ngsm_write(ddata->modem, &ddata->dlci, cmd, cmdlen);
+	printk("serdev_write\n");
+	
+	ret = serdev_device_write(ddata->serdev, cmd, cmdlen, MAX_SCHEDULE_TIMEOUT);
 	if (ret < 0)
 		goto out_unlock;
 
+	printk("wait event\n");
 	ret = wait_event_timeout(ddata->read_queue, ddata->parsed,
 				 msecs_to_jiffies(timeout_ms));
 	if (ret == 0) {
 		ret = -ETIMEDOUT;
+		printk("...timeout FIXME\n");
+		ret = 0;
 		goto out_unlock;
 	} else if (ret < 0) {
+		printk("...error FIXME\n");
+		ret = 0;
 		goto out_unlock;
 	}
 
 	if (!strstr(ddata->buf, ":OK")) {
 		dev_err(&gdev->dev, "command %s error %s\n",
 			cmd, ddata->buf);
-		ret = -EPIPE;
+		printk("did not get ok\n");
+		//ret = -EPIPE;
 	}
 
 	ret = len;
@@ -198,7 +210,7 @@ static int motmdm_gnss_finish(struct gnss_device *gdev)
 	return motmdm_gnss_send_command(ddata, cmd, strlen(cmd));
 }
 
-static int motmdm_gnss_receive_data(struct gsm_serdev_dlci *dlci,
+static int motmdm_gnss_receive_data(struct gsm_serdev_dlci_operations *dlci,
 				    const unsigned char *buf,
 				    size_t len)
 {
@@ -208,6 +220,8 @@ static int motmdm_gnss_receive_data(struct gsm_serdev_dlci *dlci,
 	size_t msglen;
 	int error = 0;
 
+	printk("motmdm_gnss_receive_data\n");
+
 	if (len <= MOTMDM_GNSS_RESP_LEN)
 		return 0;
 
@@ -283,19 +297,21 @@ static int motmdm_gnss_receive_data(struct gsm_serdev_dlci *dlci,
 static int motmdm_gnss_open(struct gnss_device *gdev)
 {
 	struct motmdm_gnss_data *ddata = gnss_get_drvdata(gdev);
-	struct gsm_serdev_dlci *dlci = &ddata->dlci;
+//	struct gsm_serdev_dlci_operations *dlci = &ddata->dlci;
 	int error;
 
+#if 0
 	dlci->drvdata = gdev;
 	dlci->receive_buf = motmdm_gnss_receive_data;
 
 	error = serdev_ngsm_register_dlci(ddata->modem, dlci);
 	if (error)
 		return error;
+#endif
 
 	error = motmdm_gnss_init(gdev);
 	if (error) {
-		serdev_ngsm_unregister_dlci(ddata->modem, dlci);
+//		serdev_ngsm_unregister_dlci(ddata->modem, dlci);
 
 		return error;
 	}
@@ -306,9 +322,10 @@ static int motmdm_gnss_open(struct gnss_device *gdev)
 static void motmdm_gnss_close(struct gnss_device *gdev)
 {
 	struct motmdm_gnss_data *ddata = gnss_get_drvdata(gdev);
-	struct gsm_serdev_dlci *dlci = &ddata->dlci;
+//	struct gsm_serdev_dlci_operations *dlci = &ddata->dlci;
 	int error;
 
+#if 0
 	dlci->receive_buf = NULL;
 	error = motmdm_gnss_finish(gdev);
 	if (error < 0)
@@ -316,15 +333,18 @@ static void motmdm_gnss_close(struct gnss_device *gdev)
 			 __func__, error);
 
 	serdev_ngsm_unregister_dlci(ddata->modem, dlci);
+#endif
 }
 
 static int motmdm_gnss_write_raw(struct gnss_device *gdev,
 				 const unsigned char *buf,
 				 size_t count)
 {
+#if 0
 	struct motmdm_gnss_data *ddata = gnss_get_drvdata(gdev);
 
 	return serdev_ngsm_write(ddata->modem, &ddata->dlci, buf, count);
+#endif
 }
 
 static const struct gnss_operations motmdm_gnss_ops = {
@@ -333,26 +353,78 @@ static const struct gnss_operations motmdm_gnss_ops = {
 	.write_raw	= motmdm_gnss_write_raw,
 };
 
-static int motmdm_gnss_probe(struct platform_device *pdev)
+static int gnss_serial_receive_buf(struct serdev_device *serdev,
+                                        const unsigned char *buf, size_t count)
+{
+	struct motmdm_gnss_data *ddata = serdev_device_get_drvdata(serdev);
+	struct gnss_device *gdev = ddata->gdev;
+	
+	printk("gnss_serial_recieve: %d bytes\n", count);
+	printk("gnss_serial_recieve: have data: %s bytes\n", buf);
+
+	return gnss_insert_raw(gdev, buf, count);
+}
+
+static const struct serdev_device_ops gnss_serial_serdev_ops = {
+        .receive_buf    = gnss_serial_receive_buf,
+        .write_wakeup   = serdev_device_write_wakeup,
+};
+
+
+int motmdm_gnss_test(struct serdev_device *serdev)
 {
-	struct device *dev = &pdev->dev;
+	int ret;
+	if (!serdev)
+		return -EIO;
+	printk("have serdev_device: %p, nr %d\n", serdev, serdev->nr);
+
+	dev_info(&serdev->dev, "interesting line, testing\n");
+	
+	/* HERE */
+        serdev_device_set_client_ops(serdev, &gnss_serial_serdev_ops);
+
+	dev_info(&serdev->dev, "opening serdev\n");
+	ret = serdev_device_open(serdev);
+        if (ret) {
+                return ret;
+	}
+
+//        serdev_device_set_baudrate(serdev, gserial->speed);
+//        serdev_device_set_flow_control(serdev, false);
+	dev_info(&serdev->dev, "writing\n");
+	
+	{
+		int count = 5;
+		ret = serdev_device_write(serdev, "HELLO", count, MAX_SCHEDULE_TIMEOUT);
+		if (ret < 0 || ret < count)
+			return ret;
+	}
+	dev_info(&serdev->dev, "waiting\n");
+	
+        serdev_device_wait_until_sent(serdev, 0);
+	dev_info(&serdev->dev, "all ok\n");
+	
+	return 0;
+}
+
+static int motmdm_gnss_probe(struct serdev_device *serdev)
+{
+	struct device *dev = &serdev->dev;
 	struct motmdm_gnss_data *ddata;
 	struct gnss_device *gdev;
 	u32 line;
 	int ret;
 
+	printk("gnss_probe\n");
+
 	ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
 	if (!ddata)
 		return -ENOMEM;
 
-	ret = of_property_read_u32(dev->of_node, "reg", &line);
-	if (ret)
-		return ret;
-
-	if (!line)
-		return -EINVAL;
+	printk("gnss_probe: searching for reg\n");
+	motmdm_gnss_test(serdev);
 
-	ddata->dlci.line = line;
+	ddata->serdev = serdev;
 	ddata->modem = dev->parent;
 	ddata->len = PAGE_SIZE;
 	mutex_init(&ddata->mutex);
@@ -363,7 +435,7 @@ static int motmdm_gnss_probe(struct platform_device *pdev)
 	if (!ddata->buf)
 		return -ENOMEM;
 
-	platform_set_drvdata(pdev, ddata);
+	serdev_device_set_drvdata(serdev, ddata);
 
 	gdev = gnss_allocate_device(dev);
 	if (!gdev)
@@ -386,14 +458,12 @@ static int motmdm_gnss_probe(struct platform_device *pdev)
 	return ret;
 }
 
-static int motmdm_gnss_remove(struct platform_device *pdev)
+static void motmdm_gnss_remove(struct serdev_device *serdev)
 {
-	struct motmdm_gnss_data *data = platform_get_drvdata(pdev);
+	struct motmdm_gnss_data *data = serdev_device_get_drvdata(serdev);
 
 	gnss_deregister_device(data->gdev);
 	gnss_put_device(data->gdev);
-
-	return 0;
 };
 
 #ifdef CONFIG_OF
@@ -404,7 +474,7 @@ static const struct of_device_id motmdm_gnss_of_match[] = {
 MODULE_DEVICE_TABLE(of, motmdm_gnss_of_match);
 #endif
 
-static struct platform_driver motmdm_gnss_driver = {
+static struct serdev_device_driver motmdm_gnss_driver = {
 	.driver	= {
 		.name		= "gnss-mot-mdm6600",
 		.of_match_table	= of_match_ptr(motmdm_gnss_of_match),
@@ -412,7 +482,7 @@ static struct platform_driver motmdm_gnss_driver = {
 	.probe	= motmdm_gnss_probe,
 	.remove	= motmdm_gnss_remove,
 };
-module_platform_driver(motmdm_gnss_driver);
+module_serdev_device_driver(motmdm_gnss_driver);
 
 MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
 MODULE_DESCRIPTION("Motorola Mapphone MDM6600 GNSS receiver driver");
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 460123447fa1..00a5c8225973 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -52,14 +52,17 @@
 #include <linux/etherdevice.h>
 #include <linux/gsmmux.h>
 #include <linux/serdev-gsm.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 
 static int debug;
 module_param(debug, int, 0600);
 
 /* Defaults: these are from the specification */
 
-#define T1	10		/* 100mS */
-#define T2	34		/* 333mS */
+#define T1	10		/* 100ms */
+#define T2	34		/* 333ms */
 #define N2	3		/* Retry 3 times */
 
 /* Use long timers for testing at low speed with debug on */
@@ -152,7 +155,7 @@ struct gsm_dlci {
 	/* Data handling callback */
 	void (*data)(struct gsm_dlci *dlci, const u8 *data, int len);
 	void (*prev_data)(struct gsm_dlci *dlci, const u8 *data, int len);
-	struct gsm_serdev_dlci *ops; /* serdev dlci ops, if used */
+	struct gsm_serdev_dlci_operations *ops; /* serdev dlci ops, if used */
 	struct net_device *net; /* network interface, if created */
 };
 
@@ -591,7 +594,8 @@ static void gsm_send(struct gsm_mux *gsm, int addr, int cr, int control)
 		WARN_ON(1);
 		return;
 	}
-	gsm->output(gsm, cbuf, len);
+	if (gsm->output)
+		gsm->output(gsm, cbuf, len);
 	gsm_print_packet("-->", addr, cr, control, NULL, 0);
 }
 
@@ -691,7 +695,7 @@ static void gsm_data_kick(struct gsm_mux *gsm, struct gsm_dlci *dlci)
 			print_hex_dump_bytes("gsm_data_kick: ",
 					     DUMP_PREFIX_OFFSET,
 					     gsm->txframe, len);
-		if (gsm->output(gsm, gsm->txframe, len) < 0)
+		if (gsm->output && gsm->output(gsm, gsm->txframe, len) < 0)
 			break;
 		/* FIXME: Can eliminate one SOF in many more cases */
 		gsm->tx_bytes -= msg->len;
@@ -1019,7 +1023,7 @@ static void gsm_control_reply(struct gsm_mux *gsm, int cmd, const u8 *data,
 static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci,
 							u32 modem, int clen)
 {
-	int  mlines = 0;
+	int mlines = 0;
 	u8 brk = 0;
 	int fc;
 
@@ -2344,38 +2348,11 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
 }
 
 #ifdef CONFIG_SERIAL_DEV_BUS
-
-/**
- * gsm_serdev_get_config - read ts 27.010 config
- * @gsd:	serdev-gsm instance
- * @c:		ts 27.010 config data
- *
- * See gsm_copy_config_values() for more information.
- */
-int gsm_serdev_get_config(struct gsm_serdev *gsd, struct gsm_config *c)
-{
-	struct gsm_mux *gsm;
-
-	if (!gsd || !gsd->gsm)
-		return -ENODEV;
-
-	gsm = gsd->gsm;
-
-	if (!c)
-		return -EINVAL;
-
-	gsm_copy_config_values(gsm, c);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(gsm_serdev_get_config);
-
+#if 1
 /**
  * gsm_serdev_set_config - set ts 27.010 config
  * @gsd:	serdev-gsm instance
  * @c:		ts 27.010 config data
- *
- * See gsm_config() for more information.
  */
 int gsm_serdev_set_config(struct gsm_serdev *gsd, struct gsm_config *c)
 {
@@ -2393,6 +2370,7 @@ int gsm_serdev_set_config(struct gsm_serdev *gsd, struct gsm_config *c)
 }
 EXPORT_SYMBOL_GPL(gsm_serdev_set_config);
 
+#endif
 static struct gsm_dlci *gsd_dlci_get(struct gsm_serdev *gsd, int line,
 				     bool allocate)
 {
@@ -2404,7 +2382,7 @@ static struct gsm_dlci *gsd_dlci_get(struct gsm_serdev *gsd, int line,
 
 	gsm = gsd->gsm;
 
-	if (line < 1 || line >= 63)
+	if (line < 1 || line >= 62)
 		return ERR_PTR(-EINVAL);
 
 	mutex_lock(&gsm->mutex);
@@ -2431,7 +2409,8 @@ static struct gsm_dlci *gsd_dlci_get(struct gsm_serdev *gsd, int line,
 	return dlci;
 }
 
-static int gsd_dlci_receive_buf(struct gsm_serdev_dlci *ops,
+#if 1
+static int gsd_dlci_receive_buf(struct gsm_serdev_dlci_operations *ops,
 				const unsigned char *buf,
 				size_t len)
 {
@@ -2449,6 +2428,7 @@ static int gsd_dlci_receive_buf(struct gsm_serdev_dlci *ops,
 
 	return len;
 }
+#endif
 
 static void gsd_dlci_data(struct gsm_dlci *dlci, const u8 *buf, int len)
 {
@@ -2471,58 +2451,6 @@ static void gsd_dlci_data(struct gsm_dlci *dlci, const u8 *buf, int len)
 	}
 }
 
-/**
- * gsm_serdev_write - write data to a ts 27.010 channel
- * @gsd:	serdev-gsm instance
- * @ops:	channel ops
- * @buf:	write buffer
- * @len:	buffer length
- */
-int gsm_serdev_write(struct gsm_serdev *gsd, struct gsm_serdev_dlci *ops,
-		     const u8 *buf, int len)
-{
-	struct gsm_mux *gsm;
-	struct gsm_dlci *dlci;
-	struct gsm_msg *msg;
-	int h, size, total_size = 0;
-	u8 *dp;
-
-	if (!gsd || !gsd->gsm)
-		return -ENODEV;
-
-	gsm = gsd->gsm;
-
-	dlci = gsd_dlci_get(gsd, ops->line, false);
-	if (IS_ERR(dlci))
-		return PTR_ERR(dlci);
-
-	h = dlci->adaption - 1;
-
-	if (len > gsm->mtu)
-		len = gsm->mtu;
-
-	size = len + h;
-
-	msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype);
-	if (!msg)
-		return -ENOMEM;
-
-	dp = msg->data;
-	switch (dlci->adaption) {
-	case 1:
-		break;
-	case 2:
-		*dp++ = gsm_encode_modem(dlci);
-		break;
-	}
-	memcpy(dp, buf, len);
-	gsm_data_queue(dlci, msg);
-	total_size += size;
-
-	return total_size;
-}
-EXPORT_SYMBOL_GPL(gsm_serdev_write);
-
 /**
  * gsm_serdev_data_kick - indicate more data can be transmitted
  * @gsd:	serdev-gsm instance
@@ -2545,13 +2473,14 @@ void gsm_serdev_data_kick(struct gsm_serdev *gsd)
 }
 EXPORT_SYMBOL_GPL(gsm_serdev_data_kick);
 
+#if 1
 /**
  * gsm_serdev_register_dlci - register a ts 27.010 channel
  * @gsd:	serdev-gsm instance
  * @ops:	channel ops
  */
 int gsm_serdev_register_dlci(struct gsm_serdev *gsd,
-			     struct gsm_serdev_dlci *ops)
+			     struct gsm_serdev_dlci_operations *ops)
 {
 	struct gsm_dlci *dlci;
 	struct gsm_mux *gsm;
@@ -2578,7 +2507,7 @@ int gsm_serdev_register_dlci(struct gsm_serdev *gsd,
 	dlci->ops = ops;
 	dlci->modem_rx = 0;
 	dlci->prev_data = dlci->data;
-	dlci->data = gsd_dlci_data;
+	dlci->data = gsd_dlci_data; /* FIXME: do we want this? */
 	mutex_unlock(&dlci->mutex);
 
 	gsm_dlci_begin_open(dlci);
@@ -2609,7 +2538,7 @@ EXPORT_SYMBOL_GPL(gsm_serdev_register_dlci);
  * @ops:	channel ops
  */
 void gsm_serdev_unregister_dlci(struct gsm_serdev *gsd,
-				struct gsm_serdev_dlci *ops)
+				struct gsm_serdev_dlci_operations *ops)
 {
 	struct gsm_mux *gsm;
 	struct gsm_dlci *dlci;
@@ -2636,6 +2565,7 @@ void gsm_serdev_unregister_dlci(struct gsm_serdev *gsd,
 	gsm_dlci_begin_close(dlci);
 }
 EXPORT_SYMBOL_GPL(gsm_serdev_unregister_dlci);
+#endif
 
 static int gsm_serdev_output(struct gsm_mux *gsm, u8 *data, int len)
 {
@@ -2647,6 +2577,7 @@ static int gsm_serdev_output(struct gsm_mux *gsm, u8 *data, int len)
 		return serdev_device_write_buf(serdev, data, len);
 }
 
+#if 1
 static int gsd_receive_buf(struct serdev_device *serdev, const u8 *data,
 			   size_t count)
 {
@@ -2680,13 +2611,18 @@ static struct serdev_device_ops gsd_client_ops = {
 	.receive_buf = gsd_receive_buf,
 	.write_wakeup = gsd_write_wakeup,
 };
+#endif
+
+extern int motmdm_gnss_attach(struct device *dev, int line);
 
 int gsm_serdev_register_tty_port(struct gsm_serdev *gsd, int line)
 {
-	struct gsm_serdev_dlci *ops;
+	struct gsm_serdev_dlci_operations *ops;
 	unsigned int base;
 	int error;
-
+	struct device *dev;
+	struct device_node *node;
+		
 	if (line < 1)
 		return -EINVAL;
 
@@ -2704,8 +2640,83 @@ int gsm_serdev_register_tty_port(struct gsm_serdev *gsd, int line)
 		return error;
 	}
 
+
 	base = mux_num_to_base(gsd->gsm);
-	tty_register_device(gsm_tty_driver, base + ops->line, NULL);
+	printk("register_tty_port: have port: %p, %d+%d\n", &gsd->gsm->dlci[line]->port, base, ops->line);
+	dev = &gsd->serdev->dev;
+	if (line != 1)
+		return 0;
+
+	for_each_available_child_of_node(dev->of_node, node) {
+		struct platform_device_info devinfo = {};
+		static int idx;
+		struct platform_device *pdev;
+		const char *c = of_get_property(node, "compatible", NULL);
+		
+		dev_info(dev, "register_tty: child -- %pOF\n", node);
+
+		if (!c)
+			continue;
+		dev_info(dev, "register_tty: child -- %pOF -- compatible %s\n", node, c);
+		if (strcmp(c, "gsmmux,port"))
+			continue;
+
+		printk("n_gsm: Have subnode with right compatible!\n");
+		
+		devinfo.name = kasprintf(GFP_KERNEL, "gsm-mux-%d", idx++);
+		devinfo.parent = dev;
+
+		/* Thanks to core.c: serdev_device_add */
+		pdev = platform_device_register_full(&devinfo);
+		pdev->dev.of_node = node;
+
+#if 0
+		tty_register_device(gsm_tty_driver, base + ops->line, NULL);
+#else
+		{
+			struct device *dev;
+
+			dev = tty_port_register_device_serdev(&gsd->gsm->dlci[line]->port, gsm_tty_driver, base + ops->line, &pdev->dev /* FIXME: needs non-null to attempt serdev registration */ );
+			printk("register_tty_port: got %p\n", dev);
+			{
+#if 0
+				struct serdev_controller *ctrl = to_serdev_controller(dev);
+				struct serdev_device *serdev = serdev_device_alloc(ctrl);
+				int err;
+				if (!serdev)
+					dev_err(dev, "could not alloc serdev, that is bad\n");
+
+				//serdev->dev.of_node = node;
+
+				err = serdev_device_add(serdev);
+				if (err) {
+					dev_err(&serdev->dev,
+						"failure adding device. status %pe\n",
+						ERR_PTR(err));
+					//serdev_device_put(serdev);
+				}
+#endif
+#if 0
+				printk("register_tty_port: Forcing attach\n");
+				/* FIXME: Need to do of_serdev_register_devices() ? */
+				motmdm_gnss_attach(dev, ops->line);
+#endif
+			}
+
+		}
+	}
+#endif
+	/* FIXME:
+
+extern struct device *tty_register_device(struct tty_driver *driver,
+                                          unsigned index, struct device *dev);
+
+Would like tty_port_register_device_attr or better
+	   	       tty_port_register_device_attr_serdev 
+
+ale chce navic struct tty_port *.
+
+		       _attr() -- last 2 arguments can be NULL. */
 
 	return 0;
 }
@@ -2730,29 +2741,14 @@ void gsm_serdev_unregister_tty_port(struct gsm_serdev *gsd, int line)
 }
 EXPORT_SYMBOL_GPL(gsm_serdev_unregister_tty_port);
 
-struct gsm_serdev_dlci *
-gsm_serdev_tty_port_get_dlci(struct gsm_serdev *gsd, int line)
-{
-	struct gsm_dlci *dlci;
-
-	if (line < 1)
-		return NULL;
-
-	dlci = gsd_dlci_get(gsd, line, false);
-	if (IS_ERR(dlci))
-		return NULL;
-
-	return dlci->ops;
-}
-EXPORT_SYMBOL_GPL(gsm_serdev_tty_port_get_dlci);
-
+#if 1
 int gsm_serdev_register_device(struct gsm_serdev *gsd)
 {
 	struct gsm_mux *gsm;
 	int error;
 
-	if (WARN(!gsd || !gsd->serdev || !gsd->output,
-		 "serdev and output must be initialized\n"))
+	if (WARN(!gsd || !gsd->serdev,
+		 "serdev must be initialized\n"))
 		return -EINVAL;
 
 	serdev_device_set_client_ops(gsd->serdev, &gsd_client_ops);
@@ -2787,7 +2783,7 @@ void gsm_serdev_unregister_device(struct gsm_serdev *gsd)
 	gsd->gsm = NULL;
 }
 EXPORT_SYMBOL_GPL(gsm_serdev_unregister_device);
-
+#endif
 #endif	/* CONFIG_SERIAL_DEV_BUS */
 
 /**
@@ -3644,7 +3640,7 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state)
 				    properly */
 		encode = 0x0F;
 	else if (state > 0) {
-		encode = state / 200;	/* mS to encoding */
+		encode = state / 200;	/* ms to encoding */
 		if (encode > 0x0F)
 			encode = 0x0F;	/* Best effort */
 	}
diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
index c5f0d936b003..081702d5479d 100644
--- a/drivers/tty/serdev/core.c
+++ b/drivers/tty/serdev/core.c
@@ -121,7 +121,7 @@ int serdev_device_add(struct serdev_device *serdev)
 		goto err_clear_serdev;
 	}
 
-	dev_dbg(&serdev->dev, "device %s registered\n", dev_name(&serdev->dev));
+	dev_info(&serdev->dev, "device %s registered, %p controller %p\n", dev_name(&serdev->dev), serdev, ctrl);
 
 	return 0;
 
@@ -509,7 +509,15 @@ struct serdev_controller *serdev_controller_alloc(struct device *parent,
 	pm_runtime_no_callbacks(&ctrl->dev);
 	pm_suspend_ignore_children(&ctrl->dev, true);
 
-	dev_dbg(&ctrl->dev, "allocated controller 0x%p id %d\n", ctrl, id);
+	/* /sys/bus/serial/drivers/serdev_ngsm/serial0-0 ?
+
+4806a000.serial:modem:audio-codec@2  modalias  subsystem
+4806a000.serial:modem:gnss@4	     of_node   supplier:phy-usb-phy@1.1
+driver				     power     uevent
+
+	*/
+	dev_info(&ctrl->dev, "allocated controller 0x%p 0x%p id %d [%d]\n",
+		 ctrl, &ctrl->dev, id, ctrl->nr);
 	return ctrl;
 
 err_free:
@@ -527,10 +535,12 @@ static int of_serdev_register_devices(struct serdev_controller *ctrl)
 	bool found = false;
 
 	for_each_available_child_of_node(ctrl->dev.of_node, node) {
+		dev_info(&ctrl->dev, "of_serdev_register_device: considering %pOF\n", node);
+		
 		if (!of_get_property(node, "compatible", NULL))
 			continue;
 
-		dev_dbg(&ctrl->dev, "adding child %pOF\n", node);
+		dev_info(&ctrl->dev, "adding child %pOF\n", node);
 
 		serdev = serdev_device_alloc(ctrl);
 		if (!serdev)
@@ -740,26 +750,34 @@ int serdev_controller_add(struct serdev_controller *ctrl)
 {
 	int ret_of, ret_acpi, ret;
 
+	printk("serdev_controller_add...\n");
+
 	/* Can't register until after driver model init */
 	if (WARN_ON(!is_registered))
 		return -EAGAIN;
 
+	printk("serdev_controller_add 1... %pOF\n", ctrl->dev.of_node);
+	
 	ret = device_add(&ctrl->dev);
 	if (ret)
 		return ret;
 
+	printk("serdev_controller_add 2...\n");	
 	pm_runtime_enable(&ctrl->dev);
 
 	ret_of = of_serdev_register_devices(ctrl);
 	ret_acpi = acpi_serdev_register_devices(ctrl);
 	if (ret_of && ret_acpi) {
-		dev_dbg(&ctrl->dev, "no devices registered: of:%pe acpi:%pe\n",
+		dev_info(&ctrl->dev, "no devices registered: of:%pe acpi:%pe\n",
 			ERR_PTR(ret_of), ERR_PTR(ret_acpi));
+#if 0
 		ret = -ENODEV;
 		goto err_rpm_disable;
+#endif		
 	}
 
-	dev_dbg(&ctrl->dev, "serdev%d registered: dev:%p\n",
+	printk("serdev_controller_add all ok?...\n");		
+	dev_info(&ctrl->dev, "serdev%d registered: dev:%p\n",
 		ctrl->nr, &ctrl->dev);
 	return 0;
 
diff --git a/drivers/tty/serdev/serdev-ngsm.c b/drivers/tty/serdev/serdev-ngsm.c
index a247cf36df4f..e8db5cc5b19d 100644
--- a/drivers/tty/serdev/serdev-ngsm.c
+++ b/drivers/tty/serdev/serdev-ngsm.c
@@ -40,7 +40,7 @@ struct serdev_ngsm {
 	const struct serdev_ngsm_cfg *cfg;
 };
 
-static int serdev_ngsm_tty_init(struct serdev_ngsm *ddata)
+static int serdev_ngsm_tty_init(struct serdev_ngsm *ddata, void *node /* will need of node here ? */)
 {
 	struct gsm_serdev *gsd = &ddata->gsd;
 	struct device *dev = ddata->dev;
@@ -50,7 +50,7 @@ static int serdev_ngsm_tty_init(struct serdev_ngsm *ddata)
 		if (BIT_ULL(bit) & TS27010_RESERVED_DLCI)
 			continue;
 
-		err = gsm_serdev_register_tty_port(gsd, bit);
+		err = gsm_serdev_register_tty_port(gsd, bit /*, node FIXME */);
 		if (err) {
 			dev_err(dev, "ngsm tty init failed for dlci%i: %i\n",
 				bit, err);
@@ -74,69 +74,6 @@ static void serdev_ngsm_tty_exit(struct serdev_ngsm *ddata)
 	}
 }
 
-/*
- * Note that we rely on gsm_serdev_register_dlci() locking for
- * reserved channels that serdev_ngsm_tty_init() and consumer
- * drivers may have already reserved.
- */
-int serdev_ngsm_register_dlci(struct device *dev,
-			      struct gsm_serdev_dlci *dlci)
-{
-	struct serdev_ngsm *ddata = gsm_serdev_get_drvdata(dev);
-	struct gsm_serdev *gsd = &ddata->gsd;
-	int err;
-
-	err = gsm_serdev_register_dlci(gsd, dlci);
-	if (err)
-		return err;
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(serdev_ngsm_register_dlci);
-
-void serdev_ngsm_unregister_dlci(struct device *dev,
-				 struct gsm_serdev_dlci *dlci)
-{
-	struct serdev_ngsm *ddata = gsm_serdev_get_drvdata(dev);
-	struct gsm_serdev *gsd = &ddata->gsd;
-
-	gsm_serdev_unregister_dlci(gsd, dlci);
-}
-EXPORT_SYMBOL_GPL(serdev_ngsm_unregister_dlci);
-
-int serdev_ngsm_write(struct device *dev, struct gsm_serdev_dlci *ops,
-		      const u8 *buf, int len)
-{
-	struct serdev_ngsm *ddata = gsm_serdev_get_drvdata(dev);
-	struct gsm_serdev *gsd = &ddata->gsd;
-	int ret;
-
-	ret = pm_runtime_get_sync(dev);
-	if ((ret != -EINPROGRESS) && ret < 0) {
-		pm_runtime_put_noidle(dev);
-
-		return ret;
-	}
-
-	ret = gsm_serdev_write(gsd, ops, buf, len);
-
-	pm_runtime_mark_last_busy(dev);
-	pm_runtime_put_autosuspend(dev);
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(serdev_ngsm_write);
-
-struct gsm_serdev_dlci *
-serdev_ngsm_get_dlci(struct device *dev, int line)
-{
-	struct serdev_ngsm *ddata = gsm_serdev_get_drvdata(dev);
-	struct gsm_serdev *gsd = &ddata->gsd;
-
-	return gsm_serdev_tty_port_get_dlci(gsd, line);
-}
-EXPORT_SYMBOL_GPL(serdev_ngsm_get_dlci);
-
 static int serdev_ngsm_set_config(struct device *dev)
 {
 	struct serdev_ngsm *ddata = gsm_serdev_get_drvdata(dev);
@@ -164,6 +101,7 @@ static int serdev_ngsm_set_config(struct device *dev)
 	return 0;
 }
 
+#if 1
 static int serdev_ngsm_output(struct gsm_serdev *gsd, u8 *data, int len)
 {
 	struct serdev_device *serdev = gsd->serdev;
@@ -183,6 +121,7 @@ static int serdev_ngsm_output(struct gsm_serdev *gsd, u8 *data, int len)
 
 	return len;
 }
+#endif
 
 static int serdev_ngsm_runtime_suspend(struct device *dev)
 {
@@ -227,7 +166,6 @@ static const struct dev_pm_ops serdev_ngsm_pm_ops = {
 			   serdev_ngsm_runtime_resume,
 			   NULL)
 };
-
 /*
  * At least Motorola MDM6600 devices have GPIO wake pins shared between the
  * USB PHY and the TS 27.010 interface. So for PM, we need to use the calls
@@ -327,7 +265,8 @@ static int serdev_ngsm_probe(struct serdev_device *serdev)
 
 	gsd = &ddata->gsd;
 	gsd->serdev = serdev;
-	gsd->output = serdev_ngsm_output;
+	gsd->output = serdev_ngsm_output; /* This is real-serial line to gsm direction; 
+					     we want to keep it */
 	serdev_device_set_drvdata(serdev, gsd);
 	gsm_serdev_set_drvdata(dev, ddata);
 
@@ -377,7 +316,7 @@ static int serdev_ngsm_probe(struct serdev_device *serdev)
 	if (err)
 		goto err_close;
 
-	err = serdev_ngsm_tty_init(ddata);
+	err = serdev_ngsm_tty_init(ddata, NULL /* FIXME! */);
 	if (err)
 		goto err_tty;
 
diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c
index d367803e2044..6f02a1546560 100644
--- a/drivers/tty/serdev/serdev-ttyport.c
+++ b/drivers/tty/serdev/serdev-ttyport.c
@@ -272,6 +272,8 @@ struct device *serdev_tty_port_register(struct tty_port *port,
 	if (!port || !drv || !parent)
 		return ERR_PTR(-ENODEV);
 
+	printk("serdev_tty_port_register: %p, %d\n", port, idx);
+
 	ctrl = serdev_controller_alloc(parent, sizeof(struct serport));
 	if (!ctrl)
 		return ERR_PTR(-ENOMEM);
@@ -291,9 +293,12 @@ struct device *serdev_tty_port_register(struct tty_port *port,
 		goto err_reset_data;
 
 	dev_info(&ctrl->dev, "tty port %s%d registered\n", drv->name, idx);
+	printk("serdev_tty_port_register: controller is  %p, serdev %p\n", ctrl, ctrl->serdev);
+	
 	return &ctrl->dev;
 
 err_reset_data:
+	printk("serdev_tty_port_register: error?\n");
 	port->client_data = NULL;
 	port->client_ops = &tty_port_default_client_ops;
 	serdev_controller_put(ctrl);
diff --git a/include/linux/serdev-gsm.h b/include/linux/serdev-gsm.h
index 4fa819a6e366..5bdc8143b7df 100644
--- a/include/linux/serdev-gsm.h
+++ b/include/linux/serdev-gsm.h
@@ -7,7 +7,7 @@
 #include <linux/serdev.h>
 #include <linux/types.h>
 
-struct gsm_serdev_dlci;
+struct gsm_serdev_dlci_operations;
 struct gsm_config;
 
 /**
@@ -28,16 +28,16 @@ struct gsm_serdev {
 };
 
 /**
- * struct gsm_serdev_dlci - serdev-gsm ts 27.010 channel data
+ * struct gsm_serdev_dlci_operations - serdev-gsm ts 27.010 channel data
  * @gsd:		serdev-gsm instance
  * @line:		ts 27.010 channel, control channel 0 is not available
  * @receive_buf:	function to handle data received for the channel
  * @drvdata:		dlci specific consumer driver data
  */
-struct gsm_serdev_dlci {
+struct gsm_serdev_dlci_operations {
 	struct gsm_serdev *gsd;
 	int line;
-	int (*receive_buf)(struct gsm_serdev_dlci *ops,
+	int (*receive_buf)(struct gsm_serdev_dlci_operations *ops,
 			   const unsigned char *buf,
 			   size_t len);
 	void *drvdata;
@@ -48,12 +48,12 @@ struct gsm_serdev_dlci {
 /* TS 27.010 channel specific functions for consumer drivers */
 #if IS_ENABLED(CONFIG_SERIAL_DEV_N_GSM)
 extern int
-serdev_ngsm_register_dlci(struct device *dev, struct gsm_serdev_dlci *dlci);
+serdev_ngsm_register_dlci(struct device *dev, struct gsm_serdev_dlci_operations *dlci);
 extern void serdev_ngsm_unregister_dlci(struct device *dev,
-					struct gsm_serdev_dlci *dlci);
-extern int serdev_ngsm_write(struct device *dev, struct gsm_serdev_dlci *ops,
+					struct gsm_serdev_dlci_operations *dlci);
+extern int serdev_ngsm_write(struct device *dev, struct gsm_serdev_dlci_operations *ops,
 			     const u8 *buf, int len);
-extern struct gsm_serdev_dlci *
+extern struct gsm_serdev_dlci_operations *
 serdev_ngsm_get_dlci(struct device *dev, int line);
 #endif
 
@@ -62,7 +62,7 @@ extern int gsm_serdev_register_device(struct gsm_serdev *gsd);
 extern void gsm_serdev_unregister_device(struct gsm_serdev *gsd);
 extern int gsm_serdev_register_tty_port(struct gsm_serdev *gsd, int line);
 extern void gsm_serdev_unregister_tty_port(struct gsm_serdev *gsd, int line);
-extern struct gsm_serdev_dlci *
+extern struct gsm_serdev_dlci_operations *
 gsm_serdev_tty_port_get_dlci(struct gsm_serdev *gsd, int line);
 
 static inline void *gsm_serdev_get_drvdata(struct device *dev)
@@ -88,10 +88,10 @@ static inline void gsm_serdev_set_drvdata(struct device *dev, void *drvdata)
 extern int gsm_serdev_get_config(struct gsm_serdev *gsd, struct gsm_config *c);
 extern int gsm_serdev_set_config(struct gsm_serdev *gsd, struct gsm_config *c);
 extern int
-gsm_serdev_register_dlci(struct gsm_serdev *gsd, struct gsm_serdev_dlci *ops);
+gsm_serdev_register_dlci(struct gsm_serdev *gsd, struct gsm_serdev_dlci_operations *ops);
 extern void
-gsm_serdev_unregister_dlci(struct gsm_serdev *gsd, struct gsm_serdev_dlci *ops);
-extern int gsm_serdev_write(struct gsm_serdev *gsd, struct gsm_serdev_dlci *ops,
+gsm_serdev_unregister_dlci(struct gsm_serdev *gsd, struct gsm_serdev_dlci_operations *ops);
+extern int gsm_serdev_write(struct gsm_serdev *gsd, struct gsm_serdev_dlci_operations *ops,
 			    const u8 *buf, int len);
 extern void gsm_serdev_data_kick(struct gsm_serdev *gsd);
 
@@ -118,7 +118,7 @@ void gsm_serdev_unregister_tty_port(struct gsm_serdev *gsd, int line)
 {
 }
 
-static inline struct gsm_serdev_dlci *
+static inline struct gsm_serdev_dlci_operations *
 gsm_serdev_tty_port_get_dlci(struct gsm_serdev *gsd, int line)
 {
 	return NULL;
@@ -148,19 +148,19 @@ int gsm_serdev_set_config(struct gsm_serdev *gsd, struct gsm_config *c)
 
 static inline
 int gsm_serdev_register_dlci(struct gsm_serdev *gsd,
-			     struct gsm_serdev_dlci *ops)
+			     struct gsm_serdev_dlci_operations *ops)
 {
 	return -ENODEV;
 }
 
 static inline
 void gsm_serdev_unregister_dlci(struct gsm_serdev *gsd,
-				struct gsm_serdev_dlci *ops)
+				struct gsm_serdev_dlci_operations *ops)
 {
 }
 
 static inline
-int gsm_serdev_write(struct gsm_serdev *gsd, struct gsm_serdev_dlci *ops,
+int gsm_serdev_write(struct gsm_serdev *gsd, struct gsm_serdev_dlci_operations *ops,
 		     const u8 *buf, int len)
 {
 	return -ENODEV;
diff --git a/include/linux/serdev.h b/include/linux/serdev.h
index 9f14f9c12ec4..efdffe34a9b5 100644
--- a/include/linux/serdev.h
+++ b/include/linux/serdev.h
@@ -128,6 +128,7 @@ static inline void serdev_device_set_drvdata(struct serdev_device *serdev, void
  */
 static inline void serdev_device_put(struct serdev_device *serdev)
 {
+	printk("serdev_device_put... %p\n", serdev);
 	if (serdev)
 		put_device(&serdev->dev);
 }
@@ -156,6 +157,8 @@ static inline void serdev_controller_set_drvdata(struct serdev_controller *ctrl,
  */
 static inline void serdev_controller_put(struct serdev_controller *ctrl)
 {
+	printk("serdev_controller_put... %p\n", ctrl);
+	WARN_ON(1);
 	if (ctrl)
 		put_device(&ctrl->dev);
 }
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index afb9521ddf91..530a0146893c 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -802,7 +802,7 @@ static char *ptr_to_id(char *buf, char *end, const void *ptr,
 	 * Print the real pointer value for NULL and error pointers,
 	 * as they are not actual addresses.
 	 */
-	if (IS_ERR_OR_NULL(ptr))
+//	if (IS_ERR_OR_NULL(ptr))
 		return pointer_string(buf, end, ptr, spec);
 
 	/* When debugging early boot use non-cryptographically secure hash. */
diff --git a/sound/soc/codecs/motmdm.c b/sound/soc/codecs/motmdm.c
index 325a860ef665..1528c89d9d57 100644
--- a/sound/soc/codecs/motmdm.c
+++ b/sound/soc/codecs/motmdm.c
@@ -28,7 +28,7 @@ struct motmdm_driver_data {
 	struct snd_soc_component *component;
 	struct snd_soc_dai *master_dai;
 	struct device *modem;
-	struct gsm_serdev_dlci dlci;
+	struct gsm_serdev_dlci_operations dlci;
 	struct regmap *regmap;
 	unsigned char *buf;
 	size_t len;
@@ -38,7 +38,7 @@ struct motmdm_driver_data {
 	struct mutex mutex;	/* for sending commands */
 	wait_queue_head_t read_queue;
 
-	int (*receive_buf_orig)(struct gsm_serdev_dlci *ops,
+	int (*receive_buf_orig)(struct gsm_serdev_dlci_operations *ops,
 				const unsigned char *buf,
 				size_t len);
 	unsigned int dtmf_val;
@@ -121,7 +121,7 @@ static int motmdm_send_command(struct motmdm_driver_data *ddata,
 }
 
 /* Handle U1234+XXXX= style command response */
-static int motmdm_receive_data(struct gsm_serdev_dlci *dlci,
+static int motmdm_receive_data(struct gsm_serdev_dlci_operations *dlci,
 			       const unsigned char *buf,
 			       size_t len)
 {
@@ -569,7 +569,7 @@ static void motmdm_voice_get_state(struct motmdm_driver_data *ddata,
 		motmdm_disable_primary_dai(ddata->component);
 }
 
-static int receive_buf_voice(struct gsm_serdev_dlci *ops,
+static int receive_buf_voice(struct gsm_serdev_dlci_operations *ops,
 			     const unsigned char *buf,
 			     size_t len)
 {
@@ -585,7 +585,7 @@ static int receive_buf_voice(struct gsm_serdev_dlci *ops,
 /* Read the voice status from dlci1 and let user space handle rest */
 static int motmdm_init_voice_dlci(struct motmdm_driver_data *ddata)
 {
-	struct gsm_serdev_dlci *dlci;
+	struct gsm_serdev_dlci_operations *dlci;
 
 	dlci = serdev_ngsm_get_dlci(ddata->modem, MOTMDM_VOICE_DLCI);
 	if (!dlci)
@@ -600,7 +600,7 @@ static int motmdm_init_voice_dlci(struct motmdm_driver_data *ddata)
 
 static void motmdm_free_voice_dlci(struct motmdm_driver_data *ddata)
 {
-	struct gsm_serdev_dlci *dlci;
+	struct gsm_serdev_dlci_operations *dlci;
 
 	dlci = serdev_ngsm_get_dlci(ddata->modem, MOTMDM_VOICE_DLCI);
 	if (!dlci)
@@ -613,7 +613,7 @@ static void motmdm_free_voice_dlci(struct motmdm_driver_data *ddata)
 static int motmdm_soc_probe(struct snd_soc_component *component)
 {
 	struct motmdm_driver_data *ddata;
-	struct gsm_serdev_dlci *dlci;
+	struct gsm_serdev_dlci_operations *dlci;
 	const unsigned char *cmd = "AT+CMUT=0";
 	int error;
 	u32 line;
@@ -690,7 +690,7 @@ static int motmdm_soc_probe(struct snd_soc_component *component)
 static void motmdm_soc_remove(struct snd_soc_component *component)
 {
 	struct motmdm_driver_data *ddata;
-	struct gsm_serdev_dlci *dlci;
+	struct gsm_serdev_dlci_operations *dlci;
 
 	ddata = snd_soc_component_get_drvdata(component);
 	dlci = &ddata->dlci;
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
index 97b4f5480a31..31ff426226ef 100644
--- a/sound/soc/generic/audio-graph-card.c
+++ b/sound/soc/generic/audio-graph-card.c
@@ -631,6 +631,8 @@ static int graph_probe(struct platform_device *pdev)
 	struct link_info li;
 	int ret;
 
+	printk("audio-graph: probe starts\n");
+
 	/* Allocate the private data and the DAI link array */
 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 	if (!priv)
@@ -648,19 +650,24 @@ static int graph_probe(struct platform_device *pdev)
 	if (!li.link || !li.dais)
 		return -EINVAL;
 
+	printk("audio-graph: 2\n");
+
 	ret = asoc_simple_init_priv(priv, &li);
 	if (ret < 0)
 		return ret;
 
 	priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);
 	if (IS_ERR(priv->pa_gpio)) {
+		printk("audio-graph: optional pa failed\n");
 		ret = PTR_ERR(priv->pa_gpio);
 		dev_err(dev, "failed to get amplifier gpio: %d\n", ret);
 		return ret;
 	}
 
+	printk("audio-graph: parsing of\n");	
 	ret = graph_parse_of(priv);
 	if (ret < 0) {
+		printk("audio-graph: parsing of failed: %d\n", ret);	
 		if (ret != -EPROBE_DEFER)
 			dev_err(dev, "parse error %d\n", ret);
 		goto err;
@@ -670,9 +677,13 @@ static int graph_probe(struct platform_device *pdev)
 
 	asoc_simple_debug_info(priv);
 
+		printk("audio-graph: registering card\n");	
+	
+
 	ret = devm_snd_soc_register_card(dev, card);
 	if (ret < 0)
 		goto err;
+		printk("audio-graph: all ok\n");	
 
 	return 0;
 err:


-- 
http://www.livejournal.com/~pavelmachek

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem
  2020-12-20 22:48       ` Pavel Machek
@ 2020-12-24  8:02         ` Tony Lindgren
  2020-12-24 14:59           ` Pavel Machek
  2021-01-02 16:23           ` Pavel Machek
  0 siblings, 2 replies; 19+ messages in thread
From: Tony Lindgren @ 2020-12-24  8:02 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Johan Hovold, phone-devel, Greg Kroah-Hartman, Rob Herring,
	Alan Cox, Lee Jones, Jiri Slaby, Merlijn Wajer, Peter Hurley,
	Sebastian Reichel, linux-serial, devicetree, linux-kernel,
	linux-omap

Hi,

* Pavel Machek <pavel@ucw.cz> [201220 22:48]:
> Hi!
> 
> > Sorry about the late reply on this.
> 
> I'm afraid I'll need some more answers in near future, but for now:
> 
> Tony, do you remember / can you figure out what gsmtty GPS is on? I
> never used it on that interface, and I can't seem to figure it out.
> 
> My notes say:
> 
> /dev/motmdm1 -- basic support, calls, on/off                                    
> /dev/motmdm3 -- send sms interface                                              
> /dev/motmdm9 -- receive sms interface                                           
>
> (and gsmtty numbering is same)

Yes I have not had a chance to look at these for several months now,
but have the latest set in droid4-pending-v5.10 branch in my github
tree.

The gnss device is at /dev/gsmtty6, see the current droid4-agps tool
to upload the almanac also on github. That's has turned out to be a
pretty good gsm serdev test too :)

> For now I converted gnss driver to use serdev interface, and n_gsm to
> provide it... Not yet finished but I believe I'm walking in the right
> direction.

Great, sounds good to me if you got things working with just serdev
calls :) I'll try to take a look at this stuff again after I have
the other pending droid4 issues out of the way like v5.12 charger
and keyboard stuff.

Regards,

Tony

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem
  2020-12-24  8:02         ` Tony Lindgren
@ 2020-12-24 14:59           ` Pavel Machek
  2021-01-02 16:23           ` Pavel Machek
  1 sibling, 0 replies; 19+ messages in thread
From: Pavel Machek @ 2020-12-24 14:59 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Johan Hovold, phone-devel, Greg Kroah-Hartman, Rob Herring,
	Alan Cox, Lee Jones, Jiri Slaby, Merlijn Wajer, Peter Hurley,
	Sebastian Reichel, linux-serial, devicetree, linux-kernel,
	linux-omap

[-- Attachment #1: Type: text/plain, Size: 1556 bytes --]

Hi!

> > My notes say:
> > 
> > /dev/motmdm1 -- basic support, calls, on/off                                    
> > /dev/motmdm3 -- send sms interface                                              
> > /dev/motmdm9 -- receive sms interface                                           
> >
> > (and gsmtty numbering is same)
> 
> Yes I have not had a chance to look at these for several months now,
> but have the latest set in droid4-pending-v5.10 branch in my github
> tree.
> 
> The gnss device is at /dev/gsmtty6, see the current droid4-agps tool
> to upload the almanac also on github. That's has turned out to be a
> pretty good gsm serdev test too :)

Thanks a lot for the info! Is it this one?
https://github.com/tmlind/droid4-agps . GPS worked for me even w/o
AGPS support, but I broke even that, so I need to fix that first.

> > For now I converted gnss driver to use serdev interface, and n_gsm to
> > provide it... Not yet finished but I believe I'm walking in the right
> > direction.
> 
> Great, sounds good to me if you got things working with just serdev
> calls :) I'll try to take a look at this stuff again after I have
> the other pending droid4 issues out of the way like v5.12 charger
> and keyboard stuff.

Just ask for latest code :-). I still believe I'm going right
direction, but now I understand why you did it the way you did. Sound
code needs to listen on gsmtty1, with the data still being provided
for userspace.

Best regards,
								Pavel
-- 
http://www.livejournal.com/~pavelmachek

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem
  2020-12-24  8:02         ` Tony Lindgren
  2020-12-24 14:59           ` Pavel Machek
@ 2021-01-02 16:23           ` Pavel Machek
  1 sibling, 0 replies; 19+ messages in thread
From: Pavel Machek @ 2021-01-02 16:23 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Johan Hovold, phone-devel, Greg Kroah-Hartman, Rob Herring,
	Alan Cox, Lee Jones, Jiri Slaby, Merlijn Wajer, Peter Hurley,
	Sebastian Reichel, linux-serial, devicetree, linux-kernel,
	linux-omap

[-- Attachment #1: Type: text/plain, Size: 1225 bytes --]

Hi!

> > I'm afraid I'll need some more answers in near future, but for now:
> > 
> > Tony, do you remember / can you figure out what gsmtty GPS is on? I
> > never used it on that interface, and I can't seem to figure it out.
> > 
> > My notes say:
> > 
> > /dev/motmdm1 -- basic support, calls, on/off                                    
> > /dev/motmdm3 -- send sms interface                                              
> > /dev/motmdm9 -- receive sms interface                                           
> >
> > (and gsmtty numbering is same)
> 
> Yes I have not had a chance to look at these for several months now,
> but have the latest set in droid4-pending-v5.10 branch in my github
> tree.
> 
> The gnss device is at /dev/gsmtty6, see the current droid4-agps tool
> to upload the almanac also on github. That's has turned out to be a
> pretty good gsm serdev test too :)

Treason uncloaked!

While A-GPS is at gsmtty6 I guess, NMEA data are on gsmtty4. And I
could not access that one, due to bitmask reserving it for gnss.

But now I have figured it out, and should have something reviewable
soon.

Thanks for support,

								Pavel
-- 
http://www.livejournal.com/~pavelmachek

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2021-01-02 16:24 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-21 23:27 [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem Tony Lindgren
2020-04-21 23:27 ` [PATCH 1/4] tty: n_gsm: Add support for serdev drivers Tony Lindgren
2020-04-21 23:27 ` [PATCH 2/4] serdev: ngsm-motmdm: Add Motorola TS 27.010 serdev modem driver for droid4 Tony Lindgren
2020-04-21 23:27 ` [PATCH 3/4] dt-bindings: serdev: motmdm: Add binding for motorola-mdm Tony Lindgren
2020-04-21 23:27 ` [PATCH 4/4] ARM: dts: omap4-droid4: Enable basic modem support Tony Lindgren
2020-04-23 11:43 ` [PATCHv6 0/4] n_gsm serdev support and protocol driver for droid4 modem Johan Hovold
2020-04-23 15:37   ` Tony Lindgren
2020-04-23 23:27     ` Tony Lindgren
2020-04-25 16:58       ` Tony Lindgren
2020-05-28  8:24     ` Johan Hovold
2020-12-20 22:48       ` Pavel Machek
2020-12-24  8:02         ` Tony Lindgren
2020-12-24 14:59           ` Pavel Machek
2021-01-02 16:23           ` Pavel Machek
2020-04-24 21:50   ` Pavel Machek
2020-04-24 22:15     ` Tony Lindgren
2020-04-26  7:27       ` Pavel Machek
2020-04-26 20:07       ` Pavel Machek
2020-04-26 23:25         ` Tony Lindgren

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).