* [PATCH v5 0/7] phy: cpcap-usb: improve charger detection and export cable state
@ 2026-07-11 20:42 Ivaylo Dimitrov
2026-07-11 20:42 ` [PATCH v5 1/7] dt-bindings: phy: motorola,cpcap-usb: add chrg_det interrupt Ivaylo Dimitrov
` (6 more replies)
0 siblings, 7 replies; 9+ messages in thread
From: Ivaylo Dimitrov @ 2026-07-11 20:42 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
Roger Quadros, Tony Lindgren, Linus Walleij, Bartosz Golaszewski,
--cc=linux-phy, devicetree, linux-kernel, linux-omap, linux-gpio
Cc: Ivaylo Dimitrov
The Motorola CPCAP USB PHY contains the hardware state machine used for
USB cable detection. Besides distinguishing USB peripheral and host
connections, it can also detect dedicated charging ports (DCP).
This series extends the CPCAP USB PHY driver to detect DCP connections
and export the detected cable state through the Extcon framework. It also
makes the idle UART mode optional, allowing the PHY to remain in its
default USB detection configuration unless UART support is explicitly
requested.
The series updates the Device Tree binding for the optional charger
detection interrupt and the optional "safe" pinctrl state. Corresponding
mapphone Device Tree entries are added to describe the charger detection
interrupt and enable the safe pinctrl state.
The charger detection interrupt is not currently used by the driver.
However, it is added to the binding and Device Tree because it describes
a hardware capability of the CPCAP USB PHY.
The series has been tested on Motorola Droid 4 hardware.
Changes in v5:
- fix race in remove()
- fix commit message of PATCH 2
- re-schedule detect_work if DCP/SDP modes cannot be reliably detected
- fully configure PHY registers in DCP mode
- fix extcon intermediate cable states
Changes in v4:
- make charger detection interrupt optional
- make driver functional without charger detection interrupt
- add bindings and Device Tree for 'safe' pinctrl mode
- use 'safe' pinctrl state before switching PHY modes
Changes in v3:
- Drop "phy: cpcap-usb: Prevent line glitches from triggering sysrq"
- Fix DT schema interrupts order
- Fix boot cable detection
Changes in v2:
- add chrg_det interrupt instead of replacing se1
- Fix remove() race by cancelling detect_work before hardware teardown
- Restore SysRq state on all error paths
- Introduce CPCAP_UNKNOWN initial mode
- Snapshot enable_uart module parameter
Ivaylo Dimitrov (7):
dt-bindings: phy: motorola,cpcap-usb: add chrg_det interrupt
dt-bindings: phy: motorola,cpcap-usb-phy: add optional safe pinctrl
state
phy: cpcap-usb: fix IRQ teardown race
phy: cpcap-usb: add DCP detection and make UART idle mode optional
phy: cpcap-usb: add extcon support
ARM: dts: ti: cpcap-mapphone: add charger detection interrupt for
CPCAP USB PHY
ARM: dts: ti: cpcap-mapphone: add USB safe pinctrl state
.../bindings/phy/motorola,cpcap-usb-phy.yaml | 13 +-
.../dts/ti/omap/motorola-cpcap-mapphone.dtsi | 9 +-
.../dts/ti/omap/motorola-mapphone-common.dtsi | 19 +
drivers/phy/motorola/phy-cpcap-usb.c | 386 +++++++++++++++---
4 files changed, 357 insertions(+), 70 deletions(-)
--
2.39.5
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v5 1/7] dt-bindings: phy: motorola,cpcap-usb: add chrg_det interrupt
2026-07-11 20:42 [PATCH v5 0/7] phy: cpcap-usb: improve charger detection and export cable state Ivaylo Dimitrov
@ 2026-07-11 20:42 ` Ivaylo Dimitrov
2026-07-11 20:42 ` [PATCH v5 2/7] dt-bindings: phy: motorola,cpcap-usb-phy: add optional safe pinctrl state Ivaylo Dimitrov
` (5 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Ivaylo Dimitrov @ 2026-07-11 20:42 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
Roger Quadros, Tony Lindgren, Linus Walleij, Bartosz Golaszewski,
--cc=linux-phy, devicetree, linux-kernel, linux-omap, linux-gpio
Cc: Ivaylo Dimitrov
Document the optional CPCAP charger detection interrupt in the USB PHY
binding.
Update the example DTS to include the corresponding "chrg_det" interrupt
name.
Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
---
.../devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml b/Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml
index 0febd04a61f4..d6d52d21280c 100644
--- a/Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml
@@ -20,6 +20,7 @@ properties:
interrupts:
description: CPCAP PMIC interrupts used by the USB PHY
+ minItems: 9
items:
- description: id_ground interrupt
- description: id_float interrupt
@@ -30,9 +31,11 @@ properties:
- description: se1 interrupt
- description: dm interrupt
- description: dp interrupt
+ - description: charger detection interrupt
interrupt-names:
description: Interrupt names
+ minItems: 9
items:
- const: id_ground
- const: id_float
@@ -43,6 +46,7 @@ properties:
- const: se1
- const: dm
- const: dp
+ - const: chrg_det
io-channels:
description: IIO ADC channels used by the USB PHY
@@ -91,10 +95,10 @@ examples:
interrupts-extended = <
&cpcap 15 0 &cpcap 14 0 &cpcap 28 0 &cpcap 19 0
&cpcap 18 0 &cpcap 17 0 &cpcap 16 0 &cpcap 49 0
- &cpcap 48 1
+ &cpcap 48 1 &cpcap 13 0
>;
interrupt-names = "id_ground", "id_float", "se0conn", "vbusvld",
- "sessvld", "sessend", "se1", "dm", "dp";
+ "sessvld", "sessend", "se1", "dm", "dp", "chrg_det";
io-channels = <&cpcap_adc 2>, <&cpcap_adc 7>;
io-channel-names = "vbus", "id";
vusb-supply = <&vusb>;
--
2.39.5
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v5 2/7] dt-bindings: phy: motorola,cpcap-usb-phy: add optional safe pinctrl state
2026-07-11 20:42 [PATCH v5 0/7] phy: cpcap-usb: improve charger detection and export cable state Ivaylo Dimitrov
2026-07-11 20:42 ` [PATCH v5 1/7] dt-bindings: phy: motorola,cpcap-usb: add chrg_det interrupt Ivaylo Dimitrov
@ 2026-07-11 20:42 ` Ivaylo Dimitrov
2026-07-11 20:42 ` [PATCH v5 3/7] phy: cpcap-usb: fix IRQ teardown race Ivaylo Dimitrov
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Ivaylo Dimitrov @ 2026-07-11 20:42 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
Roger Quadros, Tony Lindgren, Linus Walleij, Bartosz Golaszewski,
--cc=linux-phy, devicetree, linux-kernel, linux-omap, linux-gpio
Cc: Ivaylo Dimitrov
Document the optional "safe" pinctrl state for the CPCAP USB PHY.
Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
---
.../devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml b/Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml
index d6d52d21280c..25c36138bf66 100644
--- a/Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml
@@ -62,11 +62,13 @@ properties:
vusb-supply: true
pinctrl-names:
+ minItems: 4
items:
- const: default
- const: ulpi
- const: utmi
- const: uart
+ - const: safe
mode-gpios:
description: Optional GPIOs for configuring alternate modes
@@ -106,6 +108,7 @@ examples:
pinctrl-1 = <&usb_ulpi_pins>;
pinctrl-2 = <&usb_utmi_pins>;
pinctrl-3 = <&uart3_pins>;
- pinctrl-names = "default", "ulpi", "utmi", "uart";
+ pinctrl-4 = <&usb_safe_pins>;
+ pinctrl-names = "default", "ulpi", "utmi", "uart", "safe";
mode-gpios = <&gpio2 28 GPIO_ACTIVE_HIGH>, <&gpio1 0 GPIO_ACTIVE_HIGH>;
};
--
2.39.5
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v5 3/7] phy: cpcap-usb: fix IRQ teardown race
2026-07-11 20:42 [PATCH v5 0/7] phy: cpcap-usb: improve charger detection and export cable state Ivaylo Dimitrov
2026-07-11 20:42 ` [PATCH v5 1/7] dt-bindings: phy: motorola,cpcap-usb: add chrg_det interrupt Ivaylo Dimitrov
2026-07-11 20:42 ` [PATCH v5 2/7] dt-bindings: phy: motorola,cpcap-usb-phy: add optional safe pinctrl state Ivaylo Dimitrov
@ 2026-07-11 20:42 ` Ivaylo Dimitrov
2026-07-11 20:42 ` [PATCH v5 4/7] phy: cpcap-usb: add DCP detection and make UART idle mode optional Ivaylo Dimitrov
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Ivaylo Dimitrov @ 2026-07-11 20:42 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
Roger Quadros, Tony Lindgren, Linus Walleij, Bartosz Golaszewski,
--cc=linux-phy, devicetree, linux-kernel, linux-omap, linux-gpio
Cc: Ivaylo Dimitrov
there is a race between IRQ handler and remove:
IRQ thread:
ddata->active == 1
remove():
ddata->active = 0
cancel_delayed_work_sync()
IRQ thread:
schedule_delayed_work()
The IRQ handler can therefore queue detect work after it has been canceled
during remove(). Free the IRQ handlers before canceling detect work to
prevent new work from being scheduled during teardown.
Reported by Sashiko
Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
---
drivers/phy/motorola/phy-cpcap-usb.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/motorola/phy-cpcap-usb.c b/drivers/phy/motorola/phy-cpcap-usb.c
index 7cb020dd3423..741145c89e5b 100644
--- a/drivers/phy/motorola/phy-cpcap-usb.c
+++ b/drivers/phy/motorola/phy-cpcap-usb.c
@@ -393,6 +393,19 @@ static int cpcap_usb_init_interrupts(struct platform_device *pdev,
return 0;
}
+static void cpcap_usb_fini_interrupts(struct platform_device *pdev,
+ struct cpcap_phy_ddata *ddata)
+{
+ int i, irq;
+
+ for (i = 0; i < ARRAY_SIZE(cpcap_phy_irqs); i++) {
+ irq = platform_get_irq_byname(pdev, cpcap_phy_irqs[i]);
+
+ if (irq >= 0)
+ devm_free_irq(ddata->dev, irq, ddata);
+ }
+}
+
/*
* Optional pins and modes. At least Motorola mapphone devices
* are using two GPIOs and dynamic pinctrl to multiplex PHY pins
@@ -691,6 +704,8 @@ static void cpcap_usb_phy_remove(struct platform_device *pdev)
int error;
atomic_set(&ddata->active, 0);
+ cpcap_usb_fini_interrupts(pdev, ddata);
+ cancel_delayed_work_sync(&ddata->detect_work);
error = cpcap_usb_set_uart_mode(ddata);
if (error)
dev_err(ddata->dev, "could not set UART mode\n");
@@ -698,7 +713,6 @@ static void cpcap_usb_phy_remove(struct platform_device *pdev)
cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_OFF);
usb_remove_phy(&ddata->phy);
- cancel_delayed_work_sync(&ddata->detect_work);
regulator_disable(ddata->vusb);
}
--
2.39.5
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v5 4/7] phy: cpcap-usb: add DCP detection and make UART idle mode optional
2026-07-11 20:42 [PATCH v5 0/7] phy: cpcap-usb: improve charger detection and export cable state Ivaylo Dimitrov
` (2 preceding siblings ...)
2026-07-11 20:42 ` [PATCH v5 3/7] phy: cpcap-usb: fix IRQ teardown race Ivaylo Dimitrov
@ 2026-07-11 20:42 ` Ivaylo Dimitrov
2026-07-11 20:54 ` sashiko-bot
2026-07-11 20:42 ` [PATCH v5 5/7] phy: cpcap-usb: add extcon support Ivaylo Dimitrov
` (2 subsequent siblings)
6 siblings, 1 reply; 9+ messages in thread
From: Ivaylo Dimitrov @ 2026-07-11 20:42 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
Roger Quadros, Tony Lindgren, Linus Walleij, Bartosz Golaszewski,
--cc=linux-phy, devicetree, linux-kernel, linux-omap, linux-gpio
Cc: Ivaylo Dimitrov
Handle DCP separately from USB host connections using CPCAP charger
detection status.
Make the existing idle UART mode optional via the "enable_uart" module
parameter. When disabled (default), the PHY remains in its USB/charger
detection configuration while idle.
Also initialize the PHY into the baseline configuration required for
reliable charger detection during probe.
Use the optional "safe" pinctrl state before switching between modes to
avoid glitches on USB or UART lines.
Note: Enabling UART idle mode increases idle power consumption (by 25mW
on droid4).
Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
# Conflicts:
# drivers/phy/motorola/phy-cpcap-usb.c
---
drivers/phy/motorola/phy-cpcap-usb.c | 301 +++++++++++++++++++++------
1 file changed, 238 insertions(+), 63 deletions(-)
diff --git a/drivers/phy/motorola/phy-cpcap-usb.c b/drivers/phy/motorola/phy-cpcap-usb.c
index 741145c89e5b..2d770ff19e93 100644
--- a/drivers/phy/motorola/phy-cpcap-usb.c
+++ b/drivers/phy/motorola/phy-cpcap-usb.c
@@ -110,6 +110,15 @@ enum cpcap_gpio_mode {
CPCAP_OTG_DM_DP,
};
+enum cpcap_mode {
+ CPCAP_UNKNOWN,
+ CPCAP_IDLE,
+ CPCAP_CHARGER,
+ CPCAP_USB,
+ CPCAP_USB_HOST,
+ CPCAP_DOCK,
+};
+
struct cpcap_phy_ddata {
struct regmap *reg;
struct device *dev;
@@ -119,15 +128,19 @@ struct cpcap_phy_ddata {
struct pinctrl_state *pins_ulpi;
struct pinctrl_state *pins_utmi;
struct pinctrl_state *pins_uart;
+ struct pinctrl_state *pins_safe;
struct gpio_desc *gpio[2];
struct iio_channel *vbus;
struct iio_channel *id;
struct regulator *vusb;
atomic_t active;
- unsigned int vbus_provider:1;
- unsigned int docked:1;
+ enum cpcap_mode mode;
};
+static bool cpcap_enable_uart;
+module_param_named(enable_uart, cpcap_enable_uart, bool, 0644);
+MODULE_PARM_DESC(enable_uart,
+ "Enable UART on the USB connector while idle (increases power consumption)");
static bool cpcap_usb_vbus_valid(struct cpcap_phy_ddata *ddata)
{
int error, value = 0;
@@ -196,8 +209,9 @@ static int cpcap_phy_get_ints_state(struct cpcap_phy_ddata *ddata,
return 0;
}
-static int cpcap_usb_set_uart_mode(struct cpcap_phy_ddata *ddata);
+static int cpcap_usb_set_idle_mode(struct cpcap_phy_ddata *ddata);
static int cpcap_usb_set_usb_mode(struct cpcap_phy_ddata *ddata);
+static int cpcap_usb_set_dcp_mode(struct cpcap_phy_ddata *ddata);
static void cpcap_usb_try_musb_mailbox(struct cpcap_phy_ddata *ddata,
enum musb_vbus_id_status status)
@@ -228,7 +242,7 @@ static void cpcap_usb_detect(struct work_struct *work)
vbus = cpcap_usb_vbus_valid(ddata);
/* We need to kick the VBUS as USB A-host */
- if (s.id_ground && ddata->vbus_provider) {
+ if (s.id_ground && ddata->mode == CPCAP_USB_HOST) {
dev_dbg(ddata->dev, "still in USB A-host mode, kicking VBUS\n");
cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND);
@@ -243,7 +257,7 @@ static void cpcap_usb_detect(struct work_struct *work)
return;
}
- if (vbus && s.id_ground && ddata->docked) {
+ if (vbus && s.id_ground && ddata->mode == CPCAP_DOCK) {
dev_dbg(ddata->dev, "still docked as A-host, signal ID down\n");
cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND);
@@ -252,15 +266,14 @@ static void cpcap_usb_detect(struct work_struct *work)
}
/* No VBUS needed with docks */
- if (vbus && s.id_ground && !ddata->vbus_provider) {
+ if (vbus && s.id_ground && ddata->mode != CPCAP_USB_HOST) {
dev_dbg(ddata->dev, "connected to a dock\n");
- ddata->docked = true;
-
error = cpcap_usb_set_usb_mode(ddata);
if (error)
goto out_err;
+ ddata->mode = CPCAP_DOCK;
cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND);
/*
@@ -274,17 +287,13 @@ static void cpcap_usb_detect(struct work_struct *work)
return;
}
- if (s.id_ground && !ddata->docked) {
+ if (s.id_ground && ddata->mode != CPCAP_DOCK) {
dev_dbg(ddata->dev, "id ground, USB host mode\n");
- ddata->vbus_provider = true;
-
error = cpcap_usb_set_usb_mode(ddata);
if (error)
goto out_err;
- cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND);
-
error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3,
CPCAP_BIT_VBUSSTBY_EN |
CPCAP_BIT_VBUSEN_SPI,
@@ -292,38 +301,74 @@ static void cpcap_usb_detect(struct work_struct *work)
if (error)
goto out_err;
+ ddata->mode = CPCAP_USB_HOST;
+ cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND);
+
return;
}
- error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3,
- CPCAP_BIT_VBUSSTBY_EN |
- CPCAP_BIT_VBUSEN_SPI, 0);
- if (error)
- goto out_err;
-
vbus = cpcap_usb_vbus_valid(ddata);
- /* Otherwise assume we're connected to a USB host */
- if (vbus) {
- dev_dbg(ddata->dev, "connected to USB host\n");
- error = cpcap_usb_set_usb_mode(ddata);
+ /* Check if we're connected to a USB host or charger */
+ if (vbus && ddata->mode != CPCAP_USB_HOST) {
+ enum cpcap_mode new_mode;
+
+ if (s.se1)
+ new_mode = CPCAP_CHARGER;
+ else if (!s.dm && !s.dp)
+ new_mode = CPCAP_USB;
+ else {
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC2,
+ CPCAP_BIT_USBXCVREN |
+ CPCAP_BIT_UARTMUX0 |
+ CPCAP_BIT_EMUMODE0, 0);
+ if (error)
+ goto out_err;
+
+ schedule_delayed_work(&ddata->detect_work,
+ msecs_to_jiffies(100));
+ return;
+ }
+
+ dev_dbg(ddata->dev, "connected to %s\n",
+ s.se1 ? "DCP" : "USB host");
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3,
+ CPCAP_BIT_VBUSSTBY_EN |
+ CPCAP_BIT_VBUSEN_SPI, 0);
if (error)
goto out_err;
- cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_VALID);
+
+ if (new_mode == CPCAP_CHARGER)
+ error = cpcap_usb_set_dcp_mode(ddata);
+ else
+ error = cpcap_usb_set_usb_mode(ddata);
+
+ if (error)
+ goto out_err;
+
+ ddata->mode = new_mode;
+ if (ddata->mode == CPCAP_USB)
+ cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_VALID);
return;
}
- ddata->vbus_provider = false;
- ddata->docked = false;
- cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_OFF);
+ if (ddata->mode == CPCAP_IDLE)
+ return;
- /* Default to debug UART mode */
- error = cpcap_usb_set_uart_mode(ddata);
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3,
+ CPCAP_BIT_VBUSSTBY_EN |
+ CPCAP_BIT_VBUSEN_SPI, 0);
+ if (error)
+ goto out_err;
+
+ error = cpcap_usb_set_idle_mode(ddata);
if (error)
goto out_err;
- dev_dbg(ddata->dev, "set UART mode\n");
+ ddata->mode = CPCAP_IDLE;
+ cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_OFF);
return;
@@ -424,43 +469,84 @@ static int cpcap_usb_gpio_set_mode(struct cpcap_phy_ddata *ddata,
return 0;
}
-static int cpcap_usb_set_uart_mode(struct cpcap_phy_ddata *ddata)
+static int cpcap_usb_set_safe_mode(struct cpcap_phy_ddata *ddata)
{
int error;
/* Disable lines to prevent glitches from waking up mdm6600 */
error = cpcap_usb_gpio_set_mode(ddata, CPCAP_UNKNOWN_DISABLED);
if (error)
- goto out_err;
+ return error;
- if (ddata->pins_uart) {
- error = pinctrl_select_state(ddata->pins, ddata->pins_uart);
- if (error)
- goto out_err;
+ if (ddata->pins_safe) {
+ error = pinctrl_select_state(ddata->pins, ddata->pins_safe);
+ if (error) {
+ dev_err(ddata->dev, "could not set safe mode: %i\n",
+ error);
+ }
}
+ return error;
+}
+
+static int cpcap_usb_set_idle_mode(struct cpcap_phy_ddata *ddata)
+{
+ int error;
+ unsigned int val;
+ bool enable_uart = cpcap_enable_uart;
+
+ error = cpcap_usb_set_safe_mode(ddata);
+ if (error)
+ return error;
+
error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC1,
+ CPCAP_BIT_DP150KPU |
+ CPCAP_BIT_DP1K5PU |
+ CPCAP_BIT_DM1K5PU |
+ CPCAP_BIT_DPPD |
+ CPCAP_BIT_DMPD |
CPCAP_BIT_VBUSPD,
+ CPCAP_BIT_DP150KPU |
CPCAP_BIT_VBUSPD);
if (error)
goto out_err;
- error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC2,
- 0xffff, CPCAP_BIT_UARTMUX0 |
- CPCAP_BIT_EMUMODE0);
+ val = CPCAP_BIT_USBSUSPEND;
+
+ if (enable_uart)
+ val |= (CPCAP_BIT_UARTMUX0 | CPCAP_BIT_EMUMODE0);
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC2, 0xffff, val);
if (error)
goto out_err;
- error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3, 0x7fff,
- CPCAP_BIT_IDPU_SPI);
+ val = CPCAP_BIT_VBUSSTBY_EN |
+ CPCAP_BIT_VBUSPD_SPI |
+ CPCAP_BIT_DMPD_SPI |
+ CPCAP_BIT_DPPD_SPI |
+ CPCAP_BIT_PU_SPI |
+ CPCAP_BIT_IDPU_SPI |
+ CPCAP_BIT_ULPI_SPI_SEL;
+
+ if (!enable_uart)
+ val |= CPCAP_BIT_SUSPEND_SPI;
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3, 0x7fff, val);
if (error)
goto out_err;
- /* Enable UART mode */
error = cpcap_usb_gpio_set_mode(ddata, CPCAP_DM_DP);
if (error)
goto out_err;
+ if (enable_uart && ddata->pins_uart) {
+ error = pinctrl_select_state(ddata->pins, ddata->pins_uart);
+ if (error)
+ goto out_err;
+ }
+
+ dev_dbg(ddata->dev, "set %s mode\n", enable_uart ? "UART" : "IDLE");
+
return 0;
out_err:
@@ -473,21 +559,10 @@ static int cpcap_usb_set_usb_mode(struct cpcap_phy_ddata *ddata)
{
int error;
- /* Disable lines to prevent glitches from waking up mdm6600 */
- error = cpcap_usb_gpio_set_mode(ddata, CPCAP_UNKNOWN_DISABLED);
+ error = cpcap_usb_set_safe_mode(ddata);
if (error)
return error;
- if (ddata->pins_utmi) {
- error = pinctrl_select_state(ddata->pins, ddata->pins_utmi);
- if (error) {
- dev_err(ddata->dev, "could not set usb mode: %i\n",
- error);
-
- return error;
- }
- }
-
error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC1,
CPCAP_BIT_VBUSPD, 0);
if (error)
@@ -503,11 +578,23 @@ static int cpcap_usb_set_usb_mode(struct cpcap_phy_ddata *ddata)
goto out_err;
error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC2,
- CPCAP_BIT_USBXCVREN,
+ CPCAP_BIT_USBXCVREN |
+ CPCAP_BIT_UARTMUX0 |
+ CPCAP_BIT_EMUMODE0,
CPCAP_BIT_USBXCVREN);
if (error)
goto out_err;
+ if (ddata->pins_utmi) {
+ error = pinctrl_select_state(ddata->pins, ddata->pins_utmi);
+ if (error) {
+ dev_err(ddata->dev, "could not set usb mode: %i\n",
+ error);
+
+ return error;
+ }
+ }
+
/* Enable USB mode */
error = cpcap_usb_gpio_set_mode(ddata, CPCAP_OTG_DM_DP);
if (error)
@@ -521,6 +608,38 @@ static int cpcap_usb_set_usb_mode(struct cpcap_phy_ddata *ddata)
return error;
}
+static int cpcap_usb_set_dcp_mode(struct cpcap_phy_ddata *ddata)
+{
+ int error;
+
+ error = cpcap_usb_set_safe_mode(ddata);
+ if (error)
+ return error;
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC2,
+ CPCAP_BIT_USBXCVREN |
+ CPCAP_BIT_UARTMUX0 |
+ CPCAP_BIT_EMUMODE0, 0);
+ if (error)
+ goto out_err;
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3,
+ CPCAP_BIT_SUSPEND_SPI, 0);
+ if (error)
+ goto out_err;
+
+ error = cpcap_usb_gpio_set_mode(ddata, CPCAP_DM_DP);
+ if (error)
+ goto out_err;
+
+ return 0;
+
+out_err:
+ dev_err(ddata->dev, "%s failed with %i\n", __func__, error);
+
+ return error;
+}
+
static int cpcap_usb_init_optional_pins(struct cpcap_phy_ddata *ddata)
{
ddata->pins = devm_pinctrl_get(ddata->dev);
@@ -550,8 +669,11 @@ static int cpcap_usb_init_optional_pins(struct cpcap_phy_ddata *ddata)
ddata->pins_uart = NULL;
}
- if (ddata->pins_uart)
- return pinctrl_select_state(ddata->pins, ddata->pins_uart);
+ ddata->pins_safe = pinctrl_lookup_state(ddata->pins, "safe");
+ if (IS_ERR(ddata->pins_safe)) {
+ dev_info(ddata->dev, "safe pins not configured\n");
+ ddata->pins_safe = NULL;
+ }
return 0;
}
@@ -605,6 +727,59 @@ static int cpcap_usb_init_iio(struct cpcap_phy_ddata *ddata)
return error;
}
+static int cpcap_usb_init_usb(struct cpcap_phy_ddata *ddata)
+{
+ struct cpcap_usb_ints_state s;
+ int error;
+ int mask;
+
+ error = cpcap_phy_get_ints_state(ddata, &s);
+ if (error)
+ return error;
+
+ error = cpcap_usb_set_safe_mode(ddata);
+ if (error)
+ return error;
+
+ /*
+ * Do not touch DM/DP PD as this will break charger detection if charger
+ * is connected before we detect it.
+ */
+ if (s.se1) {
+ mask = CPCAP_BIT_VBUSEN_SPI | CPCAP_BIT_VBUSPU_SPI |
+ CPCAP_BIT_SUSPEND_SPI | CPCAP_BIT_ULPI_SPI_SEL;
+ } else {
+ mask = CPCAP_BIT_VBUSEN_SPI | CPCAP_BIT_VBUSPU_SPI |
+ CPCAP_BIT_DMPD_SPI | CPCAP_BIT_DPPD_SPI |
+ CPCAP_BIT_SUSPEND_SPI | CPCAP_BIT_PU_SPI |
+ CPCAP_BIT_ULPI_SPI_SEL;
+ }
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3, mask,
+ CPCAP_BIT_ULPI_SPI_SEL);
+ if (error)
+ return error;
+
+ /*
+ * Configure safe DP/DM termination state.
+ *
+ * Vendor kernel uses:
+ * DP150KPU = enabled
+ * all USB pullups/pulldowns = disabled
+ */
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC1,
+ CPCAP_BIT_DP150KPU |
+ CPCAP_BIT_DP1K5PU |
+ CPCAP_BIT_DM1K5PU |
+ CPCAP_BIT_DPPD |
+ CPCAP_BIT_DMPD,
+ CPCAP_BIT_DP150KPU);
+ if (error)
+ return error;
+
+ return cpcap_usb_gpio_set_mode(ddata, CPCAP_DM_DP);
+}
+
#ifdef CONFIG_OF
static const struct of_device_id cpcap_usb_phy_id_table[] = {
{
@@ -638,6 +813,7 @@ static int cpcap_usb_phy_probe(struct platform_device *pdev)
if (!otg)
return -ENOMEM;
+ ddata->mode = CPCAP_UNKNOWN;
ddata->dev = &pdev->dev;
ddata->phy.dev = ddata->dev;
ddata->phy.label = "cpcap_usb_phy";
@@ -682,6 +858,10 @@ static int cpcap_usb_phy_probe(struct platform_device *pdev)
if (error)
goto out_reg_disable;
+ error = cpcap_usb_init_usb(ddata);
+ if (error)
+ goto out_reg_disable;
+
error = cpcap_usb_init_interrupts(pdev, ddata);
if (error)
goto out_reg_disable;
@@ -701,17 +881,12 @@ static int cpcap_usb_phy_probe(struct platform_device *pdev)
static void cpcap_usb_phy_remove(struct platform_device *pdev)
{
struct cpcap_phy_ddata *ddata = platform_get_drvdata(pdev);
- int error;
atomic_set(&ddata->active, 0);
cpcap_usb_fini_interrupts(pdev, ddata);
cancel_delayed_work_sync(&ddata->detect_work);
- error = cpcap_usb_set_uart_mode(ddata);
- if (error)
- dev_err(ddata->dev, "could not set UART mode\n");
-
+ cpcap_usb_set_idle_mode(ddata);
cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_OFF);
-
usb_remove_phy(&ddata->phy);
regulator_disable(ddata->vusb);
}
--
2.39.5
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v5 5/7] phy: cpcap-usb: add extcon support
2026-07-11 20:42 [PATCH v5 0/7] phy: cpcap-usb: improve charger detection and export cable state Ivaylo Dimitrov
` (3 preceding siblings ...)
2026-07-11 20:42 ` [PATCH v5 4/7] phy: cpcap-usb: add DCP detection and make UART idle mode optional Ivaylo Dimitrov
@ 2026-07-11 20:42 ` Ivaylo Dimitrov
2026-07-11 20:42 ` [PATCH v5 6/7] ARM: dts: ti: cpcap-mapphone: add charger detection interrupt for CPCAP USB PHY Ivaylo Dimitrov
2026-07-11 20:42 ` [PATCH v5 7/7] ARM: dts: ti: cpcap-mapphone: add USB safe pinctrl state Ivaylo Dimitrov
6 siblings, 0 replies; 9+ messages in thread
From: Ivaylo Dimitrov @ 2026-07-11 20:42 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
Roger Quadros, Tony Lindgren, Linus Walleij, Bartosz Golaszewski,
--cc=linux-phy, devicetree, linux-kernel, linux-omap, linux-gpio
Cc: Ivaylo Dimitrov
Register an Extcon device and report the detected cable state.
The driver already determines the type of cable attached during USB cable
detection. Export the detected state through the Extcon framework so
other drivers can consume it using a standard kernel interface.
Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
---
drivers/phy/motorola/phy-cpcap-usb.c | 69 ++++++++++++++++++++++++++++
1 file changed, 69 insertions(+)
diff --git a/drivers/phy/motorola/phy-cpcap-usb.c b/drivers/phy/motorola/phy-cpcap-usb.c
index 2d770ff19e93..23d8709a0026 100644
--- a/drivers/phy/motorola/phy-cpcap-usb.c
+++ b/drivers/phy/motorola/phy-cpcap-usb.c
@@ -12,6 +12,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
+#include <linux/extcon-provider.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -123,6 +124,7 @@ struct cpcap_phy_ddata {
struct regmap *reg;
struct device *dev;
struct usb_phy phy;
+ struct extcon_dev *edev;
struct delayed_work detect_work;
struct pinctrl *pins;
struct pinctrl_state *pins_ulpi;
@@ -135,12 +137,23 @@ struct cpcap_phy_ddata {
struct regulator *vusb;
atomic_t active;
enum cpcap_mode mode;
+ int cable;
};
static bool cpcap_enable_uart;
module_param_named(enable_uart, cpcap_enable_uart, bool, 0644);
MODULE_PARM_DESC(enable_uart,
"Enable UART on the USB connector while idle (increases power consumption)");
+
+/* List of detectable cables */
+static const unsigned int cpcap_extcon_cables[] = {
+ EXTCON_USB,
+ EXTCON_USB_HOST,
+ EXTCON_CHG_USB_SDP,
+ EXTCON_CHG_USB_DCP,
+ EXTCON_NONE,
+};
+
static bool cpcap_usb_vbus_valid(struct cpcap_phy_ddata *ddata)
{
int error, value = 0;
@@ -226,6 +239,46 @@ static void cpcap_usb_try_musb_mailbox(struct cpcap_phy_ddata *ddata,
__func__, error);
}
+static void cpcap_usb_set_extcon(struct cpcap_phy_ddata *ddata)
+{
+ int cable;
+
+ switch (ddata->mode) {
+ case CPCAP_CHARGER:
+ cable = EXTCON_CHG_USB_DCP;
+ break;
+ case CPCAP_USB:
+ cable = EXTCON_CHG_USB_SDP;
+ break;
+ case CPCAP_USB_HOST:
+ case CPCAP_DOCK:
+ cable = EXTCON_USB_HOST;
+ break;
+ case CPCAP_IDLE:
+ cable = EXTCON_NONE;
+ break;
+ case CPCAP_UNKNOWN:
+ dev_warn_once(ddata->dev, "called with unknown PHY mode\n");
+ return;
+ }
+
+ if (cable == ddata->cable)
+ return;
+
+ if (ddata->cable != EXTCON_NONE)
+ extcon_set_state_sync(ddata->edev, ddata->cable, false);
+
+ if (ddata->cable == EXTCON_CHG_USB_SDP)
+ extcon_set_state_sync(ddata->edev, EXTCON_USB, false);
+ else if (cable == EXTCON_CHG_USB_SDP)
+ extcon_set_state_sync(ddata->edev, EXTCON_USB, true);
+
+ if (cable != EXTCON_NONE)
+ extcon_set_state_sync(ddata->edev, cable, true);
+
+ ddata->cable = cable;
+}
+
static void cpcap_usb_detect(struct work_struct *work)
{
struct cpcap_phy_ddata *ddata;
@@ -275,6 +328,7 @@ static void cpcap_usb_detect(struct work_struct *work)
ddata->mode = CPCAP_DOCK;
cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND);
+ cpcap_usb_set_extcon(ddata);
/*
* Force check state again after musb has reoriented,
@@ -303,6 +357,7 @@ static void cpcap_usb_detect(struct work_struct *work)
ddata->mode = CPCAP_USB_HOST;
cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND);
+ cpcap_usb_set_extcon(ddata);
return;
}
@@ -351,6 +406,8 @@ static void cpcap_usb_detect(struct work_struct *work)
if (ddata->mode == CPCAP_USB)
cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_VALID);
+ cpcap_usb_set_extcon(ddata);
+
return;
}
@@ -369,6 +426,7 @@ static void cpcap_usb_detect(struct work_struct *work)
ddata->mode = CPCAP_IDLE;
cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_OFF);
+ cpcap_usb_set_extcon(ddata);
return;
@@ -813,7 +871,18 @@ static int cpcap_usb_phy_probe(struct platform_device *pdev)
if (!otg)
return -ENOMEM;
+ ddata->edev = devm_extcon_dev_allocate(&pdev->dev, cpcap_extcon_cables);
+ if (IS_ERR(ddata->edev)) {
+ return dev_err_probe(&pdev->dev, PTR_ERR(ddata->edev),
+ "failed to allocate extcon device\n");
+ }
+
+ error = devm_extcon_dev_register(&pdev->dev, ddata->edev);
+ if (error < 0)
+ return error;
+
ddata->mode = CPCAP_UNKNOWN;
+ ddata->cable = EXTCON_NONE;
ddata->dev = &pdev->dev;
ddata->phy.dev = ddata->dev;
ddata->phy.label = "cpcap_usb_phy";
--
2.39.5
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v5 6/7] ARM: dts: ti: cpcap-mapphone: add charger detection interrupt for CPCAP USB PHY
2026-07-11 20:42 [PATCH v5 0/7] phy: cpcap-usb: improve charger detection and export cable state Ivaylo Dimitrov
` (4 preceding siblings ...)
2026-07-11 20:42 ` [PATCH v5 5/7] phy: cpcap-usb: add extcon support Ivaylo Dimitrov
@ 2026-07-11 20:42 ` Ivaylo Dimitrov
2026-07-11 20:42 ` [PATCH v5 7/7] ARM: dts: ti: cpcap-mapphone: add USB safe pinctrl state Ivaylo Dimitrov
6 siblings, 0 replies; 9+ messages in thread
From: Ivaylo Dimitrov @ 2026-07-11 20:42 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
Roger Quadros, Tony Lindgren, Linus Walleij, Bartosz Golaszewski,
--cc=linux-phy, devicetree, linux-kernel, linux-omap, linux-gpio
Cc: Ivaylo Dimitrov
Update the CPCAP USB PHY interrupt mapping to use the charger
detection interrupt and corresponding "chrg_det" interrupt name.
Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
---
arch/arm/boot/dts/ti/omap/motorola-cpcap-mapphone.dtsi | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/arch/arm/boot/dts/ti/omap/motorola-cpcap-mapphone.dtsi b/arch/arm/boot/dts/ti/omap/motorola-cpcap-mapphone.dtsi
index 6b1195c2a768..51a858021541 100644
--- a/arch/arm/boot/dts/ti/omap/motorola-cpcap-mapphone.dtsi
+++ b/arch/arm/boot/dts/ti/omap/motorola-cpcap-mapphone.dtsi
@@ -125,11 +125,13 @@ cpcap_usb2_phy: phy {
interrupts-extended =
<&cpcap 15 0>, <&cpcap 14 0>, <&cpcap 28 0>,
<&cpcap 19 0>, <&cpcap 18 0>, <&cpcap 17 0>,
- <&cpcap 16 0>, <&cpcap 49 0>, <&cpcap 48 0>;
+ <&cpcap 16 0>, <&cpcap 49 0>, <&cpcap 48 0>,
+ <&cpcap 13 0>;
interrupt-names =
"id_ground", "id_float", "se0conn",
"vbusvld", "sessvld", "sessend",
- "se1", "dm", "dp";
+ "se1", "dm", "dp",
+ "chrg_det";
mode-gpios = <&gpio2 28 GPIO_ACTIVE_HIGH>,
<&gpio1 0 GPIO_ACTIVE_HIGH>;
io-channels = <&cpcap_adc 2>, <&cpcap_adc 7>;
--
2.39.5
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v5 7/7] ARM: dts: ti: cpcap-mapphone: add USB safe pinctrl state
2026-07-11 20:42 [PATCH v5 0/7] phy: cpcap-usb: improve charger detection and export cable state Ivaylo Dimitrov
` (5 preceding siblings ...)
2026-07-11 20:42 ` [PATCH v5 6/7] ARM: dts: ti: cpcap-mapphone: add charger detection interrupt for CPCAP USB PHY Ivaylo Dimitrov
@ 2026-07-11 20:42 ` Ivaylo Dimitrov
6 siblings, 0 replies; 9+ messages in thread
From: Ivaylo Dimitrov @ 2026-07-11 20:42 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
Roger Quadros, Tony Lindgren, Linus Walleij, Bartosz Golaszewski,
--cc=linux-phy, devicetree, linux-kernel, linux-omap, linux-gpio
Cc: Ivaylo Dimitrov
Add a pinctrl state that configures the USB interface pins into a safe
GPIO configuration. This allows the CPCAP USB PHY driver to place the
pins into a defined state whenever the USB interface is not active.
Signed-off-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
---
.../dts/ti/omap/motorola-cpcap-mapphone.dtsi | 3 ++-
.../dts/ti/omap/motorola-mapphone-common.dtsi | 19 +++++++++++++++++++
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/ti/omap/motorola-cpcap-mapphone.dtsi b/arch/arm/boot/dts/ti/omap/motorola-cpcap-mapphone.dtsi
index 51a858021541..368cb539997f 100644
--- a/arch/arm/boot/dts/ti/omap/motorola-cpcap-mapphone.dtsi
+++ b/arch/arm/boot/dts/ti/omap/motorola-cpcap-mapphone.dtsi
@@ -120,7 +120,8 @@ cpcap_usb2_phy: phy {
pinctrl-1 = <&usb_ulpi_pins>;
pinctrl-2 = <&usb_utmi_pins>;
pinctrl-3 = <&uart3_pins>;
- pinctrl-names = "default", "ulpi", "utmi", "uart";
+ pinctrl-4 = <&usb_safe_pins>;
+ pinctrl-names = "default", "ulpi", "utmi", "uart", "safe";
#phy-cells = <0>;
interrupts-extended =
<&cpcap 15 0>, <&cpcap 14 0>, <&cpcap 28 0>,
diff --git a/arch/arm/boot/dts/ti/omap/motorola-mapphone-common.dtsi b/arch/arm/boot/dts/ti/omap/motorola-mapphone-common.dtsi
index acac3c243c7a..475ffe678e76 100644
--- a/arch/arm/boot/dts/ti/omap/motorola-mapphone-common.dtsi
+++ b/arch/arm/boot/dts/ti/omap/motorola-mapphone-common.dtsi
@@ -352,6 +352,25 @@ OMAP4_IOPAD(0x1c8, PIN_INPUT_PULLUP | MUX_MODE7)
>;
};
+ usb_safe_pins: usb-safe-pins {
+ pinctrl-single,pins = <
+ OMAP4_IOPAD(0x196, MUX_MODE7)
+ OMAP4_IOPAD(0x198, MUX_MODE7)
+ OMAP4_IOPAD(0x1b2, PIN_INPUT_PULLUP | MUX_MODE7)
+ OMAP4_IOPAD(0x1b4, PIN_INPUT_PULLUP | MUX_MODE7)
+ OMAP4_IOPAD(0x1b6, PIN_INPUT_PULLUP | MUX_MODE7)
+ OMAP4_IOPAD(0x1b8, PIN_INPUT_PULLUP | MUX_MODE7)
+ OMAP4_IOPAD(0x1ba, PIN_INPUT_PULLUP | MUX_MODE7)
+ OMAP4_IOPAD(0x1bc, PIN_INPUT_PULLUP | MUX_MODE7)
+ OMAP4_IOPAD(0x1be, PIN_INPUT_PULLUP | MUX_MODE7)
+ OMAP4_IOPAD(0x1c0, PIN_INPUT_PULLUP | MUX_MODE7)
+ OMAP4_IOPAD(0x1c2, PIN_INPUT_PULLUP | MUX_MODE7)
+ OMAP4_IOPAD(0x1c4, PIN_INPUT_PULLUP | MUX_MODE7)
+ OMAP4_IOPAD(0x1c6, PIN_INPUT_PULLUP | MUX_MODE7)
+ OMAP4_IOPAD(0x1c8, PIN_INPUT_PULLUP | MUX_MODE7)
+ >;
+ };
+
/*
* Note that the v3.0.8 stock userspace dynamically remuxes uart1
* rts pin probably for PM purposes to PIN_INPUT_PULLUP | MUX_MODE7
--
2.39.5
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v5 4/7] phy: cpcap-usb: add DCP detection and make UART idle mode optional
2026-07-11 20:42 ` [PATCH v5 4/7] phy: cpcap-usb: add DCP detection and make UART idle mode optional Ivaylo Dimitrov
@ 2026-07-11 20:54 ` sashiko-bot
0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-07-11 20:54 UTC (permalink / raw)
To: Ivaylo Dimitrov
Cc: vkoul, robh, neil.armstrong, devicetree, linux-phy, conor+dt,
olteanv
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The new charger detection logic introduces an infinite connect/disconnect flapping loop for active USB peripheral connections and an infinite polling loop for proprietary chargers.
- [High] Transitioning from idle mode to USB mode leaves the PHY transceiver improperly suspended because CPCAP_BIT_USBSUSPEND is not cleared.
--
--- Patch [4]: [PATCH v5 4/7] phy: cpcap-usb: add DCP detection and make UART idle mode optional ---
commit a10ef479eac91f75ee1175abda606fb7eaa3fb70
Author: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
phy: cpcap-usb: add DCP detection and make UART idle mode optional
Handle DCP separately from USB host connections using CPCAP charger
detection status. Make the existing idle UART mode optional via the
"enable_uart" module parameter. Initialize the PHY into the baseline
configuration required for reliable charger detection during probe.
> diff --git a/drivers/phy/motorola/phy-cpcap-usb.c b/drivers/phy/motorola/phy-cpcap-usb.c
> index 741145c89e5bb..2d770ff19e934 100644
> --- a/drivers/phy/motorola/phy-cpcap-usb.c
> +++ b/drivers/phy/motorola/phy-cpcap-usb.c
[ ... ]
> @@ -292,38 +301,74 @@ static void cpcap_usb_detect(struct work_struct *work)
[ ... ]
> - /* Otherwise assume we're connected to a USB host */
> - if (vbus) {
> - dev_dbg(ddata->dev, "connected to USB host\n");
> - error = cpcap_usb_set_usb_mode(ddata);
> + /* Check if we're connected to a USB host or charger */
> + if (vbus && ddata->mode != CPCAP_USB_HOST) {
> + enum cpcap_mode new_mode;
> +
> + if (s.se1)
> + new_mode = CPCAP_CHARGER;
> + else if (!s.dm && !s.dp)
> + new_mode = CPCAP_USB;
> + else {
> + error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC2,
> + CPCAP_BIT_USBXCVREN |
> + CPCAP_BIT_UARTMUX0 |
> + CPCAP_BIT_EMUMODE0, 0);
> + if (error)
> + goto out_err;
> +
> + schedule_delayed_work(&ddata->detect_work,
> + msecs_to_jiffies(100));
> + return;
> + }
[Severity: High]
Does this else block create a regression with an infinite connect/disconnect
flapping loop for active USB peripheral connections?
When the device successfully enters CPCAP_USB mode, normal USB pull-ups or
traffic will assert D+ high (s.dp == 1). Any subsequent interrupt triggers
detect_work(), and if s.se1 is false, it falls into this else block. It then
disables the transceiver (USBXCVREN = 0) and reschedules itself in 100ms,
repeatedly dropping standard PC connections or proprietary chargers.
[ ... ]
> @@ -424,43 +469,84 @@ static int cpcap_usb_gpio_set_mode(struct cpcap_phy_ddata *ddata,
[ ... ]
> +static int cpcap_usb_set_idle_mode(struct cpcap_phy_ddata *ddata)
> +{
[ ... ]
> + val = CPCAP_BIT_USBSUSPEND;
> +
> + if (enable_uart)
> + val |= (CPCAP_BIT_UARTMUX0 | CPCAP_BIT_EMUMODE0);
> +
> + error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC2, 0xffff, val);
> + if (error)
> + goto out_err;
[ ... ]
> @@ -503,11 +578,23 @@ static int cpcap_usb_set_usb_mode(struct cpcap_phy_ddata *ddata)
> goto out_err;
>
> error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC2,
> - CPCAP_BIT_USBXCVREN,
> + CPCAP_BIT_USBXCVREN |
> + CPCAP_BIT_UARTMUX0 |
> + CPCAP_BIT_EMUMODE0,
> CPCAP_BIT_USBXCVREN);
[Severity: High]
When transitioning back to USB mode, does this cause a regression where the
PHY transceiver is left improperly suspended?
In cpcap_usb_set_idle_mode(), CPCAP_REG_USBC2 is updated with a 0xffff mask to
explicitly set CPCAP_BIT_USBSUSPEND. When returning to USB mode here, the mask
is limited to CPCAP_BIT_USBXCVREN | CPCAP_BIT_UARTMUX0 | CPCAP_BIT_EMUMODE0.
Since this mask does not include CPCAP_BIT_USBSUSPEND, it looks like it fails
to clear the suspension state, which could cause communication failures.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260711204210.197144-1-ivo.g.dimitrov.75@gmail.com?part=4
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-07-11 20:54 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-11 20:42 [PATCH v5 0/7] phy: cpcap-usb: improve charger detection and export cable state Ivaylo Dimitrov
2026-07-11 20:42 ` [PATCH v5 1/7] dt-bindings: phy: motorola,cpcap-usb: add chrg_det interrupt Ivaylo Dimitrov
2026-07-11 20:42 ` [PATCH v5 2/7] dt-bindings: phy: motorola,cpcap-usb-phy: add optional safe pinctrl state Ivaylo Dimitrov
2026-07-11 20:42 ` [PATCH v5 3/7] phy: cpcap-usb: fix IRQ teardown race Ivaylo Dimitrov
2026-07-11 20:42 ` [PATCH v5 4/7] phy: cpcap-usb: add DCP detection and make UART idle mode optional Ivaylo Dimitrov
2026-07-11 20:54 ` sashiko-bot
2026-07-11 20:42 ` [PATCH v5 5/7] phy: cpcap-usb: add extcon support Ivaylo Dimitrov
2026-07-11 20:42 ` [PATCH v5 6/7] ARM: dts: ti: cpcap-mapphone: add charger detection interrupt for CPCAP USB PHY Ivaylo Dimitrov
2026-07-11 20:42 ` [PATCH v5 7/7] ARM: dts: ti: cpcap-mapphone: add USB safe pinctrl state Ivaylo Dimitrov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox