Netdev List
 help / color / mirror / Atom feed
From: Svyatoslav Ryhel <clamor95@gmail.com>
To: Andrew Lunn <andrew+netdev@lunn.ch>,
	"David S. Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	Svyatoslav Ryhel <clamor95@gmail.com>
Cc: netdev@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org
Subject: [PATCH v2 2/2] net: usb: Add Infineon XMM6260 Baseband modem support
Date: Sat, 23 May 2026 11:44:07 +0300	[thread overview]
Message-ID: <20260523084408.50346-3-clamor95@gmail.com> (raw)
In-Reply-To: <20260523084408.50346-1-clamor95@gmail.com>

The Infineon/Intel XMM6260 is a 3G-focused, slim modem platform designed
for smartphones, data cards, and Machine-to-Machine (M2M) applications.
The modem is usually connected via the application processor's USB line
in HSIC mode; however, to work properly, the modem must control this line

Dmesg with modem appearing on LG Optimus Vu (P895):

[    9.427014] ci_hdrc ci_hdrc.1: EHCI Host Controller
[    9.431488] ci_hdrc ci_hdrc.1: new USB bus registered, assigned bus number 1
[    9.457197] ci_hdrc ci_hdrc.1: USB 2.0 started, EHCI 1.00
[    9.460370] usb usb1: New USB device found, idVendor=1d6b, idProduct=0002, bcdDevice= 6.16
[    9.468470] usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    9.475597] usb usb1: Product: EHCI Host Controller
[    9.480508] usb usb1: Manufacturer: Linux 6.16.0+ ehci_hcd
[    9.485913] usb usb1: SerialNumber: ci_hdrc.1
[    9.490862] hub 1-0:1.0: USB hub found
[    9.494005] hub 1-0:1.0: 1 port detected
[    9.657191] usb 1-1: new high-speed USB device number 2 using ci_hdrc
[    9.844726] usb 1-1: New USB device found, idVendor=1519, idProduct=0020, bcdDevice=12.74
[    9.850530] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[    9.857594] usb 1-1: Product: HSIC Device
[    9.861606] usb 1-1: Manufacturer: Comneon
[    9.865627] usb 1-1: SerialNumber: 0123456789
[    9.908739] cdc_acm 1-1:1.0: ttyACM0: USB ACM device

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/net/usb/Kconfig            |  15 ++
 drivers/net/usb/Makefile           |   1 +
 drivers/net/usb/baseband-xmm6260.c | 378 +++++++++++++++++++++++++++++
 3 files changed, 394 insertions(+)
 create mode 100644 drivers/net/usb/baseband-xmm6260.c

diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 52a5c0922c79..d54d8db752df 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -642,4 +642,19 @@ config USB_RTL8153_ECM
 	  CONFIG_USB_RTL8152 is not set, or the RTL8153 device is not
 	  supported by r8152 driver.
 
+config USB_NET_XMM6260
+	tristate "Infineon XMM626X Baseband HSPA/HSUPA modem"
+	depends on USB_CHIPIDEA && (RFKILL || RFKILL=n)
+	help
+	  Select this if you want to use an Infineon XMM626X modem, found in
+	  devices such as the LG Optimus 4X P880, LG Optimus Vu P895, Samsung
+	  Galaxy S II (GT-I9100), and Galaxy Nexus (GT-I9250). This driver
+	  handles the modem configuration and provides a stable way to expose
+	  the modem's USB interface. To establish a connection, you will first
+	  need a userspace program to send the correct commands to the modem
+	  through its CDC ACM port, as well as a DHCP client.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called baseband-xmm6260.
+
 endif # USB_NET_DRIVERS
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 4964f7b326fb..ffa532c7d7d6 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -42,3 +42,4 @@ obj-$(CONFIG_USB_NET_CDC_MBIM)	+= cdc_mbim.o
 obj-$(CONFIG_USB_NET_CH9200)	+= ch9200.o
 obj-$(CONFIG_USB_NET_AQC111)	+= aqc111.o
 obj-$(CONFIG_USB_RTL8153_ECM)	+= r8153_ecm.o
+obj-$(CONFIG_USB_NET_XMM6260)	+= baseband-xmm6260.o
diff --git a/drivers/net/usb/baseband-xmm6260.c b/drivers/net/usb/baseband-xmm6260.c
new file mode 100644
index 000000000000..557ec79f5e2a
--- /dev/null
+++ b/drivers/net/usb/baseband-xmm6260.c
@@ -0,0 +1,378 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2011 NVIDIA Corporation
+ * Copyright (C) 2023 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/pwrseq/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/rfkill.h>
+#include <linux/usb.h>
+
+#define BASEBAND_XMM_INIT_DELAY		5000
+
+#define BASEBAND_PRODUCT_ID_XMM6260	0x0020
+#define BASEBAND_VENDOR_ID_COMNEON	0x1519
+
+/*
+ * Starting from ver 1145 modem starts in the IPC_AP_WAKE_IRQ_READY state.
+ * AP wake interrupt keeps low util CP starts to initiate its HSIC hw. AP wake
+ * interrupt goes up during CP HSIC init (BASEBAND_XMM_IPC_AP_WAKE_INIT1 state)
+ * at this point Host USB bus must be configured in order for modem to set
+ * properly. Then interrupt goes down when CP HSIC is ready
+ * (BASEBAND_XMM_IPC_AP_WAKE_INIT2 state) and at this point XMM6260 USB device
+ * should appear and be accessible for further work. In case it does not, the
+ * cycle must repeat.
+ */
+
+/* bits [0:2] */
+enum baseband_xmm_ipc_ap_wake_state {
+	BASEBAND_XMM_IPC_AP_WAKE_IRQ_READY,
+	BASEBAND_XMM_IPC_AP_WAKE_INIT1,
+	BASEBAND_XMM_IPC_AP_WAKE_INIT2,
+	BASEBAND_XMM_IPC_AP_WAKE_L,
+	BASEBAND_XMM_IPC_AP_WAKE_H,
+	BASEBAND_XMM_IPC_AP_WAKE_UNINIT,
+	BASEBAND_XMM_IPC_AP_WAKE_MASK = 7,
+};
+
+#define BASEBAND_XMM_IPC_AP_WAKE_MAX	3
+
+#define BASEBAND_XMM_STATE_POWERED	3 /* Tracks regulator state */
+#define BASEBAND_XMM_STATE_PROTECTED	4 /* Prevents rfkill from access */
+#define BASEBAND_XMM_STATE_PRESENT	5 /* Tracks USB device presence */
+#define BASEBAND_XMM_STATE_POWEROFF	6 /* Prevents poweroff recursive call */
+#define BASEBAND_XMM_STATE_MAX		8
+
+struct baseband_xmm_data {
+	struct device *dev;
+	struct rfkill *rfkill_dev;
+	struct pwrseq_desc *pwrseq;
+
+	DECLARE_BITMAP(state, BASEBAND_XMM_STATE_MAX);
+	int irq;
+
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *enable_gpio;
+
+	struct gpio_desc *ipc_cp_gpio;
+	struct gpio_desc *ipc_ap_gpio;
+
+	struct regulator *vbat_supply;
+
+	struct delayed_work modem_work;
+	struct notifier_block nb;
+};
+
+static int get_ipc_ap_wake(struct baseband_xmm_data *priv)
+{
+	int i, ret = 0;
+
+	for (i = 0; i < BASEBAND_XMM_IPC_AP_WAKE_MAX; i++)
+		ret |= (test_bit(i, priv->state) << i);
+
+	return ret;
+}
+
+static void set_ipc_ap_wake(struct baseband_xmm_data *priv,
+			    enum baseband_xmm_ipc_ap_wake_state state)
+{
+	for (int i = 0; i < BASEBAND_XMM_IPC_AP_WAKE_MAX; i++)
+		if (state & BIT(i))
+			set_bit(i, priv->state);
+		else
+			clear_bit(i, priv->state);
+}
+
+static void baseband_xmm_reset(struct baseband_xmm_data *priv)
+{
+	int ret;
+
+	set_bit(BASEBAND_XMM_STATE_PROTECTED, priv->state);
+
+	if (!test_bit(BASEBAND_XMM_STATE_POWERED, priv->state)) {
+		ret = regulator_enable(priv->vbat_supply);
+		if (ret)
+			dev_err(priv->dev,
+				"failed to enable vbat power supply\n");
+
+		set_bit(BASEBAND_XMM_STATE_POWERED, priv->state);
+	}
+
+	gpiod_set_value_cansleep(priv->enable_gpio, 0);
+	msleep(50);
+
+	gpiod_set_value_cansleep(priv->reset_gpio, 1);
+	msleep(200);
+	gpiod_set_value_cansleep(priv->reset_gpio, 0);
+
+	msleep(50);
+
+	/* falling edge trigger to CP */
+	gpiod_set_value_cansleep(priv->enable_gpio, 1);
+	usleep_range(50, 100);
+	gpiod_set_value_cansleep(priv->enable_gpio, 0);
+	msleep(20);
+}
+
+static void baseband_xmm_poweroff(struct baseband_xmm_data *priv)
+{
+	/*
+	 * The test_bit check prevents poweroff from being recursively
+	 * called during USB device deregistration. USB device
+	 * deregistration can be triggered by the driver by calling this
+	 * function or by some external event. The first case will cause
+	 * a recursive call by the notifier if not handled, while the
+	 * second case requires this call to handle the USB controller
+	 * properly.
+	 */
+	if (test_bit(BASEBAND_XMM_STATE_POWEROFF, priv->state))
+		return;
+
+	set_bit(BASEBAND_XMM_STATE_PROTECTED, priv->state);
+	set_bit(BASEBAND_XMM_STATE_POWEROFF, priv->state);
+
+	pwrseq_power_off(priv->pwrseq);
+	gpiod_set_value_cansleep(priv->reset_gpio, 1);
+
+	if (test_bit(BASEBAND_XMM_STATE_POWERED, priv->state)) {
+		regulator_disable(priv->vbat_supply);
+		clear_bit(BASEBAND_XMM_STATE_POWERED, priv->state);
+	}
+	set_ipc_ap_wake(priv, BASEBAND_XMM_IPC_AP_WAKE_IRQ_READY);
+
+	clear_bit(BASEBAND_XMM_STATE_PROTECTED, priv->state);
+	clear_bit(BASEBAND_XMM_STATE_PRESENT, priv->state);
+	clear_bit(BASEBAND_XMM_STATE_POWEROFF, priv->state);
+}
+
+static int baseband_xmm_usb_notifier_call(struct notifier_block *nb,
+					  unsigned long action, void *data)
+{
+	struct baseband_xmm_data *priv =
+		container_of(nb, struct baseband_xmm_data, nb);
+	struct usb_device *udev;
+	u16 product, vendor;
+
+	if (action == USB_BUS_ADD || action == USB_BUS_REMOVE)
+		return NOTIFY_OK;
+
+	udev = data;
+	product = le16_to_cpu(udev->descriptor.idProduct);
+	vendor = le16_to_cpu(udev->descriptor.idVendor);
+
+	switch (action) {
+	case USB_DEVICE_ADD:
+		/* Infineon XMM6260 ID 1519:0020 */
+		if (vendor == BASEBAND_VENDOR_ID_COMNEON &&
+		    product == BASEBAND_PRODUCT_ID_XMM6260) {
+			cancel_delayed_work_sync(&priv->modem_work);
+			clear_bit(BASEBAND_XMM_STATE_PROTECTED, priv->state);
+			set_bit(BASEBAND_XMM_STATE_PRESENT, priv->state);
+		}
+		break;
+
+	case USB_DEVICE_REMOVE:
+		/* Infineon XMM6260 ID 1519:0020 */
+		if (vendor == BASEBAND_VENDOR_ID_COMNEON &&
+		    product == BASEBAND_PRODUCT_ID_XMM6260)
+			baseband_xmm_poweroff(priv);
+		break;
+
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static int baseband_xmm_set_block(void *data, bool blocked)
+{
+	struct baseband_xmm_data *priv = data;
+
+	if (test_bit(BASEBAND_XMM_STATE_PROTECTED, priv->state))
+		return -EBUSY;
+
+	if (blocked && test_bit(BASEBAND_XMM_STATE_PRESENT, priv->state))
+		baseband_xmm_poweroff(priv);
+	else if (!blocked && !test_bit(BASEBAND_XMM_STATE_PRESENT, priv->state))
+		baseband_xmm_reset(priv);
+
+	return 0;
+}
+
+static const struct rfkill_ops baseband_xmm_rfkill_ops = {
+	.set_block = baseband_xmm_set_block,
+};
+
+static void baseband_xmm_work(struct work_struct *work)
+{
+	struct baseband_xmm_data *priv =
+		container_of(work, struct baseband_xmm_data, modem_work.work);
+
+	baseband_xmm_poweroff(priv);
+};
+
+static irqreturn_t baseband_hostwake_interrupt(int irq, void *dev_id)
+{
+	struct baseband_xmm_data *priv = dev_id;
+	int state = gpiod_get_value(priv->ipc_ap_gpio);
+
+	switch (get_ipc_ap_wake(priv)) {
+	case BASEBAND_XMM_IPC_AP_WAKE_IRQ_READY:
+		if (!state) {
+			set_ipc_ap_wake(priv, BASEBAND_XMM_IPC_AP_WAKE_INIT1);
+			pwrseq_power_on(priv->pwrseq);
+		}
+		break;
+
+	case BASEBAND_XMM_IPC_AP_WAKE_INIT1:
+		if (state) {
+			set_ipc_ap_wake(priv, BASEBAND_XMM_IPC_AP_WAKE_INIT2);
+			schedule_delayed_work(&priv->modem_work,
+					      msecs_to_jiffies(BASEBAND_XMM_INIT_DELAY));
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int baseband_xmm_probe(struct platform_device *pdev)
+{
+	struct baseband_xmm_data *priv;
+	struct device *dev = &pdev->dev;
+	unsigned long irqflags;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = dev;
+	platform_set_drvdata(pdev, priv);
+
+	priv->vbat_supply = devm_regulator_get(dev, "vbat");
+	if (IS_ERR(priv->vbat_supply))
+		return dev_err_probe(dev, PTR_ERR(priv->vbat_supply),
+				     "failed to get vbat regulator\n");
+
+	/* Own modem gpios */
+	priv->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+						   GPIOD_OUT_LOW);
+	if (IS_ERR(priv->reset_gpio))
+		return dev_err_probe(dev, PTR_ERR(priv->reset_gpio),
+				     "failed to get reset GPIO\n");
+
+	priv->enable_gpio = devm_gpiod_get_optional(dev, "enable",
+						    GPIOD_OUT_LOW);
+	if (IS_ERR(priv->enable_gpio))
+		return dev_err_probe(dev, PTR_ERR(priv->enable_gpio),
+				     "failed to get enable GPIO\n");
+
+	/* CP - AP connections */
+	priv->ipc_cp_gpio = devm_gpiod_get_optional(dev, "cp-wake",
+						    GPIOD_OUT_LOW);
+	if (IS_ERR(priv->ipc_cp_gpio))
+		return dev_err_probe(dev, PTR_ERR(priv->ipc_cp_gpio),
+				     "failed to get CP wake GPIO\n");
+
+	priv->ipc_ap_gpio = devm_gpiod_get_optional(dev, "ap-wake", GPIOD_IN);
+	if (IS_ERR(priv->ipc_ap_gpio))
+		return dev_err_probe(dev, PTR_ERR(priv->ipc_ap_gpio),
+				     "failed to get AP wake GPIO\n");
+
+	/* Modem power sequence */
+	priv->pwrseq = devm_pwrseq_get(dev, "modem-power");
+	if (IS_ERR(priv->pwrseq))
+		return dev_err_probe(dev, PTR_ERR(priv->pwrseq),
+				     "failed to get modem pwrseq");
+
+	bitmap_zero(priv->state, BASEBAND_XMM_STATE_MAX);
+	INIT_DELAYED_WORK(&priv->modem_work, baseband_xmm_work);
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (priv->irq < 0)
+		return dev_err_probe(dev, priv->irq, "failed to get IRQ\n");
+
+	/*
+	 * Systems using device tree should set up interrupt via DT,
+	 * the rest will use the default edge both interrupt.
+	 */
+	irqflags = dev->of_node ? 0 : IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+
+	ret = devm_request_threaded_irq(dev, priv->irq, NULL,
+					&baseband_hostwake_interrupt,
+					IRQF_ONESHOT | irqflags,
+					"modem-hostwake", priv);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "failed to register IRQ %d\n", priv->irq);
+
+	priv->rfkill_dev = rfkill_alloc("xmm-modem", dev, RFKILL_TYPE_WWAN,
+					&baseband_xmm_rfkill_ops, priv);
+	if (!priv->rfkill_dev)
+		return -ENOMEM;
+
+	ret = rfkill_register(priv->rfkill_dev);
+	if (ret) {
+		rfkill_destroy(priv->rfkill_dev);
+		return dev_err_probe(dev, ret,
+				     "failed to register WWAN rfkill\n");
+	}
+
+	priv->nb.notifier_call = baseband_xmm_usb_notifier_call;
+	usb_register_notify(&priv->nb);
+
+	return 0;
+}
+
+static void baseband_xmm_remove(struct platform_device *pdev)
+{
+	struct baseband_xmm_data *priv = platform_get_drvdata(pdev);
+
+	rfkill_unregister(priv->rfkill_dev);
+	rfkill_destroy(priv->rfkill_dev);
+
+	disable_irq(priv->irq);
+	cancel_delayed_work_sync(&priv->modem_work);
+
+	usb_unregister_notify(&priv->nb);
+	baseband_xmm_poweroff(priv);
+}
+
+static const struct of_device_id baseband_xmm_match[] = {
+	{ .compatible = "infineon,xmm6260" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, baseband_xmm_match);
+
+static struct platform_driver baseband_xmm_driver = {
+	.driver = {
+		.name = "baseband-xmm6260",
+		.of_match_table = baseband_xmm_match,
+	},
+	.probe = baseband_xmm_probe,
+	.remove = baseband_xmm_remove,
+};
+module_platform_driver(baseband_xmm_driver);
+
+MODULE_AUTHOR("Svyatolsav Ryhel <clamor95@gmail.com>");
+MODULE_DESCRIPTION("Baseband Infineon XMM6260 driver");
+MODULE_LICENSE("GPL");
-- 
2.51.0


  parent reply	other threads:[~2026-05-23  8:44 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-23  8:44 [PATCH v2 0/2] Add support for Infineon/Intel XMM6260 modem Svyatoslav Ryhel
2026-05-23  8:44 ` [PATCH v2 1/2] dt-bindings: net: Document " Svyatoslav Ryhel
2026-05-27  8:23   ` Krzysztof Kozlowski
2026-05-27  8:54     ` Svyatoslav Ryhel
2026-05-23  8:44 ` Svyatoslav Ryhel [this message]
2026-05-27  0:38   ` [PATCH v2 2/2] net: usb: Add Infineon XMM6260 Baseband modem support Jakub Kicinski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260523084408.50346-3-clamor95@gmail.com \
    --to=clamor95@gmail.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=conor+dt@kernel.org \
    --cc=davem@davemloft.net \
    --cc=devicetree@vger.kernel.org \
    --cc=edumazet@google.com \
    --cc=krzk+dt@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=robh@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox