* [PATCH RESEND v8 09/10] iio: adc: qcom-spmi-iadc: Migrate to devm_spmi_subdevice_alloc_and_add()
From: AngeloGioacchino Del Regno @ 2026-05-11 10:08 UTC (permalink / raw)
To: jic23
Cc: dlechner, nuno.sa, andy, arnd, gregkh, srini, vkoul,
neil.armstrong, sre, sboyd, angelogioacchino.delregno, krzk,
dmitry.baryshkov, quic_wcheng, melody.olvera, quic_nsekar,
ivo.ivanov.ivanov1, abelvesa, luca.weiss, konrad.dybcio,
mitltlatltl, krishna.kurapati, linux-arm-msm, linux-iio,
linux-kernel, linux-phy, linux-pm, kernel, Jonathan Cameron,
Andy Shevchenko
In-Reply-To: <20260511100805.121432-1-angelogioacchino.delregno@collabora.com>
Some Qualcomm PMICs integrate an Current ADC device, reachable
in a specific address range over SPMI.
Instead of using the parent SPMI device (the main PMIC) as a kind
of syscon in this driver, register a new SPMI sub-device and
initialize its own regmap with this sub-device's specific base
address, retrieved from the devicetree.
This allows to stop manually adding the register base address to
every R/W call in this driver, as this can be, and is now, handled
by the regmap API instead.
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Tested-by: Neil Armstrong <neil.armstrong@linaro.org> # on SM8650-QRD
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
drivers/iio/adc/qcom-spmi-iadc.c | 32 +++++++++++++++++++++-----------
1 file changed, 21 insertions(+), 11 deletions(-)
diff --git a/drivers/iio/adc/qcom-spmi-iadc.c b/drivers/iio/adc/qcom-spmi-iadc.c
index b64a8a407168..55a09c0e2d5c 100644
--- a/drivers/iio/adc/qcom-spmi-iadc.c
+++ b/drivers/iio/adc/qcom-spmi-iadc.c
@@ -16,6 +16,7 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
+#include <linux/spmi.h>
/* IADC register and bit definition */
#define IADC_REVISION2 0x1
@@ -94,7 +95,6 @@
* struct iadc_chip - IADC Current ADC device structure.
* @regmap: regmap for register read/write.
* @dev: This device pointer.
- * @base: base offset for the ADC peripheral.
* @rsense: Values of the internal and external sense resister in micro Ohms.
* @poll_eoc: Poll for end of conversion instead of waiting for IRQ.
* @offset: Raw offset values for the internal and external channels.
@@ -105,7 +105,6 @@
struct iadc_chip {
struct regmap *regmap;
struct device *dev;
- u16 base;
bool poll_eoc;
u32 rsense[2];
u16 offset[2];
@@ -119,7 +118,7 @@ static int iadc_read(struct iadc_chip *iadc, u16 offset, u8 *data)
unsigned int val;
int ret;
- ret = regmap_read(iadc->regmap, iadc->base + offset, &val);
+ ret = regmap_read(iadc->regmap, offset, &val);
if (ret < 0)
return ret;
@@ -129,7 +128,7 @@ static int iadc_read(struct iadc_chip *iadc, u16 offset, u8 *data)
static int iadc_write(struct iadc_chip *iadc, u16 offset, u8 data)
{
- return regmap_write(iadc->regmap, iadc->base + offset, data);
+ return regmap_write(iadc->regmap, offset, data);
}
static int iadc_reset(struct iadc_chip *iadc)
@@ -270,7 +269,7 @@ static int iadc_poll_wait_eoc(struct iadc_chip *iadc, unsigned int interval_us)
static int iadc_read_result(struct iadc_chip *iadc, u16 *data)
{
- return regmap_bulk_read(iadc->regmap, iadc->base + IADC_DATA, data, 2);
+ return regmap_bulk_read(iadc->regmap, IADC_DATA, data, 2);
}
static int iadc_do_conversion(struct iadc_chip *iadc, int chan, u16 *data)
@@ -483,12 +482,19 @@ static const struct iio_chan_spec iadc_channels[] = {
static int iadc_probe(struct platform_device *pdev)
{
+ struct regmap_config iadc_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = 0xff,
+ .fast_io = true,
+ };
struct device_node *node = pdev->dev.of_node;
struct device *dev = &pdev->dev;
+ struct spmi_subdevice *sub_sdev;
+ struct spmi_device *sparent;
struct iio_dev *indio_dev;
struct iadc_chip *iadc;
int ret, irq_eoc;
- u32 res;
indio_dev = devm_iio_device_alloc(dev, sizeof(*iadc));
if (!indio_dev)
@@ -497,18 +503,21 @@ static int iadc_probe(struct platform_device *pdev)
iadc = iio_priv(indio_dev);
iadc->dev = dev;
- iadc->regmap = dev_get_regmap(dev->parent, NULL);
- if (!iadc->regmap)
- return -ENODEV;
+ sparent = to_spmi_device(dev->parent);
+ sub_sdev = devm_spmi_subdevice_alloc_and_add(dev, sparent);
+ if (IS_ERR(sub_sdev))
+ return PTR_ERR(sub_sdev);
init_completion(&iadc->complete);
mutex_init(&iadc->lock);
- ret = of_property_read_u32(node, "reg", &res);
+ ret = device_property_read_u32(dev, "reg", &iadc_regmap_config.reg_base);
if (ret < 0)
return -ENODEV;
- iadc->base = res;
+ iadc->regmap = devm_regmap_init_spmi_ext(&sub_sdev->sdev, &iadc_regmap_config);
+ if (IS_ERR(iadc->regmap))
+ return PTR_ERR(iadc->regmap);
ret = iadc_version_check(iadc);
if (ret < 0)
@@ -584,3 +593,4 @@ MODULE_ALIAS("platform:qcom-spmi-iadc");
MODULE_DESCRIPTION("Qualcomm SPMI PMIC current ADC driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
+MODULE_IMPORT_NS("SPMI");
--
2.53.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH RESEND v8 10/10] iio: adc: qcom-spmi-iadc: Remove regmap R/W wrapper functions
From: AngeloGioacchino Del Regno @ 2026-05-11 10:08 UTC (permalink / raw)
To: jic23
Cc: dlechner, nuno.sa, andy, arnd, gregkh, srini, vkoul,
neil.armstrong, sre, sboyd, angelogioacchino.delregno, krzk,
dmitry.baryshkov, quic_wcheng, melody.olvera, quic_nsekar,
ivo.ivanov.ivanov1, abelvesa, luca.weiss, konrad.dybcio,
mitltlatltl, krishna.kurapati, linux-arm-msm, linux-iio,
linux-kernel, linux-phy, linux-pm, kernel, Jonathan Cameron,
Andy Shevchenko
In-Reply-To: <20260511100805.121432-1-angelogioacchino.delregno@collabora.com>
This driver doesn't need to add any register base address to any
regmap call anymore since it was migrated to register as a SPMI
subdevice with its own regmap reg_base, which makes the regmap
API to automatically add such base address internally.
Since the iadc_{read,write,read_result}() functions now only do
call regmap_{read,write,bulk_read}() and nothing else, simplify
the driver by removing them and by calling regmap APIs directly.
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Tested-by: Neil Armstrong <neil.armstrong@linaro.org> # on SM8650-QRD
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
drivers/iio/adc/qcom-spmi-iadc.c | 83 ++++++++++++--------------------
1 file changed, 30 insertions(+), 53 deletions(-)
diff --git a/drivers/iio/adc/qcom-spmi-iadc.c b/drivers/iio/adc/qcom-spmi-iadc.c
index 55a09c0e2d5c..503f90b2a5ba 100644
--- a/drivers/iio/adc/qcom-spmi-iadc.c
+++ b/drivers/iio/adc/qcom-spmi-iadc.c
@@ -113,77 +113,59 @@ struct iadc_chip {
struct completion complete;
};
-static int iadc_read(struct iadc_chip *iadc, u16 offset, u8 *data)
-{
- unsigned int val;
- int ret;
-
- ret = regmap_read(iadc->regmap, offset, &val);
- if (ret < 0)
- return ret;
-
- *data = val;
- return 0;
-}
-
-static int iadc_write(struct iadc_chip *iadc, u16 offset, u8 data)
-{
- return regmap_write(iadc->regmap, offset, data);
-}
-
static int iadc_reset(struct iadc_chip *iadc)
{
- u8 data;
+ u32 data;
int ret;
- ret = iadc_write(iadc, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA);
+ ret = regmap_write(iadc->regmap, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA);
if (ret < 0)
return ret;
- ret = iadc_read(iadc, IADC_PERH_RESET_CTL3, &data);
+ ret = regmap_read(iadc->regmap, IADC_PERH_RESET_CTL3, &data);
if (ret < 0)
return ret;
- ret = iadc_write(iadc, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA);
+ ret = regmap_write(iadc->regmap, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA);
if (ret < 0)
return ret;
data |= IADC_FOLLOW_WARM_RB;
- return iadc_write(iadc, IADC_PERH_RESET_CTL3, data);
+ return regmap_write(iadc->regmap, IADC_PERH_RESET_CTL3, data);
}
static int iadc_set_state(struct iadc_chip *iadc, bool state)
{
- return iadc_write(iadc, IADC_EN_CTL1, state ? IADC_EN_CTL1_SET : 0);
+ return regmap_write(iadc->regmap, IADC_EN_CTL1, state ? IADC_EN_CTL1_SET : 0);
}
static void iadc_status_show(struct iadc_chip *iadc)
{
- u8 mode, sta1, chan, dig, en, req;
+ u32 mode, sta1, chan, dig, en, req;
int ret;
- ret = iadc_read(iadc, IADC_MODE_CTL, &mode);
+ ret = regmap_read(iadc->regmap, IADC_MODE_CTL, &mode);
if (ret < 0)
return;
- ret = iadc_read(iadc, IADC_DIG_PARAM, &dig);
+ ret = regmap_read(iadc->regmap, IADC_DIG_PARAM, &dig);
if (ret < 0)
return;
- ret = iadc_read(iadc, IADC_CH_SEL_CTL, &chan);
+ ret = regmap_read(iadc->regmap, IADC_CH_SEL_CTL, &chan);
if (ret < 0)
return;
- ret = iadc_read(iadc, IADC_CONV_REQ, &req);
+ ret = regmap_read(iadc->regmap, IADC_CONV_REQ, &req);
if (ret < 0)
return;
- ret = iadc_read(iadc, IADC_STATUS1, &sta1);
+ ret = regmap_read(iadc->regmap, IADC_STATUS1, &sta1);
if (ret < 0)
return;
- ret = iadc_read(iadc, IADC_EN_CTL1, &en);
+ ret = regmap_read(iadc->regmap, IADC_EN_CTL1, &en);
if (ret < 0)
return;
@@ -199,34 +181,34 @@ static int iadc_configure(struct iadc_chip *iadc, int channel)
/* Mode selection */
mode = (IADC_OP_MODE_NORMAL << IADC_OP_MODE_SHIFT) | IADC_TRIM_EN;
- ret = iadc_write(iadc, IADC_MODE_CTL, mode);
+ ret = regmap_write(iadc->regmap, IADC_MODE_CTL, mode);
if (ret < 0)
return ret;
/* Channel selection */
- ret = iadc_write(iadc, IADC_CH_SEL_CTL, channel);
+ ret = regmap_write(iadc->regmap, IADC_CH_SEL_CTL, channel);
if (ret < 0)
return ret;
/* Digital parameter setup */
decim = IADC_DEF_DECIMATION << IADC_DIG_DEC_RATIO_SEL_SHIFT;
- ret = iadc_write(iadc, IADC_DIG_PARAM, decim);
+ ret = regmap_write(iadc->regmap, IADC_DIG_PARAM, decim);
if (ret < 0)
return ret;
/* HW settle time delay */
- ret = iadc_write(iadc, IADC_HW_SETTLE_DELAY, IADC_DEF_HW_SETTLE_TIME);
+ ret = regmap_write(iadc->regmap, IADC_HW_SETTLE_DELAY, IADC_DEF_HW_SETTLE_TIME);
if (ret < 0)
return ret;
- ret = iadc_write(iadc, IADC_FAST_AVG_CTL, IADC_DEF_AVG_SAMPLES);
+ ret = regmap_write(iadc->regmap, IADC_FAST_AVG_CTL, IADC_DEF_AVG_SAMPLES);
if (ret < 0)
return ret;
if (IADC_DEF_AVG_SAMPLES)
- ret = iadc_write(iadc, IADC_FAST_AVG_EN, IADC_FAST_AVG_EN_SET);
+ ret = regmap_write(iadc->regmap, IADC_FAST_AVG_EN, IADC_FAST_AVG_EN_SET);
else
- ret = iadc_write(iadc, IADC_FAST_AVG_EN, 0);
+ ret = regmap_write(iadc->regmap, IADC_FAST_AVG_EN, 0);
if (ret < 0)
return ret;
@@ -239,19 +221,19 @@ static int iadc_configure(struct iadc_chip *iadc, int channel)
return ret;
/* Request conversion */
- return iadc_write(iadc, IADC_CONV_REQ, IADC_CONV_REQ_SET);
+ return regmap_write(iadc->regmap, IADC_CONV_REQ, IADC_CONV_REQ_SET);
}
static int iadc_poll_wait_eoc(struct iadc_chip *iadc, unsigned int interval_us)
{
unsigned int count, retry;
+ u32 sta1;
int ret;
- u8 sta1;
retry = interval_us / IADC_CONV_TIME_MIN_US;
for (count = 0; count < retry; count++) {
- ret = iadc_read(iadc, IADC_STATUS1, &sta1);
+ ret = regmap_read(iadc->regmap, IADC_STATUS1, &sta1);
if (ret < 0)
return ret;
@@ -267,11 +249,6 @@ static int iadc_poll_wait_eoc(struct iadc_chip *iadc, unsigned int interval_us)
return -ETIMEDOUT;
}
-static int iadc_read_result(struct iadc_chip *iadc, u16 *data)
-{
- return regmap_bulk_read(iadc->regmap, IADC_DATA, data, 2);
-}
-
static int iadc_do_conversion(struct iadc_chip *iadc, int chan, u16 *data)
{
unsigned int wait;
@@ -296,7 +273,7 @@ static int iadc_do_conversion(struct iadc_chip *iadc, int chan, u16 *data)
}
if (!ret)
- ret = iadc_read_result(iadc, data);
+ ret = regmap_bulk_read(iadc->regmap, IADC_DATA, data, sizeof(*data));
exit:
iadc_set_state(iadc, false);
if (ret < 0)
@@ -392,10 +369,10 @@ static int iadc_update_offset(struct iadc_chip *iadc)
static int iadc_version_check(struct iadc_chip *iadc)
{
- u8 val;
+ u32 val;
int ret;
- ret = iadc_read(iadc, IADC_PERPH_TYPE, &val);
+ ret = regmap_read(iadc->regmap, IADC_PERPH_TYPE, &val);
if (ret < 0)
return ret;
@@ -404,7 +381,7 @@ static int iadc_version_check(struct iadc_chip *iadc)
return -EINVAL;
}
- ret = iadc_read(iadc, IADC_PERPH_SUBTYPE, &val);
+ ret = regmap_read(iadc->regmap, IADC_PERPH_SUBTYPE, &val);
if (ret < 0)
return ret;
@@ -413,7 +390,7 @@ static int iadc_version_check(struct iadc_chip *iadc)
return -EINVAL;
}
- ret = iadc_read(iadc, IADC_REVISION2, &val);
+ ret = regmap_read(iadc->regmap, IADC_REVISION2, &val);
if (ret < 0)
return ret;
@@ -428,7 +405,7 @@ static int iadc_version_check(struct iadc_chip *iadc)
static int iadc_rsense_read(struct iadc_chip *iadc, struct device_node *node)
{
int ret, sign, int_sense;
- u8 deviation;
+ u32 deviation;
ret = of_property_read_u32(node, "qcom,external-resistor-micro-ohms",
&iadc->rsense[IADC_EXT_RSENSE]);
@@ -440,7 +417,7 @@ static int iadc_rsense_read(struct iadc_chip *iadc, struct device_node *node)
return -EINVAL;
}
- ret = iadc_read(iadc, IADC_NOMINAL_RSENSE, &deviation);
+ ret = regmap_read(iadc->regmap, IADC_NOMINAL_RSENSE, &deviation);
if (ret < 0)
return ret;
--
2.53.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: (subset) [PATCH 00/12] Add TH1520 USB support
From: Bartosz Golaszewski @ 2026-05-11 10:54 UTC (permalink / raw)
To: Drew Fustini, Guo Ren, Fu Wei, Michael Turquette, Stephen Boyd,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vinod Koul,
Neil Armstrong, Greg Kroah-Hartman, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Jisheng Zhang, Icenowy Zheng
Cc: Bartosz Golaszewski, Philipp Zabel, linux-riscv, linux-clk,
devicetree, linux-kernel, linux-gpio, linux-phy, linux-usb,
Icenowy Zheng, Han Gao, Yao Zi
In-Reply-To: <20260507081710.4090814-1-zhengxingda@iscas.ac.cn>
On Thu, 07 May 2026 16:16:58 +0800, Icenowy Zheng wrote:
> This patchset adds support for T-Head TH1520's USB functionality, and
> enabled it on the Lichee Pi 4A board.
>
> The first 3 patches add support for the MISC subsystem clock
> contrtoller, which contains some USB clocks.
>
> The next 2 patches add support for the USB PHY of T-Head TH1520, which
> is a wrapped Synopsys USB3.0 FemtoPHY with a little integration quirk;
> the controller itself is a properly configured DWC3 controller with sane
> default register values set.
>
> [...]
Applied, thanks!
[07/12] dt-bindings: gpio: dwapb: allow GPIO hogs
https://git.kernel.org/brgl/c/f76c8be440e53465a306c95a7d50ca8675252f82
Best regards,
--
Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH RESEND v8 00/10] SPMI: Implement sub-devices and migrate drivers
From: Jonathan Cameron @ 2026-05-11 13:17 UTC (permalink / raw)
To: AngeloGioacchino Del Regno
Cc: dlechner, nuno.sa, andy, arnd, gregkh, srini, vkoul,
neil.armstrong, sre, sboyd, krzk, dmitry.baryshkov, quic_wcheng,
melody.olvera, quic_nsekar, ivo.ivanov.ivanov1, abelvesa,
luca.weiss, konrad.dybcio, mitltlatltl, krishna.kurapati,
linux-arm-msm, linux-iio, linux-kernel, linux-phy, linux-pm,
kernel
In-Reply-To: <20260511100805.121432-1-angelogioacchino.delregno@collabora.com>
On Mon, 11 May 2026 12:07:55 +0200
AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> wrote:
Hi Angelo,
Why the resend? If marking a series with that I expect to see it
called out as first thing in the cover letter.
Jonathan
> Changes in v8:
> - Renamed *res to *sub_sdev in devm_spmi_subdevice_remove() (Andy)
> - Changed kerneldoc wording to "error pointer" for function
> spmi_subdevice_alloc_and_add() (Andy)
> - Shuffled around some assignments in spmi_subdevice_alloc_and_add() (Andy)
> - Used device_property_read_u32() instead of of_property_read_u32()
> in all of the migrated drivers (Andy)
> - Changed .max_register field in all of the migrated drivers from
> 0x100 to 0xff (Andy)
> - Kept `sta1` declaration in reversed xmas tree order in function
> iadc_poll_wait_eoc() of qcom-spmi-iadc.c (Andy)
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v1 0/6] Add support for Infineon/Intel XMM6260 modem
From: Svyatoslav Ryhel @ 2026-05-11 13:56 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Vinod Koul, Neil Armstrong, Thierry Reding, Jonathan Hunter,
Greg Kroah-Hartman, Peter Chen, Svyatoslav Ryhel
Cc: netdev, devicetree, linux-kernel, linux-phy, linux-tegra,
linux-usb
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 typically connected via the application processor's USB line
in HSIC mode. To function correctly, the modem must control this line, as
it requires precise timing to initiate or de-initialize the USB connection.
This control is necessary to successfully enumerate the next stage of the
USB device loader (moving from firmware loading to the actual device
interface for example).
Patches 1 and 2 adjust the Tegra-specific portion of the Chipidea USB
controller to allow for the disabling of automatic PHY and USB controller
registration. This is achieved by adding the nvidia,external-control
property/flag. It does not affect any existing configurations, but it
allows the USB line to be registered or deregistered by an external
device — in this case, the modem.
Patches 3 and 4 add support for the generic portion of the
Infineon XMM6260 baseband modem, which was used in many Tegra-, OMAP-,
and Exynos-based devices circa 2012. This driver provides power sequences,
manages initial communication with the application processor, handles the
SoC-specific modem PHY, and verifies that the modem USB device appears
correctly.
Patches 5 and 6 implement support for the Tegra-specific modem physical
layer, which handles the registration and unregistration of the USB
controller.
While current support is relatively basic, this configuration already
allows the modem device to appear in the dmesg of my device
(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
Svyatoslav Ryhel (6):
dt-bindings: usb: ci-hdrc-usb2: Document nvidia,external-control
property
usb: chipidea: tegra: Avoid controller/PHY init if bus is externally
controlled
dt-bindings: net: Document Infineon/Intel XMM6260 modem
net: usb: Add Infineon XMM6260 Baseband modem support
dt-bindings: phy: tegra: Document Nvidia Tegra XMM6260 PHY
phy: tegra: Add support for Nvidia Tegra XMM6260 PHY
.../bindings/net/infineon,xmm6260.yaml | 72 ++++
.../bindings/phy/nvidia,tegra-xmm6260.yaml | 58 +++
.../devicetree/bindings/usb/ci-hdrc-usb2.yaml | 7 +
drivers/net/usb/Kconfig | 15 +
drivers/net/usb/Makefile | 1 +
drivers/net/usb/baseband-xmm6260.c | 335 ++++++++++++++++++
drivers/phy/tegra/Kconfig | 12 +
drivers/phy/tegra/Makefile | 1 +
drivers/phy/tegra/phy-tegra-xmm6260.c | 144 ++++++++
drivers/usb/chipidea/ci_hdrc_tegra.c | 36 +-
10 files changed, 667 insertions(+), 14 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/infineon,xmm6260.yaml
create mode 100644 Documentation/devicetree/bindings/phy/nvidia,tegra-xmm6260.yaml
create mode 100644 drivers/net/usb/baseband-xmm6260.c
create mode 100644 drivers/phy/tegra/phy-tegra-xmm6260.c
--
2.51.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v1 2/6] usb: chipidea: tegra: Avoid controller/PHY init if bus is externally controlled
From: Svyatoslav Ryhel @ 2026-05-11 13:56 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Vinod Koul, Neil Armstrong, Thierry Reding, Jonathan Hunter,
Greg Kroah-Hartman, Peter Chen, Svyatoslav Ryhel
Cc: netdev, devicetree, linux-kernel, linux-phy, linux-tegra,
linux-usb
In-Reply-To: <20260511135703.62470-1-clamor95@gmail.com>
If the USB controller and PHY are externally controlled, then the
registration of the controller and the PHY initialization should be
skipped, since these configurations must be done by the device that
controls the bus to work correctly.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/usb/chipidea/ci_hdrc_tegra.c | 36 +++++++++++++++++-----------
1 file changed, 22 insertions(+), 14 deletions(-)
diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c
index 372788f0f970..593390a818d1 100644
--- a/drivers/usb/chipidea/ci_hdrc_tegra.c
+++ b/drivers/usb/chipidea/ci_hdrc_tegra.c
@@ -32,6 +32,7 @@ struct tegra_usb {
struct clk *clk;
bool needs_double_reset;
+ bool externally_controlled;
};
struct tegra_usb_soc_info {
@@ -312,20 +313,25 @@ static int tegra_usb_probe(struct platform_device *pdev)
if (device_property_present(&pdev->dev, "nvidia,needs-double-reset"))
usb->needs_double_reset = true;
+ if (device_property_present(&pdev->dev, "nvidia,external-control"))
+ usb->externally_controlled = true;
+
err = tegra_usb_reset_controller(&pdev->dev);
if (err) {
dev_err_probe(&pdev->dev, err, "failed to reset controller");
goto fail_power_off;
}
- /*
- * USB controller registers shouldn't be touched before PHY is
- * initialized, otherwise CPU will hang because clocks are gated.
- * PHY driver controls gating of internal USB clocks on Tegra.
- */
- err = usb_phy_init(usb->phy);
- if (err)
- goto fail_power_off;
+ if (!usb->externally_controlled) {
+ /*
+ * USB controller registers shouldn't be touched before PHY is
+ * initialized, otherwise CPU will hang because clocks are gated.
+ * PHY driver controls gating of internal USB clocks on Tegra.
+ */
+ err = usb_phy_init(usb->phy);
+ if (err)
+ goto fail_power_off;
+ }
/* setup and register ChipIdea HDRC device */
usb->soc = soc;
@@ -342,12 +348,14 @@ static int tegra_usb_probe(struct platform_device *pdev)
if (of_usb_get_phy_mode(pdev->dev.of_node) == USBPHY_INTERFACE_MODE_ULPI)
usb->data.flags &= ~CI_HDRC_SUPPORTS_RUNTIME_PM;
- usb->dev = ci_hdrc_add_device(&pdev->dev, pdev->resource,
- pdev->num_resources, &usb->data);
- if (IS_ERR(usb->dev)) {
- err = dev_err_probe(&pdev->dev, PTR_ERR(usb->dev),
- "failed to add HDRC device");
- goto phy_shutdown;
+ if (!usb->externally_controlled) {
+ usb->dev = ci_hdrc_add_device(&pdev->dev, pdev->resource,
+ pdev->num_resources, &usb->data);
+ if (IS_ERR(usb->dev)) {
+ err = dev_err_probe(&pdev->dev, PTR_ERR(usb->dev),
+ "failed to add HDRC device");
+ goto phy_shutdown;
+ }
}
return 0;
--
2.51.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v1 3/6] dt-bindings: net: Document Infineon/Intel XMM6260 modem
From: Svyatoslav Ryhel @ 2026-05-11 13:56 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Vinod Koul, Neil Armstrong, Thierry Reding, Jonathan Hunter,
Greg Kroah-Hartman, Peter Chen, Svyatoslav Ryhel
Cc: netdev, devicetree, linux-kernel, linux-phy, linux-tegra,
linux-usb
In-Reply-To: <20260511135703.62470-1-clamor95@gmail.com>
Describe the Infineon/Intel XMM6260, a 3G-focused, slim modem platform
designed for smartphones, data cards, and Machine-to-Machine (M2M)
applications.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
.../bindings/net/infineon,xmm6260.yaml | 72 +++++++++++++++++++
1 file changed, 72 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/infineon,xmm6260.yaml
diff --git a/Documentation/devicetree/bindings/net/infineon,xmm6260.yaml b/Documentation/devicetree/bindings/net/infineon,xmm6260.yaml
new file mode 100644
index 000000000000..d34d7125274f
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/infineon,xmm6260.yaml
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/infineon,xmm6260.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Infineon/Intel XMM6260 embedded USB modem
+
+description:
+ 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
+
+maintainers:
+ - Svyatoslav Ryhel <clamor95@gmail.com>
+
+properties:
+ compatible:
+ const: infineon,xmm6260
+
+ interrupts:
+ maxItems: 1
+
+ enable-gpios:
+ description: GPIO connected to the ON1 pin
+ maxItems: 1
+
+ reset-gpios:
+ description: GPIO connected to the RESET_PWRDWN_N pin
+ maxItems: 1
+
+ ap-wake-gpios:
+ description: GPIO connected to the EINT3 pin
+ maxItems: 1
+
+ cp-wake-gpios:
+ description: GPIO connected to the EINT2 pin
+ maxItems: 1
+
+ phys:
+ maxItems: 1
+
+ vbat-supply:
+ description: Supply connected to the VBAT lines.
+
+required:
+ - compatible
+ - interrupts
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ modem {
+ compatible = "infineon,xmm6260";
+
+ interrupt-parent = <&gpio>;
+ interrupts = <168 IRQ_TYPE_EDGE_BOTH>;
+
+ enable-gpios = <&gpio 112 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&gpio 169 GPIO_ACTIVE_LOW>;
+
+ cp-wake-gpios = <&gpio 151 GPIO_ACTIVE_HIGH>;
+ ap-wake-gpios = <&gpio 168 GPIO_ACTIVE_HIGH>;
+
+ phys = <&xmm6260_phy>;
+ vbat-supply = <&vdd_3v3_vbat>;
+ };
--
2.51.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v1 4/6] net: usb: Add Infineon XMM6260 Baseband modem support
From: Svyatoslav Ryhel @ 2026-05-11 13:56 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Vinod Koul, Neil Armstrong, Thierry Reding, Jonathan Hunter,
Greg Kroah-Hartman, Peter Chen, Svyatoslav Ryhel
Cc: netdev, devicetree, linux-kernel, linux-phy, linux-tegra,
linux-usb
In-Reply-To: <20260511135703.62470-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 | 335 +++++++++++++++++++++++++++++
3 files changed, 351 insertions(+)
create mode 100644 drivers/net/usb/baseband-xmm6260.c
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 52a5c0922c79..503f24a3cfa6 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 GPIOLIB && OF && USB_CHIPIDEA
+ 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..658f5351fab7
--- /dev/null
+++ b/drivers/net/usb/baseband-xmm6260.c
@@ -0,0 +1,335 @@
+// 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/devm-helpers.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/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/property.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
+
+enum ipc_ap_wake_state {
+ IPC_AP_WAKE_IRQ_READY,
+ IPC_AP_WAKE_INIT1,
+ IPC_AP_WAKE_INIT2,
+ IPC_AP_WAKE_L,
+ IPC_AP_WAKE_H,
+ IPC_AP_WAKE_UNINIT,
+};
+
+struct baseband_xmm_data {
+ struct device *dev;
+ struct rfkill *rfkill_dev;
+ struct phy *mphy;
+
+ 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;
+
+ enum ipc_ap_wake_state ap_state;
+
+ bool powered; /* tracks usb bus state */
+ bool inited; /* tracks modem 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 = data;
+ u16 product = le16_to_cpu(udev->descriptor.idProduct);
+ u16 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);
+ priv->inited = true;
+ }
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static void baseband_xmm_reset(struct baseband_xmm_data *priv)
+{
+ int ret;
+
+ ret = regulator_enable(priv->vbat_supply);
+ if (ret)
+ dev_err(priv->dev, "failed to enable vbat power supply\n");
+
+ 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 int baseband_xmm_set_block(void *data, bool blocked)
+{
+ struct baseband_xmm_data *priv = data;
+
+ if (blocked) {
+ if (priv->inited && priv->powered) {
+ phy_power_off(priv->mphy);
+
+ msleep(500);
+
+ gpiod_set_value_cansleep(priv->reset_gpio, 1);
+ regulator_disable(priv->vbat_supply);
+
+ priv->powered = false;
+ priv->inited = false;
+ }
+ } else {
+ if (priv->inited)
+ return 0;
+
+ priv->ap_state = IPC_AP_WAKE_IRQ_READY;
+ baseband_xmm_reset(priv);
+
+ priv->powered = false;
+ priv->inited = false;
+ }
+
+ 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);
+
+ switch (priv->ap_state) {
+ case IPC_AP_WAKE_INIT1:
+ if (priv->powered)
+ return;
+
+ phy_power_on(priv->mphy);
+ priv->powered = true;
+ break;
+
+ case IPC_AP_WAKE_INIT2:
+ priv->ap_state = IPC_AP_WAKE_IRQ_READY;
+
+ phy_power_off(priv->mphy);
+
+ priv->powered = false;
+ priv->inited = false;
+
+ msleep(500);
+ break;
+
+ default:
+ break;
+ }
+};
+
+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 (priv->ap_state) {
+ case IPC_AP_WAKE_IRQ_READY:
+ if (!state) {
+ priv->ap_state = IPC_AP_WAKE_INIT1;
+ schedule_delayed_work(&priv->modem_work, 0);
+ }
+
+ break;
+
+ case IPC_AP_WAKE_INIT1:
+ if (state) {
+ priv->ap_state = 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 irq, 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 PHY */
+ priv->mphy = devm_phy_optional_get(dev, NULL);
+ if (IS_ERR(priv->mphy))
+ return dev_err_probe(dev, PTR_ERR(priv->mphy),
+ "failed to get modem PHY");
+
+ /*
+ * Strting from ver 1145 modem starts in READY state. AP wake
+ * interrupt keeps low util CP starts to initiate HSIC hw. AP
+ * wake interrupt goes up during CP HSIC init and then it goes
+ * down when CP HSIC is ready.
+ */
+ priv->ap_state = IPC_AP_WAKE_IRQ_READY;
+ priv->inited = false;
+
+ devm_delayed_work_autocancel(dev, &priv->modem_work, baseband_xmm_work);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return dev_err_probe(dev, 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, 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", irq);
+
+ priv->rfkill_dev = rfkill_alloc("xmm-modem", dev, RFKILL_TYPE_WWAN,
+ &baseband_xmm_rfkill_ops, priv);
+ if (priv->rfkill_dev) {
+ ret = rfkill_register(priv->rfkill_dev);
+ if (ret < 0) {
+ rfkill_destroy(priv->rfkill_dev);
+ return dev_err_probe(dev, ret,
+ "failed to register WWAN rfkill\n");
+ }
+ } else {
+ return dev_err_probe(dev, PTR_ERR(priv->rfkill_dev),
+ "failed to allocate WWAN rfkill\n");
+ }
+
+ priv->nb.notifier_call = baseband_xmm_usb_notifier_call;
+ usb_register_notify(&priv->nb);
+
+ baseband_xmm_reset(priv);
+ priv->powered = false;
+
+ 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);
+
+ usb_unregister_notify(&priv->nb);
+ phy_power_off(priv->mphy);
+
+ gpiod_set_value_cansleep(priv->reset_gpio, 1);
+ regulator_disable(priv->vbat_supply);
+}
+
+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
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v1 5/6] dt-bindings: phy: tegra: Document Nvidia Tegra XMM6260 PHY
From: Svyatoslav Ryhel @ 2026-05-11 13:57 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Vinod Koul, Neil Armstrong, Thierry Reding, Jonathan Hunter,
Greg Kroah-Hartman, Peter Chen, Svyatoslav Ryhel
Cc: netdev, devicetree, linux-kernel, linux-phy, linux-tegra,
linux-usb
In-Reply-To: <20260511135703.62470-1-clamor95@gmail.com>
Document the XMM6260 PHY used by various devices based on the Nvidia Tegra
SoC, describing its usage
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
.../bindings/phy/nvidia,tegra-xmm6260.yaml | 58 +++++++++++++++++++
1 file changed, 58 insertions(+)
create mode 100644 Documentation/devicetree/bindings/phy/nvidia,tegra-xmm6260.yaml
diff --git a/Documentation/devicetree/bindings/phy/nvidia,tegra-xmm6260.yaml b/Documentation/devicetree/bindings/phy/nvidia,tegra-xmm6260.yaml
new file mode 100644
index 000000000000..0346433c9772
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/nvidia,tegra-xmm6260.yaml
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/nvidia,tegra-xmm6260.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nvidia Tegra PHY for XMM6260 modem
+
+description:
+ A hardware configuration used in Tegra SoCs to provide proper interaction
+ between the application processor and the modem, as well as control over
+ one of the SoC's USB lines for the modem.
+
+maintainers:
+ - Svyatoslav Ryhel <clamor95@gmail.com>
+
+properties:
+ compatible:
+ const: nvidia,tegra-xmm6260
+
+ enable-gpios:
+ description: GPIO connected to the EINT1 pin
+ maxItems: 1
+
+ nvidia,usb-bus:
+ description:
+ Contains two phandles; the first is pointing to the Host's USB controller
+ and the second linking to the controller's PHY.
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ minItems: 2
+ maxItems: 2
+
+ phy-supply:
+ description: Supply powering the PHY.
+
+ "#phy-cells":
+ const: 0
+
+required:
+ - compatible
+ - nvidia,usb-bus
+ - "#phy-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ phy {
+ compatible = "nvidia,tegra-xmm6260";
+
+ enable-gpios = <&gpio 165 GPIO_ACTIVE_HIGH>;
+ phy-supply = <&vdd_3v3_vbat>;
+
+ nvidia,usb-bus = <&hsic_usb>, <&phy2>;
+ #phy-cells = <0>;
+ };
--
2.51.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v1 6/6] phy: tegra: Add support for Nvidia Tegra XMM6260 PHY
From: Svyatoslav Ryhel @ 2026-05-11 13:57 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Vinod Koul, Neil Armstrong, Thierry Reding, Jonathan Hunter,
Greg Kroah-Hartman, Peter Chen, Svyatoslav Ryhel
Cc: netdev, devicetree, linux-kernel, linux-phy, linux-tegra,
linux-usb
In-Reply-To: <20260511135703.62470-1-clamor95@gmail.com>
Nvidia Tegra XMM6260 PHY is a hardware configuration used in Tegra SoCs
to provide proper interaction between the application processor and the
modem, as well as control over one of the SoC's USB lines for the modem.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/phy/tegra/Kconfig | 12 +++
drivers/phy/tegra/Makefile | 1 +
drivers/phy/tegra/phy-tegra-xmm6260.c | 144 ++++++++++++++++++++++++++
3 files changed, 157 insertions(+)
create mode 100644 drivers/phy/tegra/phy-tegra-xmm6260.c
diff --git a/drivers/phy/tegra/Kconfig b/drivers/phy/tegra/Kconfig
index 342fb736da4b..41b5ce460f37 100644
--- a/drivers/phy/tegra/Kconfig
+++ b/drivers/phy/tegra/Kconfig
@@ -18,3 +18,15 @@ config PHY_TEGRA194_P2U
help
Enable this to support the P2U (PIPE to UPHY) that is part of Tegra 19x
and 234 SOCs.
+
+config PHY_TEGRA_XMM6260
+ tristate "NVIDIA Tegra XMM6260 PHY driver"
+ depends on ARCH_TEGRA && USB_NET_XMM6260 && USB_SUPPORT
+ select GENERIC_PHY
+ help
+ Enable this to support XMM6260 modem found in various Tegra devices
+ with cellular capabilities, like LG Optimus 4X P880, LG Optimus Vu
+ P895, Google Nexus 7 (2012) 3G and ASUS Transformer Pad 3G TF300TG.
+
+ To compile this driver as a module, choose M here: the module will
+ be called phy-tegra-xmm6260.
diff --git a/drivers/phy/tegra/Makefile b/drivers/phy/tegra/Makefile
index eeeea72de117..829e298ee56c 100644
--- a/drivers/phy/tegra/Makefile
+++ b/drivers/phy/tegra/Makefile
@@ -9,3 +9,4 @@ phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_186_SOC) += xusb-tegra186.o
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_194_SOC) += xusb-tegra186.o
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_234_SOC) += xusb-tegra186.o
obj-$(CONFIG_PHY_TEGRA194_P2U) += phy-tegra194-p2u.o
+obj-$(CONFIG_PHY_TEGRA_XMM6260) += phy-tegra-xmm6260.o
diff --git a/drivers/phy/tegra/phy-tegra-xmm6260.c b/drivers/phy/tegra/phy-tegra-xmm6260.c
new file mode 100644
index 000000000000..7511de1333aa
--- /dev/null
+++ b/drivers/phy/tegra/phy-tegra-xmm6260.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#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/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb.h>
+#include <linux/usb/chipidea.h>
+#include <linux/usb/phy.h>
+
+struct tegra_usb_device {
+ struct ci_hdrc_platform_data data;
+ struct platform_device *dev;
+};
+
+struct tegra_xmm6260_phy {
+ struct device *dev;
+ struct platform_device *usb_dev;
+ struct usb_phy *usb_phy;
+ struct gpio_desc *enable_gpio;
+};
+
+static int tegra_xmm6260_phy_power_on(struct phy *phy)
+{
+ struct tegra_xmm6260_phy *mphy = phy_get_drvdata(phy);
+ struct tegra_usb_device *usb = platform_get_drvdata(mphy->usb_dev);
+ int ret;
+
+ gpiod_set_value_cansleep(mphy->enable_gpio, 1);
+
+ ret = usb_phy_init(mphy->usb_phy);
+ if (ret) {
+ gpiod_set_value_cansleep(mphy->enable_gpio, 0);
+ return dev_err_probe(mphy->dev, ret,
+ "failed to init USB PHY\n");
+ }
+
+ usb->dev = ci_hdrc_add_device(&mphy->usb_dev->dev,
+ mphy->usb_dev->resource,
+ mphy->usb_dev->num_resources,
+ &usb->data);
+ if (IS_ERR(usb->dev)) {
+ gpiod_set_value_cansleep(mphy->enable_gpio, 0);
+ usb_phy_shutdown(mphy->usb_phy);
+ return dev_err_probe(mphy->dev, PTR_ERR(usb->dev),
+ "failed to register USB controller\n");
+ }
+
+ return 0;
+}
+
+static int tegra_xmm6260_phy_power_off(struct phy *phy)
+{
+ struct tegra_xmm6260_phy *mphy = phy_get_drvdata(phy);
+ struct tegra_usb_device *usb = platform_get_drvdata(mphy->usb_dev);
+
+ ci_hdrc_remove_device(usb->dev);
+ usb_phy_shutdown(mphy->usb_phy);
+
+ gpiod_set_value_cansleep(mphy->enable_gpio, 0);
+
+ return 0;
+}
+
+static const struct phy_ops tegra_xmm6260_phy_ops = {
+ .power_on = tegra_xmm6260_phy_power_on,
+ .power_off = tegra_xmm6260_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int tegra_xmm6260_phy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct device_node *usb_node;
+ struct phy *generic_phy;
+ struct tegra_xmm6260_phy *mphy;
+
+ mphy = devm_kzalloc(dev, sizeof(*mphy), GFP_KERNEL);
+ if (!mphy)
+ return -ENOMEM;
+
+ mphy->enable_gpio = devm_gpiod_get_optional(dev, "enable",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(mphy->enable_gpio))
+ return dev_err_probe(dev, PTR_ERR(mphy->enable_gpio),
+ "failed to get enable GPIO\n");
+
+ usb_node = of_parse_phandle(dev->of_node, "nvidia,usb-bus", 0);
+ if (IS_ERR(usb_node))
+ return dev_err_probe(dev, PTR_ERR(usb_node),
+ "failed to parse modem USB bus\n");
+
+ mphy->usb_dev = of_find_device_by_node(usb_node);
+ of_node_put(usb_node);
+ if (!mphy->usb_dev)
+ return dev_err_probe(dev, -ENODEV,
+ "failed to get modem USB bus\n");
+
+ mphy->usb_phy = devm_usb_get_phy_by_phandle(dev, "nvidia,usb-bus", 1);
+ if (IS_ERR(mphy->usb_phy))
+ return dev_err_probe(dev, PTR_ERR(mphy->usb_phy),
+ "failed to get USB PHY");
+
+ generic_phy = devm_phy_create(dev, NULL, &tegra_xmm6260_phy_ops);
+ if (IS_ERR(generic_phy))
+ return dev_err_probe(dev, PTR_ERR(generic_phy),
+ "failed to create PHY\n");
+
+ phy_set_drvdata(generic_phy, mphy);
+ mphy->dev = dev;
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider))
+ return dev_err_probe(dev, PTR_ERR(phy_provider),
+ "failed to register PHY\n");
+
+ return 0;
+}
+
+static const struct of_device_id tegra_xmm6260_phy_match[] = {
+ { .compatible = "nvidia,tegra-xmm6260" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tegra_xmm6260_phy_match);
+
+static struct platform_driver tegra_xmm6260_phy_driver = {
+ .driver = {
+ .name = "tegra-xmm6260-phy",
+ .of_match_table = tegra_xmm6260_phy_match,
+ },
+ .probe = tegra_xmm6260_phy_probe,
+};
+module_platform_driver(tegra_xmm6260_phy_driver);
+
+MODULE_AUTHOR("Svyatolsav Ryhel <clamor95@gmail.com>");
+MODULE_DESCRIPTION("Tegra XMM6260 PHY driver");
+MODULE_LICENSE("GPL");
--
2.51.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v1 1/6] dt-bindings: usb: ci-hdrc-usb2: Document nvidia,external-control property
From: Svyatoslav Ryhel @ 2026-05-11 13:56 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Vinod Koul, Neil Armstrong, Thierry Reding, Jonathan Hunter,
Greg Kroah-Hartman, Peter Chen, Svyatoslav Ryhel
Cc: netdev, devicetree, linux-kernel, linux-phy, linux-tegra,
linux-usb
In-Reply-To: <20260511135703.62470-1-clamor95@gmail.com>
Document the nvidia,external-control property required, for example, for
USB lines in HSIC mode connected to a modem, where the modem requires
precise control over the USB bus to properly enumerate all its stages and
intermediate devices.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml
index 691d6cf02c27..a13c1ef49a57 100644
--- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml
+++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml
@@ -75,6 +75,13 @@ properties:
type: boolean
deprecated: true
+ nvidia,external-control:
+ description:
+ Indicates that the controller is configured externally and that the host
+ should not attempt to touch it. Usually used by a modem which requires
+ precise bus configuration.
+ type: boolean
+
ulpi:
type: object
additionalProperties: false
--
2.51.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH v3 0/5] phy: qcom: Fix possible NULL-deref and runtime PM race conditions
From: Vladimir Oltean @ 2026-05-11 14:02 UTC (permalink / raw)
To: Loic Poulain
Cc: vkoul, kishon, linux-arm-msm, linux-phy, dmitry.baryshkov,
neil.armstrong, konrad.dybcio
In-Reply-To: <20260205160240.748371-1-loic.poulain@oss.qualcomm.com>
On Thu, Feb 05, 2026 at 05:02:35PM +0100, Loic Poulain wrote:
> Address potential NULL pointer dereferences and race conditions related
> to runtime PM in several Qualcomm PHY drivers. In all cases, enabling
> runtime PM before the PHY instance is fully initialized can lead to
> crashes during early runtime suspend callbacks.
>
> - Attach driver data before enabling runtime PM.
> - Introduce initialization flags where needed to avoid dereferencing
> uninitialized pointers.
> - Reorder pm_runtime_enable() and pm_runtime_forbid() calls to prevent
> unnecessary suspend/resume cycles during driver probe.
> - Use devres-managed PM runtime helpers for proper cleanup.
>
> Changes in V3:
> Rebase on next and remove 2/6 (obsolete)
>
> Changes in V2:
> Split patches 2/4 and 3/4 so that the null‑pointer dereference fix and
> the runtime‑PM enable/forbid reordering are logically separated.
Evicting this set from Patchwork, which does not even apply on linux-phy/next.
Loic, please follow up with an updated set if you want a different form
of this to be merged.
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH phy-next 0/5] Lynx 28G SerDes: 25GbE support
From: Vladimir Oltean @ 2026-05-11 15:00 UTC (permalink / raw)
To: linux-phy
Cc: netdev, Ioana Ciornei, Vinod Koul, Neil Armstrong, Josua Mayer,
linux-kernel
This is the remainder of "Lynx 28G improvements part 2", previously
submitted here:
https://lore.kernel.org/linux-phy/176850672122.1082429.444623229961712368.robh@kernel.org/
but split up into smaller portions (merged separately):
- https://lore.kernel.org/linux-phy/20260226182853.1103616-1-vladimir.oltean@nxp.com/
- https://lore.kernel.org/linux-phy/20260321011451.1557091-1-vladimir.oltean@nxp.com/
What remains is the highlight feature (patch 5/5): support for dynamic
protocol changes to/from 25GBase-R, required by SFP28 modules. These are
used with the NXP LX2160A and the (Ethernet) dpaa2-mac consumer.
Patches 1-4 handle the following situation: with current device trees,
the driver will think 25GBase-R will work on a lane, but it may work or
may not. This is because not all lanes support this protocol. So we
modify the SerDes compatible strings to identify them, and we use a
driver-internal database to figure out on which lanes does each SerDes
instance support this protocol.
On current device trees, 25GbE is not supported.
Detailed change log in patches. Summary:
- reworded commit messages
- change match condition from dt-bindings change 2/5
- patch 3/5 is new (reject probing on devices with no OF node)
Ioana Ciornei (1):
phy: lynx-28g: add support for 25GBASER
Vladimir Oltean (4):
dt-bindings: phy: lynx-28g: add compatible strings per SerDes and
instantiation
dt-bindings: phy: lynx-28g: add constraint on LX2162A lane indices
phy: lynx-28g: require an OF node to probe
phy: lynx-28g: probe on per-SoC and per-instance compatible strings
.../devicetree/bindings/phy/fsl,lynx-28g.yaml | 48 +++-
drivers/phy/freescale/phy-fsl-lynx-28g.c | 221 +++++++++++++++++-
2 files changed, 254 insertions(+), 15 deletions(-)
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH phy-next 1/5] dt-bindings: phy: lynx-28g: add compatible strings per SerDes and instantiation
From: Vladimir Oltean @ 2026-05-11 15:00 UTC (permalink / raw)
To: linux-phy
Cc: netdev, Ioana Ciornei, Vinod Koul, Neil Armstrong, Josua Mayer,
linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
devicetree
In-Reply-To: <20260511150023.1903577-1-vladimir.oltean@nxp.com>
The 28G Lynx SerDes is instantiated 3 times in the NXP LX2160A SoC and
twice in the NXP LX2162A. All these instances share the same register
map, but the number of lanes and the protocols supported by each lane
differs in a way that isn't detectable by the programming model.
For example, not all lanes of all SerDes block instantiations support
25GbE.
So, using a generic "fsl,lynx-28g" compatible string and expecting all
SerDes instantiations to use it was a mistake that needs to be fixed.
The option chosen is to encode the SoC and the SerDes instance in the
compatible string, with everything else being the responsibility of the
driver to derive.
An alternative considered but dismissed was to add sufficient device
tree properties to describe the per-lane differences (implying:
supported protocols), as well as the different lane count.
Any decision made for the 28G Lynx should be consistent with the
decisions taken for the yet-to-be-introduced 10G Lynx SerDes (older
generation for older SoCs), because of how similar they are.
I've seen the alternative at play in this unmerged patch set for the 10G
Lynx here, and I didn't like it:
https://lore.kernel.org/linux-phy/20230413160607.4128315-3-sean.anderson@seco.com/
This is because there, we have a higher degree of variability in the
PCCR register values that need to be written per protocol. This makes
that approach more drawn-out and more prone to errors, compared to the
compatible strings which are more succinct and obviously correct.
NXP SoC reference manuals clearly document the SerDes instantiations as
not identical, and refers to them as such (SerDes 1, 2, etc).
The per-SoC compatible string is prepended to the "fsl,lynx-28g" generic
compatible, which is left there for compatibility with old kernels. An
exception would be LX2160A SerDes #3, which at the time of writing is
not described in fsl-lx2160a.dtsi. As "fsl,lx2160a-serdes3" implies it
is a 28G Lynx SerDes, it makes "fsl,lynx-28g" redundant so we don't
accept it.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
---
Cc: Rob Herring <robh@kernel.org>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: devicetree@vger.kernel.org
Previously submitted here:
https://lore.kernel.org/linux-phy/20260114152111.625350-3-vladimir.oltean@nxp.com/
Changes:
- Update commit message to remove leftover information stating that we
use the per-SoC compatible strings to impose constraints
- Add review tag from Rob Herring
---
.../devicetree/bindings/phy/fsl,lynx-28g.yaml | 33 +++++++++++++++++--
1 file changed, 30 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml b/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml
index e96229c2f8fb..8375bca810cc 100644
--- a/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml
+++ b/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml
@@ -9,10 +9,37 @@ title: Freescale Lynx 28G SerDes PHY
maintainers:
- Ioana Ciornei <ioana.ciornei@nxp.com>
+description:
+ The Lynx 28G is a multi-lane, multi-protocol SerDes (PCIe, SATA, Ethernet)
+ present in multiple instances on NXP LX2160A and LX2162A SoCs. All instances
+ share a common register map and programming model, however they differ in
+ supported protocols per lane in a way that is not detectable by said
+ programming model without prior knowledge. The distinction is made through
+ the compatible string.
+
properties:
compatible:
- enum:
- - fsl,lynx-28g
+ oneOf:
+ - const: fsl,lynx-28g
+ deprecated: true
+ description:
+ Legacy compatibility string for Lynx 28G SerDes. Any assumption
+ regarding whether a certain lane supports a certain protocol may
+ be incorrect. Deprecated except when used as a fallback. Use
+ device-specific strings instead.
+ - items:
+ - const: fsl,lx2160a-serdes1
+ - const: fsl,lynx-28g
+ - items:
+ - const: fsl,lx2160a-serdes2
+ - const: fsl,lynx-28g
+ - items:
+ - const: fsl,lx2162a-serdes1
+ - const: fsl,lynx-28g
+ - items:
+ - const: fsl,lx2162a-serdes2
+ - const: fsl,lynx-28g
+ - const: fsl,lx2160a-serdes3
reg:
maxItems: 1
@@ -60,7 +87,7 @@ examples:
#size-cells = <2>;
serdes@1ea0000 {
- compatible = "fsl,lynx-28g";
+ compatible = "fsl,lx2160a-serdes1", "fsl,lynx-28g";
reg = <0x0 0x1ea0000 0x0 0x1e30>;
#address-cells = <1>;
#size-cells = <0>;
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH phy-next 2/5] dt-bindings: phy: lynx-28g: add constraint on LX2162A lane indices
From: Vladimir Oltean @ 2026-05-11 15:00 UTC (permalink / raw)
To: linux-phy
Cc: netdev, Ioana Ciornei, Vinod Koul, Neil Armstrong, Josua Mayer,
linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
devicetree
In-Reply-To: <20260511150023.1903577-1-vladimir.oltean@nxp.com>
The SerDes 1 of LX2162A has fewer lanes than all other instances, and
strangely, their indices are not 0-3, but 4-7.
This is a best-effort constraint, since we can only impose it when using
per-SoC compatible string and per-lane OF nodes.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Cc: Rob Herring <robh@kernel.org>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: devicetree@vger.kernel.org
Patch previously submitted at:
https://lore.kernel.org/linux-phy/20260114152111.625350-4-vladimir.oltean@nxp.com/
Changes:
- remove redundant patternProperties: "^phy@[0-7]$": true from the
match; having it makes no difference
- clarify that the constraint is best effort
---
.../devicetree/bindings/phy/fsl,lynx-28g.yaml | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml b/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml
index 8375bca810cc..d73591315d4b 100644
--- a/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml
+++ b/Documentation/devicetree/bindings/phy/fsl,lynx-28g.yaml
@@ -78,6 +78,21 @@ required:
- reg
- "#phy-cells"
+allOf:
+ # LX2162A SerDes 1 has fewer lanes than the others
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: fsl,lx2162a-serdes1
+ then:
+ patternProperties:
+ "^phy@[0-7]$":
+ properties:
+ reg:
+ minimum: 4
+ maximum: 7
+
additionalProperties: false
examples:
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH phy-next 3/5] phy: lynx-28g: require an OF node to probe
From: Vladimir Oltean @ 2026-05-11 15:00 UTC (permalink / raw)
To: linux-phy
Cc: netdev, Ioana Ciornei, Vinod Koul, Neil Armstrong, Josua Mayer,
linux-kernel
In-Reply-To: <20260511150023.1903577-1-vladimir.oltean@nxp.com>
The driver will gain support for variants in an upcoming change, and
will use of_device_get_match_data() to deduce the running variant from
the compatible string.
Currently, the driver expects the schema at phy/fsl,lynx-28g.yaml, and
OF-based consumers, but doesn't enforce this. And it is possible for
user space to force-bind the driver to a device without OF node using
the driver_override sysfs.
To avoid future surprise crashes for an unsupported configuration,
explicitly test for the presence of an OF node and fail probing if
found.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Change is new.
---
drivers/phy/freescale/phy-fsl-lynx-28g.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c
index 4ec3fb7a0d69..6d0c395d20e5 100644
--- a/drivers/phy/freescale/phy-fsl-lynx-28g.c
+++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c
@@ -1286,6 +1286,12 @@ static int lynx_28g_probe(struct platform_device *pdev)
struct device_node *dn;
int err;
+ dn = dev_of_node(dev);
+ if (!dn) {
+ dev_err(dev, "Device requires an OF node\n");
+ return -EINVAL;
+ }
+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -1301,7 +1307,6 @@ static int lynx_28g_probe(struct platform_device *pdev)
lynx_28g_pll_read_configuration(priv);
- dn = dev_of_node(dev);
if (of_get_child_count(dn)) {
struct device_node *child;
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH phy-next 4/5] phy: lynx-28g: probe on per-SoC and per-instance compatible strings
From: Vladimir Oltean @ 2026-05-11 15:00 UTC (permalink / raw)
To: linux-phy
Cc: netdev, Ioana Ciornei, Vinod Koul, Neil Armstrong, Josua Mayer,
linux-kernel, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
devicetree
In-Reply-To: <20260511150023.1903577-1-vladimir.oltean@nxp.com>
Add driver support for probing on the new, per-instance and per-SoC
bindings, which provide the main benefit that they allow rejecting
unsupported protocols per lane (10GbE on SerDes 2 lanes 0-5), but they
also allow avoiding the creation of PHYs for lanes that don't exist
(LX2162A lanes 0-3).
For old device trees with just "fsl,lynx-28g", the only things that
change are:
- a probe time warning/encouragement to update the device tree. This is
warranted by the fact that using "fsl,lynx-28g" may already provide
incorrect behaviour (undetected absent 10GbE support on LX2160A
SerDes 2 lanes 0-5). But we retain bug compatibility nonetheless.
- the feature set is frozen in time (e.g. no 25GbE). Since we cannot
guarantee that this protocol will work on a lane, just err on the safe
side and don't offer it (and require a device tree update to get it).
In terms of code, the lynx_28g_supports_lane_mode() function prototype
changes. It was a SerDes-global function and now becomes per lane, to
reflect the specific capabilities each instance may have. The
implementation goes through priv->info->lane_supports_mode().
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Cc: Rob Herring <robh@kernel.org>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: devicetree@vger.kernel.org
Change previously submitted at:
https://lore.kernel.org/linux-phy/20260114152111.625350-5-vladimir.oltean@nxp.com/
Changes:
- reword commit message
---
drivers/phy/freescale/phy-fsl-lynx-28g.c | 126 +++++++++++++++++++++--
1 file changed, 116 insertions(+), 10 deletions(-)
diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c
index 6d0c395d20e5..5eddc2723e78 100644
--- a/drivers/phy/freescale/phy-fsl-lynx-28g.c
+++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c
@@ -446,9 +446,15 @@ struct lynx_28g_lane {
enum lynx_lane_mode mode;
};
+struct lynx_info {
+ bool (*lane_supports_mode)(int lane, enum lynx_lane_mode mode);
+ int first_lane;
+};
+
struct lynx_28g_priv {
void __iomem *base;
struct device *dev;
+ const struct lynx_info *info;
/* Serialize concurrent access to registers shared between lanes,
* like PCCn
*/
@@ -513,11 +519,18 @@ static enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf)
}
}
-static bool lynx_28g_supports_lane_mode(struct lynx_28g_priv *priv,
+/* A lane mode is supported if we have a PLL that can provide its required
+ * clock net, and if there is a protocol converter for that mode on that lane.
+ */
+static bool lynx_28g_supports_lane_mode(struct lynx_28g_lane *lane,
enum lynx_lane_mode mode)
{
+ struct lynx_28g_priv *priv = lane->priv;
int i;
+ if (!priv->info->lane_supports_mode(lane->id, mode))
+ return false;
+
for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
if (PLLnRSTCTL_DIS(priv->pll[i].rstctl))
continue;
@@ -783,6 +796,87 @@ static int lynx_28g_get_pcvt_offset(int lane, enum lynx_lane_mode lane_mode)
}
}
+static bool lx2160a_serdes1_lane_supports_mode(int lane,
+ enum lynx_lane_mode mode)
+{
+ return true;
+}
+
+static bool lx2160a_serdes2_lane_supports_mode(int lane,
+ enum lynx_lane_mode mode)
+{
+ switch (mode) {
+ case LANE_MODE_1000BASEX_SGMII:
+ return true;
+ case LANE_MODE_USXGMII:
+ case LANE_MODE_10GBASER:
+ return lane == 6 || lane == 7;
+ default:
+ return false;
+ }
+}
+
+static bool lx2160a_serdes3_lane_supports_mode(int lane,
+ enum lynx_lane_mode mode)
+{
+ /*
+ * Non-networking SerDes, and this driver supports only
+ * networking protocols
+ */
+ return false;
+}
+
+static bool lx2162a_serdes1_lane_supports_mode(int lane,
+ enum lynx_lane_mode mode)
+{
+ return true;
+}
+
+static bool lx2162a_serdes2_lane_supports_mode(int lane,
+ enum lynx_lane_mode mode)
+{
+ return lx2160a_serdes2_lane_supports_mode(lane, mode);
+}
+
+/* Feature set is not expected to grow for the deprecated compatible string */
+static bool lynx_28g_compat_lane_supports_mode(int lane,
+ enum lynx_lane_mode mode)
+{
+ switch (mode) {
+ case LANE_MODE_1000BASEX_SGMII:
+ case LANE_MODE_USXGMII:
+ case LANE_MODE_10GBASER:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct lynx_info lynx_info_compat = {
+ .lane_supports_mode = lynx_28g_compat_lane_supports_mode,
+};
+
+static const struct lynx_info lynx_info_lx2160a_serdes1 = {
+ .lane_supports_mode = lx2160a_serdes1_lane_supports_mode,
+};
+
+static const struct lynx_info lynx_info_lx2160a_serdes2 = {
+ .lane_supports_mode = lx2160a_serdes2_lane_supports_mode,
+};
+
+static const struct lynx_info lynx_info_lx2160a_serdes3 = {
+ .lane_supports_mode = lx2160a_serdes3_lane_supports_mode,
+};
+
+static const struct lynx_info lynx_info_lx2162a_serdes1 = {
+ .lane_supports_mode = lx2162a_serdes1_lane_supports_mode,
+ .first_lane = 4,
+};
+
+static const struct lynx_info lynx_info_lx2162a_serdes2 = {
+ .lane_supports_mode = lx2162a_serdes2_lane_supports_mode,
+};
+
static int lynx_pccr_read(struct lynx_28g_lane *lane, enum lynx_lane_mode mode,
u32 *val)
{
@@ -1035,7 +1129,6 @@ static int lynx_28g_lane_enable_pcvt(struct lynx_28g_lane *lane,
static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode)
{
struct lynx_28g_lane *lane = phy_get_drvdata(phy);
- struct lynx_28g_priv *priv = lane->priv;
int powered_up = lane->powered_up;
enum lynx_lane_mode lane_mode;
int err = 0;
@@ -1047,7 +1140,7 @@ static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode)
return -EOPNOTSUPP;
lane_mode = phy_interface_to_lane_mode(submode);
- if (!lynx_28g_supports_lane_mode(priv, lane_mode))
+ if (!lynx_28g_supports_lane_mode(lane, lane_mode))
return -EOPNOTSUPP;
if (lane_mode == lane->mode)
@@ -1083,14 +1176,13 @@ static int lynx_28g_validate(struct phy *phy, enum phy_mode mode, int submode,
union phy_configure_opts *opts __always_unused)
{
struct lynx_28g_lane *lane = phy_get_drvdata(phy);
- struct lynx_28g_priv *priv = lane->priv;
enum lynx_lane_mode lane_mode;
if (mode != PHY_MODE_ETHERNET)
return -EOPNOTSUPP;
lane_mode = phy_interface_to_lane_mode(submode);
- if (!lynx_28g_supports_lane_mode(priv, lane_mode))
+ if (!lynx_28g_supports_lane_mode(lane, lane_mode))
return -EOPNOTSUPP;
return 0;
@@ -1183,7 +1275,7 @@ static void lynx_28g_cdr_lock_check(struct work_struct *work)
u32 rrstctl;
int err, i;
- for (i = 0; i < LYNX_28G_NUM_LANE; i++) {
+ for (i = priv->info->first_lane; i < LYNX_28G_NUM_LANE; i++) {
lane = &priv->lane[i];
if (!lane->phy)
continue;
@@ -1253,7 +1345,8 @@ static struct phy *lynx_28g_xlate(struct device *dev,
idx = args->args[0];
- if (WARN_ON(idx >= LYNX_28G_NUM_LANE))
+ if (WARN_ON(idx >= LYNX_28G_NUM_LANE ||
+ idx < priv->info->first_lane))
return ERR_PTR(-EINVAL);
return priv->lane[idx].phy;
@@ -1297,10 +1390,18 @@ static int lynx_28g_probe(struct platform_device *pdev)
return -ENOMEM;
priv->dev = dev;
+ priv->info = of_device_get_match_data(dev);
dev_set_drvdata(dev, priv);
spin_lock_init(&priv->pcc_lock);
INIT_DELAYED_WORK(&priv->cdr_check, lynx_28g_cdr_lock_check);
+ /*
+ * If we get here it means we probed on a device tree where
+ * "fsl,lynx-28g" wasn't the fallback, but the sole compatible string.
+ */
+ if (priv->info == &lynx_info_compat)
+ dev_warn(dev, "Please update device tree to use per-device compatible strings\n");
+
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
@@ -1323,7 +1424,7 @@ static int lynx_28g_probe(struct platform_device *pdev)
return -EINVAL;
}
- if (reg >= LYNX_28G_NUM_LANE) {
+ if (reg < priv->info->first_lane || reg >= LYNX_28G_NUM_LANE) {
dev_err(dev, "\"reg\" property out of range for %pOF\n", child);
of_node_put(child);
return -EINVAL;
@@ -1336,7 +1437,7 @@ static int lynx_28g_probe(struct platform_device *pdev)
}
}
} else {
- for (int i = 0; i < LYNX_28G_NUM_LANE; i++) {
+ for (int i = priv->info->first_lane; i < LYNX_28G_NUM_LANE; i++) {
err = lynx_28g_probe_lane(priv, i, NULL);
if (err)
return err;
@@ -1362,7 +1463,12 @@ static void lynx_28g_remove(struct platform_device *pdev)
}
static const struct of_device_id lynx_28g_of_match_table[] = {
- { .compatible = "fsl,lynx-28g" },
+ { .compatible = "fsl,lx2160a-serdes1", .data = &lynx_info_lx2160a_serdes1 },
+ { .compatible = "fsl,lx2160a-serdes2", .data = &lynx_info_lx2160a_serdes2 },
+ { .compatible = "fsl,lx2160a-serdes3", .data = &lynx_info_lx2160a_serdes3 },
+ { .compatible = "fsl,lx2162a-serdes1", .data = &lynx_info_lx2162a_serdes1 },
+ { .compatible = "fsl,lx2162a-serdes2", .data = &lynx_info_lx2162a_serdes2 },
+ { .compatible = "fsl,lynx-28g", .data = &lynx_info_compat }, /* fallback, keep last */
{ },
};
MODULE_DEVICE_TABLE(of, lynx_28g_of_match_table);
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH phy-next 5/5] phy: lynx-28g: add support for 25GBASER
From: Vladimir Oltean @ 2026-05-11 15:00 UTC (permalink / raw)
To: linux-phy
Cc: netdev, Ioana Ciornei, Vinod Koul, Neil Armstrong, Josua Mayer,
linux-kernel
In-Reply-To: <20260511150023.1903577-1-vladimir.oltean@nxp.com>
From: Ioana Ciornei <ioana.ciornei@nxp.com>
Add support for 25GBASE-R in the Lynx 28G SerDes PHY driver. This will
be used by the dpaa2-mac consumer on LX2160A with:
- phy_validate(phy, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_25GBASER) to
detect support.
- phy_set_mode_ext(phy, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_25GBASER)
to reconfigure the lane for this protocol.
The intended use case for dynamic protocol switching to 25GBase-R is
with SFP28 modules, and protocol switching is triggered by the SFP
module insertion. There also exists a 25GBase-KR use case, where the
protocol switching is covered by IEEE 802.3 clause 73 auto-negotiation.
However, that is not handled here; it merely needs the support added
here as basic ground work.
The lane frequency for 25GbE is sourced from a clock net frequency of
12.890625 GHz, as produced by PLLF or PLLS, further multiplied by the
lane by 2. The clock net frequencies produced by the PLLs are treated as
read-only by the driver, so the absence of a PLL provisioned for the
right clock net frequency implies absence of 25GbE support, even though
a lane might have the appropriate protocol converter for it.
In terms of implementation, the change consists of:
- determining at probe time if any PLL was preconfigured for the
required clock net frequency for 25GbE
- adding the default lane parameters for reconfiguring a lane to 25GbE
irrespective of the original protocol
- allowing this operating mode only on supported lanes, i.e. all lanes
of LX2162A SerDes #1, and LX2160A SerDes lanes 0-1, 4-7.
Signed-off-by: Ioana Ciornei <ioana.ciornei@nxp.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Change previously submitted at:
https://lore.kernel.org/linux-phy/20260114152111.625350-6-vladimir.oltean@nxp.com/
Changes:
- reword commit message
---
drivers/phy/freescale/phy-fsl-lynx-28g.c | 90 +++++++++++++++++++++++-
1 file changed, 88 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c
index 5eddc2723e78..92bfc5f65e0b 100644
--- a/drivers/phy/freescale/phy-fsl-lynx-28g.c
+++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c
@@ -57,6 +57,7 @@
#define PLLnCR1_FRATE_5G_10GVCO 0x0
#define PLLnCR1_FRATE_5G_25GVCO 0x10
#define PLLnCR1_FRATE_10G_20GVCO 0x6
+#define PLLnCR1_FRATE_12G_25GVCO 0x16
/* Per SerDes lane registers */
/* Lane a General Control Register */
@@ -64,9 +65,11 @@
#define LNaGCR0_PROTO_SEL GENMASK(7, 3)
#define LNaGCR0_PROTO_SEL_SGMII 0x1
#define LNaGCR0_PROTO_SEL_XFI 0xa
+#define LNaGCR0_PROTO_SEL_25G 0x1a
#define LNaGCR0_IF_WIDTH GENMASK(2, 0)
#define LNaGCR0_IF_WIDTH_10_BIT 0x0
#define LNaGCR0_IF_WIDTH_20_BIT 0x2
+#define LNaGCR0_IF_WIDTH_40_BIT 0x4
/* Lane a Tx Reset Control Register */
#define LNaTRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x20)
@@ -85,6 +88,7 @@
#define LNaTGCR0_N_RATE_FULL 0x0
#define LNaTGCR0_N_RATE_HALF 0x1
#define LNaTGCR0_N_RATE_QUARTER 0x2
+#define LNaTGCR0_N_RATE_DOUBLE 0x3
#define LNaTECR0(lane) (0x800 + (lane) * 0x100 + 0x30)
#define LNaTECR0_EQ_TYPE GENMASK(30, 28)
@@ -116,6 +120,7 @@
#define LNaRGCR0_N_RATE_FULL 0x0
#define LNaRGCR0_N_RATE_HALF 0x1
#define LNaRGCR0_N_RATE_QUARTER 0x2
+#define LNaRGCR0_N_RATE_DOUBLE 0x3
#define LNaRGCR1(lane) (0x800 + (lane) * 0x100 + 0x48)
#define LNaRGCR1_RX_ORD_ELECIDLE BIT(31)
@@ -282,6 +287,7 @@ enum lynx_lane_mode {
LANE_MODE_1000BASEX_SGMII,
LANE_MODE_10GBASER,
LANE_MODE_USXGMII,
+ LANE_MODE_25GBASER,
LANE_MODE_MAX,
};
@@ -420,6 +426,41 @@ static const struct lynx_28g_proto_conf lynx_28g_proto_conf[LANE_MODE_MAX] = {
.ttlcr0 = LNaTTLCR0_TTL_SLO_PM_BYP |
LNaTTLCR0_DATA_IN_SSC,
},
+ [LANE_MODE_25GBASER] = {
+ .proto_sel = LNaGCR0_PROTO_SEL_25G,
+ .if_width = LNaGCR0_IF_WIDTH_40_BIT,
+ .teq_type = EQ_TYPE_3TAP,
+ .sgn_preq = 1,
+ .ratio_preq = 2,
+ .sgn_post1q = 1,
+ .ratio_post1q = 7,
+ .amp_red = 0,
+ .adpt_eq = 48,
+ .enter_idle_flt_sel = 0,
+ .exit_idle_flt_sel = 0,
+ .data_lost_th_sel = 0,
+ .gk2ovd = 0,
+ .gk3ovd = 0,
+ .gk4ovd = 5,
+ .gk2ovd_en = 0,
+ .gk3ovd_en = 0,
+ .gk4ovd_en = 1,
+ .eq_offset_ovd = 0x1f,
+ .eq_offset_ovd_en = 0,
+ .eq_offset_rng_dbl = 1,
+ .eq_blw_sel = 1,
+ .eq_boost = 2,
+ .spare_in = 3,
+ .smp_autoz_d1r = 2,
+ .smp_autoz_eg1r = 2,
+ .rccr0 = LNaRCCR0_CAL_EN |
+ LNaRCCR0_CAL_DC3_DIS |
+ LNaRCCR0_CAL_DC2_DIS |
+ LNaRCCR0_CAL_DC1_DIS |
+ LNaRCCR0_CAL_DC0_DIS,
+ .ttlcr0 = LNaTTLCR0_DATA_IN_SSC |
+ FIELD_PREP_CONST(LNaTTLCR0_CDR_MIN_SMP_ON, 1),
+ },
};
struct lynx_pccr {
@@ -499,6 +540,8 @@ static const char *lynx_lane_mode_str(enum lynx_lane_mode lane_mode)
return "10GBase-R";
case LANE_MODE_USXGMII:
return "USXGMII";
+ case LANE_MODE_25GBASER:
+ return "25GBase-R";
default:
return "unknown";
}
@@ -514,6 +557,8 @@ static enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf)
return LANE_MODE_10GBASER;
case PHY_INTERFACE_MODE_USXGMII:
return LANE_MODE_USXGMII;
+ case PHY_INTERFACE_MODE_25GBASER:
+ return LANE_MODE_25GBASER;
default:
return LANE_MODE_UNKNOWN;
}
@@ -601,6 +646,20 @@ static void lynx_28g_lane_set_nrate(struct lynx_28g_lane *lane,
break;
}
break;
+ case PLLnCR1_FRATE_12G_25GVCO:
+ switch (lane_mode) {
+ case LANE_MODE_25GBASER:
+ lynx_28g_lane_rmw(lane, LNaTGCR0,
+ FIELD_PREP(LNaTGCR0_N_RATE, LNaTGCR0_N_RATE_DOUBLE),
+ LNaTGCR0_N_RATE);
+ lynx_28g_lane_rmw(lane, LNaRGCR0,
+ FIELD_PREP(LNaRGCR0_N_RATE, LNaRGCR0_N_RATE_DOUBLE),
+ LNaRGCR0_N_RATE);
+ break;
+ default:
+ break;
+ }
+ break;
default:
break;
}
@@ -761,6 +820,11 @@ static int lynx_28g_power_on(struct phy *phy)
return 0;
}
+static int lynx_28g_e25g_pcvt(int lane)
+{
+ return 7 - lane;
+}
+
static int lynx_28g_get_pccr(enum lynx_lane_mode lane_mode, int lane,
struct lynx_pccr *pccr)
{
@@ -776,6 +840,11 @@ static int lynx_28g_get_pccr(enum lynx_lane_mode lane_mode, int lane,
pccr->width = 4;
pccr->shift = SXGMII_CFG(lane);
break;
+ case LANE_MODE_25GBASER:
+ pccr->offset = PCCD;
+ pccr->width = 4;
+ pccr->shift = E25G_CFG(lynx_28g_e25g_pcvt(lane));
+ break;
default:
return -EOPNOTSUPP;
}
@@ -791,6 +860,8 @@ static int lynx_28g_get_pcvt_offset(int lane, enum lynx_lane_mode lane_mode)
case LANE_MODE_USXGMII:
case LANE_MODE_10GBASER:
return SXGMIIaCR0(lane);
+ case LANE_MODE_25GBASER:
+ return E25GaCR0(lynx_28g_e25g_pcvt(lane));
default:
return -EOPNOTSUPP;
}
@@ -799,7 +870,12 @@ static int lynx_28g_get_pcvt_offset(int lane, enum lynx_lane_mode lane_mode)
static bool lx2160a_serdes1_lane_supports_mode(int lane,
enum lynx_lane_mode mode)
{
- return true;
+ switch (mode) {
+ case LANE_MODE_25GBASER:
+ return lane != 2 && lane != 3;
+ default:
+ return true;
+ }
}
static bool lx2160a_serdes2_lane_supports_mode(int lane,
@@ -1115,6 +1191,9 @@ static int lynx_28g_lane_enable_pcvt(struct lynx_28g_lane *lane,
case LANE_MODE_USXGMII:
val |= PCCC_SXGMIIn_CFG;
break;
+ case LANE_MODE_25GBASER:
+ val |= PCCD_E25Gn_CFG;
+ break;
default:
break;
}
@@ -1259,8 +1338,12 @@ static void lynx_28g_pll_read_configuration(struct lynx_28g_priv *priv)
__set_bit(LANE_MODE_10GBASER, pll->supported);
__set_bit(LANE_MODE_USXGMII, pll->supported);
break;
+ case PLLnCR1_FRATE_12G_25GVCO:
+ /* 12.890625GHz clock net */
+ __set_bit(LANE_MODE_25GBASER, pll->supported);
+ break;
default:
- /* 6GHz, 12.890625GHz, 8GHz */
+ /* 6GHz, 8GHz */
break;
}
}
@@ -1327,6 +1410,9 @@ static void lynx_28g_lane_read_configuration(struct lynx_28g_lane *lane)
else
lane->mode = LANE_MODE_USXGMII;
break;
+ case LNaPSS_TYPE_25G:
+ lane->mode = LANE_MODE_25GBASER;
+ break;
default:
lane->mode = LANE_MODE_UNKNOWN;
}
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH 05/12] phy: add a driver for T-Head TH1520 USB PHY
From: Vinod Koul @ 2026-05-11 16:16 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Drew Fustini, Guo Ren, Fu Wei, Michael Turquette, Stephen Boyd,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Neil Armstrong,
Greg Kroah-Hartman, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti, Jisheng Zhang, Philipp Zabel, linux-riscv,
linux-clk, devicetree, linux-kernel, linux-gpio, linux-phy,
linux-usb, Han Gao, Yao Zi
In-Reply-To: <244b01b015ffd8e859ff30101d7743ff1c0fd899.camel@icenowy.me>
On 10-05-26, 17:17, Icenowy Zheng wrote:
> 在 2026-05-10日的 13:14 +0530,Vinod Koul写道:
> > On 07-05-26, 16:17, Icenowy Zheng wrote:
> > Why do need these test registers, they seem unused?
>
> The register list is copied from the SoC manual, although most of them
> are unused.
>
> Should I remove all unused registers, as the manual is publicly
> available?
yes please
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v3 0/4] phy: phy-can-transceiver: Ad-hoc cleanups and refactoring
From: Vinod Koul @ 2026-05-11 16:17 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Peng Fan, linux-can, linux-phy, linux-kernel, Marc Kleine-Budde,
Vincent Mailhol, Neil Armstrong, Josua Mayer, Ulf Hansson
In-Reply-To: <agB_aj_2hzF3ON2h@ashevche-desk.local>
On 10-05-26, 15:51, Andy Shevchenko wrote:
> On Sun, May 10, 2026 at 04:21:38PM +0530, Vinod Koul wrote:
> > On 04-05-26, 08:58, Andy Shevchenko wrote:
> > > The driver does two things that need to be addressed:
> > > - includes subject to remove gpio.h
> > > - checks for error code from device property APIs when it can be done in
> > > a robust way
> > >
> > > This series addresses the above and adds a couple of additional refactoring.
> >
> > Sashiko flagged some issues, some of them not introduced by this, can
> > you please check this:
> >
> > https://sashiko.dev/#/patchset/20260504070054.29508-1-andriy.shevchenko%40linux.intel.com
>
> "Could this result in a null pointer dereference if device_get_match_data()
> returns null?"
> Yes, it sounds legit but not introduced here.
>
> "In the original code, the warning was suppressed when the property was missing
> because err evaluated to -EINVAL. Now, if the property is absent, max_bitrate
> is explicitly set to 0 in the else block, which then unconditionally triggers
> this warning."
> True, but I don't know which is better here, I consider that it's
> good to inform user about default being used as a fallback. I can change
> this back to the original logic. What do you prefer?
>
> The third one is the repetition of the first one (see above).
>
> TL;DR: The only one legitimated question is about a (new old) warning.
Yeah would be great if we could fix these as well please
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH 0/6] phy: rockchip: samsung-hdptx: Clock fixes and API transition cleanups
From: Vinod Koul @ 2026-05-11 16:18 UTC (permalink / raw)
To: Cristian Ciocaltea
Cc: Neil Armstrong, Heiko Stuebner, Algea Cao, Dmitry Baryshkov,
kernel, linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel
In-Reply-To: <c76d7020-2b3b-4f30-ab40-a8442c953966@collabora.com>
On 10-05-26, 11:55, Cristian Ciocaltea wrote:
> Hi Vinod,
>
> On 5/10/26 10:36 AM, Vinod Koul wrote:
> > On 27-02-26, 22:48, Cristian Ciocaltea wrote:
> >> This series provides a set of bug fixes and cleanups for the Rockchip
> >> Samsung HDPTX PHY driver.
> >>
> >> The first part of the series (i.e. PATCH 1 & 2) addresses clock rate
> >> calculation and synchronization issues. Specifically, it fixes edge
> >> cases where the PHY PLL is pre-programmed by an external component (like
> >> a bootloader) or when changing the color depth (bpc) while keeping the
> >> modeline constant. Because the Common Clock Framework .set_rate()
> >> callback might not be invoked if the pixel clock remains unchanged, this
> >> previously led to out-of-sync states between CCF and the actual HDMI PHY
> >> configuration.
> >>
> >> The second part focuses on code cleanups and modernizing the register
> >> access. Now that dw_hdmi_qp driver has fully switched to using
> >> phy_configure(), we can drop the deprecated TMDS rate setup workarounds
> >> and the restrict_rate_change flag logic. Finally, it refactors the
> >> driver to consistently use standard bitfield macros.
> >
> > Sorry looks like I have missed to review this one.
> > Can you please rebase on phy/fixes and send...
>
> I've just verified and it applies cleanly on top of phy/fixes.
> Do you still need a resend?
Yes please, it didnt apply for me
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH phy-next 2/5] dt-bindings: phy: lynx-28g: add constraint on LX2162A lane indices
From: Conor Dooley @ 2026-05-11 16:30 UTC (permalink / raw)
To: Vladimir Oltean
Cc: linux-phy, netdev, Ioana Ciornei, Vinod Koul, Neil Armstrong,
Josua Mayer, linux-kernel, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, devicetree
In-Reply-To: <20260511150023.1903577-3-vladimir.oltean@nxp.com>
[-- Attachment #1.1: Type: text/plain, Size: 440 bytes --]
On Mon, May 11, 2026 at 06:00:20PM +0300, Vladimir Oltean wrote:
> The SerDes 1 of LX2162A has fewer lanes than all other instances, and
> strangely, their indices are not 0-3, but 4-7.
>
> This is a best-effort constraint, since we can only impose it when using
> per-SoC compatible string and per-lane OF nodes.
>
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
[-- Attachment #2: Type: text/plain, Size: 112 bytes --]
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH 2/3] phy: zynqmp: use read-modify-write for SERDES scrambler bypass
From: Radhey Shyam Pandey @ 2026-05-11 16:31 UTC (permalink / raw)
To: laurent.pinchart, vkoul, neil.armstrong, michal.simek
Cc: linux-kernel, linux-phy, linux-arm-kernel, git,
Nava kishore Manne, stable, Radhey Shyam Pandey
In-Reply-To: <20260511163135.2924642-1-radhey.shyam.pandey@amd.com>
From: Nava kishore Manne <nava.kishore.manne@amd.com>
xpsgtr_bypass_scrambler_8b10b() used xpsgtr_write_phy() which performs
a full register write, silently clearing any bits beyond the intended
bypass control fields.
Switch to xpsgtr_clr_set_phy() with clr=mask, set=mask to set only
the bypass bits while preserving the remaining bits in each register.
Fixes: 4a33bea00314 ("phy: zynqmp: Add PHY driver for the Xilinx ZynqMP Gigabit Transceiver")
Cc: stable@vger.kernel.org
Signed-off-by: Nava kishore Manne <nava.kishore.manne@amd.com>
Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
---
drivers/phy/xilinx/phy-zynqmp.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
index c037d7c13d48..6c56c4df8523 100644
--- a/drivers/phy/xilinx/phy-zynqmp.c
+++ b/drivers/phy/xilinx/phy-zynqmp.c
@@ -505,8 +505,12 @@ static void xpsgtr_lane_set_protocol(struct xpsgtr_phy *gtr_phy)
/* Bypass (de)scrambler and 8b/10b decoder and encoder. */
static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy)
{
- xpsgtr_write_phy(gtr_phy, L0_TM_DIG_6, L0_TM_DIS_DESCRAMBLE_DECODER);
- xpsgtr_write_phy(gtr_phy, L0_TX_DIG_61, L0_TM_DISABLE_SCRAMBLE_ENCODER);
+ xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6,
+ L0_TM_DIS_DESCRAMBLE_DECODER,
+ L0_TM_DIS_DESCRAMBLE_DECODER);
+ xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61,
+ L0_TM_DISABLE_SCRAMBLE_ENCODER,
+ L0_TM_DISABLE_SCRAMBLE_ENCODER);
}
/* DP-specific initialization. */
--
2.44.4
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH 3/3] phy: zynqmp: keep SERDES scrambler and 8b/10b enabled for USB
From: Radhey Shyam Pandey @ 2026-05-11 16:31 UTC (permalink / raw)
To: laurent.pinchart, vkoul, neil.armstrong, michal.simek
Cc: linux-kernel, linux-phy, linux-arm-kernel, git,
Nava kishore Manne, stable, Radhey Shyam Pandey
In-Reply-To: <20260511163135.2924642-1-radhey.shyam.pandey@amd.com>
From: Nava kishore Manne <nava.kishore.manne@amd.com>
USB Gen1 requires scrambling and 8b/10b encoding to be performed in the
physical layer. Do not bypass PHY-side scrambler or encoder/decoder for
USB operation, as mandated by the USB 3.x specification.
Scrambler and 8b/10b bypass remain restricted to SATA and SGMII
modes, where encoding is handled in the controller.
Fixes: 4a33bea00314 ("phy: zynqmp: Add PHY driver for the Xilinx ZynqMP Gigabit Transceiver")
Cc: stable@vger.kernel.org
Signed-off-by: Nava kishore Manne <nava.kishore.manne@amd.com>
Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
---
drivers/phy/xilinx/phy-zynqmp.c | 39 ++++++++++++++++++++++++---------
1 file changed, 29 insertions(+), 10 deletions(-)
diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
index 6c56c4df8523..087fe402e4e2 100644
--- a/drivers/phy/xilinx/phy-zynqmp.c
+++ b/drivers/phy/xilinx/phy-zynqmp.c
@@ -502,15 +502,30 @@ static void xpsgtr_lane_set_protocol(struct xpsgtr_phy *gtr_phy)
}
}
-/* Bypass (de)scrambler and 8b/10b decoder and encoder. */
-static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy)
+/**
+ * xpsgtr_bypass_scrambler_8b10b - Configure scrambler/encoder behavior
+ * @gtr_phy: pointer to lane context
+ * @bypass: true to enable scrambler/encoder bypass (SATA/SGMII),
+ * false to disable scrambler/encoder bypass (USB3)
+ *
+ * Uses RMW to preserve reserved and unrelated register fields.
+ */
+static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy,
+ bool bypass)
{
- xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6,
- L0_TM_DIS_DESCRAMBLE_DECODER,
- L0_TM_DIS_DESCRAMBLE_DECODER);
- xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61,
- L0_TM_DISABLE_SCRAMBLE_ENCODER,
- L0_TM_DISABLE_SCRAMBLE_ENCODER);
+ if (bypass) {
+ xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6,
+ L0_TM_DIS_DESCRAMBLE_DECODER,
+ L0_TM_DIS_DESCRAMBLE_DECODER);
+ xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61,
+ L0_TM_DISABLE_SCRAMBLE_ENCODER,
+ L0_TM_DISABLE_SCRAMBLE_ENCODER);
+ } else {
+ xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6,
+ L0_TM_DIS_DESCRAMBLE_DECODER, 0);
+ xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61,
+ L0_TM_DISABLE_SCRAMBLE_ENCODER, 0);
+ }
}
/* DP-specific initialization. */
@@ -531,7 +546,7 @@ static void xpsgtr_phy_init_sata(struct xpsgtr_phy *gtr_phy)
{
struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
- xpsgtr_bypass_scrambler_8b10b(gtr_phy);
+ xpsgtr_bypass_scrambler_8b10b(gtr_phy, true);
writel(gtr_phy->lane, gtr_dev->siou + SATA_CONTROL_OFFSET);
}
@@ -547,7 +562,7 @@ static void xpsgtr_phy_init_sgmii(struct xpsgtr_phy *gtr_phy)
xpsgtr_clr_set(gtr_dev, TX_PROT_BUS_WIDTH, mask, val);
xpsgtr_clr_set(gtr_dev, RX_PROT_BUS_WIDTH, mask, val);
- xpsgtr_bypass_scrambler_8b10b(gtr_phy);
+ xpsgtr_bypass_scrambler_8b10b(gtr_phy, true);
}
/* Configure TX de-emphasis and margining for DP. */
@@ -707,6 +722,10 @@ static int xpsgtr_phy_init(struct phy *phy)
case ICM_PROTOCOL_SGMII:
xpsgtr_phy_init_sgmii(gtr_phy);
break;
+
+ case ICM_PROTOCOL_USB:
+ xpsgtr_bypass_scrambler_8b10b(gtr_phy, false);
+ break;
}
out:
--
2.44.4
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH 0/3] phy: zynqmp: fix SERDES scrambler register handling and enable for USB
From: Radhey Shyam Pandey @ 2026-05-11 16:31 UTC (permalink / raw)
To: laurent.pinchart, vkoul, neil.armstrong, michal.simek
Cc: linux-kernel, linux-phy, linux-arm-kernel, git,
Radhey Shyam Pandey
This series fixes three related issues in the ZynqMP SERDES PHY
scrambler/encoder bypass path:
1. The L0_TM_DISABLE_SCRAMBLE_ENCODER mask incorrectly included bit 2
of L0_TX_DIG_61, which is a reserved read-only field. Correct the
mask to (BIT(3) | GENMASK(1, 0)).
2. xpsgtr_bypass_scrambler_8b10b() used xpsgtr_write_phy() which
performs a full register write, clobbering unrelated bits. Switch
to xpsgtr_clr_set_phy() with clr=mask, set=mask to preserve other
register fields.
3. USB Gen1 requires PHY-side scrambling and 8b/10b encoding as
mandated by the USB 3.x specification. The driver was incorrectly
bypassing these for USB, the same as SATA and SGMII where encoding
is handled in the controller.
Nava kishore Manne (3):
phy: zynqmp: fix L0_TM_DISABLE_SCRAMBLE_ENCODER mask
phy: zynqmp: use read-modify-write for SERDES scrambler bypass
phy: zynqmp: keep SERDES scrambler and 8b/10b enabled for USB
drivers/phy/xilinx/phy-zynqmp.c | 37 ++++++++++++++++++++++++++-------
1 file changed, 30 insertions(+), 7 deletions(-)
base-commit: 5d6919055dec134de3c40167a490f33c74c12581
--
2.44.4
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox