Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: typec: hd3ss3220: Enable VBUS based on ID pin state

There is a ID pin present on HD3SS3220 controller that can be routed
to SoC. As per the datasheet:

"Upon detecting a UFP device, HD3SS3220 will keep ID pin high if VBUS is
not at VSafe0V. Once VBUS is at VSafe0V, the HD3SS3220 will assert ID pin
low. This is done to enforce Type-C requirement that VBUS must be at
VSafe0V before re-enabling VBUS"

Add support to read the ID pin state and enable VBUS accordingly.

Signed-off-by: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://patch.msgid.link/20251111072025.2199142-3-krishna.kurapati@oss.qualcomm.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Krishna Kurapati and committed by
Greg Kroah-Hartman
f8d2bf7c d53bdaae

+73 -2
+73 -2
drivers/usb/typec/hd3ss3220.c
··· 15 15 #include <linux/usb/typec.h> 16 16 #include <linux/delay.h> 17 17 #include <linux/workqueue.h> 18 + #include <linux/gpio/consumer.h> 19 + #include <linux/regulator/consumer.h> 20 + #include <linux/of_graph.h> 18 21 19 22 #define HD3SS3220_REG_CN_STAT 0x08 20 23 #define HD3SS3220_REG_CN_STAT_CTRL 0x09 ··· 57 54 struct delayed_work output_poll_work; 58 55 enum usb_role role_state; 59 56 bool poll; 57 + 58 + struct gpio_desc *id_gpiod; 59 + int id_irq; 60 + 61 + struct regulator *vbus; 60 62 }; 61 63 62 64 static int hd3ss3220_set_power_opmode(struct hd3ss3220 *hd3ss3220, int power_opmode) ··· 327 319 .max_register = 0x0A, 328 320 }; 329 321 322 + static irqreturn_t hd3ss3220_id_isr(int irq, void *dev_id) 323 + { 324 + struct hd3ss3220 *hd3ss3220 = dev_id; 325 + int ret; 326 + int id; 327 + 328 + id = gpiod_get_value_cansleep(hd3ss3220->id_gpiod); 329 + if (!id) 330 + ret = regulator_enable(hd3ss3220->vbus); 331 + else 332 + ret = regulator_disable(hd3ss3220->vbus); 333 + 334 + if (ret) 335 + dev_err(hd3ss3220->dev, 336 + "vbus regulator %s failed: %d\n", id ? "disable" : "enable", ret); 337 + 338 + return IRQ_HANDLED; 339 + } 340 + 330 341 static int hd3ss3220_probe(struct i2c_client *client) 331 342 { 332 343 struct typec_capability typec_cap = { }; 333 - struct hd3ss3220 *hd3ss3220; 334 344 struct fwnode_handle *connector, *ep; 335 - int ret; 345 + struct hd3ss3220 *hd3ss3220; 346 + struct regulator *vbus; 336 347 unsigned int data; 348 + int ret; 337 349 338 350 hd3ss3220 = devm_kzalloc(&client->dev, sizeof(struct hd3ss3220), 339 351 GFP_KERNEL); ··· 385 357 if (IS_ERR(hd3ss3220->role_sw)) { 386 358 ret = PTR_ERR(hd3ss3220->role_sw); 387 359 goto err_put_fwnode; 360 + } 361 + 362 + vbus = devm_of_regulator_get_optional(hd3ss3220->dev, 363 + to_of_node(connector), 364 + "vbus"); 365 + if (IS_ERR(vbus) && vbus != ERR_PTR(-ENODEV)) { 366 + ret = PTR_ERR(vbus); 367 + dev_err(hd3ss3220->dev, "failed to get vbus: %d", ret); 368 + goto err_put_fwnode; 369 + } 370 + 371 + hd3ss3220->vbus = (vbus == ERR_PTR(-ENODEV) ? NULL : vbus); 372 + 373 + if (hd3ss3220->vbus) { 374 + hd3ss3220->id_gpiod = devm_gpiod_get_optional(hd3ss3220->dev, 375 + "id", 376 + GPIOD_IN); 377 + if (IS_ERR(hd3ss3220->id_gpiod)) { 378 + ret = PTR_ERR(hd3ss3220->id_gpiod); 379 + goto err_put_fwnode; 380 + } 381 + } 382 + 383 + if (hd3ss3220->id_gpiod) { 384 + hd3ss3220->id_irq = gpiod_to_irq(hd3ss3220->id_gpiod); 385 + if (hd3ss3220->id_irq < 0) { 386 + ret = hd3ss3220->id_irq; 387 + dev_err(hd3ss3220->dev, 388 + "failed to get ID gpio: %d\n", 389 + hd3ss3220->id_irq); 390 + goto err_put_fwnode; 391 + } 392 + 393 + ret = devm_request_threaded_irq(hd3ss3220->dev, 394 + hd3ss3220->id_irq, NULL, 395 + hd3ss3220_id_isr, 396 + IRQF_TRIGGER_RISING | 397 + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 398 + dev_name(hd3ss3220->dev), hd3ss3220); 399 + if (ret < 0) { 400 + dev_err(hd3ss3220->dev, "failed to get ID irq: %d\n", ret); 401 + goto err_put_fwnode; 402 + } 388 403 } 389 404 390 405 typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE;