Enable VBUS on HD3SS3220 when the ID pin is low, as required by the Type-C
specification. The ID pin stays high when VBUS is not at VSafe0V, and goes
low when VBUS is at VSafe0V.
Add support to read the ID pin state and enable VBUS accordingly.
Signed-off-by: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
---
drivers/usb/typec/hd3ss3220.c | 58 +++++++++++++++++++++++++++++++++++
1 file changed, 58 insertions(+)
diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c
index 3ecc688dda82..44ee0be27644 100644
--- a/drivers/usb/typec/hd3ss3220.c
+++ b/drivers/usb/typec/hd3ss3220.c
@@ -54,6 +54,11 @@ struct hd3ss3220 {
struct delayed_work output_poll_work;
enum usb_role role_state;
bool poll;
+
+ struct gpio_desc *id_gpiod;
+ int id_irq;
+
+ struct regulator *vbus;
};
static int hd3ss3220_set_power_opmode(struct hd3ss3220 *hd3ss3220, int power_opmode)
@@ -319,6 +324,28 @@ static const struct regmap_config config = {
.max_register = 0x0A,
};
+static irqreturn_t hd3ss3220_id_isr(int irq, void *dev_id)
+{
+ struct hd3ss3220 *hd3ss3220 = dev_id;
+ int ret;
+ int id;
+
+ if (IS_ERR_OR_NULL(hd3ss3220->vbus))
+ return IRQ_HANDLED;
+
+ id = hd3ss3220->id_gpiod ? gpiod_get_value_cansleep(hd3ss3220->id_gpiod) : 1;
+
+ if (!id) {
+ ret = regulator_enable(hd3ss3220->vbus);
+ if (ret)
+ dev_err(hd3ss3220->dev, "enable vbus regulator failed\n");
+ } else {
+ regulator_disable(hd3ss3220->vbus);
+ }
+
+ return IRQ_HANDLED;
+}
+
static int hd3ss3220_probe(struct i2c_client *client)
{
struct typec_capability typec_cap = { };
@@ -354,6 +381,37 @@ static int hd3ss3220_probe(struct i2c_client *client)
hd3ss3220->role_sw = usb_role_switch_get(hd3ss3220->dev);
}
+ hd3ss3220->id_gpiod = devm_gpiod_get_optional(hd3ss3220->dev, "id", GPIOD_IN);
+ if (IS_ERR(hd3ss3220->id_gpiod))
+ return PTR_ERR(hd3ss3220->id_gpiod);
+
+ if (hd3ss3220->id_gpiod) {
+ hd3ss3220->id_irq = gpiod_to_irq(hd3ss3220->id_gpiod);
+ if (hd3ss3220->id_irq < 0) {
+ dev_err(hd3ss3220->dev, "failed to get ID IRQ\n");
+ return hd3ss3220->id_irq;
+ }
+
+ ret = devm_request_threaded_irq(hd3ss3220->dev,
+ hd3ss3220->id_irq, NULL,
+ hd3ss3220_id_isr,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ dev_name(hd3ss3220->dev), hd3ss3220);
+ if (ret < 0) {
+ dev_err(hd3ss3220->dev, "failed to get id irq\n");
+ return ret;
+ }
+ }
+
+ hd3ss3220->vbus = devm_regulator_get_optional(hd3ss3220->dev, "vbus");
+ if (PTR_ERR(hd3ss3220->vbus) == -ENODEV)
+ hd3ss3220->vbus = NULL;
+
+ if (IS_ERR(hd3ss3220->vbus))
+ return dev_err_probe(hd3ss3220->dev,
+ PTR_ERR(hd3ss3220->vbus), "failed to get vbus\n");
+
if (IS_ERR(hd3ss3220->role_sw)) {
ret = PTR_ERR(hd3ss3220->role_sw);
goto err_put_fwnode;
--
2.34.1
On Thu, Oct 02, 2025 at 10:55:39PM +0530, Krishna Kurapati wrote:
> Enable VBUS on HD3SS3220 when the ID pin is low, as required by the Type-C
> specification.
There is not ID pin on Type-C connector.
> The ID pin stays high when VBUS is not at VSafe0V, and goes
> low when VBUS is at VSafe0V.
>
> Add support to read the ID pin state and enable VBUS accordingly.
I'm a bit confused about this... Why can't you just check the attached
state, and if it's DFP, then you drive VBUS?
thanks,
> Signed-off-by: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
> ---
> drivers/usb/typec/hd3ss3220.c | 58 +++++++++++++++++++++++++++++++++++
> 1 file changed, 58 insertions(+)
>
> diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c
> index 3ecc688dda82..44ee0be27644 100644
> --- a/drivers/usb/typec/hd3ss3220.c
> +++ b/drivers/usb/typec/hd3ss3220.c
> @@ -54,6 +54,11 @@ struct hd3ss3220 {
> struct delayed_work output_poll_work;
> enum usb_role role_state;
> bool poll;
> +
> + struct gpio_desc *id_gpiod;
> + int id_irq;
> +
> + struct regulator *vbus;
> };
>
> static int hd3ss3220_set_power_opmode(struct hd3ss3220 *hd3ss3220, int power_opmode)
> @@ -319,6 +324,28 @@ static const struct regmap_config config = {
> .max_register = 0x0A,
> };
>
> +static irqreturn_t hd3ss3220_id_isr(int irq, void *dev_id)
> +{
> + struct hd3ss3220 *hd3ss3220 = dev_id;
> + int ret;
> + int id;
> +
> + if (IS_ERR_OR_NULL(hd3ss3220->vbus))
> + return IRQ_HANDLED;
> +
> + id = hd3ss3220->id_gpiod ? gpiod_get_value_cansleep(hd3ss3220->id_gpiod) : 1;
> +
> + if (!id) {
> + ret = regulator_enable(hd3ss3220->vbus);
> + if (ret)
> + dev_err(hd3ss3220->dev, "enable vbus regulator failed\n");
> + } else {
> + regulator_disable(hd3ss3220->vbus);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> static int hd3ss3220_probe(struct i2c_client *client)
> {
> struct typec_capability typec_cap = { };
> @@ -354,6 +381,37 @@ static int hd3ss3220_probe(struct i2c_client *client)
> hd3ss3220->role_sw = usb_role_switch_get(hd3ss3220->dev);
> }
>
> + hd3ss3220->id_gpiod = devm_gpiod_get_optional(hd3ss3220->dev, "id", GPIOD_IN);
> + if (IS_ERR(hd3ss3220->id_gpiod))
> + return PTR_ERR(hd3ss3220->id_gpiod);
> +
> + if (hd3ss3220->id_gpiod) {
> + hd3ss3220->id_irq = gpiod_to_irq(hd3ss3220->id_gpiod);
> + if (hd3ss3220->id_irq < 0) {
> + dev_err(hd3ss3220->dev, "failed to get ID IRQ\n");
> + return hd3ss3220->id_irq;
> + }
> +
> + ret = devm_request_threaded_irq(hd3ss3220->dev,
> + hd3ss3220->id_irq, NULL,
> + hd3ss3220_id_isr,
> + IRQF_TRIGGER_RISING |
> + IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> + dev_name(hd3ss3220->dev), hd3ss3220);
> + if (ret < 0) {
> + dev_err(hd3ss3220->dev, "failed to get id irq\n");
> + return ret;
> + }
> + }
> +
> + hd3ss3220->vbus = devm_regulator_get_optional(hd3ss3220->dev, "vbus");
> + if (PTR_ERR(hd3ss3220->vbus) == -ENODEV)
> + hd3ss3220->vbus = NULL;
> +
> + if (IS_ERR(hd3ss3220->vbus))
> + return dev_err_probe(hd3ss3220->dev,
> + PTR_ERR(hd3ss3220->vbus), "failed to get vbus\n");
> +
> if (IS_ERR(hd3ss3220->role_sw)) {
> ret = PTR_ERR(hd3ss3220->role_sw);
> goto err_put_fwnode;
> --
> 2.34.1
--
heikki
On 10/8/2025 4:39 PM, Heikki Krogerus wrote:
> On Thu, Oct 02, 2025 at 10:55:39PM +0530, Krishna Kurapati wrote:
>> Enable VBUS on HD3SS3220 when the ID pin is low, as required by the Type-C
>> specification.
>
> There is not ID pin on Type-C connector.
There is an ID pin coming out from HD3SS3220 controller to SoC that is
being referred to here.
>
>> The ID pin stays high when VBUS is not at VSafe0V, and goes
>> low when VBUS is at VSafe0V.
>>
>> Add support to read the ID pin state and enable VBUS accordingly.
>
> I'm a bit confused about this... Why can't you just check the attached
> state, and if it's DFP, then you drive VBUS?
>
We could, but checking for DFP doesn't ensure VBUS is at VSafe0V as per
the datasheet. So using the ID pin to enable vbus.
Regards,
Krishna,
> thanks,
>
>> Signed-off-by: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
>> ---
>> drivers/usb/typec/hd3ss3220.c | 58 +++++++++++++++++++++++++++++++++++
>> 1 file changed, 58 insertions(+)
>>
>> diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c
>> index 3ecc688dda82..44ee0be27644 100644
>> --- a/drivers/usb/typec/hd3ss3220.c
>> +++ b/drivers/usb/typec/hd3ss3220.c
>> @@ -54,6 +54,11 @@ struct hd3ss3220 {
>> struct delayed_work output_poll_work;
>> enum usb_role role_state;
>> bool poll;
>> +
>> + struct gpio_desc *id_gpiod;
>> + int id_irq;
>> +
>> + struct regulator *vbus;
>> };
>>
>> static int hd3ss3220_set_power_opmode(struct hd3ss3220 *hd3ss3220, int power_opmode)
>> @@ -319,6 +324,28 @@ static const struct regmap_config config = {
>> .max_register = 0x0A,
>> };
>>
>> +static irqreturn_t hd3ss3220_id_isr(int irq, void *dev_id)
>> +{
>> + struct hd3ss3220 *hd3ss3220 = dev_id;
>> + int ret;
>> + int id;
>> +
>> + if (IS_ERR_OR_NULL(hd3ss3220->vbus))
>> + return IRQ_HANDLED;
>> +
>> + id = hd3ss3220->id_gpiod ? gpiod_get_value_cansleep(hd3ss3220->id_gpiod) : 1;
>> +
>> + if (!id) {
>> + ret = regulator_enable(hd3ss3220->vbus);
>> + if (ret)
>> + dev_err(hd3ss3220->dev, "enable vbus regulator failed\n");
>> + } else {
>> + regulator_disable(hd3ss3220->vbus);
>> + }
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> static int hd3ss3220_probe(struct i2c_client *client)
>> {
>> struct typec_capability typec_cap = { };
>> @@ -354,6 +381,37 @@ static int hd3ss3220_probe(struct i2c_client *client)
>> hd3ss3220->role_sw = usb_role_switch_get(hd3ss3220->dev);
>> }
>>
>> + hd3ss3220->id_gpiod = devm_gpiod_get_optional(hd3ss3220->dev, "id", GPIOD_IN);
>> + if (IS_ERR(hd3ss3220->id_gpiod))
>> + return PTR_ERR(hd3ss3220->id_gpiod);
>> +
>> + if (hd3ss3220->id_gpiod) {
>> + hd3ss3220->id_irq = gpiod_to_irq(hd3ss3220->id_gpiod);
>> + if (hd3ss3220->id_irq < 0) {
>> + dev_err(hd3ss3220->dev, "failed to get ID IRQ\n");
>> + return hd3ss3220->id_irq;
>> + }
>> +
>> + ret = devm_request_threaded_irq(hd3ss3220->dev,
>> + hd3ss3220->id_irq, NULL,
>> + hd3ss3220_id_isr,
>> + IRQF_TRIGGER_RISING |
>> + IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
>> + dev_name(hd3ss3220->dev), hd3ss3220);
>> + if (ret < 0) {
>> + dev_err(hd3ss3220->dev, "failed to get id irq\n");
>> + return ret;
>> + }
>> + }
>> +
>> + hd3ss3220->vbus = devm_regulator_get_optional(hd3ss3220->dev, "vbus");
>> + if (PTR_ERR(hd3ss3220->vbus) == -ENODEV)
>> + hd3ss3220->vbus = NULL;
>> +
>> + if (IS_ERR(hd3ss3220->vbus))
>> + return dev_err_probe(hd3ss3220->dev,
>> + PTR_ERR(hd3ss3220->vbus), "failed to get vbus\n");
>> +
>> if (IS_ERR(hd3ss3220->role_sw)) {
>> ret = PTR_ERR(hd3ss3220->role_sw);
>> goto err_put_fwnode;
>> --
>> 2.34.1
>
On Wed, Oct 08, 2025 at 04:45:06PM +0530, Krishna Kurapati PSSNV wrote:
> On 10/8/2025 4:39 PM, Heikki Krogerus wrote:
> > On Thu, Oct 02, 2025 at 10:55:39PM +0530, Krishna Kurapati wrote:
> > > Enable VBUS on HD3SS3220 when the ID pin is low, as required by the Type-C
> > > specification.
> >
> > There is not ID pin on Type-C connector.
>
> There is an ID pin coming out from HD3SS3220 controller to SoC that is being
> referred to here.
So please fix the statement. The Type-C specification does not place
any requirements on the ID pin.
> > > The ID pin stays high when VBUS is not at VSafe0V, and goes
> > > low when VBUS is at VSafe0V.
> > >
> > > Add support to read the ID pin state and enable VBUS accordingly.
> >
> > I'm a bit confused about this... Why can't you just check the attached
> > state, and if it's DFP, then you drive VBUS?
> >
>
> We could, but checking for DFP doesn't ensure VBUS is at VSafe0V as per the
> datasheet. So using the ID pin to enable vbus.
Fair enough.
thanks,
> > > Signed-off-by: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
> > > ---
> > > drivers/usb/typec/hd3ss3220.c | 58 +++++++++++++++++++++++++++++++++++
> > > 1 file changed, 58 insertions(+)
> > >
> > > diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c
> > > index 3ecc688dda82..44ee0be27644 100644
> > > --- a/drivers/usb/typec/hd3ss3220.c
> > > +++ b/drivers/usb/typec/hd3ss3220.c
> > > @@ -54,6 +54,11 @@ struct hd3ss3220 {
> > > struct delayed_work output_poll_work;
> > > enum usb_role role_state;
> > > bool poll;
> > > +
> > > + struct gpio_desc *id_gpiod;
> > > + int id_irq;
> > > +
> > > + struct regulator *vbus;
> > > };
> > > static int hd3ss3220_set_power_opmode(struct hd3ss3220 *hd3ss3220, int power_opmode)
> > > @@ -319,6 +324,28 @@ static const struct regmap_config config = {
> > > .max_register = 0x0A,
> > > };
> > > +static irqreturn_t hd3ss3220_id_isr(int irq, void *dev_id)
> > > +{
> > > + struct hd3ss3220 *hd3ss3220 = dev_id;
> > > + int ret;
> > > + int id;
> > > +
> > > + if (IS_ERR_OR_NULL(hd3ss3220->vbus))
> > > + return IRQ_HANDLED;
> > > +
> > > + id = hd3ss3220->id_gpiod ? gpiod_get_value_cansleep(hd3ss3220->id_gpiod) : 1;
> > > +
> > > + if (!id) {
> > > + ret = regulator_enable(hd3ss3220->vbus);
> > > + if (ret)
> > > + dev_err(hd3ss3220->dev, "enable vbus regulator failed\n");
> > > + } else {
> > > + regulator_disable(hd3ss3220->vbus);
> > > + }
> > > +
> > > + return IRQ_HANDLED;
> > > +}
> > > +
> > > static int hd3ss3220_probe(struct i2c_client *client)
> > > {
> > > struct typec_capability typec_cap = { };
> > > @@ -354,6 +381,37 @@ static int hd3ss3220_probe(struct i2c_client *client)
> > > hd3ss3220->role_sw = usb_role_switch_get(hd3ss3220->dev);
> > > }
> > > + hd3ss3220->id_gpiod = devm_gpiod_get_optional(hd3ss3220->dev, "id", GPIOD_IN);
> > > + if (IS_ERR(hd3ss3220->id_gpiod))
> > > + return PTR_ERR(hd3ss3220->id_gpiod);
> > > +
> > > + if (hd3ss3220->id_gpiod) {
> > > + hd3ss3220->id_irq = gpiod_to_irq(hd3ss3220->id_gpiod);
> > > + if (hd3ss3220->id_irq < 0) {
> > > + dev_err(hd3ss3220->dev, "failed to get ID IRQ\n");
> > > + return hd3ss3220->id_irq;
> > > + }
> > > +
> > > + ret = devm_request_threaded_irq(hd3ss3220->dev,
> > > + hd3ss3220->id_irq, NULL,
> > > + hd3ss3220_id_isr,
> > > + IRQF_TRIGGER_RISING |
> > > + IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> > > + dev_name(hd3ss3220->dev), hd3ss3220);
> > > + if (ret < 0) {
> > > + dev_err(hd3ss3220->dev, "failed to get id irq\n");
> > > + return ret;
> > > + }
> > > + }
> > > +
> > > + hd3ss3220->vbus = devm_regulator_get_optional(hd3ss3220->dev, "vbus");
> > > + if (PTR_ERR(hd3ss3220->vbus) == -ENODEV)
> > > + hd3ss3220->vbus = NULL;
> > > +
> > > + if (IS_ERR(hd3ss3220->vbus))
> > > + return dev_err_probe(hd3ss3220->dev,
> > > + PTR_ERR(hd3ss3220->vbus), "failed to get vbus\n");
> > > +
> > > if (IS_ERR(hd3ss3220->role_sw)) {
> > > ret = PTR_ERR(hd3ss3220->role_sw);
> > > goto err_put_fwnode;
> > > --
> > > 2.34.1
> >
--
heikki
Hi Krishna,
kernel test robot noticed the following build errors:
[auto build test ERROR on usb/usb-testing]
[also build test ERROR on usb/usb-next usb/usb-linus robh/for-next krzk-dt/for-next linus/master v6.17 next-20251002]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Krishna-Kurapati/dt-bindings-usb-ti-hd3ss3220-Add-support-for-VBUS-based-on-ID-state/20251003-012933
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
patch link: https://lore.kernel.org/r/20251002172539.586538-3-krishna.kurapati%40oss.qualcomm.com
patch subject: [PATCH 2/2] usb: typec: hd3ss3220: Enable VBUS based on ID pin state
config: x86_64-buildonly-randconfig-001-20251003 (https://download.01.org/0day-ci/archive/20251003/202510031333.zZYEFOH0-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251003/202510031333.zZYEFOH0-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202510031333.zZYEFOH0-lkp@intel.com/
All errors (new ones prefixed by >>):
>> drivers/usb/typec/hd3ss3220.c:336:29: error: call to undeclared function 'gpiod_get_value_cansleep'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
336 | id = hd3ss3220->id_gpiod ? gpiod_get_value_cansleep(hd3ss3220->id_gpiod) : 1;
| ^
>> drivers/usb/typec/hd3ss3220.c:384:24: error: call to undeclared function 'devm_gpiod_get_optional'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
384 | hd3ss3220->id_gpiod = devm_gpiod_get_optional(hd3ss3220->dev, "id", GPIOD_IN);
| ^
drivers/usb/typec/hd3ss3220.c:384:24: note: did you mean 'devm_regulator_get_optional'?
include/linux/regulator/consumer.h:163:32: note: 'devm_regulator_get_optional' declared here
163 | struct regulator *__must_check devm_regulator_get_optional(struct device *dev,
| ^
>> drivers/usb/typec/hd3ss3220.c:384:70: error: use of undeclared identifier 'GPIOD_IN'
384 | hd3ss3220->id_gpiod = devm_gpiod_get_optional(hd3ss3220->dev, "id", GPIOD_IN);
| ^
>> drivers/usb/typec/hd3ss3220.c:389:23: error: call to undeclared function 'gpiod_to_irq'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
389 | hd3ss3220->id_irq = gpiod_to_irq(hd3ss3220->id_gpiod);
| ^
4 errors generated.
vim +/gpiod_get_value_cansleep +336 drivers/usb/typec/hd3ss3220.c
326
327 static irqreturn_t hd3ss3220_id_isr(int irq, void *dev_id)
328 {
329 struct hd3ss3220 *hd3ss3220 = dev_id;
330 int ret;
331 int id;
332
333 if (IS_ERR_OR_NULL(hd3ss3220->vbus))
334 return IRQ_HANDLED;
335
> 336 id = hd3ss3220->id_gpiod ? gpiod_get_value_cansleep(hd3ss3220->id_gpiod) : 1;
337
338 if (!id) {
339 ret = regulator_enable(hd3ss3220->vbus);
340 if (ret)
341 dev_err(hd3ss3220->dev, "enable vbus regulator failed\n");
342 } else {
343 regulator_disable(hd3ss3220->vbus);
344 }
345
346 return IRQ_HANDLED;
347 }
348
349 static int hd3ss3220_probe(struct i2c_client *client)
350 {
351 struct typec_capability typec_cap = { };
352 struct hd3ss3220 *hd3ss3220;
353 struct fwnode_handle *connector, *ep;
354 int ret;
355 unsigned int data;
356
357 hd3ss3220 = devm_kzalloc(&client->dev, sizeof(struct hd3ss3220),
358 GFP_KERNEL);
359 if (!hd3ss3220)
360 return -ENOMEM;
361
362 i2c_set_clientdata(client, hd3ss3220);
363
364 hd3ss3220->dev = &client->dev;
365 hd3ss3220->regmap = devm_regmap_init_i2c(client, &config);
366 if (IS_ERR(hd3ss3220->regmap))
367 return PTR_ERR(hd3ss3220->regmap);
368
369 /* For backward compatibility check the connector child node first */
370 connector = device_get_named_child_node(hd3ss3220->dev, "connector");
371 if (connector) {
372 hd3ss3220->role_sw = fwnode_usb_role_switch_get(connector);
373 } else {
374 ep = fwnode_graph_get_next_endpoint(dev_fwnode(hd3ss3220->dev), NULL);
375 if (!ep)
376 return -ENODEV;
377 connector = fwnode_graph_get_remote_port_parent(ep);
378 fwnode_handle_put(ep);
379 if (!connector)
380 return -ENODEV;
381 hd3ss3220->role_sw = usb_role_switch_get(hd3ss3220->dev);
382 }
383
> 384 hd3ss3220->id_gpiod = devm_gpiod_get_optional(hd3ss3220->dev, "id", GPIOD_IN);
385 if (IS_ERR(hd3ss3220->id_gpiod))
386 return PTR_ERR(hd3ss3220->id_gpiod);
387
388 if (hd3ss3220->id_gpiod) {
> 389 hd3ss3220->id_irq = gpiod_to_irq(hd3ss3220->id_gpiod);
390 if (hd3ss3220->id_irq < 0) {
391 dev_err(hd3ss3220->dev, "failed to get ID IRQ\n");
392 return hd3ss3220->id_irq;
393 }
394
395 ret = devm_request_threaded_irq(hd3ss3220->dev,
396 hd3ss3220->id_irq, NULL,
397 hd3ss3220_id_isr,
398 IRQF_TRIGGER_RISING |
399 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
400 dev_name(hd3ss3220->dev), hd3ss3220);
401 if (ret < 0) {
402 dev_err(hd3ss3220->dev, "failed to get id irq\n");
403 return ret;
404 }
405 }
406
407 hd3ss3220->vbus = devm_regulator_get_optional(hd3ss3220->dev, "vbus");
408 if (PTR_ERR(hd3ss3220->vbus) == -ENODEV)
409 hd3ss3220->vbus = NULL;
410
411 if (IS_ERR(hd3ss3220->vbus))
412 return dev_err_probe(hd3ss3220->dev,
413 PTR_ERR(hd3ss3220->vbus), "failed to get vbus\n");
414
415 if (IS_ERR(hd3ss3220->role_sw)) {
416 ret = PTR_ERR(hd3ss3220->role_sw);
417 goto err_put_fwnode;
418 }
419
420 typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE;
421 typec_cap.driver_data = hd3ss3220;
422 typec_cap.type = TYPEC_PORT_DRP;
423 typec_cap.data = TYPEC_PORT_DRD;
424 typec_cap.ops = &hd3ss3220_ops;
425 typec_cap.fwnode = connector;
426
427 ret = hd3ss3220_configure_source_pref(hd3ss3220, connector, &typec_cap);
428 if (ret < 0)
429 goto err_put_role;
430
431 ret = hd3ss3220_configure_port_type(hd3ss3220, connector, &typec_cap);
432 if (ret < 0)
433 goto err_put_role;
434
435 hd3ss3220->port = typec_register_port(&client->dev, &typec_cap);
436 if (IS_ERR(hd3ss3220->port)) {
437 ret = PTR_ERR(hd3ss3220->port);
438 goto err_put_role;
439 }
440
441 ret = hd3ss3220_configure_power_opmode(hd3ss3220, connector);
442 if (ret < 0)
443 goto err_unreg_port;
444
445 hd3ss3220_set_role(hd3ss3220);
446 ret = regmap_read(hd3ss3220->regmap, HD3SS3220_REG_CN_STAT_CTRL, &data);
447 if (ret < 0)
448 goto err_unreg_port;
449
450 if (data & HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS) {
451 ret = regmap_write(hd3ss3220->regmap,
452 HD3SS3220_REG_CN_STAT_CTRL,
453 data | HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS);
454 if (ret < 0)
455 goto err_unreg_port;
456 }
457
458 if (client->irq > 0) {
459 ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
460 hd3ss3220_irq_handler,
461 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
462 "hd3ss3220", &client->dev);
463 if (ret)
464 goto err_unreg_port;
465 } else {
466 INIT_DELAYED_WORK(&hd3ss3220->output_poll_work, output_poll_execute);
467 hd3ss3220->poll = true;
468 }
469
470 ret = i2c_smbus_read_byte_data(client, HD3SS3220_REG_DEV_REV);
471 if (ret < 0)
472 goto err_unreg_port;
473
474 fwnode_handle_put(connector);
475
476 if (hd3ss3220->poll)
477 schedule_delayed_work(&hd3ss3220->output_poll_work, HZ);
478
479 dev_info(&client->dev, "probed revision=0x%x\n", ret);
480
481 return 0;
482 err_unreg_port:
483 typec_unregister_port(hd3ss3220->port);
484 err_put_role:
485 usb_role_switch_put(hd3ss3220->role_sw);
486 err_put_fwnode:
487 fwnode_handle_put(connector);
488
489 return ret;
490 }
491
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
© 2016 - 2026 Red Hat, Inc.