All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH v3 2/2] usb: typec: rt1719: Add support for Richtek RT1719
@ 2022-02-08 13:27 kernel test robot
  0 siblings, 0 replies; 4+ messages in thread
From: kernel test robot @ 2022-02-08 13:27 UTC (permalink / raw)
  To: kbuild

[-- Attachment #1: Type: text/plain, Size: 8597 bytes --]

CC: kbuild-all(a)lists.01.org
In-Reply-To: <1644246970-18305-3-git-send-email-u0084500@gmail.com>
References: <1644246970-18305-3-git-send-email-u0084500@gmail.com>
TO: "cy_huang" <u0084500@gmail.com>

Hi cy_huang,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on usb/usb-testing]
[also build test WARNING on robh/for-next v5.17-rc3 next-20220208]
[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]

url:    https://github.com/0day-ci/linux/commits/cy_huang/Add-Richtek-RT1719-USBPD-controller-support/20220207-232502
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
:::::: branch date: 22 hours ago
:::::: commit date: 22 hours ago
config: openrisc-randconfig-m031-20220208 (https://download.01.org/0day-ci/archive/20220208/202202082156.rDIyTxRl-lkp(a)intel.com/config)
compiler: or1k-linux-gcc (GCC) 11.2.0

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>

smatch warnings:
drivers/usb/typec/rt1719.c:604 rt1719_irq_handler() error: uninitialized symbol 'conn_info'.
drivers/usb/typec/rt1719.c:605 rt1719_irq_handler() error: uninitialized symbol 'conn_stat'.
drivers/usb/typec/rt1719.c:671 rt1719_init_attach_state() error: uninitialized symbol 'conn_info'.
drivers/usb/typec/rt1719.c:672 rt1719_init_attach_state() error: uninitialized symbol 'conn_stat'.

vim +/conn_info +604 drivers/usb/typec/rt1719.c

cbda9406acddfe ChiYuan Huang 2022-02-07  590  
cbda9406acddfe ChiYuan Huang 2022-02-07  591  static irqreturn_t rt1719_irq_handler(int irq, void *priv)
cbda9406acddfe ChiYuan Huang 2022-02-07  592  {
cbda9406acddfe ChiYuan Huang 2022-02-07  593  	struct rt1719_data *data = priv;
cbda9406acddfe ChiYuan Huang 2022-02-07  594  	u32 events, conn_info;
cbda9406acddfe ChiYuan Huang 2022-02-07  595  	u16 conn_stat;
cbda9406acddfe ChiYuan Huang 2022-02-07  596  	int ret;
cbda9406acddfe ChiYuan Huang 2022-02-07  597  
cbda9406acddfe ChiYuan Huang 2022-02-07  598  	ret = rt1719_read32(data, RT1719_REG_EVENTS, &events);
cbda9406acddfe ChiYuan Huang 2022-02-07  599  	ret |= rt1719_read32(data, RT1719_REG_POLICYINFO, &conn_info);
cbda9406acddfe ChiYuan Huang 2022-02-07  600  	ret |= rt1719_read16(data, RT1719_REG_STATS, &conn_stat);
cbda9406acddfe ChiYuan Huang 2022-02-07  601  	if (ret)
cbda9406acddfe ChiYuan Huang 2022-02-07  602  		return IRQ_NONE;
cbda9406acddfe ChiYuan Huang 2022-02-07  603  
cbda9406acddfe ChiYuan Huang 2022-02-07 @604  	data->conn_info = conn_info;
cbda9406acddfe ChiYuan Huang 2022-02-07 @605  	data->conn_stat = conn_stat;
cbda9406acddfe ChiYuan Huang 2022-02-07  606  
cbda9406acddfe ChiYuan Huang 2022-02-07  607  	events &= (RT1719_INT_DRSW_ACCEPT | RT1719_INT_RX_SRCCAP |
cbda9406acddfe ChiYuan Huang 2022-02-07  608  		   RT1719_INT_VBUS_PRESENT | RT1719_INT_VBUS_DCT |
cbda9406acddfe ChiYuan Huang 2022-02-07  609  		   RT1719_INT_PE_SNK_RDY);
cbda9406acddfe ChiYuan Huang 2022-02-07  610  
cbda9406acddfe ChiYuan Huang 2022-02-07  611  	if (events & RT1719_INT_DRSW_ACCEPT)
cbda9406acddfe ChiYuan Huang 2022-02-07  612  		rt1719_update_data_role(data);
cbda9406acddfe ChiYuan Huang 2022-02-07  613  
cbda9406acddfe ChiYuan Huang 2022-02-07  614  	if (events & RT1719_INT_VBUS_PRESENT)
cbda9406acddfe ChiYuan Huang 2022-02-07  615  		rt1719_attach(data);
cbda9406acddfe ChiYuan Huang 2022-02-07  616  
cbda9406acddfe ChiYuan Huang 2022-02-07  617  	if (events & RT1719_INT_VBUS_DCT)
cbda9406acddfe ChiYuan Huang 2022-02-07  618  		rt1719_detach(data);
cbda9406acddfe ChiYuan Huang 2022-02-07  619  
cbda9406acddfe ChiYuan Huang 2022-02-07  620  	if (events & RT1719_INT_RX_SRCCAP)
cbda9406acddfe ChiYuan Huang 2022-02-07  621  		rt1719_update_source_pdos(data);
cbda9406acddfe ChiYuan Huang 2022-02-07  622  
cbda9406acddfe ChiYuan Huang 2022-02-07  623  	if (events & RT1719_INT_PE_SNK_RDY) {
cbda9406acddfe ChiYuan Huang 2022-02-07  624  		complete(&data->req_completion);
cbda9406acddfe ChiYuan Huang 2022-02-07  625  		rt1719_update_pwr_opmode(data);
cbda9406acddfe ChiYuan Huang 2022-02-07  626  	}
cbda9406acddfe ChiYuan Huang 2022-02-07  627  
cbda9406acddfe ChiYuan Huang 2022-02-07  628  	/* Write 1 to clear already handled events */
cbda9406acddfe ChiYuan Huang 2022-02-07  629  	rt1719_write32(data, RT1719_REG_EVENTS, events);
cbda9406acddfe ChiYuan Huang 2022-02-07  630  
cbda9406acddfe ChiYuan Huang 2022-02-07  631  	return IRQ_HANDLED;
cbda9406acddfe ChiYuan Huang 2022-02-07  632  }
cbda9406acddfe ChiYuan Huang 2022-02-07  633  
cbda9406acddfe ChiYuan Huang 2022-02-07  634  static int rt1719_irq_init(struct rt1719_data *data)
cbda9406acddfe ChiYuan Huang 2022-02-07  635  {
cbda9406acddfe ChiYuan Huang 2022-02-07  636  	struct i2c_client *i2c = to_i2c_client(data->dev);
cbda9406acddfe ChiYuan Huang 2022-02-07  637  	u32 irq_enable;
cbda9406acddfe ChiYuan Huang 2022-02-07  638  	int ret;
cbda9406acddfe ChiYuan Huang 2022-02-07  639  
cbda9406acddfe ChiYuan Huang 2022-02-07  640  	irq_enable = RT1719_INT_DRSW_ACCEPT | RT1719_INT_RX_SRCCAP |
cbda9406acddfe ChiYuan Huang 2022-02-07  641  		     RT1719_INT_VBUS_DCT | RT1719_INT_VBUS_PRESENT |
cbda9406acddfe ChiYuan Huang 2022-02-07  642  		     RT1719_INT_PE_SNK_RDY;
cbda9406acddfe ChiYuan Huang 2022-02-07  643  
cbda9406acddfe ChiYuan Huang 2022-02-07  644  	ret = rt1719_write32(data, RT1719_REG_MASKS, irq_enable);
cbda9406acddfe ChiYuan Huang 2022-02-07  645  	if (ret) {
cbda9406acddfe ChiYuan Huang 2022-02-07  646  		dev_err(&i2c->dev, "Failed to config irq enable\n");
cbda9406acddfe ChiYuan Huang 2022-02-07  647  		return ret;
cbda9406acddfe ChiYuan Huang 2022-02-07  648  	}
cbda9406acddfe ChiYuan Huang 2022-02-07  649  
cbda9406acddfe ChiYuan Huang 2022-02-07  650  	return devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
cbda9406acddfe ChiYuan Huang 2022-02-07  651  					 rt1719_irq_handler, IRQF_ONESHOT,
cbda9406acddfe ChiYuan Huang 2022-02-07  652  					 dev_name(&i2c->dev), data);
cbda9406acddfe ChiYuan Huang 2022-02-07  653  }
cbda9406acddfe ChiYuan Huang 2022-02-07  654  
cbda9406acddfe ChiYuan Huang 2022-02-07  655  static int rt1719_init_attach_state(struct rt1719_data *data)
cbda9406acddfe ChiYuan Huang 2022-02-07  656  {
cbda9406acddfe ChiYuan Huang 2022-02-07  657  	u32 conn_info, irq_clear;
cbda9406acddfe ChiYuan Huang 2022-02-07  658  	u16 conn_stat;
cbda9406acddfe ChiYuan Huang 2022-02-07  659  	int ret;
cbda9406acddfe ChiYuan Huang 2022-02-07  660  
cbda9406acddfe ChiYuan Huang 2022-02-07  661  	irq_clear = RT1719_INT_DRSW_ACCEPT | RT1719_INT_RX_SRCCAP |
cbda9406acddfe ChiYuan Huang 2022-02-07  662  		    RT1719_INT_VBUS_DCT | RT1719_INT_VBUS_PRESENT |
cbda9406acddfe ChiYuan Huang 2022-02-07  663  		    RT1719_INT_PE_SNK_RDY;
cbda9406acddfe ChiYuan Huang 2022-02-07  664  
cbda9406acddfe ChiYuan Huang 2022-02-07  665  	ret = rt1719_read32(data, RT1719_REG_POLICYINFO, &conn_info);
cbda9406acddfe ChiYuan Huang 2022-02-07  666  	ret |= rt1719_read16(data, RT1719_REG_STATS, &conn_stat);
cbda9406acddfe ChiYuan Huang 2022-02-07  667  	ret |= rt1719_write32(data, RT1719_REG_EVENTS, irq_clear);
cbda9406acddfe ChiYuan Huang 2022-02-07  668  	if (ret)
cbda9406acddfe ChiYuan Huang 2022-02-07  669  		return ret;
cbda9406acddfe ChiYuan Huang 2022-02-07  670  
cbda9406acddfe ChiYuan Huang 2022-02-07 @671  	data->conn_info = conn_info;
cbda9406acddfe ChiYuan Huang 2022-02-07 @672  	data->conn_stat = conn_stat;
cbda9406acddfe ChiYuan Huang 2022-02-07  673  
cbda9406acddfe ChiYuan Huang 2022-02-07  674  	if (conn_info & RT1719_ATTACHDEV_MASK)
cbda9406acddfe ChiYuan Huang 2022-02-07  675  		rt1719_attach(data);
cbda9406acddfe ChiYuan Huang 2022-02-07  676  
cbda9406acddfe ChiYuan Huang 2022-02-07  677  	if (conn_info & RT1719_PE_EXP_CONTRACT) {
cbda9406acddfe ChiYuan Huang 2022-02-07  678  		rt1719_update_source_pdos(data);
cbda9406acddfe ChiYuan Huang 2022-02-07  679  		rt1719_update_pwr_opmode(data);
cbda9406acddfe ChiYuan Huang 2022-02-07  680  	}
cbda9406acddfe ChiYuan Huang 2022-02-07  681  
cbda9406acddfe ChiYuan Huang 2022-02-07  682  	return 0;
cbda9406acddfe ChiYuan Huang 2022-02-07  683  }
cbda9406acddfe ChiYuan Huang 2022-02-07  684  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v3 2/2] usb: typec: rt1719: Add support for Richtek RT1719
  2022-02-09 12:52   ` Heikki Krogerus
@ 2022-02-09 13:26     ` ChiYuan Huang
  0 siblings, 0 replies; 4+ messages in thread
From: ChiYuan Huang @ 2022-02-09 13:26 UTC (permalink / raw)
  To: Heikki Krogerus
  Cc: Rob Herring, Greg KH,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Linux USB List, lkml, cy_huang, will_lin, th_chuang

Heikki Krogerus <heikki.krogerus@linux.intel.com> 於 2022年2月9日 週三 下午8:52寫道:
>
> Hi,
>
> Sorry, there is still one more problem that I realised - not your
> problem, but a problem with fw_devlink_purge_absent_suppliers()...
>
> On Mon, Feb 07, 2022 at 11:16:10PM +0800, cy_huang wrote:
> > +static int rt1719_probe(struct i2c_client *i2c)
> > +{
> > +     struct rt1719_data *data;
> > +     struct fwnode_handle *fwnode;
> > +     struct typec_capability typec_cap = { };
> > +     int ret;
> > +
> > +     data = devm_kzalloc(&i2c->dev, sizeof(*data), GFP_KERNEL);
> > +     if (!data)
> > +             return -ENOMEM;
> > +
> > +     data->dev = &i2c->dev;
> > +     init_completion(&data->req_completion);
> > +
> > +     data->regmap = devm_regmap_init_i2c(i2c, &rt1719_regmap_config);
> > +     if (IS_ERR(data->regmap)) {
> > +             ret = PTR_ERR(data->regmap);
> > +             dev_err(&i2c->dev, "Failed to init regmap (%d)\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     ret = rt1719_check_exist(data);
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = rt1719_get_caps(data);
> > +     if (ret)
> > +             return ret;
> > +
> > +     /*
> > +      * This fwnode has a "compatible" property, but is never populated as a
> > +      * struct device. Instead we simply parse it to read the properties.
> > +      * This it breaks fw_devlink=on. To maintain backward compatibility
> > +      * with existing DT files, we work around this by deleting any
> > +      * fwnode_links to/from this fwnode.
> > +      */
> > +     fwnode = device_get_named_child_node(&i2c->dev, "connector");
> > +     if (!fwnode)
> > +             return -ENODEV;
> > +
> > +     fw_devlink_purge_absent_suppliers(fwnode);
>
> So don't use that function unless you really see some issue that it's
> fixing yourself.
>
> That function is broken. It breaks at least if the fwnode is shared -
> fwnodes can be, and are, shared - most likely it's broken in
> some other ways too. That function seems to be a hack for some
> individual problem that was caused by some deeper problem with the
> device links.
>
> We really need to figure out a fix for the core problem now instead of
> trying to come up with these hacks for every single separate scenario
> that is breaking because of the core problem, what ever that core
> problem may be.
>
OK, I'll remove it.

Originally, I think the others use it to solve fwlink problem.
But I didn't realize actually it's a workaround.
> > +     data->role_sw = fwnode_usb_role_switch_get(fwnode);
> > +     if (IS_ERR(data->role_sw)) {
> > +             ret = PTR_ERR(data->role_sw);
> > +             dev_err(&i2c->dev, "Failed to get usb role switch (%d)\n", ret);
> > +             goto err_fwnode_put;
> > +     }
> > +
> > +     ret = devm_rt1719_psy_register(data);
> > +     if (ret) {
> > +             dev_err(&i2c->dev, "Failed to register psy (%d)\n", ret);
> > +             goto err_role_put;
> > +     }
> > +
> > +     typec_cap.revision = USB_TYPEC_REV_1_2;
> > +     typec_cap.pd_revision = 0x300;  /* USB-PD spec release 3.0 */
> > +     typec_cap.type = TYPEC_PORT_SNK;
> > +     typec_cap.data = TYPEC_PORT_DRD;
> > +     typec_cap.ops = &rt1719_port_ops;
> > +     typec_cap.fwnode = fwnode;
> > +     typec_cap.driver_data = data;
> > +     typec_cap.accessory[0] = TYPEC_ACCESSORY_DEBUG;
> > +
> > +     data->partner_desc.identity = &data->partner_ident;
> > +
> > +     data->port = typec_register_port(&i2c->dev, &typec_cap);
> > +     if (IS_ERR(data->port)) {
> > +             ret = PTR_ERR(data->port);
> > +             dev_err(&i2c->dev, "Failed to register typec port (%d)\n", ret);
> > +             goto err_role_put;
> > +     }
> > +
> > +     ret = rt1719_init_attach_state(data);
> > +     if (ret) {
> > +             dev_err(&i2c->dev, "Failed to init attach state (%d)\n", ret);
> > +             goto err_role_put;
> > +     }
> > +
> > +     ret = rt1719_irq_init(data);
> > +     if (ret) {
> > +             dev_err(&i2c->dev, "Failed to init irq\n");
> > +             goto err_role_put;
> > +     }
> > +
> > +     fwnode_handle_put(fwnode);
> > +
> > +     i2c_set_clientdata(i2c, data);
> > +
> > +     return 0;
> > +
> > +err_role_put:
> > +     usb_role_switch_put(data->role_sw);
> > +err_fwnode_put:
> > +     fwnode_handle_put(fwnode);
> > +
> > +     return ret;
> > +}
> > +
> > +static int rt1719_remove(struct i2c_client *i2c)
> > +{
> > +     struct rt1719_data *data = i2c_get_clientdata(i2c);
> > +
> > +     typec_unregister_port(data->port);
> > +     usb_role_switch_put(data->role_sw);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct of_device_id __maybe_unused rt1719_device_table[] = {
> > +     { .compatible = "richtek,rt1719", },
> > +     { }
> > +};
> > +MODULE_DEVICE_TABLE(of, rt1719_device_table);
> > +
> > +static struct i2c_driver rt1719_driver = {
> > +     .driver = {
> > +             .name = "rt1719",
> > +             .of_match_table = rt1719_device_table,
> > +     },
> > +     .probe_new = rt1719_probe,
> > +     .remove = rt1719_remove,
> > +};
> > +module_i2c_driver(rt1719_driver);
> > +
> > +MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
> > +MODULE_DESCRIPTION("Richtek RT1719 Sink Only USBPD Controller Driver");
> > +MODULE_LICENSE("GPL v2");
>
> Oh, you probable want the make that "GPL" instead. See
> Documentation/process/license-rules.rst.
>
Ack in next.
> thanks,
>
> --
> heikki

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v3 2/2] usb: typec: rt1719: Add support for Richtek RT1719
  2022-02-07 15:16 ` [PATCH v3 2/2] usb: typec: rt1719: Add support for Richtek RT1719 cy_huang
@ 2022-02-09 12:52   ` Heikki Krogerus
  2022-02-09 13:26     ` ChiYuan Huang
  0 siblings, 1 reply; 4+ messages in thread
From: Heikki Krogerus @ 2022-02-09 12:52 UTC (permalink / raw)
  To: cy_huang
  Cc: robh+dt, gregkh, devicetree, linux-usb, linux-kernel, cy_huang,
	will_lin, th_chuang

Hi,

Sorry, there is still one more problem that I realised - not your
problem, but a problem with fw_devlink_purge_absent_suppliers()...

On Mon, Feb 07, 2022 at 11:16:10PM +0800, cy_huang wrote:
> +static int rt1719_probe(struct i2c_client *i2c)
> +{
> +	struct rt1719_data *data;
> +	struct fwnode_handle *fwnode;
> +	struct typec_capability typec_cap = { };
> +	int ret;
> +
> +	data = devm_kzalloc(&i2c->dev, sizeof(*data), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	data->dev = &i2c->dev;
> +	init_completion(&data->req_completion);
> +
> +	data->regmap = devm_regmap_init_i2c(i2c, &rt1719_regmap_config);
> +	if (IS_ERR(data->regmap)) {
> +		ret = PTR_ERR(data->regmap);
> +		dev_err(&i2c->dev, "Failed to init regmap (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = rt1719_check_exist(data);
> +	if (ret)
> +		return ret;
> +
> +	ret = rt1719_get_caps(data);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * This fwnode has a "compatible" property, but is never populated as a
> +	 * struct device. Instead we simply parse it to read the properties.
> +	 * This it breaks fw_devlink=on. To maintain backward compatibility
> +	 * with existing DT files, we work around this by deleting any
> +	 * fwnode_links to/from this fwnode.
> +	 */
> +	fwnode = device_get_named_child_node(&i2c->dev, "connector");
> +	if (!fwnode)
> +		return -ENODEV;
> +
> +	fw_devlink_purge_absent_suppliers(fwnode);

So don't use that function unless you really see some issue that it's
fixing yourself.

That function is broken. It breaks at least if the fwnode is shared -
fwnodes can be, and are, shared - most likely it's broken in
some other ways too. That function seems to be a hack for some
individual problem that was caused by some deeper problem with the
device links.

We really need to figure out a fix for the core problem now instead of
trying to come up with these hacks for every single separate scenario
that is breaking because of the core problem, what ever that core
problem may be.

> +	data->role_sw = fwnode_usb_role_switch_get(fwnode);
> +	if (IS_ERR(data->role_sw)) {
> +		ret = PTR_ERR(data->role_sw);
> +		dev_err(&i2c->dev, "Failed to get usb role switch (%d)\n", ret);
> +		goto err_fwnode_put;
> +	}
> +
> +	ret = devm_rt1719_psy_register(data);
> +	if (ret) {
> +		dev_err(&i2c->dev, "Failed to register psy (%d)\n", ret);
> +		goto err_role_put;
> +	}
> +
> +	typec_cap.revision = USB_TYPEC_REV_1_2;
> +	typec_cap.pd_revision = 0x300;	/* USB-PD spec release 3.0 */
> +	typec_cap.type = TYPEC_PORT_SNK;
> +	typec_cap.data = TYPEC_PORT_DRD;
> +	typec_cap.ops = &rt1719_port_ops;
> +	typec_cap.fwnode = fwnode;
> +	typec_cap.driver_data = data;
> +	typec_cap.accessory[0] = TYPEC_ACCESSORY_DEBUG;
> +
> +	data->partner_desc.identity = &data->partner_ident;
> +
> +	data->port = typec_register_port(&i2c->dev, &typec_cap);
> +	if (IS_ERR(data->port)) {
> +		ret = PTR_ERR(data->port);
> +		dev_err(&i2c->dev, "Failed to register typec port (%d)\n", ret);
> +		goto err_role_put;
> +	}
> +
> +	ret = rt1719_init_attach_state(data);
> +	if (ret) {
> +		dev_err(&i2c->dev, "Failed to init attach state (%d)\n", ret);
> +		goto err_role_put;
> +	}
> +
> +	ret = rt1719_irq_init(data);
> +	if (ret) {
> +		dev_err(&i2c->dev, "Failed to init irq\n");
> +		goto err_role_put;
> +	}
> +
> +	fwnode_handle_put(fwnode);
> +
> +	i2c_set_clientdata(i2c, data);
> +
> +	return 0;
> +
> +err_role_put:
> +	usb_role_switch_put(data->role_sw);
> +err_fwnode_put:
> +	fwnode_handle_put(fwnode);
> +
> +	return ret;
> +}
> +
> +static int rt1719_remove(struct i2c_client *i2c)
> +{
> +	struct rt1719_data *data = i2c_get_clientdata(i2c);
> +
> +	typec_unregister_port(data->port);
> +	usb_role_switch_put(data->role_sw);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id __maybe_unused rt1719_device_table[] = {
> +	{ .compatible = "richtek,rt1719", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, rt1719_device_table);
> +
> +static struct i2c_driver rt1719_driver = {
> +	.driver = {
> +		.name = "rt1719",
> +		.of_match_table = rt1719_device_table,
> +	},
> +	.probe_new = rt1719_probe,
> +	.remove = rt1719_remove,
> +};
> +module_i2c_driver(rt1719_driver);
> +
> +MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
> +MODULE_DESCRIPTION("Richtek RT1719 Sink Only USBPD Controller Driver");
> +MODULE_LICENSE("GPL v2");

Oh, you probable want the make that "GPL" instead. See
Documentation/process/license-rules.rst.

thanks,

-- 
heikki

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v3 2/2] usb: typec: rt1719: Add support for Richtek RT1719
  2022-02-07 15:16 [PATCH v3 0/2] Add Richtek RT1719 USBPD controller support cy_huang
@ 2022-02-07 15:16 ` cy_huang
  2022-02-09 12:52   ` Heikki Krogerus
  0 siblings, 1 reply; 4+ messages in thread
From: cy_huang @ 2022-02-07 15:16 UTC (permalink / raw)
  To: robh+dt, heikki.krogerus
  Cc: gregkh, devicetree, linux-usb, linux-kernel, cy_huang, will_lin,
	th_chuang

From: ChiYuan Huang <cy_huang@richtek.com>

Richtek RT1719 is a sink-only Type-C PD controller it complies with
latest USB Type-C and PD standards. It integrates the physical layer of
USB power delivery protocol to allow up to 100W of power.

Signed-off-by: ChiYuan Huang <cy_huang@richtek.com>
---
 drivers/usb/typec/Kconfig  |  12 +
 drivers/usb/typec/Makefile |   1 +
 drivers/usb/typec/rt1719.c | 970 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 983 insertions(+)
 create mode 100644 drivers/usb/typec/rt1719.c

diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index ab480f3..bc918ca 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -52,6 +52,18 @@ source "drivers/usb/typec/ucsi/Kconfig"
 
 source "drivers/usb/typec/tipd/Kconfig"
 
+config TYPEC_RT1719
+	tristate "Richtek RT1719 Sink Only Type-C controller driver"
+	depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  Say Y or M here if your system has Richtek RT1719 sink only
+	  Type-C port controller driver.
+
+	  If you choose to build this driver as a dynamically linked module, the
+	  module will be called rt1719.ko
+
 config TYPEC_HD3SS3220
 	tristate "TI HD3SS3220 Type-C DRP Port controller driver"
 	depends on I2C
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index 57870a2..441dd6c 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -9,4 +9,5 @@ obj-$(CONFIG_TYPEC_TPS6598X)	+= tipd/
 obj-$(CONFIG_TYPEC_HD3SS3220)	+= hd3ss3220.o
 obj-$(CONFIG_TYPEC_QCOM_PMIC)	+= qcom-pmic-typec.o
 obj-$(CONFIG_TYPEC_STUSB160X) 	+= stusb160x.o
+obj-$(CONFIG_TYPEC_RT1719)	+= rt1719.o
 obj-$(CONFIG_TYPEC)		+= mux/
diff --git a/drivers/usb/typec/rt1719.c b/drivers/usb/typec/rt1719.c
new file mode 100644
index 00000000..84ed69c
--- /dev/null
+++ b/drivers/usb/typec/rt1719.c
@@ -0,0 +1,970 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bitfield.h>
+#include <linux/completion.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/usb/pd.h>
+#include <linux/usb/role.h>
+#include <linux/usb/typec.h>
+
+#define RT1719_REG_TXCTRL1	0x03
+#define RT1719_REG_TXCTRL2	0x04
+#define RT1719_REG_POLICYINFO	0x0E
+#define RT1719_REG_SRCPDO1	0x11
+#define RT1719_REG_MASKS	0x2D
+#define RT1719_REG_EVENTS	0x33
+#define RT1719_REG_STATS	0x37
+#define RT1719_REG_PSELINFO	0x3C
+#define RT1719_REG_USBSETINFO	0x3E
+#define RT1719_REG_VENID	0x82
+
+#define RT1719_UNIQUE_PID	0x1719
+#define RT1719_REQDRSWAP_MASK	BIT(7)
+#define RT1719_EVALMODE_MASK	BIT(4)
+#define RT1719_REQSRCPDO_MASK	GENMASK(2, 0)
+#define RT1719_TXSPDOREQ_MASK	BIT(7)
+#define RT1719_INT_DRSW_ACCEPT	BIT(23)
+#define RT1719_INT_RX_SRCCAP	BIT(21)
+#define RT1719_INT_VBUS_DCT	BIT(6)
+#define RT1719_INT_VBUS_PRESENT	BIT(5)
+#define RT1719_INT_PE_SNK_RDY	BIT(2)
+#define RT1719_CC1_STAT		GENMASK(9, 8)
+#define RT1719_CC2_STAT		GENMASK(11, 10)
+#define RT1719_POLARITY_MASK	BIT(23)
+#define RT1719_DATAROLE_MASK	BIT(22)
+#define RT1719_PDSPECREV_MASK	GENMASK(21, 20)
+#define RT1719_SPDOSEL_MASK	GENMASK(18, 16)
+#define RT1719_SPDONUM_MASK	GENMASK(15, 13)
+#define RT1719_ATTACH_VBUS	BIT(12)
+#define RT1719_ATTACH_DBG	BIT(10)
+#define RT1719_ATTACH_SNK	BIT(9)
+#define RT1719_ATTACHDEV_MASK	(RT1719_ATTACH_VBUS | RT1719_ATTACH_DBG | \
+				 RT1719_ATTACH_SNK)
+#define RT1719_PE_EXP_CONTRACT	BIT(2)
+#define RT1719_PSEL_SUPPORT	BIT(15)
+#define RT1719_TBLSEL_MASK	BIT(6)
+#define RT1719_LATPSEL_MASK	GENMASK(5, 0)
+#define RT1719_USBINFO_MASK	GENMASK(1, 0)
+#define RT1719_USB_DFPUFP	3
+#define RT1719_MAX_SRCPDO	7
+
+enum {
+	SNK_PWR_OPEN = 0,
+	SNK_PWR_DEF,
+	SNK_PWR_1P5A,
+	SNK_PWR_3A
+};
+
+enum {
+	USBPD_SPECREV_1_0 = 0,
+	USBPD_SPECREV_2_0,
+	USBPD_SPECREV_3_0
+};
+
+enum rt1719_snkcap {
+	RT1719_SNKCAP_5V = 0,
+	RT1719_SNKCAP_9V,
+	RT1719_SNKCAP_12V,
+	RT1719_SNKCAP_15V,
+	RT1719_SNKCAP_20V,
+	RT1719_MAX_SNKCAP
+};
+
+struct rt1719_psel_cap {
+	u8 lomask;
+	u8 himask;
+	u32 milliwatt;
+	u32 milliamp;
+};
+
+struct rt1719_data {
+	struct device *dev;
+	struct regmap *regmap;
+	struct typec_port *port;
+	struct usb_role_switch *role_sw;
+	struct power_supply *psy;
+	struct typec_partner *partner;
+	struct power_supply_desc psy_desc;
+	struct usb_pd_identity partner_ident;
+	struct typec_partner_desc partner_desc;
+	struct completion req_completion;
+	enum power_supply_usb_type usb_type;
+	bool attached;
+	bool pd_capable;
+	bool drswap_support;
+	u32 voltage;
+	u32 req_voltage;
+	u32 max_current;
+	u32 op_current;
+	u32 spdos[RT1719_MAX_SRCPDO];
+	u16 snkcaps[RT1719_MAX_SNKCAP];
+	int spdo_num;
+	int spdo_sel;
+	u32 conn_info;
+	u16 conn_stat;
+};
+
+static const enum power_supply_usb_type rt1719_psy_usb_types[] = {
+	POWER_SUPPLY_USB_TYPE_C,
+	POWER_SUPPLY_USB_TYPE_PD,
+	POWER_SUPPLY_USB_TYPE_PD_PPS
+};
+
+static const enum power_supply_property rt1719_psy_properties[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_USB_TYPE,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_CURRENT_NOW
+};
+
+static int rt1719_read16(struct rt1719_data *data, unsigned int reg, u16 *val)
+{
+	__le16 regval;
+	int ret;
+
+	ret = regmap_raw_read(data->regmap, reg, &regval, sizeof(regval));
+	if (ret)
+		return ret;
+
+	*val = le16_to_cpu(regval);
+	return 0;
+}
+
+static int rt1719_read32(struct rt1719_data *data, unsigned int reg, u32 *val)
+{
+	__le32 regval;
+	int ret;
+
+	ret = regmap_raw_read(data->regmap, reg, &regval, sizeof(regval));
+	if (ret)
+		return ret;
+
+	*val = le32_to_cpu(regval);
+	return 0;
+}
+
+static int rt1719_write32(struct rt1719_data *data, unsigned int reg, u32 val)
+{
+	__le32 regval = cpu_to_le32(val);
+
+	return regmap_raw_write(data->regmap, reg, &regval, sizeof(regval));
+}
+
+static enum typec_pwr_opmode rt1719_get_pwr_opmode(u32 conn, u16 stat)
+{
+	u16 cc1, cc2, cc_stat;
+
+	cc1 = FIELD_GET(RT1719_CC1_STAT, stat);
+	cc2 = FIELD_GET(RT1719_CC2_STAT, stat);
+
+	if (conn & RT1719_ATTACH_SNK) {
+		if (conn & RT1719_POLARITY_MASK)
+			cc_stat = cc2;
+		else
+			cc_stat = cc1;
+
+		switch (cc_stat) {
+		case SNK_PWR_3A:
+			return TYPEC_PWR_MODE_3_0A;
+		case SNK_PWR_1P5A:
+			return TYPEC_PWR_MODE_1_5A;
+		}
+	} else if (conn & RT1719_ATTACH_DBG) {
+		if ((cc1 == SNK_PWR_1P5A && cc2 == SNK_PWR_DEF) ||
+		    (cc1 == SNK_PWR_DEF && cc2 == SNK_PWR_1P5A))
+			return TYPEC_PWR_MODE_1_5A;
+		else if ((cc1 == SNK_PWR_3A && cc2 == SNK_PWR_DEF) ||
+			 (cc1 == SNK_PWR_DEF && cc2 == SNK_PWR_3A))
+			return TYPEC_PWR_MODE_3_0A;
+	}
+
+	return TYPEC_PWR_MODE_USB;
+}
+
+static enum typec_data_role rt1719_get_data_role(u32 conn)
+{
+	if (conn & RT1719_DATAROLE_MASK)
+		return TYPEC_HOST;
+	return TYPEC_DEVICE;
+}
+
+static void rt1719_set_data_role(struct rt1719_data *data,
+				 enum typec_data_role data_role,
+				 bool attached)
+{
+	enum usb_role usb_role = USB_ROLE_NONE;
+
+	if (attached) {
+		if (data_role == TYPEC_HOST)
+			usb_role = USB_ROLE_HOST;
+		else
+			usb_role = USB_ROLE_DEVICE;
+	}
+
+	usb_role_switch_set_role(data->role_sw, usb_role);
+	typec_set_data_role(data->port, data_role);
+}
+
+static void rt1719_update_data_role(struct rt1719_data *data)
+{
+	if (!data->attached)
+		return;
+
+	rt1719_set_data_role(data, rt1719_get_data_role(data->conn_info), true);
+}
+
+static void rt1719_register_partner(struct rt1719_data *data)
+{
+	u16 spec_rev = 0;
+
+	if (data->pd_capable) {
+		u32 rev;
+
+		rev = FIELD_GET(RT1719_PDSPECREV_MASK, data->conn_info);
+		switch (rev) {
+		case USBPD_SPECREV_3_0:
+			spec_rev = 0x0300;
+			break;
+		case USBPD_SPECREV_2_0:
+			spec_rev = 0x0200;
+			break;
+		default:
+			spec_rev = 0x0100;
+			break;
+		}
+	}
+
+	/* Just to prevent multiple times attach */
+	if (data->partner)
+		typec_unregister_partner(data->partner);
+
+	memset(&data->partner_ident, 0, sizeof(data->partner_ident));
+	data->partner_desc.usb_pd = data->pd_capable;
+	data->partner_desc.pd_revision = spec_rev;
+
+	if (data->conn_info & RT1719_ATTACH_DBG)
+		data->partner_desc.accessory = TYPEC_ACCESSORY_DEBUG;
+	else
+		data->partner_desc.accessory = TYPEC_ACCESSORY_NONE;
+
+	data->partner = typec_register_partner(data->port, &data->partner_desc);
+}
+
+static void rt1719_attach(struct rt1719_data *data)
+{
+	enum typec_pwr_opmode pwr_opmode;
+	enum typec_data_role data_role;
+	u32 volt = 5000, curr = 500;
+
+	if (!(data->conn_info & RT1719_ATTACHDEV_MASK))
+		return;
+
+	pwr_opmode = rt1719_get_pwr_opmode(data->conn_info, data->conn_stat);
+	data_role = rt1719_get_data_role(data->conn_info);
+
+	typec_set_pwr_opmode(data->port, pwr_opmode);
+	rt1719_set_data_role(data, data_role, true);
+
+	if (data->conn_info & RT1719_ATTACH_SNK)
+		rt1719_register_partner(data);
+
+	if (pwr_opmode == TYPEC_PWR_MODE_3_0A)
+		curr = 3000;
+	else if (pwr_opmode == TYPEC_PWR_MODE_1_5A)
+		curr = 1500;
+
+	data->voltage = volt * 1000;
+	data->max_current = data->op_current = curr * 1000;
+	data->attached = true;
+
+	power_supply_changed(data->psy);
+}
+
+static void rt1719_detach(struct rt1719_data *data)
+{
+	if (!data->attached || (data->conn_info & RT1719_ATTACHDEV_MASK))
+		return;
+
+	typec_unregister_partner(data->partner);
+	data->partner = NULL;
+
+	typec_set_pwr_opmode(data->port, TYPEC_PWR_MODE_USB);
+	rt1719_set_data_role(data, TYPEC_DEVICE, false);
+
+	memset32(data->spdos, 0, RT1719_MAX_SRCPDO);
+	data->spdo_num = 0;
+	data->voltage = data->max_current = data->op_current = 0;
+	data->attached = data->pd_capable = false;
+
+	data->usb_type = POWER_SUPPLY_USB_TYPE_C;
+
+	power_supply_changed(data->psy);
+}
+
+static void rt1719_update_operating_status(struct rt1719_data *data)
+{
+	enum power_supply_usb_type usb_type = POWER_SUPPLY_USB_TYPE_PD;
+	u32 voltage, max_current, op_current;
+	int i, snk_sel;
+
+	for (i = 0; i < data->spdo_num; i++) {
+		u32 pdo = data->spdos[i];
+		enum pd_pdo_type type = pdo_type(pdo);
+
+		if (type == PDO_TYPE_APDO) {
+			usb_type = POWER_SUPPLY_USB_TYPE_PD_PPS;
+			break;
+		}
+	}
+
+	data->spdo_sel = FIELD_GET(RT1719_SPDOSEL_MASK, data->conn_info);
+	if (data->spdo_sel <= 0)
+		return;
+
+	data->usb_type = usb_type;
+
+	voltage = pdo_fixed_voltage(data->spdos[data->spdo_sel - 1]);
+	max_current = pdo_max_current(data->spdos[data->spdo_sel - 1]);
+
+	switch (voltage) {
+	case 5000:
+		snk_sel = RT1719_SNKCAP_5V;
+		break;
+	case 9000:
+		snk_sel = RT1719_SNKCAP_9V;
+		break;
+	case 12000:
+		snk_sel = RT1719_SNKCAP_12V;
+		break;
+	case 15000:
+		snk_sel = RT1719_SNKCAP_15V;
+		break;
+	case 20000:
+		snk_sel = RT1719_SNKCAP_20V;
+		break;
+	default:
+		return;
+	}
+
+	op_current = min(max_current, pdo_max_current(data->snkcaps[snk_sel]));
+
+	/* covert mV/mA to uV/uA */
+	data->voltage = voltage * 1000;
+	data->max_current = max_current * 1000;
+	data->op_current = op_current * 1000;
+
+	power_supply_changed(data->psy);
+}
+
+static void rt1719_update_pwr_opmode(struct rt1719_data *data)
+{
+	if (!data->attached)
+		return;
+
+	if (!data->pd_capable) {
+		data->pd_capable = true;
+
+		typec_set_pwr_opmode(data->port, TYPEC_PWR_MODE_PD);
+		rt1719_register_partner(data);
+	}
+
+	rt1719_update_operating_status(data);
+}
+
+static void rt1719_update_source_pdos(struct rt1719_data *data)
+{
+	int spdo_num = FIELD_GET(RT1719_SPDONUM_MASK, data->conn_info);
+	__le32 src_pdos[RT1719_MAX_SRCPDO] = { };
+	int i, ret;
+
+	if (!data->attached)
+		return;
+
+	ret = regmap_raw_read(data->regmap, RT1719_REG_SRCPDO1, src_pdos,
+			      sizeof(__le32) * spdo_num);
+	if (ret)
+		return;
+
+	data->spdo_num = spdo_num;
+	for (i = 0; i < spdo_num; i++)
+		data->spdos[i] = le32_to_cpu(src_pdos[i]);
+}
+
+static int rt1719_dr_set(struct typec_port *port, enum typec_data_role role)
+{
+	struct rt1719_data *data = typec_get_drvdata(port);
+	enum typec_data_role cur_role;
+	int ret;
+
+	if (!data->attached || !data->pd_capable || !data->drswap_support)
+		return -EOPNOTSUPP;
+
+	if (data->spdo_num > 0 && !(data->spdos[0] & PDO_FIXED_DATA_SWAP))
+		return -EINVAL;
+
+	cur_role = rt1719_get_data_role(data->conn_info);
+	if (cur_role == role)
+		return 0;
+
+	ret = regmap_update_bits(data->regmap, RT1719_REG_TXCTRL1,
+				 RT1719_REQDRSWAP_MASK, RT1719_REQDRSWAP_MASK);
+	if (ret)
+		return ret;
+
+	reinit_completion(&data->req_completion);
+	ret = wait_for_completion_timeout(&data->req_completion,
+					  msecs_to_jiffies(400));
+	if (ret == 0)
+		return -ETIMEDOUT;
+
+	cur_role = rt1719_get_data_role(data->conn_info);
+	if (cur_role != role)
+		return -EAGAIN;
+
+	rt1719_set_data_role(data, role, true);
+	return 0;
+}
+
+static const struct typec_operations rt1719_port_ops = {
+	.dr_set = rt1719_dr_set,
+};
+
+static int rt1719_usbpd_request_voltage(struct rt1719_data *data)
+{
+	u32 src_voltage;
+	int snk_sel, src_sel = -1;
+	int i, ret;
+
+	if (!data->attached || !data->pd_capable || data->spdo_sel <= 0)
+		return -EINVAL;
+
+	src_voltage = pdo_fixed_voltage(data->spdos[data->spdo_sel - 1]);
+	if (src_voltage == data->req_voltage)
+		return 0;
+
+	switch (data->req_voltage) {
+	case 5000:
+		snk_sel = RT1719_SNKCAP_5V;
+		break;
+	case 9000:
+		snk_sel = RT1719_SNKCAP_9V;
+		break;
+	case 12000:
+		snk_sel = RT1719_SNKCAP_12V;
+		break;
+	case 15000:
+		snk_sel = RT1719_SNKCAP_15V;
+		break;
+	case 20000:
+		snk_sel = RT1719_SNKCAP_20V;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (!(data->snkcaps[snk_sel] & RT1719_PSEL_SUPPORT))
+		return -EINVAL;
+
+	for (i = 0; i < data->spdo_num; i++) {
+		enum pd_pdo_type type = pdo_type(data->spdos[i]);
+
+		if (type != PDO_TYPE_FIXED)
+			continue;
+
+		src_voltage = pdo_fixed_voltage(data->spdos[i]);
+		if (src_voltage == data->req_voltage) {
+			src_sel = i;
+			break;
+		}
+	}
+
+	if (src_sel == -1)
+		return -EOPNOTSUPP;
+
+	ret = regmap_update_bits(data->regmap, RT1719_REG_TXCTRL1,
+				 RT1719_EVALMODE_MASK | RT1719_REQSRCPDO_MASK,
+				 RT1719_EVALMODE_MASK | (src_sel + 1));
+	ret |= regmap_update_bits(data->regmap, RT1719_REG_TXCTRL2,
+				  RT1719_TXSPDOREQ_MASK, RT1719_TXSPDOREQ_MASK);
+	if (ret)
+		return ret;
+
+	reinit_completion(&data->req_completion);
+	ret = wait_for_completion_timeout(&data->req_completion,
+					  msecs_to_jiffies(400));
+	if (!ret)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static int rt1719_psy_set_property(struct power_supply *psy,
+				   enum power_supply_property psp,
+				   const union power_supply_propval *val)
+{
+	struct rt1719_data *data = power_supply_get_drvdata(psy);
+
+	if (psp == POWER_SUPPLY_PROP_VOLTAGE_NOW) {
+		data->req_voltage = val->intval / 1000;
+		return rt1719_usbpd_request_voltage(data);
+	}
+
+	return -EINVAL;
+}
+
+static int rt1719_psy_get_property(struct power_supply *psy,
+				   enum power_supply_property psp,
+				   union power_supply_propval *val)
+{
+	struct rt1719_data *data = power_supply_get_drvdata(psy);
+	int ret = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = data->attached ? 1 : 0;
+		break;
+	case POWER_SUPPLY_PROP_USB_TYPE:
+		val->intval = data->usb_type;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = data->voltage;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		val->intval = data->max_current;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		val->intval = data->op_current;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int rt1719_psy_property_is_writeable(struct power_supply *psy,
+					    enum power_supply_property psp)
+{
+	if (psp == POWER_SUPPLY_PROP_VOLTAGE_NOW)
+		return 1;
+	return 0;
+}
+
+static int devm_rt1719_psy_register(struct rt1719_data *data)
+{
+	struct power_supply_config psy_cfg = { };
+	char *psy_name;
+
+	psy_cfg.fwnode = dev_fwnode(data->dev);
+	psy_cfg.drv_data = data;
+
+	psy_name = devm_kasprintf(data->dev, GFP_KERNEL, "rt1719-source-psy-%s",
+				  dev_name(data->dev));
+	if (!psy_name)
+		return -ENOMEM;
+
+	data->psy_desc.name = psy_name;
+	data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
+	data->psy_desc.usb_types = rt1719_psy_usb_types;
+	data->psy_desc.num_usb_types = ARRAY_SIZE(rt1719_psy_usb_types);
+	data->psy_desc.properties = rt1719_psy_properties;
+	data->psy_desc.num_properties = ARRAY_SIZE(rt1719_psy_properties);
+	data->psy_desc.get_property = rt1719_psy_get_property;
+	data->psy_desc.set_property = rt1719_psy_set_property;
+	data->psy_desc.property_is_writeable = rt1719_psy_property_is_writeable;
+
+	data->usb_type = POWER_SUPPLY_USB_TYPE_C;
+
+	data->psy = devm_power_supply_register(data->dev, &data->psy_desc,
+					       &psy_cfg);
+
+	return PTR_ERR_OR_ZERO(data->psy);
+}
+
+static irqreturn_t rt1719_irq_handler(int irq, void *priv)
+{
+	struct rt1719_data *data = priv;
+	u32 events, conn_info;
+	u16 conn_stat;
+	int ret;
+
+	ret = rt1719_read32(data, RT1719_REG_EVENTS, &events);
+	ret |= rt1719_read32(data, RT1719_REG_POLICYINFO, &conn_info);
+	ret |= rt1719_read16(data, RT1719_REG_STATS, &conn_stat);
+	if (ret)
+		return IRQ_NONE;
+
+	data->conn_info = conn_info;
+	data->conn_stat = conn_stat;
+
+	events &= (RT1719_INT_DRSW_ACCEPT | RT1719_INT_RX_SRCCAP |
+		   RT1719_INT_VBUS_PRESENT | RT1719_INT_VBUS_DCT |
+		   RT1719_INT_PE_SNK_RDY);
+
+	if (events & RT1719_INT_DRSW_ACCEPT)
+		rt1719_update_data_role(data);
+
+	if (events & RT1719_INT_VBUS_PRESENT)
+		rt1719_attach(data);
+
+	if (events & RT1719_INT_VBUS_DCT)
+		rt1719_detach(data);
+
+	if (events & RT1719_INT_RX_SRCCAP)
+		rt1719_update_source_pdos(data);
+
+	if (events & RT1719_INT_PE_SNK_RDY) {
+		complete(&data->req_completion);
+		rt1719_update_pwr_opmode(data);
+	}
+
+	/* Write 1 to clear already handled events */
+	rt1719_write32(data, RT1719_REG_EVENTS, events);
+
+	return IRQ_HANDLED;
+}
+
+static int rt1719_irq_init(struct rt1719_data *data)
+{
+	struct i2c_client *i2c = to_i2c_client(data->dev);
+	u32 irq_enable;
+	int ret;
+
+	irq_enable = RT1719_INT_DRSW_ACCEPT | RT1719_INT_RX_SRCCAP |
+		     RT1719_INT_VBUS_DCT | RT1719_INT_VBUS_PRESENT |
+		     RT1719_INT_PE_SNK_RDY;
+
+	ret = rt1719_write32(data, RT1719_REG_MASKS, irq_enable);
+	if (ret) {
+		dev_err(&i2c->dev, "Failed to config irq enable\n");
+		return ret;
+	}
+
+	return devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+					 rt1719_irq_handler, IRQF_ONESHOT,
+					 dev_name(&i2c->dev), data);
+}
+
+static int rt1719_init_attach_state(struct rt1719_data *data)
+{
+	u32 conn_info, irq_clear;
+	u16 conn_stat;
+	int ret;
+
+	irq_clear = RT1719_INT_DRSW_ACCEPT | RT1719_INT_RX_SRCCAP |
+		    RT1719_INT_VBUS_DCT | RT1719_INT_VBUS_PRESENT |
+		    RT1719_INT_PE_SNK_RDY;
+
+	ret = rt1719_read32(data, RT1719_REG_POLICYINFO, &conn_info);
+	ret |= rt1719_read16(data, RT1719_REG_STATS, &conn_stat);
+	ret |= rt1719_write32(data, RT1719_REG_EVENTS, irq_clear);
+	if (ret)
+		return ret;
+
+	data->conn_info = conn_info;
+	data->conn_stat = conn_stat;
+
+	if (conn_info & RT1719_ATTACHDEV_MASK)
+		rt1719_attach(data);
+
+	if (conn_info & RT1719_PE_EXP_CONTRACT) {
+		rt1719_update_source_pdos(data);
+		rt1719_update_pwr_opmode(data);
+	}
+
+	return 0;
+}
+
+#define RT1719_PSEL_CAPINFO(_lomask, _milliwatt, _himask, _milliamp) { \
+	.lomask		= _lomask, \
+	.milliwatt	= _milliwatt, \
+	.himask		= _himask, \
+	.milliamp	= _milliamp, \
+}
+
+static const struct rt1719_psel_cap rt1719_psel_caps[] = {
+	RT1719_PSEL_CAPINFO(0x18, 75000, 0x10, 5000),
+	RT1719_PSEL_CAPINFO(0x18, 60000, 0x10, 4500),
+	RT1719_PSEL_CAPINFO(0x18, 45000, 0x10, 4000),
+	RT1719_PSEL_CAPINFO(0x18, 30000, 0x10, 3500),
+	RT1719_PSEL_CAPINFO(0x18, 25000, 0x10, 3000),
+	RT1719_PSEL_CAPINFO(0x18, 20000, 0x10, 2500),
+	RT1719_PSEL_CAPINFO(0x18, 15000, 0x10, 2000),
+	RT1719_PSEL_CAPINFO(0x18, 10000, 0x10, 1000),
+	RT1719_PSEL_CAPINFO(0x1C, 60000, 0x1F, 5000),
+	RT1719_PSEL_CAPINFO(0x1C, 45000, 0x1F, 4500),
+	RT1719_PSEL_CAPINFO(0x1C, 30000, 0x1F, 4000),
+	RT1719_PSEL_CAPINFO(0x1C, 24000, 0x1F, 3500),
+	RT1719_PSEL_CAPINFO(0x1C, 15000, 0x1F, 3000),
+	RT1719_PSEL_CAPINFO(0x1C, 10000, 0x1F, 2500),
+	RT1719_PSEL_CAPINFO(0x0C, 60000, 0x1F, 2000),
+	RT1719_PSEL_CAPINFO(0x0C, 45000, 0x1F, 1000),
+	RT1719_PSEL_CAPINFO(0x0C, 36000, 0x08, 5000),
+	RT1719_PSEL_CAPINFO(0x0C, 30000, 0x08, 4500),
+	RT1719_PSEL_CAPINFO(0x0C, 24000, 0x08, 4000),
+	RT1719_PSEL_CAPINFO(0x0C, 15000, 0x08, 3500),
+	RT1719_PSEL_CAPINFO(0x0C, 10000, 0x08, 3000),
+	RT1719_PSEL_CAPINFO(0x1E, 45000, 0x08, 2500),
+	RT1719_PSEL_CAPINFO(0x1E, 36000, 0x08, 2000),
+	RT1719_PSEL_CAPINFO(0x1E, 27000, 0x08, 1500),
+	RT1719_PSEL_CAPINFO(0x1E, 20000, 0x08, 1000),
+	RT1719_PSEL_CAPINFO(0x1E, 15000, 0x0F, 5000),
+	RT1719_PSEL_CAPINFO(0x1E, 9000, 0x0F, 4500),
+	RT1719_PSEL_CAPINFO(0x0E, 45000, 0x0F, 4000),
+	RT1719_PSEL_CAPINFO(0x0E, 36000, 0x0F, 3500),
+	RT1719_PSEL_CAPINFO(0x0E, 27000, 0x0F, 3000),
+	RT1719_PSEL_CAPINFO(0x0E, 20000, 0x0F, 2500),
+	RT1719_PSEL_CAPINFO(0x0E, 15000, 0x0F, 2000),
+	RT1719_PSEL_CAPINFO(0x0E, 9000, 0x0F, 1500),
+	RT1719_PSEL_CAPINFO(0x06, 45000, 0x0F, 1000),
+	RT1719_PSEL_CAPINFO(0x06, 36000, 0x0F, 500),
+	RT1719_PSEL_CAPINFO(0x06, 27000, 0x04, 5000),
+	RT1719_PSEL_CAPINFO(0x06, 24000, 0x04, 4500),
+	RT1719_PSEL_CAPINFO(0x06, 18000, 0x04, 4000),
+	RT1719_PSEL_CAPINFO(0x06, 12000, 0x04, 3500),
+	RT1719_PSEL_CAPINFO(0x06, 9000, 0x04, 3000),
+	RT1719_PSEL_CAPINFO(0x1F, 25000, 0x04, 2500),
+	RT1719_PSEL_CAPINFO(0x1F, 20000, 0x04, 2000),
+	RT1719_PSEL_CAPINFO(0x1F, 15000, 0x04, 1500),
+	RT1719_PSEL_CAPINFO(0x1F, 10000, 0x04, 1000),
+	RT1719_PSEL_CAPINFO(0x1F, 7500, 0x07, 5000),
+	RT1719_PSEL_CAPINFO(0x0F, 25000, 0x07, 4500),
+	RT1719_PSEL_CAPINFO(0x0F, 20000, 0x07, 4000),
+	RT1719_PSEL_CAPINFO(0x0F, 15000, 0x07, 3500),
+	RT1719_PSEL_CAPINFO(0x0F, 10000, 0x07, 3000),
+	RT1719_PSEL_CAPINFO(0x0F, 7500, 0x07, 2500),
+	RT1719_PSEL_CAPINFO(0x07, 25000, 0x07, 2000),
+	RT1719_PSEL_CAPINFO(0x07, 20000, 0x07, 1500),
+	RT1719_PSEL_CAPINFO(0x07, 15000, 0x07, 1000),
+	RT1719_PSEL_CAPINFO(0x07, 10000, 0x07, 500),
+	RT1719_PSEL_CAPINFO(0x07, 7500, 0x03, 5000),
+	RT1719_PSEL_CAPINFO(0x03, 25000, 0x03, 4500),
+	RT1719_PSEL_CAPINFO(0x03, 20000, 0x03, 4000),
+	RT1719_PSEL_CAPINFO(0x03, 15000, 0x03, 3500),
+	RT1719_PSEL_CAPINFO(0x03, 10000, 0x03, 3000),
+	RT1719_PSEL_CAPINFO(0x03, 7500, 0x03, 2500),
+	RT1719_PSEL_CAPINFO(0x01, 15000, 0x03, 2000),
+	RT1719_PSEL_CAPINFO(0x01, 10000, 0x03, 1500),
+	RT1719_PSEL_CAPINFO(0x01, 7500, 0x03, 1000),
+	RT1719_PSEL_CAPINFO(0x01, 2500, 0x03, 500)
+};
+
+static u16 rt1719_gen_snkcap_by_current(const struct rt1719_psel_cap *psel_cap,
+					enum rt1719_snkcap capsel)
+{
+	u16 cap = RT1719_PSEL_SUPPORT;
+
+	if (!(psel_cap->himask & BIT(capsel)))
+		return 0;
+
+	cap |= psel_cap->milliamp / 10;
+	return cap;
+}
+
+static u16 rt1719_gen_snkcap_by_watt(const struct rt1719_psel_cap *psel_cap,
+				     enum rt1719_snkcap capsel)
+{
+	u32 volt_div[RT1719_MAX_SNKCAP] = { 5, 9, 12, 15, 20 };
+	u16 cap = RT1719_PSEL_SUPPORT;
+
+	if (!(psel_cap->lomask & BIT(capsel)))
+		return 0;
+
+	cap |= min(psel_cap->milliwatt / volt_div[capsel], (u32)5000) / 10;
+	return cap;
+}
+
+static u16 rt1719_gen_snkcap(unsigned int pselinfo, enum rt1719_snkcap capsel)
+{
+	int psel = FIELD_GET(RT1719_LATPSEL_MASK, pselinfo);
+	const struct rt1719_psel_cap *psel_cap;
+	bool by_current = false;
+
+	if (pselinfo & RT1719_TBLSEL_MASK)
+		by_current = true;
+
+	psel_cap = rt1719_psel_caps + psel;
+	if (by_current)
+		return rt1719_gen_snkcap_by_current(psel_cap, capsel);
+
+	return rt1719_gen_snkcap_by_watt(psel_cap, capsel);
+}
+
+static int rt1719_get_caps(struct rt1719_data *data)
+{
+	unsigned int pselinfo, usbinfo;
+	int i, ret;
+
+	ret = regmap_read(data->regmap, RT1719_REG_PSELINFO, &pselinfo);
+	ret |= regmap_read(data->regmap, RT1719_REG_USBSETINFO, &usbinfo);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < RT1719_MAX_SNKCAP; i++)
+		data->snkcaps[i] = rt1719_gen_snkcap(pselinfo, i);
+
+	usbinfo = FIELD_GET(RT1719_USBINFO_MASK, usbinfo);
+	if (usbinfo == RT1719_USB_DFPUFP)
+		data->drswap_support = true;
+
+	return 0;
+}
+
+static int rt1719_check_exist(struct rt1719_data *data)
+{
+	u16 pid;
+	int ret;
+
+	ret = rt1719_read16(data, RT1719_REG_VENID, &pid);
+	if (ret)
+		return ret;
+
+	if (pid != RT1719_UNIQUE_PID) {
+		dev_err(data->dev, "Incorrect PID 0x%04x\n", pid);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static const struct regmap_config rt1719_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0xff,
+};
+
+static int rt1719_probe(struct i2c_client *i2c)
+{
+	struct rt1719_data *data;
+	struct fwnode_handle *fwnode;
+	struct typec_capability typec_cap = { };
+	int ret;
+
+	data = devm_kzalloc(&i2c->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->dev = &i2c->dev;
+	init_completion(&data->req_completion);
+
+	data->regmap = devm_regmap_init_i2c(i2c, &rt1719_regmap_config);
+	if (IS_ERR(data->regmap)) {
+		ret = PTR_ERR(data->regmap);
+		dev_err(&i2c->dev, "Failed to init regmap (%d)\n", ret);
+		return ret;
+	}
+
+	ret = rt1719_check_exist(data);
+	if (ret)
+		return ret;
+
+	ret = rt1719_get_caps(data);
+	if (ret)
+		return ret;
+
+	/*
+	 * This fwnode has a "compatible" property, but is never populated as a
+	 * struct device. Instead we simply parse it to read the properties.
+	 * This it breaks fw_devlink=on. To maintain backward compatibility
+	 * with existing DT files, we work around this by deleting any
+	 * fwnode_links to/from this fwnode.
+	 */
+	fwnode = device_get_named_child_node(&i2c->dev, "connector");
+	if (!fwnode)
+		return -ENODEV;
+
+	fw_devlink_purge_absent_suppliers(fwnode);
+
+	data->role_sw = fwnode_usb_role_switch_get(fwnode);
+	if (IS_ERR(data->role_sw)) {
+		ret = PTR_ERR(data->role_sw);
+		dev_err(&i2c->dev, "Failed to get usb role switch (%d)\n", ret);
+		goto err_fwnode_put;
+	}
+
+	ret = devm_rt1719_psy_register(data);
+	if (ret) {
+		dev_err(&i2c->dev, "Failed to register psy (%d)\n", ret);
+		goto err_role_put;
+	}
+
+	typec_cap.revision = USB_TYPEC_REV_1_2;
+	typec_cap.pd_revision = 0x300;	/* USB-PD spec release 3.0 */
+	typec_cap.type = TYPEC_PORT_SNK;
+	typec_cap.data = TYPEC_PORT_DRD;
+	typec_cap.ops = &rt1719_port_ops;
+	typec_cap.fwnode = fwnode;
+	typec_cap.driver_data = data;
+	typec_cap.accessory[0] = TYPEC_ACCESSORY_DEBUG;
+
+	data->partner_desc.identity = &data->partner_ident;
+
+	data->port = typec_register_port(&i2c->dev, &typec_cap);
+	if (IS_ERR(data->port)) {
+		ret = PTR_ERR(data->port);
+		dev_err(&i2c->dev, "Failed to register typec port (%d)\n", ret);
+		goto err_role_put;
+	}
+
+	ret = rt1719_init_attach_state(data);
+	if (ret) {
+		dev_err(&i2c->dev, "Failed to init attach state (%d)\n", ret);
+		goto err_role_put;
+	}
+
+	ret = rt1719_irq_init(data);
+	if (ret) {
+		dev_err(&i2c->dev, "Failed to init irq\n");
+		goto err_role_put;
+	}
+
+	fwnode_handle_put(fwnode);
+
+	i2c_set_clientdata(i2c, data);
+
+	return 0;
+
+err_role_put:
+	usb_role_switch_put(data->role_sw);
+err_fwnode_put:
+	fwnode_handle_put(fwnode);
+
+	return ret;
+}
+
+static int rt1719_remove(struct i2c_client *i2c)
+{
+	struct rt1719_data *data = i2c_get_clientdata(i2c);
+
+	typec_unregister_port(data->port);
+	usb_role_switch_put(data->role_sw);
+
+	return 0;
+}
+
+static const struct of_device_id __maybe_unused rt1719_device_table[] = {
+	{ .compatible = "richtek,rt1719", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, rt1719_device_table);
+
+static struct i2c_driver rt1719_driver = {
+	.driver = {
+		.name = "rt1719",
+		.of_match_table = rt1719_device_table,
+	},
+	.probe_new = rt1719_probe,
+	.remove = rt1719_remove,
+};
+module_i2c_driver(rt1719_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("Richtek RT1719 Sink Only USBPD Controller Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2022-02-09 13:26 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-08 13:27 [PATCH v3 2/2] usb: typec: rt1719: Add support for Richtek RT1719 kernel test robot
  -- strict thread matches above, loose matches on Subject: below --
2022-02-07 15:16 [PATCH v3 0/2] Add Richtek RT1719 USBPD controller support cy_huang
2022-02-07 15:16 ` [PATCH v3 2/2] usb: typec: rt1719: Add support for Richtek RT1719 cy_huang
2022-02-09 12:52   ` Heikki Krogerus
2022-02-09 13:26     ` ChiYuan Huang

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.