Re: [PATCHv3 2/2] usb: typec: anx7688: Add driver for ANX7688 USB-C HDMI bridge

2024-04-16 Thread Heikki Krogerus
On Tue, Apr 09, 2024 at 01:04:12PM +0200, Pavel Machek wrote:
> Hi!
> 
> > > This is driver for ANX7688 USB-C HDMI, with flashing and debugging
> > > features removed. ANX7688 is rather criticial piece on PinePhone,
> > > there's no display and no battery charging without it.
> > > 
> > > There's likely more work to be done here, but having basic support
> > > in mainline is needed to be able to work on the other stuff
> > > (networking, cameras, power management).
> > > 
> > > Signed-off-by: Ondrej Jirman 
> > > Co-developed-by: Martijn Braam 
> > > Co-developed-by: Samuel Holland 
> > > Signed-off-by: Pavel Machek 
> > 
> > Just couple of quick comments below - I did not have time to go over
> > this very thoroughly, but I think you need to make a new version in
> > any case because of comments in 1/2.
> 
> Yes, there will be new version.
> 
> There is ton of sleep primitives, and existing ones are okay. I can
> change everything to fdelay or whatever primitive-of-the-day is, but
> I'd rather not do pointless changes.
> 
> You can ask for major changes, or complain about extra newlines, but
> doing both in the same email does not make sense.

I'm not telling you to do any major changes, yet. Right now I'm trying
to understand why you are doing thing the way you are doing them.

> > Btw, Co-developed-by comes before Signed-off-by I think.
> 
> I believe this order is fine, too.
> 
> > > +++ b/drivers/usb/typec/anx7688.c
> > > @@ -0,0 +1,1830 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * ANX7688 USB-C HDMI bridge/PD driver
> > > + *
> > > + * Warning, this driver is somewhat PinePhone specific.
> > 
> > So why not split this into core part and PinePhone specific glue
> > part?
> 
> Potentially a lot of work I can't really test and would rather not do.

Glue layer is usually an easy way to organise your driver into smaller
more manageable chunks, and most importantly, keep the core parts
generic. But just to be clear here, it is not always necessary - maybe
that's the case here.

> > > + struct delayed_work work;
> > > + struct timer_list work_timer;
> > > +
> > > + struct mutex lock;
> > 
> > Undocumented lock.
> 
> This is simple driver. How do you expect me to document it? Protects
> this data structure, not exactly uncommon.

You use a comment to describe what the lock protects. You can use
"scripts/checkpatch.pl --strict" to spot this kind of stuff for you.

> > > +
> > > + /* wait for power to stabilize and release reset */
> > > + msleep(10);
> > > + gpiod_set_value(anx7688->gpio_reset, 0);
> > > + usleep_range(2, 4);
> > 
> > Why not just use usleep_range() in both cases.
> 
> It does not really make code any better. Can do if you insist.

Consistency makes any code better.

> > > +static int anx7688_connect(struct anx7688 *anx7688)
> > > +{
> > > + struct typec_partner_desc desc = {};
> > > + int ret, i;
> > > + u8 fw[2];
> > > + const u8 dp_snk_identity[16] = {
> > > + 0x00, 0x00, 0x00, 0xec, /* id header */
> > > + 0x00, 0x00, 0x00, 0x00, /* cert stat */
> > > + 0x00, 0x00, 0x00, 0x00, /* product type */
> > > + 0x39, 0x00, 0x00, 0x51  /* alt mode adapter */
> > > + };
> > > + const u8 svid[4] = {
> > > + 0x00, 0x00, 0x01, 0xff,
> > > + };
> > 
> > Why not get those from DT?
> 
> Are you sure it belongs to the DT (and that DT people will agree)?

Yes, they belong to the DT, and there are already bindings. Dmitry
gave you the link to the bindings I think (thanks for that Dmitry).

> > > + u32 caps[8];
> > > +
> > > + dev_dbg(anx7688->dev, "cable inserted\n");
> > > +
> > > + anx7688->last_status = -1;
> > > + anx7688->last_cc_status = -1;
> > > + anx7688->last_dp_state = -1;
> > > +
> > > + msleep(10);
> > 
> > Please make a comment here why you have to wait, and use
> > usleep_range().
> 
> /* Dunno because working code does that and waiting for hardware to
> settle down after cable insertion kind of looks like a good thing */
> 
> I did not write the driver, and there's no good documentation for this
> chip. I can try to invent something, but...
> 
> > > + i = 0;
> > > + while (1) {
> > > + ret = anx7688_reg_read(anx7688, 
> > > ANX7688_REG_EEPROM_LOAD_STATUS0);
> > > +
> > > + if (ret >= 0 && (ret & ANX7688_EEPROM_FW_LOADED) == 
> > > ANX7688_EEPROM_FW_LOADED) {
> > > + dev_dbg(anx7688->dev, "eeprom0 = 0x%02x\n", ret);
> > > + dev_dbg(anx7688->dev, "fw loaded after %d ms\n", i * 
> > > 10);
> > > + break;
> > > + }
> > > +
> > > + if (i > 99) {
> > > + set_bit(ANX7688_F_FW_FAILED, anx7688->flags);
> > > + dev_err(anx7688->dev,
> > > + "boot firmware load failed (you may need to 
> > > flash FW to anx7688 first)\n");
> > > + ret = -ETIMEDOUT;
> > > + goto err_vconoff;
> > > + }
> > > + msleep(5);
> > > + i++;
> > > + }
> > 
> > You need to use 

Re: [PATCHv3 2/2] usb: typec: anx7688: Add driver for ANX7688 USB-C HDMI bridge

2024-04-09 Thread Heikki Krogerus
Hi,

On Mon, Apr 08, 2024 at 12:54:25PM +0200, Pavel Machek wrote:
> From: Ondrej Jirman 
> 
> This is driver for ANX7688 USB-C HDMI, with flashing and debugging
> features removed. ANX7688 is rather criticial piece on PinePhone,
> there's no display and no battery charging without it.
> 
> There's likely more work to be done here, but having basic support
> in mainline is needed to be able to work on the other stuff
> (networking, cameras, power management).
> 
> Signed-off-by: Ondrej Jirman 
> Co-developed-by: Martijn Braam 
> Co-developed-by: Samuel Holland 
> Signed-off-by: Pavel Machek 

Just couple of quick comments below - I did not have time to go over
this very thoroughly, but I think you need to make a new version in
any case because of comments in 1/2.

Btw, Co-developed-by comes before Signed-off-by I think.

> ---
> 
> v2: Fix checkpatch stuff. Some cleanups, adapt to dts format in 1/2.
> v3: Turn down debugging, as requested during review.
> 
> diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
> index 2f80c2792dbd..c9043ae61546 100644
> --- a/drivers/usb/typec/Kconfig
> +++ b/drivers/usb/typec/Kconfig
> @@ -64,6 +64,17 @@ config TYPEC_ANX7411
> If you choose to build this driver as a dynamically linked module, the
> module will be called anx7411.ko.
>  
> +config TYPEC_ANX7688
> + tristate "Analogix ANX7688 Type-C DRP Port controller and mux driver"
> + depends on I2C
> + depends on USB_ROLE_SWITCH
> + help
> +   Say Y or M here if your system has Analogix ANX7688 Type-C Bridge
> +   controller driver.
> +
> +   If you choose to build this driver as a dynamically linked module, the
> +   module will be called anx7688.ko.
> +
>  config TYPEC_RT1719
>   tristate "Richtek RT1719 Sink Only Type-C controller driver"
>   depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH
> diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
> index 7a368fea61bc..3f8ff94ad294 100644
> --- a/drivers/usb/typec/Makefile
> +++ b/drivers/usb/typec/Makefile
> @@ -7,6 +7,7 @@ obj-$(CONFIG_TYPEC_TCPM)  += tcpm/
>  obj-$(CONFIG_TYPEC_UCSI) += ucsi/
>  obj-$(CONFIG_TYPEC_TPS6598X) += tipd/
>  obj-$(CONFIG_TYPEC_ANX7411)  += anx7411.o
> +obj-$(CONFIG_TYPEC_ANX7688)  += anx7688.o
>  obj-$(CONFIG_TYPEC_HD3SS3220)+= hd3ss3220.o
>  obj-$(CONFIG_TYPEC_STUSB160X)+= stusb160x.o
>  obj-$(CONFIG_TYPEC_RT1719)   += rt1719.o
> diff --git a/drivers/usb/typec/anx7688.c b/drivers/usb/typec/anx7688.c
> new file mode 100644
> index ..407cbd5fedba
> --- /dev/null
> +++ b/drivers/usb/typec/anx7688.c
> @@ -0,0 +1,1830 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * ANX7688 USB-C HDMI bridge/PD driver
> + *
> + * Warning, this driver is somewhat PinePhone specific.

So why not split this into core part and PinePhone specific glue part?

> + * How this works:
> + * - this driver allows to program firmware into ANX7688 EEPROM, and
> + *   initialize it
> + * - it then communicates with the firmware running on the OCM (on-chip
> + *   microcontroller)
> + * - it detects whether there is cable plugged in or not and powers
> + *   up or down the ANX7688 based on that
> + * - when the cable is connected the firmware on the OCM will handle
> + *   the detection of the nature of the device on the other end
> + *   of the USB-C cable
> + * - this driver then communicates with the USB phy to let it swap
> + *   data roles accordingly
> + * - it also enables VBUS and VCONN regulators as appropriate
> + * - USB phy driver (Allwinner) needs to know whether to switch to
> + *   device or host mode, or whether to turn off
> + * - when the firmware detects SRC.1.5A or SRC.3.0A via CC pins
> + *   or something else via PD, it notifies this driver via software
> + *   interrupt and this driver will determine how to update the TypeC
> + *   port status and what input current limit is appropriate
> + * - input current limit determination happens 500ms after cable
> + *   insertion or hard reset (delay is necessary to determine whether
> + *   the remote end is PD capable or not)
> + * - this driver tells to the PMIC driver that the input current limit
> + *   needs to be changed
> + * - this driver also monitors PMIC status and re-sets the input current
> + *   limit if it changes for some reason (due to PMIC internal decision
> + *   making) (this is disabled for now)
> + *
> + * ANX7688 FW behavior as observed:
> + *
> + * - DO NOT SET MORE THAN 1 SINK CAPABILITY! Firmware will ignore what
> + *   you set and send hardcoded PDO_BATT 5-21V 30W message!
> + *
> + * Product brief:
> + * 
> https://www.analogix.com/en/system/files/AA-002281-PB-6-ANX7688_Product_Brief_0.pdf
> + * Development notes:
> + * https://xnux.eu/devices/feature/anx7688.html
> + */
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> 

Re: [PATCH] usb: typec: ptn36502: switch to DRM_AUX_BRIDGE

2024-03-18 Thread Heikki Krogerus
On Fri, Mar 15, 2024 at 05:04:22PM +0100, Luca Weiss wrote:
> Switch to using the new DRM_AUX_BRIDGE helper to create the transparent
> DRM bridge device instead of handcoding corresponding functionality.
> 
> Signed-off-by: Luca Weiss 

Reviewed-by: Heikki Krogerus 

> ---
> Very similar to this patch:
> c5d296bad640 ("usb: typec: nb7vpq904m: switch to DRM_AUX_BRIDGE")
> ---
>  drivers/usb/typec/mux/Kconfig|  2 +-
>  drivers/usb/typec/mux/ptn36502.c | 44 
> ++--
>  2 files changed, 3 insertions(+), 43 deletions(-)
> 
> diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig
> index 399c7b0983df..4827e86fed6d 100644
> --- a/drivers/usb/typec/mux/Kconfig
> +++ b/drivers/usb/typec/mux/Kconfig
> @@ -60,7 +60,7 @@ config TYPEC_MUX_PTN36502
>   tristate "NXP PTN36502 Type-C redriver driver"
>   depends on I2C
>   depends on DRM || DRM=n
> - select DRM_PANEL_BRIDGE if DRM
> + select DRM_AUX_BRIDGE if DRM_BRIDGE
>   select REGMAP_I2C
>   help
> Say Y or M if your system has a NXP PTN36502 Type-C redriver chip
> diff --git a/drivers/usb/typec/mux/ptn36502.c 
> b/drivers/usb/typec/mux/ptn36502.c
> index 72ae38a1b2be..0ec86ef32a87 100644
> --- a/drivers/usb/typec/mux/ptn36502.c
> +++ b/drivers/usb/typec/mux/ptn36502.c
> @@ -8,7 +8,7 @@
>   * Copyright (C) 2023 Dmitry Baryshkov 
>   */
>  
> -#include 
> +#include 
>  #include 
>  #include 
>  #include 
> @@ -68,8 +68,6 @@ struct ptn36502 {
>  
>   struct typec_switch *typec_switch;
>  
> - struct drm_bridge bridge;
> -
>   struct mutex lock; /* protect non-concurrent retimer & switch */
>  
>   enum typec_orientation orientation;
> @@ -283,44 +281,6 @@ static int ptn36502_detect(struct ptn36502 *ptn)
>   return 0;
>  }
>  
> -#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DRM_PANEL_BRIDGE)
> -static int ptn36502_bridge_attach(struct drm_bridge *bridge,
> -   enum drm_bridge_attach_flags flags)
> -{
> - struct ptn36502 *ptn = container_of(bridge, struct ptn36502, bridge);
> - struct drm_bridge *next_bridge;
> -
> - if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
> - return -EINVAL;
> -
> - next_bridge = devm_drm_of_get_bridge(>client->dev, 
> ptn->client->dev.of_node, 0, 0);
> - if (IS_ERR(next_bridge)) {
> - dev_err(>client->dev, "failed to acquire drm_bridge: 
> %pe\n", next_bridge);
> - return PTR_ERR(next_bridge);
> - }
> -
> - return drm_bridge_attach(bridge->encoder, next_bridge, bridge,
> -  DRM_BRIDGE_ATTACH_NO_CONNECTOR);
> -}
> -
> -static const struct drm_bridge_funcs ptn36502_bridge_funcs = {
> - .attach = ptn36502_bridge_attach,
> -};
> -
> -static int ptn36502_register_bridge(struct ptn36502 *ptn)
> -{
> - ptn->bridge.funcs = _bridge_funcs;
> - ptn->bridge.of_node = ptn->client->dev.of_node;
> -
> - return devm_drm_bridge_add(>client->dev, >bridge);
> -}
> -#else
> -static int ptn36502_register_bridge(struct ptn36502 *ptn)
> -{
> - return 0;
> -}
> -#endif
> -
>  static const struct regmap_config ptn36502_regmap = {
>   .max_register = 0x0d,
>   .reg_bits = 8,
> @@ -369,7 +329,7 @@ static int ptn36502_probe(struct i2c_client *client)
>   if (ret)
>   goto err_disable_regulator;
>  
> - ret = ptn36502_register_bridge(ptn);
> + ret = drm_aux_bridge_register(dev);
>   if (ret)
>   goto err_disable_regulator;
>  
> 
> ---
> base-commit: 9bb9b28d0568991b1d63e66fe75afa5f97ad1156
> change-id: 20240315-ptn36502-aux-15dd6f289aff
> 
> Best regards,
> -- 
> Luca Weiss 

-- 
heikki



Re: [PATCHv2 2/2] usb: typec: anx7688: Add driver for ANX7688 USB-C HDMI bridge

2024-03-12 Thread Heikki Krogerus
Hi Pavel,

I'm sorry to keep you waiting.

On Fri, Feb 23, 2024 at 10:28:49PM +0100, Pavel Machek wrote:
> From: Ondrej Jirman 
> 
> This is driver for ANX7688 USB-C HDMI, with flashing and debugging
> features removed. ANX7688 is rather criticial piece on PinePhone,
> there's no display and no battery charging without it.
> 
> There's likely more work to be done here, but having basic support
> in mainline is needed to be able to work on the other stuff
> (networking, cameras, power management).
> 
> Signed-off-by: Ondrej Jirman 
> Co-developed-by: Martijn Braam 
> Co-developed-by: Samuel Holland 
> Signed-off-by: Pavel Machek 
> 
> ---
> 
> v2: Fix checkpatch stuff. Some cleanups, adapt to dts format in 1/2.
> 
> diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
> index 2f80c2792dbd..c9043ae61546 100644
> --- a/drivers/usb/typec/Kconfig
> +++ b/drivers/usb/typec/Kconfig
> @@ -64,6 +64,17 @@ config TYPEC_ANX7411
> If you choose to build this driver as a dynamically linked module, the
> module will be called anx7411.ko.
>  
> +config TYPEC_ANX7688
> + tristate "Analogix ANX7688 Type-C DRP Port controller and mux driver"
> + depends on I2C
> + depends on USB_ROLE_SWITCH
> + help
> +   Say Y or M here if your system has Analogix ANX7688 Type-C Bridge
> +   controller driver.
> +
> +   If you choose to build this driver as a dynamically linked module, the
> +   module will be called anx7688.ko.
> +
>  config TYPEC_RT1719
>   tristate "Richtek RT1719 Sink Only Type-C controller driver"
>   depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH
> diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
> index 7a368fea61bc..3f8ff94ad294 100644
> --- a/drivers/usb/typec/Makefile
> +++ b/drivers/usb/typec/Makefile
> @@ -7,6 +7,7 @@ obj-$(CONFIG_TYPEC_TCPM)  += tcpm/
>  obj-$(CONFIG_TYPEC_UCSI) += ucsi/
>  obj-$(CONFIG_TYPEC_TPS6598X) += tipd/
>  obj-$(CONFIG_TYPEC_ANX7411)  += anx7411.o
> +obj-$(CONFIG_TYPEC_ANX7688)  += anx7688.o
>  obj-$(CONFIG_TYPEC_HD3SS3220)+= hd3ss3220.o
>  obj-$(CONFIG_TYPEC_STUSB160X)+= stusb160x.o
>  obj-$(CONFIG_TYPEC_RT1719)   += rt1719.o
> diff --git a/drivers/usb/typec/anx7688.c b/drivers/usb/typec/anx7688.c
> new file mode 100644
> index ..14d033bbc0c7
> --- /dev/null
> +++ b/drivers/usb/typec/anx7688.c
> @@ -0,0 +1,1927 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * ANX7688 USB-C HDMI bridge/PD driver
> + *
> + * Warning, this driver is somewhat PinePhone specific.
> + *
> + * How this works:
> + * - this driver allows to program firmware into ANX7688 EEPROM, and
> + *   initialize it
> + * - it then communicates with the firmware running on the OCM (on-chip
> + *   microcontroller)
> + * - it detects whether there is cable plugged in or not and powers
> + *   up or down the ANX7688 based on that
> + * - when the cable is connected the firmware on the OCM will handle
> + *   the detection of the nature of the device on the other end
> + *   of the USB-C cable
> + * - this driver then communicates with the USB phy to let it swap
> + *   data roles accordingly
> + * - it also enables VBUS and VCONN regulators as appropriate
> + * - USB phy driver (Allwinner) needs to know whether to switch to
> + *   device or host mode, or whether to turn off
> + * - when the firmware detects SRC.1.5A or SRC.3.0A via CC pins
> + *   or something else via PD, it notifies this driver via software
> + *   interrupt and this driver will determine how to update the TypeC
> + *   port status and what input current limit is appropriate
> + * - input current limit determination happens 500ms after cable
> + *   insertion or hard reset (delay is necessary to determine whether
> + *   the remote end is PD capable or not)
> + * - this driver tells to the PMIC driver that the input current limit
> + *   needs to be changed
> + * - this driver also monitors PMIC status and re-sets the input current
> + *   limit if it changes for some reason (due to PMIC internal decision
> + *   making) (this is disabled for now)
> + *
> + * ANX7688 FW behavior as observed:
> + *
> + * - DO NOT SET MORE THAN 1 SINK CAPABILITY! Firmware will ignore what
> + *   you set and send hardcoded PDO_BATT 5-21V 30W message!
> + *
> + * Product brief:
> + * 
> https://www.analogix.com/en/system/files/AA-002281-PB-6-ANX7688_Product_Brief_0.pdf
> + * Development notes:
> + * https://xnux.eu/devices/feature/anx7688.html
> + */
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +/* firmware regs */
> +
> +#define ANX7688_REG_VBUS_OFF_DELAY_TIME 0x22
> +#define ANX7688_REG_FEATURE_CTRL0x27
> +#define ANX7688_REG_EEPROM_LOAD_STATUS1 0x11
> +#define ANX7688_REG_EEPROM_LOAD_STATUS0 0x12
> +#define ANX7688_REG_FW_VERSION1 0x15
> +#define 

Re: [PATCH 2/3] usb: typec: ucsi: Add qcm6490-pmic-glink as needing PDOS quirk

2023-12-20 Thread Heikki Krogerus
On Wed, Dec 20, 2023 at 11:02:57AM +0100, Luca Weiss wrote:
> The QCM6490 Linux Android firmware needs this workaround as well. Add it
> to the list.
> 
> Signed-off-by: Luca Weiss 

Acked-by: Heikki Krogerus 

> ---
>  drivers/usb/typec/ucsi/ucsi_glink.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c 
> b/drivers/usb/typec/ucsi/ucsi_glink.c
> index 53a7ede8556d..0bd3f6dee678 100644
> --- a/drivers/usb/typec/ucsi/ucsi_glink.c
> +++ b/drivers/usb/typec/ucsi/ucsi_glink.c
> @@ -298,6 +298,7 @@ static void pmic_glink_ucsi_destroy(void *data)
>  }
>  
>  static const struct of_device_id pmic_glink_ucsi_of_quirks[] = {
> + { .compatible = "qcom,qcm6490-pmic-glink", .data = (void 
> *)UCSI_NO_PARTNER_PDOS, },
>   { .compatible = "qcom,sc8180x-pmic-glink", .data = (void 
> *)UCSI_NO_PARTNER_PDOS, },
>   { .compatible = "qcom,sc8280xp-pmic-glink", .data = (void 
> *)UCSI_NO_PARTNER_PDOS, },
>   { .compatible = "qcom,sm8350-pmic-glink", .data = (void 
> *)UCSI_NO_PARTNER_PDOS, },
> 
> -- 
> 2.43.0

-- 
heikki



Re: [PATCH v2 2/3] usb: typec: fsa4480: Add support to swap SBU orientation

2023-10-30 Thread Heikki Krogerus
On Fri, Oct 20, 2023 at 11:33:19AM +0200, Luca Weiss wrote:
> On some hardware designs the AUX+/- lanes are connected reversed to
> SBU1/2 compared to the expected design by FSA4480.
> 
> Made more complicated, the otherwise compatible Orient-Chip OCP96011
> expects the lanes to be connected reversed compared to FSA4480.
> 
> * FSA4480 block diagram shows AUX+ connected to SBU2 and AUX- to SBU1.
> * OCP96011 block diagram shows AUX+ connected to SBU1 and AUX- to SBU2.
> 
> So if OCP96011 is used as drop-in for FSA4480 then the orientation
> handling in the driver needs to be reversed to match the expectation of
> the OCP96011 hardware.
> 
> Support parsing the data-lanes parameter in the endpoint node to swap
> this in the driver.
> 
> The parse_data_lanes_mapping function is mostly taken from nb7vpq904m.c.
> 
> Reviewed-by: Neil Armstrong 
> Signed-off-by: Luca Weiss 

Reviewed-by: Heikki Krogerus 

> ---
>  drivers/usb/typec/mux/fsa4480.c | 71 
> +
>  1 file changed, 71 insertions(+)
> 
> diff --git a/drivers/usb/typec/mux/fsa4480.c b/drivers/usb/typec/mux/fsa4480.c
> index e0ee1f621abb..cb7cdf90cb0a 100644
> --- a/drivers/usb/typec/mux/fsa4480.c
> +++ b/drivers/usb/typec/mux/fsa4480.c
> @@ -60,6 +60,7 @@ struct fsa4480 {
>   unsigned int svid;
>  
>   u8 cur_enable;
> + bool swap_sbu_lanes;
>  };
>  
>  static const struct regmap_config fsa4480_regmap_config = {
> @@ -76,6 +77,9 @@ static int fsa4480_set(struct fsa4480 *fsa)
>   u8 enable = FSA4480_ENABLE_DEVICE;
>   u8 sel = 0;
>  
> + if (fsa->swap_sbu_lanes)
> + reverse = !reverse;
> +
>   /* USB Mode */
>   if (fsa->mode < TYPEC_STATE_MODAL ||
>   (!fsa->svid && (fsa->mode == TYPEC_MODE_USB2 ||
> @@ -179,12 +183,75 @@ static int fsa4480_mux_set(struct typec_mux_dev *mux, 
> struct typec_mux_state *st
>   return ret;
>  }
>  
> +enum {
> + NORMAL_LANE_MAPPING,
> + INVERT_LANE_MAPPING,
> +};
> +
> +#define DATA_LANES_COUNT 2
> +
> +static const int supported_data_lane_mapping[][DATA_LANES_COUNT] = {
> + [NORMAL_LANE_MAPPING] = { 0, 1 },
> + [INVERT_LANE_MAPPING] = { 1, 0 },
> +};
> +
> +static int fsa4480_parse_data_lanes_mapping(struct fsa4480 *fsa)
> +{
> + struct fwnode_handle *ep;
> + u32 data_lanes[DATA_LANES_COUNT];
> + int ret, i, j;
> +
> + ep = fwnode_graph_get_next_endpoint(dev_fwnode(>client->dev), 
> NULL);
> + if (!ep)
> + return 0;
> +
> + ret = fwnode_property_read_u32_array(ep, "data-lanes", data_lanes, 
> DATA_LANES_COUNT);
> + if (ret == -EINVAL)
> + /* Property isn't here, consider default mapping */
> + goto out_done;
> + if (ret) {
> + dev_err(>client->dev, "invalid data-lanes property: %d\n", 
> ret);
> + goto out_error;
> + }
> +
> + for (i = 0; i < ARRAY_SIZE(supported_data_lane_mapping); i++) {
> + for (j = 0; j < DATA_LANES_COUNT; j++) {
> + if (data_lanes[j] != supported_data_lane_mapping[i][j])
> + break;
> + }
> +
> + if (j == DATA_LANES_COUNT)
> + break;
> + }
> +
> + switch (i) {
> + case NORMAL_LANE_MAPPING:
> + break;
> + case INVERT_LANE_MAPPING:
> + fsa->swap_sbu_lanes = true;
> + break;
> + default:
> + dev_err(>client->dev, "invalid data-lanes mapping\n");
> + ret = -EINVAL;
> + goto out_error;
> + }
> +
> +out_done:
> + ret = 0;
> +
> +out_error:
> + fwnode_handle_put(ep);
> +
> + return ret;
> +}
> +
>  static int fsa4480_probe(struct i2c_client *client)
>  {
>   struct device *dev = >dev;
>   struct typec_switch_desc sw_desc = { };
>   struct typec_mux_desc mux_desc = { };
>   struct fsa4480 *fsa;
> + int ret;
>  
>   fsa = devm_kzalloc(dev, sizeof(*fsa), GFP_KERNEL);
>   if (!fsa)
> @@ -193,6 +260,10 @@ static int fsa4480_probe(struct i2c_client *client)
>   fsa->client = client;
>   mutex_init(>lock);
>  
> + ret = fsa4480_parse_data_lanes_mapping(fsa);
> + if (ret)
> + return ret;
> +
>   fsa->regmap = devm_regmap_init_i2c(client, _regmap_config);
>   if (IS_ERR(fsa->regmap))
>   return dev_err_probe(dev, PTR_ERR(fsa->regmap), "failed to 
> initialize regmap\n");
> 
> -- 
> 2.42.0

-- 
heikki


Re: [PATCH 2/3] usb: typec: fsa4480: Add support to swap SBU orientation

2023-10-18 Thread Heikki Krogerus
Hi Luca,

> > Shouldn't you loop through the endpoints? In any case:
> >
> > ep = fwnode_graph_get_next_endpoint(dev_fwnode(>client->dev, 
> > NULL));
> 
> The docs only mention one endpoint so I'm assuming just next_endpoint is
> fine?

I'm mostly concerned about what we may have in the future. If one day
you have more than the one connection in your graph, then you have to
be able to identify the endpoint you are after.

But that may not be a problem in this case (maybe that "data-lanes"
device property can be used to identify the correct endpoint?).

We can worry about it then when/if we ever have another endpoint to
deal with.

thanks,

-- 
heikki


Re: [PATCH 2/3] usb: typec: fsa4480: Add support to swap SBU orientation

2023-10-17 Thread Heikki Krogerus
Hi Luca,

On Fri, Oct 13, 2023 at 01:38:06PM +0200, Luca Weiss wrote:
> On some hardware designs the AUX+/- lanes are connected reversed to
> SBU1/2 compared to the expected design by FSA4480.
> 
> Made more complicated, the otherwise compatible Orient-Chip OCP96011
> expects the lanes to be connected reversed compared to FSA4480.
> 
> * FSA4480 block diagram shows AUX+ connected to SBU2 and AUX- to SBU1.
> * OCP96011 block diagram shows AUX+ connected to SBU1 and AUX- to SBU2.
> 
> So if OCP96011 is used as drop-in for FSA4480 then the orientation
> handling in the driver needs to be reversed to match the expectation of
> the OCP96011 hardware.
> 
> Support parsing the data-lanes parameter in the endpoint node to swap
> this in the driver.
> 
> The parse_data_lanes_mapping function is mostly taken from nb7vpq904m.c.
> 
> Signed-off-by: Luca Weiss 
> ---
>  drivers/usb/typec/mux/fsa4480.c | 81 
> +
>  1 file changed, 81 insertions(+)
> 
> diff --git a/drivers/usb/typec/mux/fsa4480.c b/drivers/usb/typec/mux/fsa4480.c
> index e0ee1f621abb..6ee467c96fb6 100644
> --- a/drivers/usb/typec/mux/fsa4480.c
> +++ b/drivers/usb/typec/mux/fsa4480.c
> @@ -9,6 +9,7 @@
>  #include 
>  #include 
>  #include 
> +#include 

If you don't mind, let's keep this driver ready for ACPI, just in
case...

>  #include 
>  #include 
>  #include 
> @@ -60,6 +61,7 @@ struct fsa4480 {
>   unsigned int svid;
>  
>   u8 cur_enable;
> + bool swap_sbu_lanes;
>  };
>  
>  static const struct regmap_config fsa4480_regmap_config = {
> @@ -76,6 +78,9 @@ static int fsa4480_set(struct fsa4480 *fsa)
>   u8 enable = FSA4480_ENABLE_DEVICE;
>   u8 sel = 0;
>  
> + if (fsa->swap_sbu_lanes)
> + reverse = !reverse;
> +
>   /* USB Mode */
>   if (fsa->mode < TYPEC_STATE_MODAL ||
>   (!fsa->svid && (fsa->mode == TYPEC_MODE_USB2 ||
> @@ -179,12 +184,84 @@ static int fsa4480_mux_set(struct typec_mux_dev *mux, 
> struct typec_mux_state *st
>   return ret;
>  }
>  
> +enum {
> + NORMAL_LANE_MAPPING,
> + INVERT_LANE_MAPPING,
> +};
> +
> +#define DATA_LANES_COUNT 2
> +
> +static const int supported_data_lane_mapping[][DATA_LANES_COUNT] = {
> + [NORMAL_LANE_MAPPING] = { 0, 1 },
> + [INVERT_LANE_MAPPING] = { 1, 0 },
> +};
> +
> +static int fsa4480_parse_data_lanes_mapping(struct fsa4480 *fsa)
> +{
> + struct device_node *ep;

struct fwnode_handle *ep;

> + u32 data_lanes[DATA_LANES_COUNT];
> + int ret, i, j;
> +
> + ep = of_graph_get_next_endpoint(fsa->client->dev.of_node, NULL);

Shouldn't you loop through the endpoints? In any case:

ep = fwnode_graph_get_next_endpoint(dev_fwnode(>client->dev, 
NULL));

> + if (!ep)
> + return 0;
> +
> + ret = of_property_count_u32_elems(ep, "data-lanes");

ret = fwnode_property_count_u32(ep, "data-lanes");

But is this necessary at all in this case - why not just read the
array since you expect a fixed size for it (if the read fails it fails)?

> + if (ret == -EINVAL)
> + /* Property isn't here, consider default mapping */
> + goto out_done;
> + if (ret < 0)
> + goto out_error;
> +
> + if (ret != DATA_LANES_COUNT) {
> + dev_err(>client->dev, "expected 2 data lanes\n");
> + ret = -EINVAL;
> + goto out_error;
> + }
> +
> + ret = of_property_read_u32_array(ep, "data-lanes", data_lanes, 
> DATA_LANES_COUNT);

ret = fwnode_property_read_u32_array(ep, "data-lanes", data_lanes, 
DATA_LANES_COUNT);

> + if (ret)
> + goto out_error;
> +
> + for (i = 0; i < ARRAY_SIZE(supported_data_lane_mapping); i++) {
> + for (j = 0; j < DATA_LANES_COUNT; j++) {
> + if (data_lanes[j] != supported_data_lane_mapping[i][j])
> + break;
> + }
> +
> + if (j == DATA_LANES_COUNT)
> + break;
> + }
> +
> + switch (i) {
> + case NORMAL_LANE_MAPPING:
> + break;
> + case INVERT_LANE_MAPPING:
> + fsa->swap_sbu_lanes = true;
> + dev_info(>client->dev, "using inverted data lanes 
> mapping\n");

That is just noise. Please drop it.

> + break;
> + default:
> + dev_err(>client->dev, "invalid data lanes mapping\n");
> + ret = -EINVAL;
> + goto out_error;
> + }
> +
> +out_done:
> + ret = 0;
> +
> +out_error:
> + of_node_put(ep);
> +
> + return ret;
> +}
> +
>  static int fsa4480_probe(struct i2c_client *client)
>  {
>   struct device *dev = >dev;
>   struct typec_switch_desc sw_desc = { };
>   struct typec_mux_desc mux_desc = { };
>   struct fsa4480 *fsa;
> + int ret;
>  
>   fsa = devm_kzalloc(dev, sizeof(*fsa), GFP_KERNEL);
>   if (!fsa)
> @@ -193,6 +270,10 @@ static int fsa4480_probe(struct i2c_client *client)
>   fsa->client 

Re: [PATCH 2/2] usb: typec: add support for PTN36502 redriver

2023-10-17 Thread Heikki Krogerus
Hi,

On Fri, Oct 13, 2023 at 04:24:48PM +0200, Luca Weiss wrote:
> Add a driver for the NXP PTN36502 Type-C USB 3.1 Gen 1 and DisplayPort
> v1.2 combo redriver.
> 
> Signed-off-by: Luca Weiss 

Looks OK to me, but couple of nitpicks below. With those fixed:

Reviewed-by: Heikki Krogerus 

> ---
>  drivers/usb/typec/mux/Kconfig|  10 +
>  drivers/usb/typec/mux/Makefile   |   1 +
>  drivers/usb/typec/mux/ptn36502.c | 421 
> +++
>  3 files changed, 432 insertions(+)
> 
> diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig
> index 65da61150ba7..816b9bd08355 100644
> --- a/drivers/usb/typec/mux/Kconfig
> +++ b/drivers/usb/typec/mux/Kconfig
> @@ -46,4 +46,14 @@ config TYPEC_MUX_NB7VPQ904M
> Say Y or M if your system has a On Semiconductor NB7VPQ904M Type-C
> redriver chip found on some devices with a Type-C port.
>  
> +config TYPEC_MUX_PTN36502
> + tristate "NXP PTN36502 Type-C redriver driver"
> + depends on I2C
> + depends on DRM || DRM=n
> + select DRM_PANEL_BRIDGE if DRM
> + select REGMAP_I2C
> + help
> +   Say Y or M if your system has a NXP PTN36502 Type-C redriver chip
> +   found on some devices with a Type-C port.
> +
>  endmenu
> diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile
> index 76196096ef41..9d6a5557b0bd 100644
> --- a/drivers/usb/typec/mux/Makefile
> +++ b/drivers/usb/typec/mux/Makefile
> @@ -5,3 +5,4 @@ obj-$(CONFIG_TYPEC_MUX_GPIO_SBU)  += gpio-sbu-mux.o
>  obj-$(CONFIG_TYPEC_MUX_PI3USB30532)  += pi3usb30532.o
>  obj-$(CONFIG_TYPEC_MUX_INTEL_PMC)+= intel_pmc_mux.o
>  obj-$(CONFIG_TYPEC_MUX_NB7VPQ904M)   += nb7vpq904m.o
> +obj-$(CONFIG_TYPEC_MUX_PTN36502) += ptn36502.o
> diff --git a/drivers/usb/typec/mux/ptn36502.c 
> b/drivers/usb/typec/mux/ptn36502.c
> new file mode 100644
> index ..91684a856f3a
> --- /dev/null
> +++ b/drivers/usb/typec/mux/ptn36502.c
> @@ -0,0 +1,421 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * NXP PTN36502 Type-C driver
> + *
> + * Copyright (C) 2023 Luca Weiss 
> + *
> + * Based on NB7VPQ904M driver:
> + * Copyright (C) 2023 Dmitry Baryshkov 
> + */
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#define PTN36502_CHIP_ID_REG 0x00
> +#define PTN36502_CHIP_ID 0x02
> +
> +#define PTN36502_CHIP_REVISION_REG   0x01
> +#define PTN36502_CHIP_REVISION_BASE(val) FIELD_GET(GENMASK(7, 
> 4), (val))
> +#define PTN36502_CHIP_REVISION_METAL(val)FIELD_GET(GENMASK(3, 
> 0), (val))
> +
> +#define PTN36502_DP_LINK_CTRL_REG0x06
> +#define PTN36502_DP_LINK_CTRL_LANES_2(2 << 2)
> +#define PTN36502_DP_LINK_CTRL_LANES_4(3 << 2)
> +#define PTN36502_DP_LINK_CTRL_LINK_RATE_5_4GBPS  (2 << 0)
> +
> +/* Registers for lane 0 (0x07) to lane 3 (0x0a) have the same layout */
> +#define PTN36502_DP_LANE_CTRL_REG(n) (0x07 + (n))
> +#define PTN36502_DP_LANE_CTRL_RX_GAIN_3DB(2<<4)
> +#define PTN36502_DP_LANE_CTRL_TX_SWING_800MVPPD  (2<<2)
> +#define PTN36502_DP_LANE_CTRL_PRE_EMPHASIS_3_5DB (1<<0)
> +
> +#define PTN36502_MODE_CTRL1_REG  0x0b
> +#define PTN36502_MODE_CTRL1_PLUG_ORIENT_REVERSE  (1<<5)
> +#define PTN36502_MODE_CTRL1_AUX_CROSSBAR_SW_ON   (1<<3)
> +#define PTN36502_MODE_CTRL1_MODE_OFF (0<<0)
> +#define PTN36502_MODE_CTRL1_MODE_USB_ONLY(1<<0)
> +#define PTN36502_MODE_CTRL1_MODE_USB_DP  (2<<0)
> +#define PTN36502_MODE_CTRL1_MODE_DP  (3<<0)
> +
> +#define PTN36502_DEVICE_CTRL_REG 0x0d
> +#define PTN36502_DEVICE_CTRL_AUX_MONITORING_EN   (1<<7)

You have couple of different styles here. Please try to always use
BIT() and GENMASK() macros when possible. At the very least put spaces
around << and >>.

> +struct ptn36502 {
> + struct i2c_client *client;
> + struct regulator *vdd18_supply;
> + struct regmap *regmap;
> + struct typec_switch_dev *sw;
> + struct typec_retimer *retimer;
> +
> + struct typec_switch *typec_switch;
> +
> + struct drm_bridge bridge;
> +
> + struct mutex lock; /* protect non-concurrent retimer & switch */
> +
> + enum typec_orientation orientation;
> + uns

Re: [PATCH] software node: Allow node addition to already existing device

2021-04-14 Thread Heikki Krogerus
On Wed, Apr 14, 2021 at 11:17:55AM +0200, Greg Kroah-Hartman wrote:
> On Wed, Apr 14, 2021 at 12:13:35PM +0300, Heikki Krogerus wrote:
> > +Greg
> > 
> > Sorry about that. Should I resend this?
> 
> No worries, I can pick it up, thanks
> 
> `b4` really is nice to use :)

Yes, it's a really nice tool.

thanks,

-- 
heikki


Re: [PATCH] software node: Allow node addition to already existing device

2021-04-14 Thread Heikki Krogerus
+Greg

Sorry about that. Should I resend this?

On Wed, Apr 14, 2021 at 10:54:38AM +0300, Heikki Krogerus wrote:
> If the node is added to an already exiting device, the node
> needs to be also linked to the device separately.
> 
> This will make sure the reference count is kept in balance
> also when the node is injected to a device afterwards.
> 
> Reported-by: Pierre-Louis Bossart 
> Fixes: e68d0119e328 ("software node: Introduce device_add_software_node()")
> Signed-off-by: Heikki Krogerus 
> ---
>  drivers/base/swnode.c | 5 +++--
>  1 file changed, 3 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
> index 740333629b420..3cc11b813f28c 100644
> --- a/drivers/base/swnode.c
> +++ b/drivers/base/swnode.c
> @@ -1045,6 +1045,7 @@ int device_add_software_node(struct device *dev, const 
> struct software_node *nod
>   }
>  
>   set_secondary_fwnode(dev, >fwnode);
> + software_node_notify(dev, KOBJ_ADD);
>  
>   return 0;
>  }
> @@ -1118,8 +1119,8 @@ int software_node_notify(struct device *dev, unsigned 
> long action)
>  
>   switch (action) {
>   case KOBJ_ADD:
> - ret = sysfs_create_link(>kobj, >kobj,
> - "software_node");
> + ret = sysfs_create_link_nowarn(>kobj, >kobj,
> +"software_node");
>   if (ret)
>   break;
>  
> -- 
> 2.30.2

-- 
heikki


Re: [PATCH] usb: typec: silence a static checker warning

2021-04-14 Thread Heikki Krogerus
On Wed, Apr 14, 2021 at 10:44:40AM +0300, Dan Carpenter wrote:
> Smatch complains about a potential missing error code:
> 
> drivers/usb/typec/port-mapper.c:168 typec_link_port()
> warn: missing error code 'ret'
> 
> This is a false positive and returning zero is intentional.  Let's
> re-arrange the code to silence the warning and make the intent more
> clear.
> 
> Signed-off-by: Dan Carpenter 

Reviewed-by: Heikki Krogerus 

> ---
>  drivers/usb/typec/port-mapper.c | 6 --
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c
> index fae736eb0601..9b0991bdf391 100644
> --- a/drivers/usb/typec/port-mapper.c
> +++ b/drivers/usb/typec/port-mapper.c
> @@ -157,15 +157,17 @@ int typec_link_port(struct device *port)
>  {
>   struct device *connector;
>   struct port_node *node;
> - int ret = 0;
> + int ret;
>  
>   node = create_port_node(port);
>   if (IS_ERR(node))
>   return PTR_ERR(node);
>  
>   connector = find_connector(node);
> - if (!connector)
> + if (!connector) {
> + ret = 0;
>   goto remove_node;
> + }
>  
>   ret = link_port(to_typec_port(connector), node);
>   if (ret)
> -- 
> 2.30.2

thanks,

-- 
heikki


Re: [PATCH v3 2/3] usb: typec: tcpm: Allow slow charging loops to comply to pSnkStby

2021-04-14 Thread Heikki Krogerus
On Tue, Apr 13, 2021 at 07:39:59PM -0700, Badhri Jagan Sridharan wrote:
> When a PD charger advertising Rp-3.0 is connected to a sink port, the
> sink port current limit would 3A, during SNK_DISCOVERY, till power
> negotiation starts. Once the negotiation starts the power limit needs
> to drop down to pSnkStby(500mA @ 5V) and to negotiated current limit
> once the explicit contract is in place. Not all charging loops can ramp
> up to 3A and drop down to 500mA within tSnkStdby which is 15ms. The port
> partner might hard reset if tSnkStdby is not met.
> 
> To solve this problem, this patch introduces slow-charger-loop which
> when set makes the port request PD_P_SNK_STDBY_MW upon entering
> SNK_DISCOVERY(instead of 3A or the 1.5A during SNK_DISCOVERY) and the
> actual currrent limit after RX of PD_CTRL_PSRDY for PD link or during
> SNK_READY for non-pd link.
> 
> Signed-off-by: Badhri Jagan Sridharan 

Reviewed-by: Heikki Krogerus 

> ---
> Changes since V2:
> * Refactored code based on Heikki's suggestion
> ---
>  drivers/usb/typec/tcpm/tcpm.c | 17 ++---
>  1 file changed, 14 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
> index aedc8bb9532a..2ad5e14a6867 100644
> --- a/drivers/usb/typec/tcpm/tcpm.c
> +++ b/drivers/usb/typec/tcpm/tcpm.c
> @@ -459,6 +459,12 @@ struct tcpm_port {
>   /* Auto vbus discharge status */
>   bool auto_vbus_discharge_enabled;
>  
> + /*
> +  * When set, port requests PD_P_SNK_STDBY_MW upon entering 
> SNK_DISCOVERY and
> +  * the actual currrent limit after RX of PD_CTRL_PSRDY for PD link,
> +  * SNK_READY for non-pd link.
> +  */
> + bool slow_charger_loop;
>  #ifdef CONFIG_DEBUG_FS
>   struct dentry *dentry;
>   struct mutex logbuffer_lock;/* log buffer access lock */
> @@ -4047,9 +4053,11 @@ static void run_state_machine(struct tcpm_port *port)
>   break;
>   case SNK_DISCOVERY:
>   if (port->vbus_present) {
> - tcpm_set_current_limit(port,
> -tcpm_get_current_limit(port),
> -5000);
> + u32 current_lim = tcpm_get_current_limit(port);
> +
> + if (port->slow_charger_loop || (current_lim > 
> PD_P_SNK_STDBY_MW / 5))
> + current_lim = PD_P_SNK_STDBY_MW / 5;
> + tcpm_set_current_limit(port, current_lim, 5000);
>   tcpm_set_charge(port, true);
>   tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0);
>   break;
> @@ -4161,6 +4169,8 @@ static void run_state_machine(struct tcpm_port *port)
>   port->pwr_opmode = TYPEC_PWR_MODE_PD;
>   }
>  
> + if (!port->pd_capable && port->slow_charger_loop)
> + tcpm_set_current_limit(port, 
> tcpm_get_current_limit(port), 5000);
>   tcpm_swap_complete(port, 0);
>   tcpm_typec_connect(port);
>   mod_enable_frs_delayed_work(port, 0);
> @@ -5763,6 +5773,7 @@ static int tcpm_fw_get_caps(struct tcpm_port *port,
>   port->typec_caps.type = ret;
>   port->port_type = port->typec_caps.type;
>  
> + port->slow_charger_loop = fwnode_property_read_bool(fwnode, 
> "slow-charger-loop");
>   if (port->port_type == TYPEC_PORT_SNK)
>   goto sink;
>  
> -- 
> 2.31.1.295.g9ea45b61b8-goog

thanks,

-- 
heikki


Re: [PATCH v3 1/3] usb: typec: tcpm: Honour pSnkStdby requirement during negotiation

2021-04-14 Thread Heikki Krogerus
9.077172]  PDO 0: type 0, 5000 mV, 3000 mA [E]
> [  169.077178]  PDO 1: type 0, 9000 mV, 2000 mA []
> [  169.077183] state change SNK_WAIT_CAPABILITIES -> 
> SNK_NEGOTIATE_CAPABILITIES [rev2 POWER_NEGOTIATION]
> [  169.077191] Setting usb_comm capable false
> [  169.077753] cc=0 cc1=0 cc2=5 vbus=0 vconn=sink polarity=1
> [  169.077759] Requesting PDO 1: 9000 mV, 2000 mA
> [  169.077762] PD TX, header: 0x1042
> [  169.079990] PD TX complete, status: 0
> [  169.080013] pending state change SNK_NEGOTIATE_CAPABILITIES -> 
> HARD_RESET_SEND @ 60 ms [rev2 POWER_NEGOTIATION]
> [  169.083183] VBUS on
> [  169.084195] PD RX, header: 0x363 [1]
> [  169.084200] state change SNK_NEGOTIATE_CAPABILITIES -> SNK_TRANSITION_SINK 
> [rev2 POWER_NEGOTIATION]
> [  169.084206] Setting standby current 5000 mV @ 500 mA
> [  169.084209] Setting voltage/current limit 5000 mV 500 mA
> [  169.084220] pending state change SNK_TRANSITION_SINK -> HARD_RESET_SEND @ 
> 500 ms [rev2 POWER_NEGOTIATION]
> [  169.260222] PD RX, header: 0x566 [1]
> [  169.260227] Setting voltage/current limit 9000 mV 2000 mA
> [  169.261315] set_auto_vbus_discharge_threshold mode:3 pps_active:n 
> vbus:9000 ret:0
> [  169.261321] state change SNK_TRANSITION_SINK -> SNK_READY [rev2 
> POWER_NEGOTIATION]
> [  169.261570] AMS POWER_NEGOTIATION finished
> 
> Fixes: f0690a25a140b ("staging: typec: USB Type-C Port Manager (tcpm)")
> Signed-off-by: Badhri Jagan Sridharan 
> Reviewed-by: Guenter Roeck 

Reviewed-by: Heikki Krogerus 

> ---
> Changes since V2:
> * Refactored code based on Heikki's suggestion
> * Added reviewed-by tag from Guenter
> ---
>  drivers/usb/typec/tcpm/tcpm.c | 17 +
>  include/linux/usb/pd.h|  2 ++
>  2 files changed, 19 insertions(+)
> 
> diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
> index 1c32bdf62852..aedc8bb9532a 100644
> --- a/drivers/usb/typec/tcpm/tcpm.c
> +++ b/drivers/usb/typec/tcpm/tcpm.c
> @@ -4131,6 +4131,23 @@ static void run_state_machine(struct tcpm_port *port)
>   }
>   break;
>   case SNK_TRANSITION_SINK:
> + /* From the USB PD spec:
> +  * "The Sink Shall transition to Sink Standby before a positive 
> or
> +  * negative voltage transition of VBUS. During Sink Standby
> +  * the Sink Shall reduce its power draw to pSnkStdby."
> +  *
> +  * This is not applicable to PPS though as the port can continue
> +  * to draw negotiated power without switching to standby.
> +  */
> + if (port->supply_voltage != port->req_supply_voltage && 
> !port->pps_data.active &&
> + port->current_limit * port->supply_voltage / 1000 > 
> PD_P_SNK_STDBY_MW) {
> + u32 stdby_ma = PD_P_SNK_STDBY_MW * 1000 / 
> port->supply_voltage;
> +
> + tcpm_log(port, "Setting standby current %u mV @ %u mA",
> +  port->supply_voltage, stdby_ma);
> + tcpm_set_current_limit(port, stdby_ma, 
> port->supply_voltage);
> + }
> + fallthrough;
>   case SNK_TRANSITION_SINK_VBUS:
>   tcpm_set_state(port, hard_reset_state(port),
>  PD_T_PS_TRANSITION);
> diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h
> index 70d681918d01..bf00259493e0 100644
> --- a/include/linux/usb/pd.h
> +++ b/include/linux/usb/pd.h
> @@ -493,4 +493,6 @@ static inline unsigned int rdo_max_power(u32 rdo)
>  #define PD_N_CAPS_COUNT  (PD_T_NO_RESPONSE / 
> PD_T_SEND_SOURCE_CAP)
>  #define PD_N_HARD_RESET_COUNT2
>  
> +#define PD_P_SNK_STDBY_MW2500/* 2500 mW */
> +
>  #endif /* __LINUX_USB_PD_H */
> -- 
> 2.31.1.295.g9ea45b61b8-goog

thanks,

-- 
heikki


[PATCH] software node: Allow node addition to already existing device

2021-04-14 Thread Heikki Krogerus
If the node is added to an already exiting device, the node
needs to be also linked to the device separately.

This will make sure the reference count is kept in balance
also when the node is injected to a device afterwards.

Reported-by: Pierre-Louis Bossart 
Fixes: e68d0119e328 ("software node: Introduce device_add_software_node()")
Signed-off-by: Heikki Krogerus 
---
 drivers/base/swnode.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index 740333629b420..3cc11b813f28c 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -1045,6 +1045,7 @@ int device_add_software_node(struct device *dev, const 
struct software_node *nod
}
 
set_secondary_fwnode(dev, >fwnode);
+   software_node_notify(dev, KOBJ_ADD);
 
return 0;
 }
@@ -1118,8 +1119,8 @@ int software_node_notify(struct device *dev, unsigned 
long action)
 
switch (action) {
case KOBJ_ADD:
-   ret = sysfs_create_link(>kobj, >kobj,
-   "software_node");
+   ret = sysfs_create_link_nowarn(>kobj, >kobj,
+  "software_node");
if (ret)
break;
 
-- 
2.30.2



Re: [PATCH] ASoC: Intel: Handle device properties with software node API

2021-04-14 Thread Heikki Krogerus
On Tue, Apr 13, 2021 at 10:47:49AM -0500, Pierre-Louis Bossart wrote:
> 
> 
> On 4/13/21 9:05 AM, Heikki Krogerus wrote:
> > On Tue, Apr 13, 2021 at 03:20:45PM +0300, Heikki Krogerus wrote:
> > > On Mon, Apr 12, 2021 at 03:36:20PM -0500, Pierre-Louis Bossart wrote:
> > > > I took the code and split it in two for BYT/CHT (modified to remove 
> > > > devm_)
> > > > and SoundWire parts (added as is).
> > > > 
> > > > https://github.com/thesofproject/linux/pull/2810
> > > > 
> > > > Both cases result in a refcount error on device_remove_sof when 
> > > > removing the
> > > > platform device. I don't understand the code well enough to figure out 
> > > > what
> > > > happens, but it's likely a case of the software node being removed 
> > > > twice?
> > > 
> > > Right. Because you are injecting the node to an already existing
> > > device, the node does not get linked with the device in sysfs. That
> > > would increment the reference count in a normal case. It all happens
> > > in the function software_node_notify(). Driver core calls it when a
> > > device is added and also when it's removed. In this case it is only
> > > called when it's removed.
> > > 
> > > I think the best way to handle this now is to simply not decrementing
> > > the ref count when you add the properties, so don't call
> > > fwnode_handle_put() there (but add a comment explaining why you are
> > > not calling it).
> > 
> > No, sorry... That's just too hacky. Let's not do that after all.
> > 
> > We can also fix this in the software node code. I'm attaching a patch
> > that should make it possible to inject the software nodes also
> > afterwards safely. This is definitely also not without its problems,
> > but we can go with that if it works. Let me know.
> 
> I tested manually on bytcr w/ RT5640 and used the SOF CI farm to test the
> SoundWire cases, I don't see any issues with your patch. The refcount issue
> is gone and the module load/unload cycles don't report any problems.
> 
> Would you queue it for 5.13-rc1, or is this too late already?

I'll send it out now. Let's see what happens.

thanks,

> > > For a better solution you would call device_reprobe() after you have
> > > injected the software node, but before that you need to modify
> > > device_reprobe() so it calls device_platform_notify() (which it really
> > > should call in any case). But this should probable be done later,
> > > separately.
> > > 
> > > thanks,
> > > 
> > > P.S.
> > > 
> > > Have you guys considered the possibility of describing the connections
> > > between all these components by using one of the methods that we now
> > > have for that in kernel, for example device graph? It can now be
> > > used also with software nodes (OF graph and ACPI device graph are of
> > > course already fully supported).
> 
> I must admit I've never heard of a 'device graph'. Any pointers or APIs you
> can think of?
> It's a good comment since we are planning to rework the SOF clients and
> machine driver handling.

-- 
heikki


Re: [PATCH] ASoC: Intel: Handle device properties with software node API

2021-04-13 Thread Heikki Krogerus
On Tue, Apr 13, 2021 at 03:20:45PM +0300, Heikki Krogerus wrote:
> On Mon, Apr 12, 2021 at 03:36:20PM -0500, Pierre-Louis Bossart wrote:
> > I took the code and split it in two for BYT/CHT (modified to remove devm_)
> > and SoundWire parts (added as is).
> > 
> > https://github.com/thesofproject/linux/pull/2810
> > 
> > Both cases result in a refcount error on device_remove_sof when removing the
> > platform device. I don't understand the code well enough to figure out what
> > happens, but it's likely a case of the software node being removed twice?
> 
> Right. Because you are injecting the node to an already existing
> device, the node does not get linked with the device in sysfs. That
> would increment the reference count in a normal case. It all happens
> in the function software_node_notify(). Driver core calls it when a
> device is added and also when it's removed. In this case it is only
> called when it's removed.
> 
> I think the best way to handle this now is to simply not decrementing
> the ref count when you add the properties, so don't call
> fwnode_handle_put() there (but add a comment explaining why you are
> not calling it).

No, sorry... That's just too hacky. Let's not do that after all.

We can also fix this in the software node code. I'm attaching a patch
that should make it possible to inject the software nodes also
afterwards safely. This is definitely also not without its problems,
but we can go with that if it works. Let me know.


> For a better solution you would call device_reprobe() after you have
> injected the software node, but before that you need to modify
> device_reprobe() so it calls device_platform_notify() (which it really
> should call in any case). But this should probable be done later,
> separately.
> 
> thanks,
> 
> P.S.
> 
> Have you guys considered the possibility of describing the connections
> between all these components by using one of the methods that we now
> have for that in kernel, for example device graph? It can now be
> used also with software nodes (OF graph and ACPI device graph are of
> course already fully supported).

Br,

-- 
heikki
>From 3d3dca1f80941f8975390bc8f488176d00acef22 Mon Sep 17 00:00:00 2001
From: Heikki Krogerus 
Date: Tue, 13 Apr 2021 16:28:34 +0300
Subject: [PATCH] software node: Allow node addition to already existing device

If the node is added to an already exiting device, the node
needs to be also linked to the device separately.

This will make sure the reference count is kept in balance
also when the node is injected to a device afterwards.

Reported-by: Pierre-Louis Bossart 
Fixes: e68d0119e328 ("software node: Introduce device_add_software_node()")
Signed-off-by: Heikki Krogerus 
---
 drivers/base/swnode.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index fa3719ef80e4d..88310ac9ce906 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -1032,6 +1032,7 @@ int device_add_software_node(struct device *dev, const 
struct software_node *nod
}
 
set_secondary_fwnode(dev, >fwnode);
+   software_node_notify(dev, KOBJ_ADD);
 
return 0;
 }
@@ -1105,8 +1106,8 @@ int software_node_notify(struct device *dev, unsigned 
long action)
 
switch (action) {
case KOBJ_ADD:
-   ret = sysfs_create_link(>kobj, >kobj,
-   "software_node");
+   ret = sysfs_create_link_nowarn(>kobj, >kobj,
+  "software_node");
if (ret)
break;
 
-- 
2.30.2



Re: [PATCH] ASoC: Intel: Handle device properties with software node API

2021-04-13 Thread Heikki Krogerus
On Mon, Apr 12, 2021 at 03:36:20PM -0500, Pierre-Louis Bossart wrote:
> I took the code and split it in two for BYT/CHT (modified to remove devm_)
> and SoundWire parts (added as is).
> 
> https://github.com/thesofproject/linux/pull/2810
> 
> Both cases result in a refcount error on device_remove_sof when removing the
> platform device. I don't understand the code well enough to figure out what
> happens, but it's likely a case of the software node being removed twice?

Right. Because you are injecting the node to an already existing
device, the node does not get linked with the device in sysfs. That
would increment the reference count in a normal case. It all happens
in the function software_node_notify(). Driver core calls it when a
device is added and also when it's removed. In this case it is only
called when it's removed.

I think the best way to handle this now is to simply not decrementing
the ref count when you add the properties, so don't call
fwnode_handle_put() there (but add a comment explaining why you are
not calling it).

For a better solution you would call device_reprobe() after you have
injected the software node, but before that you need to modify
device_reprobe() so it calls device_platform_notify() (which it really
should call in any case). But this should probable be done later,
separately.

thanks,

P.S.

Have you guys considered the possibility of describing the connections
between all these components by using one of the methods that we now
have for that in kernel, for example device graph? It can now be
used also with software nodes (OF graph and ACPI device graph are of
course already fully supported).

-- 
heikki


Re: [PATCH v2 1/1] devres: Enable trace events

2021-04-13 Thread Heikki Krogerus
On Tue, Apr 13, 2021 at 02:38:01PM +0300, Andy Shevchenko wrote:
> In some cases the printf() mechanism is too heavy and can't be used.
> For example, when debugging a race condition involving devres API.
> When CONFIG_DEBUG_DEVRES is enabled I can't reproduce an issue, and
> otherwise it's quite visible with a useful information being collected.
> 
> Enable trace events for devres part of the driver core.
> 
> Signed-off-by: Andy Shevchenko 
> ---
> v2: fixed compilation error (lkp), elaborate commit message (Greg)
>  drivers/base/Makefile |  3 +++
>  drivers/base/devres.c | 23 +++---
>  drivers/base/trace.c  | 10 
>  drivers/base/trace.h  | 56 +++
>  4 files changed, 83 insertions(+), 9 deletions(-)
>  create mode 100644 drivers/base/trace.c
>  create mode 100644 drivers/base/trace.h
> 
> diff --git a/drivers/base/Makefile b/drivers/base/Makefile
> index 8b93a7f291ec..ef8e44a7d288 100644
> --- a/drivers/base/Makefile
> +++ b/drivers/base/Makefile
> @@ -30,3 +30,6 @@ obj-y   += test/
>  
>  ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
>  
> +# define_trace.h needs to know how to find our header
> +CFLAGS_trace.o   := -I$(src)
> +obj-$(CONFIG_TRACING)+= trace.o
> diff --git a/drivers/base/devres.c b/drivers/base/devres.c
> index db1f3137fc81..a0850bd1eab7 100644
> --- a/drivers/base/devres.c
> +++ b/drivers/base/devres.c
> @@ -14,14 +14,13 @@
>  #include 
>  
>  #include "base.h"
> +#include "trace.h"
>  
>  struct devres_node {
>   struct list_headentry;
>   dr_release_trelease;
> -#ifdef CONFIG_DEBUG_DEVRES
>   const char  *name;
>   size_t  size;
> -#endif

Those ifdefs are still required.

>  };

thanks,

-- 
heikki


Re: linux-next: build failure after merge of the usb tree

2021-04-12 Thread Heikki Krogerus
On Mon, Apr 12, 2021 at 02:35:44PM +0200, Greg KH wrote:
> On Mon, Apr 12, 2021 at 09:36:55PM +1000, Stephen Rothwell wrote:
> > Hi all,
> > 
> > After merging the usb tree, today's linux-next build (x86_64 almodconfig
> > modules_install) failed like this:
> > 
> > depmod: ERROR: Cycle detected: usbcore -> typec -> usbcore
> > depmod: ERROR: Found 2 modules in dependency cycles!
> > 
> > Caused by commit
> > 
> >   63cd78617350 ("usb: Link the ports to the connectors they are attached 
> > to")
> > 
> > I have reverted that commit for today.
> 
> Ugh, good catch, odd that 0-day didn't catch that :(
> 
> I'll go revert it in my tree as well.  Heikki, can you send a fixed up
> version when you get a chance?

Sure thing.

thanks,

-- 
heikki


Re: [PATCH 00/12] i2c: Adding support for software nodes

2021-04-09 Thread Heikki Krogerus
On Thu, Apr 08, 2021 at 11:53:23PM +0200, Wolfram Sang wrote:
> On Mon, Mar 29, 2021 at 01:50:35PM +0300, Heikki Krogerus wrote:
> > Hi,
> > 
> > The old device property API (device_add_properties()) is going to be
> > removed. These prepare the i2c subsystem and drivers for the change.
> > The change is fairly trivial in case of i2c. All we need to do is add
> > complete software nodes to the devices instead of only the device
> > properties in those nodes.
> > 
> > thanks,
> > 
> > Heikki Krogerus (12):
> >   i2c: Add support for software nodes
> >   ARM: davinci: Constify the software nodes
> >   ARM: omap1: osk: Constify the software node
> >   ARM: pxa: stargate2: Constify the software node
> >   ARM: s3c: mini2440: Constify the software node
> >   platform/x86: intel_cht_int33fe_microb: Constify the software node
> >   i2c: cht-wc: Constify the software node
> >   i2c: nvidia-gpu: Constify the software node
> >   i2c: icy: Constify the software node
> >   platform/chrome: chromeos_laptop - Prepare complete software nodes
> >   Input: elantech - Prepare a complete software node for the device
> >   i2c: Remove support for dangling device properties
> 
> I applied all patches to an immutable branch "i2c/software-nodes" now.
> Once buildbot successfully checked this branch, I will merge it into
> for-next and advertise it to interested parties.

Thank you!


-- 
heikki


Re: [PATCH 1/1] usb: typec: tcpm: remove unused static variable 'tcpm_altmode_ops'

2021-04-09 Thread Heikki Krogerus
On Thu, Apr 08, 2021 at 07:14:01PM +0200, Hans de Goede wrote:
> Hi,
> 
> On 4/8/21 3:55 PM, Guenter Roeck wrote:
> > On 4/8/21 1:28 AM, Heikki Krogerus wrote:
> >> On Wed, Apr 07, 2021 at 05:15:40PM +0800, Zhen Lei wrote:
> >>> Fixes the following W=1 kernel build warning:
> >>>
> >>> drivers/usb/typec/tcpm/tcpm.c:2107:39: warning: ‘tcpm_altmode_ops’ 
> >>> defined but not used [-Wunused-const-variable=]
> >>>
> >>> The reference to the variable 'tcpm_altmode_ops' is deleted by the
> >>> commit a079973f462a ("usb: typec: tcpm: Remove tcpc_config configuration
> >>> mechanism").
> >>>
> >>> By the way, the static functions referenced only by the variable
> >>> 'tcpm_altmode_ops' are deleted accordingly.
> >>>
> >>> Reported-by: Hulk Robot 
> >>> Signed-off-by: Zhen Lei 
> >>
> >> Oh, I thought this was already fixed. Should this go into the stable
> >> trees as well?
> >>
> > 
> > I thought there was some code supposed to be coming which would make use of 
> > it,
> > but my memory may defeat me.
> 
> There is code coming which uses this; and this is necessary to make
> DP altmode work on some devices.
> 
> I have dropped the ball a bit on posting a v2 of my series using this.
> 
> I'll prepare a v2 of my series, addressing Heikki's review comments
> to my v1 right away. I'll post a v2 at the latest tomorrow.
> 
> This is something which is on my TODO list anyways and this way we will
> save some churn with these functions going away only to be re-introduced
> again relatively soon after they were removed.

Cool. Greg, I think we can skip this patch then.

thanks,

-- 
heikki


Re: [PATCH] usb: typec: tcpm: remove useless variable

2021-04-09 Thread Heikki Krogerus
On Fri, Apr 09, 2021 at 05:22:16PM +0800, Jiapeng Chong wrote:
> Fix the following gcc warning:
> 
> drivers/usb/typec/tcpm/tcpm.c:2107:39: warning: ‘tcpm_altmode_ops’
> defined but not used [-Wunused-const-variable=].
> 
> Reported-by: Abaci Robot 
> Signed-off-by: Jiapeng Chong 
> ---
>  drivers/usb/typec/tcpm/tcpm.c | 6 --
>  1 file changed, 6 deletions(-)
> 
> diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
> index ce7af39..4585785 100644
> --- a/drivers/usb/typec/tcpm/tcpm.c
> +++ b/drivers/usb/typec/tcpm/tcpm.c
> @@ -2104,12 +2104,6 @@ static int tcpm_altmode_vdm(struct typec_altmode 
> *altmode,
>   return 0;
>  }
>  
> -static const struct typec_altmode_ops tcpm_altmode_ops = {
> - .enter = tcpm_altmode_enter,
> - .exit = tcpm_altmode_exit,
> - .vdm = tcpm_altmode_vdm,
> -};

You remove that but leave the functions. That should create even more
warnings for you, because now there are no users for those functions.

There is another, more complete patch for this, but I don't think we
should take either of these now. We about to get a user for
tcpm_altmode_ops.

Br,

-- 
heikki


Re: [PATCH 1/1] usb: typec: tcpm: remove unused static variable 'tcpm_altmode_ops'

2021-04-08 Thread Heikki Krogerus
On Thu, Apr 08, 2021 at 11:10:38AM +0200, Hans de Goede wrote:
> Hi,
> 
> On 4/7/21 11:15 AM, Zhen Lei wrote:
> > Fixes the following W=1 kernel build warning:
> > 
> > drivers/usb/typec/tcpm/tcpm.c:2107:39: warning: ‘tcpm_altmode_ops’ defined 
> > but not used [-Wunused-const-variable=]
> > 
> > The reference to the variable 'tcpm_altmode_ops' is deleted by the
> > commit a079973f462a ("usb: typec: tcpm: Remove tcpc_config configuration
> > mechanism").
> > 
> > By the way, the static functions referenced only by the variable
> > 'tcpm_altmode_ops' are deleted accordingly.
> > 
> > Reported-by: Hulk Robot 
> > Signed-off-by: Zhen Lei 
> 
> I have a patch pending:
> 
> https://www.spinics.net/lists/linux-usb/msg197684.html
> 
> Which actually uses this. I really need to (and plan to) brush the dust of
> this one soon and submit a new version.
> 
> As such I would prefer for these ops to not get removed. But I guess I
> can always include a patch in my series reverting the removal...

Well, can we then just leave the ops there? If we're going to
re-introduce them back soon in any case, then why drop them in the
first place.

thanks,

-- 
heikki


Re: [PATCH 1/1] usb: typec: tcpm: remove unused static variable 'tcpm_altmode_ops'

2021-04-08 Thread Heikki Krogerus
On Thu, Apr 08, 2021 at 10:38:15AM +0200, Greg Kroah-Hartman wrote:
> On Thu, Apr 08, 2021 at 11:28:09AM +0300, Heikki Krogerus wrote:
> > On Wed, Apr 07, 2021 at 05:15:40PM +0800, Zhen Lei wrote:
> > > Fixes the following W=1 kernel build warning:
> > > 
> > > drivers/usb/typec/tcpm/tcpm.c:2107:39: warning: ‘tcpm_altmode_ops’ 
> > > defined but not used [-Wunused-const-variable=]
> > > 
> > > The reference to the variable 'tcpm_altmode_ops' is deleted by the
> > > commit a079973f462a ("usb: typec: tcpm: Remove tcpc_config configuration
> > > mechanism").
> > > 
> > > By the way, the static functions referenced only by the variable
> > > 'tcpm_altmode_ops' are deleted accordingly.
> > > 
> > > Reported-by: Hulk Robot 
> > > Signed-off-by: Zhen Lei 
> > 
> > Oh, I thought this was already fixed. Should this go into the stable
> > trees as well?
> 
> We do not build kernels by default with "W=1" yet, so it's not needed
> in stable kernels.

Okay, got it.

thanks,

-- 
heikki


Re: [PATCH 1/1] usb: typec: tcpm: remove unused static variable 'tcpm_altmode_ops'

2021-04-08 Thread Heikki Krogerus
On Wed, Apr 07, 2021 at 05:15:40PM +0800, Zhen Lei wrote:
> Fixes the following W=1 kernel build warning:
> 
> drivers/usb/typec/tcpm/tcpm.c:2107:39: warning: ‘tcpm_altmode_ops’ defined 
> but not used [-Wunused-const-variable=]
> 
> The reference to the variable 'tcpm_altmode_ops' is deleted by the
> commit a079973f462a ("usb: typec: tcpm: Remove tcpc_config configuration
> mechanism").
> 
> By the way, the static functions referenced only by the variable
> 'tcpm_altmode_ops' are deleted accordingly.
> 
> Reported-by: Hulk Robot 
> Signed-off-by: Zhen Lei 

Oh, I thought this was already fixed. Should this go into the stable
trees as well?

Acked-by: Heikki Krogerus 

> ---
>  drivers/usb/typec/tcpm/tcpm.c | 60 
> ---
>  1 file changed, 60 deletions(-)
> 
> diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
> index ce7af398c7c1c1f..2f89bae29c0c297 100644
> --- a/drivers/usb/typec/tcpm/tcpm.c
> +++ b/drivers/usb/typec/tcpm/tcpm.c
> @@ -1365,14 +1365,6 @@ static void tcpm_queue_vdm(struct tcpm_port *port, 
> const u32 header,
>   mod_vdm_delayed_work(port, 0);
>  }
>  
> -static void tcpm_queue_vdm_unlocked(struct tcpm_port *port, const u32 header,
> - const u32 *data, int cnt)
> -{
> - mutex_lock(>lock);
> - tcpm_queue_vdm(port, header, data, cnt);
> - mutex_unlock(>lock);
> -}
> -
>  static void svdm_consume_identity(struct tcpm_port *port, const u32 *p, int 
> cnt)
>  {
>   u32 vdo = p[VDO_INDEX_IDH];
> @@ -1705,8 +1697,6 @@ static void tcpm_handle_vdm_request(struct tcpm_port 
> *port,
>*
>* And we also have this ordering:
>* 1. alt-mode driver takes the alt-mode's lock
> -  * 2. alt-mode driver calls tcpm_altmode_enter which takes the
> -  *tcpm port lock
>*
>* Dropping our lock here avoids this.
>*/
> @@ -2060,56 +2050,6 @@ static int tcpm_validate_caps(struct tcpm_port *port, 
> const u32 *pdo,
>   return 0;
>  }
>  
> -static int tcpm_altmode_enter(struct typec_altmode *altmode, u32 *vdo)
> -{
> - struct tcpm_port *port = typec_altmode_get_drvdata(altmode);
> - int svdm_version;
> - u32 header;
> -
> - svdm_version = typec_get_negotiated_svdm_version(port->typec_port);
> - if (svdm_version < 0)
> - return svdm_version;
> -
> - header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE);
> - header |= VDO_OPOS(altmode->mode);
> -
> - tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0);
> - return 0;
> -}
> -
> -static int tcpm_altmode_exit(struct typec_altmode *altmode)
> -{
> - struct tcpm_port *port = typec_altmode_get_drvdata(altmode);
> - int svdm_version;
> - u32 header;
> -
> - svdm_version = typec_get_negotiated_svdm_version(port->typec_port);
> - if (svdm_version < 0)
> - return svdm_version;
> -
> - header = VDO(altmode->svid, 1, svdm_version, CMD_EXIT_MODE);
> - header |= VDO_OPOS(altmode->mode);
> -
> - tcpm_queue_vdm_unlocked(port, header, NULL, 0);
> - return 0;
> -}
> -
> -static int tcpm_altmode_vdm(struct typec_altmode *altmode,
> - u32 header, const u32 *data, int count)
> -{
> - struct tcpm_port *port = typec_altmode_get_drvdata(altmode);
> -
> - tcpm_queue_vdm_unlocked(port, header, data, count - 1);
> -
> - return 0;
> -}
> -
> -static const struct typec_altmode_ops tcpm_altmode_ops = {
> - .enter = tcpm_altmode_enter,
> - .exit = tcpm_altmode_exit,
> - .vdm = tcpm_altmode_vdm,
> -};
> -
>  /*
>   * PD (data, control) command handling functions
>   */
> -- 
> 1.8.3
> 

-- 
heikki


Re: [PATCH v2 5/6] usb: typec: tcpm: Allow slow charging loops to comply to pSnkStby

2021-04-08 Thread Heikki Krogerus
> > @@ -4047,9 +4053,12 @@ static void run_state_machine(struct tcpm_port *port)
> > break;
> > case SNK_DISCOVERY:
> > if (port->vbus_present) {
> > -   tcpm_set_current_limit(port,
> > -  tcpm_get_current_limit(port),
> > -  5000);
> > +   u32 current_lim = (!port->slow_charger_loop ||
> > +  (tcpm_get_current_limit(port) <=
> > +   PD_P_SNK_STDBY_MW / 5)) ?
> > +   tcpm_get_current_limit(port) :
> > +   PD_P_SNK_STDBY_MW / 5;
> 
> Here the use of the ternary operator is not appropriate. Please try to
> clean that up somehow. Maybe something like this would be better?
> 
> u32 current_lim = tcpm_get_current_limit(port);
> 
>   if (port->slow_charger_loop || (current_lim < 
> PD_P_SNK_STDBY_MW / 5))
>   current_lim = PD_P_SNK_STDBY_MW / 5;

Sorry, I mean:

if (port->slow_charger_loop || (current_lim > 
PD_P_SNK_STDBY_MW / 5))
current_lim = PD_P_SNK_STDBY_MW / 5;

thanks,

-- 
heikki


Re: [PATCH v2 5/6] usb: typec: tcpm: Allow slow charging loops to comply to pSnkStby

2021-04-08 Thread Heikki Krogerus
On Wed, Apr 07, 2021 at 01:07:22PM -0700, Badhri Jagan Sridharan wrote:
> When a PD charger advertising Rp-3.0 is connected to a sink port, the
> sink port current limit would 3A, during SNK_DISCOVERY, till power
> negotiation starts. Once the negotiation starts the power limit needs
> to drop down to pSnkStby(500mA @ 5V) and to negotiated current limit
> once the explicit contract is in place. Not all charging loops can ramp
> up to 3A and drop down to 500mA within tSnkStdby which is 15ms. The port
> partner might hard reset if tSnkStdby is not met.
> 
> To solve this problem, this patch introduces slow-charger-loop which
> when set makes the port request PD_P_SNK_STDBY_MW upon entering
> SNK_DISCOVERY(instead of 3A or the 1.5A during SNK_DISCOVERY) and the
> actual currrent limit after RX of PD_CTRL_PSRDY for PD link or during
> SNK_READY for non-pd link.
> 
> Signed-off-by: Badhri Jagan Sridharan 
> ---
>  drivers/usb/typec/tcpm/tcpm.c | 18 +++---
>  1 file changed, 15 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
> index 770b2edd9a04..b5bed6866a2b 100644
> --- a/drivers/usb/typec/tcpm/tcpm.c
> +++ b/drivers/usb/typec/tcpm/tcpm.c
> @@ -459,6 +459,12 @@ struct tcpm_port {
>   /* Auto vbus discharge status */
>   bool auto_vbus_discharge_enabled;
>  
> + /*
> +  * When set, port requests PD_P_SNK_STDBY_MW upon entering 
> SNK_DISCOVERY and
> +  * the actual currrent limit after RX of PD_CTRL_PSRDY for PD link,
> +  * SNK_READY for non-pd link.
> +  */
> + bool slow_charger_loop;
>  #ifdef CONFIG_DEBUG_FS
>   struct dentry *dentry;
>   struct mutex logbuffer_lock;/* log buffer access lock */
> @@ -4047,9 +4053,12 @@ static void run_state_machine(struct tcpm_port *port)
>   break;
>   case SNK_DISCOVERY:
>   if (port->vbus_present) {
> - tcpm_set_current_limit(port,
> -tcpm_get_current_limit(port),
> -5000);
> + u32 current_lim = (!port->slow_charger_loop ||
> +(tcpm_get_current_limit(port) <=
> + PD_P_SNK_STDBY_MW / 5)) ?
> + tcpm_get_current_limit(port) :
> + PD_P_SNK_STDBY_MW / 5;

Here the use of the ternary operator is not appropriate. Please try to
clean that up somehow. Maybe something like this would be better?

u32 current_lim = tcpm_get_current_limit(port);

if (port->slow_charger_loop || (current_lim < 
PD_P_SNK_STDBY_MW / 5))
current_lim = PD_P_SNK_STDBY_MW / 5;

> + tcpm_set_current_limit(port, current_lim, 5000);
>   tcpm_set_charge(port, true);
>   tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0);
>   break;
> @@ -4161,6 +4170,8 @@ static void run_state_machine(struct tcpm_port *port)
>   port->pwr_opmode = TYPEC_PWR_MODE_PD;
>   }
>  
> + if (!port->pd_capable && port->slow_charger_loop)
> + tcpm_set_current_limit(port, 
> tcpm_get_current_limit(port), 5000);
>   tcpm_swap_complete(port, 0);
>   tcpm_typec_connect(port);
>   mod_enable_frs_delayed_work(port, 0);
> @@ -5763,6 +5774,7 @@ static int tcpm_fw_get_caps(struct tcpm_port *port,
>   port->typec_caps.type = ret;
>   port->port_type = port->typec_caps.type;
>  
> + port->slow_charger_loop = fwnode_property_read_bool(fwnode, 
> "slow-charger-loop");
>   if (port->port_type == TYPEC_PORT_SNK)
>   goto sink;
>  
> -- 
> 2.31.1.295.g9ea45b61b8-goog

thanks,

-- 
heikki


Re: [PATCH v2 4/6] usb: typec: tcpm: Honour pSnkStdby requirement during negotiation

2021-04-08 Thread Heikki Krogerus
On Wed, Apr 07, 2021 at 01:07:21PM -0700, Badhri Jagan Sridharan wrote:
> >From PD Spec:
> The Sink Shall transition to Sink Standby before a positive or
> negative voltage transition of VBUS. During Sink Standby
> the Sink Shall reduce its power draw to pSnkStdby. This allows
> the Source to manage the voltage transition as well as
> supply sufficient operating current to the Sink to maintain PD
> operation during the transition. The Sink Shall
> complete this transition to Sink Standby within tSnkStdby
> after evaluating the Accept Message from the Source. The
> transition when returning to Sink operation from Sink Standby
> Shall be completed within tSnkNewPower. The
> pSnkStdby requirement Shall only apply if the Sink power draw
> is higher than this level.
> 
> The above requirement needs to be met to prevent hard resets
> from port partner.
> 
> Without the patch: (5V/3A during SNK_DISCOVERY all the way through
> explicit contract)
> [   95.711984] CC1: 0 -> 0, CC2: 0 -> 5 [state TOGGLING, polarity 0, 
> connected]
> [   95.712007] state change TOGGLING -> SNK_ATTACH_WAIT [rev3 NONE_AMS]
> [   95.712017] pending state change SNK_ATTACH_WAIT -> SNK_DEBOUNCED @ 170 ms 
> [rev3 NONE_AMS]
> [   95.837190] VBUS on
> [   95.882075] state change SNK_ATTACH_WAIT -> SNK_DEBOUNCED [delayed 170 ms]
> [   95.882082] state change SNK_DEBOUNCED -> SNK_ATTACHED [rev3 NONE_AMS]
> [   95.882086] polarity 1
> [   95.883151] set_auto_vbus_discharge_threshold mode:0 pps_active:n 
> vbus:5000 ret:0
> [   95.883441] enable vbus discharge ret:0
> [   95.883445] Requesting mux state 1, usb-role 2, orientation 2
> [   95.883776] state change SNK_ATTACHED -> SNK_STARTUP [rev3 NONE_AMS]
> [   95.883879] pending state change SNK_STARTUP -> SNK_DISCOVERY @ 500 ms 
> [rev3 NONE_AMS]
> [   96.038960] VBUS on
> [   96.383939] state change SNK_STARTUP -> SNK_DISCOVERY [delayed 500 ms]
> [   96.383946] Setting voltage/current limit 5000 mV 3000 mA
> [   96.383961] vbus=0 charge:=1
> [   96.386044] state change SNK_DISCOVERY -> SNK_WAIT_CAPABILITIES [rev3 
> NONE_AMS]
> [   96.386309] pending state change SNK_WAIT_CAPABILITIES -> HARD_RESET_SEND 
> @ 450 ms [rev3 NONE_AMS]
> [   96.394404] PD RX, header: 0x2161 [1]
> [   96.394408]  PDO 0: type 0, 5000 mV, 3000 mA [E]
> [   96.394410]  PDO 1: type 0, 9000 mV, 2000 mA []
> [   96.394412] state change SNK_WAIT_CAPABILITIES -> 
> SNK_NEGOTIATE_CAPABILITIES [rev2 POWER_NEGOTIATION]
> [   96.394416] Setting usb_comm capable false
> [   96.395083] cc=0 cc1=0 cc2=5 vbus=0 vconn=sink polarity=1
> [   96.395089] Requesting PDO 1: 9000 mV, 2000 mA
> [   96.395093] PD TX, header: 0x1042
> [   96.397404] PD TX complete, status: 0
> [   96.397424] pending state change SNK_NEGOTIATE_CAPABILITIES -> 
> HARD_RESET_SEND @ 60 ms [rev2 POWER_NEGOTIATION]
> [   96.400826] PD RX, header: 0x363 [1]
> [   96.400829] state change SNK_NEGOTIATE_CAPABILITIES -> SNK_TRANSITION_SINK 
> [rev2 POWER_NEGOTIATION]
> [   96.400832] pending state change SNK_TRANSITION_SINK -> HARD_RESET_SEND @ 
> 500 ms [rev2 POWER_NEGOTIATION]
> [   96.577315] PD RX, header: 0x566 [1]
> [   96.577321] Setting voltage/current limit 9000 mV 2000 mA
> [   96.578363] set_auto_vbus_discharge_threshold mode:3 pps_active:n 
> vbus:9000 ret:0
> [   96.578370] state change SNK_TRANSITION_SINK -> SNK_READY [rev2 
> POWER_NEGOTIATION]
> 
> With the patch:
> [  168.398573] CC1: 0 -> 0, CC2: 0 -> 5 [state TOGGLING, polarity 0, 
> connected]
> [  168.398605] state change TOGGLING -> SNK_ATTACH_WAIT [rev3 NONE_AMS]
> [  168.398619] pending state change SNK_ATTACH_WAIT -> SNK_DEBOUNCED @ 170 ms 
> [rev3 NONE_AMS]
> [  168.522348] VBUS on
> [  168.568676] state change SNK_ATTACH_WAIT -> SNK_DEBOUNCED [delayed 170 ms]
> [  168.568684] state change SNK_DEBOUNCED -> SNK_ATTACHED [rev3 NONE_AMS]
> [  168.568688] polarity 1
> [  168.569867] set_auto_vbus_discharge_threshold mode:0 pps_active:n 
> vbus:5000 ret:0
> [  168.570158] enable vbus discharge ret:0
> [  168.570161] Requesting mux state 1, usb-role 2, orientation 2
> [  168.570504] state change SNK_ATTACHED -> SNK_STARTUP [rev3 NONE_AMS]
> [  168.570634] pending state change SNK_STARTUP -> SNK_DISCOVERY @ 500 ms 
> [rev3 NONE_AMS]
> [  169.070689] state change SNK_STARTUP -> SNK_DISCOVERY [delayed 500 ms]
> [  169.070695] Setting voltage/current limit 5000 mV 3000 mA
> [  169.070702] vbus=0 charge:=1
> [  169.072719] state change SNK_DISCOVERY -> SNK_WAIT_CAPABILITIES [rev3 
> NONE_AMS]
> [  169.073145] pending state change SNK_WAIT_CAPABILITIES -> HARD_RESET_SEND 
> @ 450 ms [rev3 NONE_AMS]
> [  169.077162] PD RX, header: 0x2161 [1]
> [  169.077172]  PDO 0: type 0, 5000 mV, 3000 mA [E]
> [  169.077178]  PDO 1: type 0, 9000 mV, 2000 mA []
> [  169.077183] state change SNK_WAIT_CAPABILITIES -> 
> SNK_NEGOTIATE_CAPABILITIES [rev2 POWER_NEGOTIATION]
> [  169.077191] Setting usb_comm capable false
> [  169.077753] cc=0 cc1=0 cc2=5 vbus=0 vconn=sink polarity=1
> [  169.077759] Requesting PDO 1: 9000 

Re: [PATCH v2 3/6] usb: typec: tcpm: update power supply once partner accepts

2021-04-08 Thread Heikki Krogerus
On Wed, Apr 07, 2021 at 01:07:20PM -0700, Badhri Jagan Sridharan wrote:
> power_supply_changed needs to be called to notify clients
> after the partner accepts the requested values for the pps
> case.
> 
> Also, remove the redundant power_supply_changed at the end
> of the tcpm_reset_port as power_supply_changed is already
> called right after usb_type is changed.
> 
> Fixes: f2a8aa053c176 ("typec: tcpm: Represent source supply through 
> power_supply")
> Signed-off-by: Badhri Jagan Sridharan 
> Reviewed-by: Adam Thomson 

Reviewed-by: Heikki Krogerus 

> ---
> Changes since V1:
> * Updated commit description to clarify Guenter Roeck's concern.
> * Added Reviewed-by tags
> ---
>  drivers/usb/typec/tcpm/tcpm.c | 4 +---
>  1 file changed, 1 insertion(+), 3 deletions(-)
> 
> diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
> index b4a40099d7e9..d1d03ee90d8f 100644
> --- a/drivers/usb/typec/tcpm/tcpm.c
> +++ b/drivers/usb/typec/tcpm/tcpm.c
> @@ -2568,6 +2568,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
>   port->pps_data.max_curr = port->pps_data.req_max_curr;
>   port->req_supply_voltage = port->pps_data.req_out_volt;
>   port->req_current_limit = port->pps_data.req_op_curr;
> + power_supply_changed(port->psy);
>   tcpm_set_state(port, SNK_TRANSITION_SINK, 0);
>   break;
>   case SOFT_RESET_SEND:
> @@ -3136,7 +3137,6 @@ static unsigned int tcpm_pd_select_pps_apdo(struct 
> tcpm_port *port)
> 
> port->pps_data.req_out_volt));
>   port->pps_data.req_op_curr = min(port->pps_data.max_curr,
>port->pps_data.req_op_curr);
> - power_supply_changed(port->psy);
>   }
>  
>   return src_pdo;
> @@ -3561,8 +3561,6 @@ static void tcpm_reset_port(struct tcpm_port *port)
>   port->sink_cap_done = false;
>   if (port->tcpc->enable_frs)
>   port->tcpc->enable_frs(port->tcpc, false);
> -
> - power_supply_changed(port->psy);
>  }
>  
>  static void tcpm_detach(struct tcpm_port *port)
> -- 
> 2.31.1.295.g9ea45b61b8-goog

-- 
heikki


Re: [PATCH v2 2/6] usb: typec: tcpm: Address incorrect values of tcpm psy for pps supply

2021-04-08 Thread Heikki Krogerus
On Wed, Apr 07, 2021 at 01:07:19PM -0700, Badhri Jagan Sridharan wrote:
> tcpm_pd_select_pps_apdo overwrites port->pps_data.min_volt,
> port->pps_data.max_volt, port->pps_data.max_curr even before
> port partner accepts the requests. This leaves incorrect values
> in current_limit and supply_voltage that get exported by
> "tcpm-source-psy-". Solving this problem by caching the request
> values in req_min_volt, req_max_volt, req_max_curr, req_out_volt,
> req_op_curr. min_volt, max_volt, max_curr gets updated once the
> partner accepts the request. current_limit, supply_voltage gets updated
> once local port's tcpm enters SNK_TRANSITION_SINK when the accepted
> current_limit and supply_voltage is enforced.
> 
> Fixes: f2a8aa053c176 ("typec: tcpm: Represent source supply through 
> power_supply")
> Signed-off-by: Badhri Jagan Sridharan 
> Reviewed-by: Adam Thomson 

Reviewed-by: Heikki Krogerus 

> ---
> Changes since V1:
> * Moved to kerneldoc header as suggested by Greg KH.
> * Added reviewed by tags.
> ---
>  drivers/usb/typec/tcpm/tcpm.c | 88 +--
>  1 file changed, 53 insertions(+), 35 deletions(-)
> 
> diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
> index 4ea4b30ae885..b4a40099d7e9 100644
> --- a/drivers/usb/typec/tcpm/tcpm.c
> +++ b/drivers/usb/typec/tcpm/tcpm.c
> @@ -268,12 +268,27 @@ struct pd_mode_data {
>   struct typec_altmode_desc altmode_desc[ALTMODE_DISCOVERY_MAX];
>  };
>  
> +/*
> + * @min_volt: Actual min voltage at the local port
> + * @req_min_volt: Requested min voltage to the port partner
> + * @max_volt: Actual max voltage at the local port
> + * @req_max_volt: Requested max voltage to the port partner
> + * @max_curr: Actual max current at the local port
> + * @req_max_curr: Requested max current of the port partner
> + * @req_out_volt: Requested output voltage to the port partner
> + * @req_op_curr: Requested operating current to the port partner
> + * @supported: Parter has atleast one APDO hence supports PPS
> + * @active: PPS mode is active
> + */
>  struct pd_pps_data {
>   u32 min_volt;
> + u32 req_min_volt;
>   u32 max_volt;
> + u32 req_max_volt;
>   u32 max_curr;
> - u32 out_volt;
> - u32 op_curr;
> + u32 req_max_curr;
> + u32 req_out_volt;
> + u32 req_op_curr;
>   bool supported;
>   bool active;
>  };
> @@ -2498,8 +2513,8 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
>   break;
>   case SNK_NEGOTIATE_PPS_CAPABILITIES:
>   /* Revert data back from any requested PPS updates */
> - port->pps_data.out_volt = port->supply_voltage;
> - port->pps_data.op_curr = port->current_limit;
> + port->pps_data.req_out_volt = port->supply_voltage;
> + port->pps_data.req_op_curr = port->current_limit;
>   port->pps_status = (type == PD_CTRL_WAIT ?
>   -EAGAIN : -EOPNOTSUPP);
>  
> @@ -2548,8 +2563,11 @@ static void tcpm_pd_ctrl_request(struct tcpm_port 
> *port,
>   break;
>   case SNK_NEGOTIATE_PPS_CAPABILITIES:
>   port->pps_data.active = true;
> - port->req_supply_voltage = port->pps_data.out_volt;
> - port->req_current_limit = port->pps_data.op_curr;
> + port->pps_data.min_volt = port->pps_data.req_min_volt;
> + port->pps_data.max_volt = port->pps_data.req_max_volt;
> + port->pps_data.max_curr = port->pps_data.req_max_curr;
> + port->req_supply_voltage = port->pps_data.req_out_volt;
> + port->req_current_limit = port->pps_data.req_op_curr;
>   tcpm_set_state(port, SNK_TRANSITION_SINK, 0);
>   break;
>   case SOFT_RESET_SEND:
> @@ -3108,16 +3126,16 @@ static unsigned int tcpm_pd_select_pps_apdo(struct 
> tcpm_port *port)
>   src = port->source_caps[src_pdo];
>   snk = port->snk_pdo[snk_pdo];
>  
> - port->pps_data.min_volt = max(pdo_pps_apdo_min_voltage(src),
> -   pdo_pps_apdo_min_voltage(snk));
> - port->pps_data.max_volt = min(pdo_pps_apdo_max_voltage(src),
> -   pdo_pps_apdo_max_voltage(snk));
> - port->pps_data.max_curr = min_pps_apdo_current(src, snk);
> - port->pps_data.out_volt 

Re: [PATCH v2 1/6] usb: typec: tcpm: Address incorrect values of tcpm psy for fixed supply

2021-04-08 Thread Heikki Krogerus
On Wed, Apr 07, 2021 at 01:07:18PM -0700, Badhri Jagan Sridharan wrote:
> tcpm_pd_build_request overwrites current_limit and supply_voltage
> even before port partner accepts the requests. This leaves stale
> values in current_limit and supply_voltage that get exported by
> "tcpm-source-psy-". Solving this problem by caching the request
> values of current limit/supply voltage in req_current_limit
> and req_supply_voltage. current_limit/supply_voltage gets updated
> once the port partner accepts the request.
> 
> Fixes: f2a8aa053c176 ("typec: tcpm: Represent source supply through 
> power_supply")
> Signed-off-by: Badhri Jagan Sridharan 
> Reviewed-by: Guenter Roeck 
> Reviewed-by: Adam Thomson 

Reviewed-by: Heikki Krogerus 

> ---
> Changes since V1:
> * Fixed typo as suggested by Guenter Roeck.
> * Added Reviewed-by tags.
> ---
>  drivers/usb/typec/tcpm/tcpm.c | 17 ++---
>  1 file changed, 10 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
> index ca1fc77697fc..4ea4b30ae885 100644
> --- a/drivers/usb/typec/tcpm/tcpm.c
> +++ b/drivers/usb/typec/tcpm/tcpm.c
> @@ -389,7 +389,10 @@ struct tcpm_port {
>   unsigned int operating_snk_mw;
>   bool update_sink_caps;
>  
> - /* Requested current / voltage */
> + /* Requested current / voltage to the port partner */
> + u32 req_current_limit;
> + u32 req_supply_voltage;
> + /* Actual current / voltage limit of the local port */
>   u32 current_limit;
>   u32 supply_voltage;
>  
> @@ -2435,8 +2438,8 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
>   case SNK_TRANSITION_SINK:
>   if (port->vbus_present) {
>   tcpm_set_current_limit(port,
> -port->current_limit,
> -port->supply_voltage);
> +port->req_current_limit,
> +
> port->req_supply_voltage);
>   port->explicit_contract = true;
>   tcpm_set_auto_vbus_discharge_threshold(port,
>  
> TYPEC_PWR_MODE_PD,
> @@ -2545,8 +2548,8 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
>   break;
>   case SNK_NEGOTIATE_PPS_CAPABILITIES:
>   port->pps_data.active = true;
> - port->supply_voltage = port->pps_data.out_volt;
> - port->current_limit = port->pps_data.op_curr;
> + port->req_supply_voltage = port->pps_data.out_volt;
> + port->req_current_limit = port->pps_data.op_curr;
>   tcpm_set_state(port, SNK_TRANSITION_SINK, 0);
>   break;
>   case SOFT_RESET_SEND:
> @@ -3195,8 +3198,8 @@ static int tcpm_pd_build_request(struct tcpm_port 
> *port, u32 *rdo)
>flags & RDO_CAP_MISMATCH ? " [mismatch]" : "");
>   }
>  
> - port->current_limit = ma;
> - port->supply_voltage = mv;
> + port->req_current_limit = ma;
> + port->req_supply_voltage = mv;
>  
>   return 0;
>  }
> -- 
> 2.31.1.295.g9ea45b61b8-goog

-- 
heikki


[PATCH v6 4/4] usb: typec: Link all ports during connector registration

2021-04-07 Thread Heikki Krogerus
The connectors may be registered after the ports, so the
"connector" links need to be created for the ports also when
ever a new connector gets registered.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/class.c   |  9 +++--
 drivers/usb/typec/class.h   |  4 +--
 drivers/usb/typec/port-mapper.c | 62 +++--
 3 files changed, 68 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index ff199e2d26c7b..f1c2d823c6509 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -1601,7 +1601,6 @@ static void typec_release(struct device *dev)
ida_destroy(>mode_ids);
typec_switch_put(port->sw);
typec_mux_put(port->mux);
-   free_pld(port->pld);
kfree(port->cap);
kfree(port);
 }
@@ -2027,7 +2026,9 @@ struct typec_port *typec_register_port(struct device 
*parent,
return ERR_PTR(ret);
}
 
-   port->pld = get_pld(>dev);
+   ret = typec_link_ports(port);
+   if (ret)
+   dev_warn(>dev, "failed to create symlinks (%d)\n", ret);
 
return port;
 }
@@ -2041,8 +2042,10 @@ EXPORT_SYMBOL_GPL(typec_register_port);
  */
 void typec_unregister_port(struct typec_port *port)
 {
-   if (!IS_ERR_OR_NULL(port))
+   if (!IS_ERR_OR_NULL(port)) {
+   typec_unlink_ports(port);
device_unregister(>dev);
+   }
 }
 EXPORT_SYMBOL_GPL(typec_unregister_port);
 
diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h
index 52294f7020a8b..aef03eb7e1523 100644
--- a/drivers/usb/typec/class.h
+++ b/drivers/usb/typec/class.h
@@ -79,7 +79,7 @@ extern const struct device_type typec_port_dev_type;
 extern struct class typec_mux_class;
 extern struct class typec_class;
 
-void *get_pld(struct device *dev);
-void free_pld(void *pld);
+int typec_link_ports(struct typec_port *connector);
+void typec_unlink_ports(struct typec_port *connector);
 
 #endif /* __USB_TYPEC_CLASS__ */
diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c
index 5bee7a97242fe..fae736eb0601e 100644
--- a/drivers/usb/typec/port-mapper.c
+++ b/drivers/usb/typec/port-mapper.c
@@ -34,7 +34,7 @@ static int acpi_pld_match(const struct acpi_pld_info *pld1,
return 0;
 }
 
-void *get_pld(struct device *dev)
+static void *get_pld(struct device *dev)
 {
 #ifdef CONFIG_ACPI
struct acpi_pld_info *pld;
@@ -53,7 +53,7 @@ void *get_pld(struct device *dev)
 #endif
 }
 
-void free_pld(void *pld)
+static void free_pld(void *pld)
 {
 #ifdef CONFIG_ACPI
ACPI_FREE(pld);
@@ -217,3 +217,61 @@ void typec_unlink_port(struct device *port)
class_for_each_device(_class, NULL, port, port_match_and_unlink);
 }
 EXPORT_SYMBOL_GPL(typec_unlink_port);
+
+static int each_port(struct device *port, void *connector)
+{
+   struct port_node *node;
+   int ret;
+
+   node = create_port_node(port);
+   if (IS_ERR(node))
+   return PTR_ERR(node);
+
+   if (!connector_match(connector, node)) {
+   remove_port_node(node);
+   return 0;
+   }
+
+   ret = link_port(to_typec_port(connector), node);
+   if (ret) {
+   remove_port_node(node->pld);
+   return ret;
+   }
+
+   get_device(connector);
+
+   return 0;
+}
+
+int typec_link_ports(struct typec_port *con)
+{
+   int ret = 0;
+
+   con->pld = get_pld(>dev);
+   if (!con->pld)
+   return 0;
+
+   ret = usb_for_each_port(>dev, each_port);
+   if (ret)
+   typec_unlink_ports(con);
+
+   return ret;
+}
+
+void typec_unlink_ports(struct typec_port *con)
+{
+   struct port_node *node;
+   struct port_node *tmp;
+
+   mutex_lock(>port_list_lock);
+
+   list_for_each_entry_safe(node, tmp, >port_list, list) {
+   __unlink_port(con, node);
+   remove_port_node(node);
+   put_device(>dev);
+   }
+
+   mutex_unlock(>port_list_lock);
+
+   free_pld(con->pld);
+}
-- 
2.30.2



[PATCH v6 3/4] usb: Iterator for ports

2021-04-07 Thread Heikki Krogerus
Introducing usb_for_each_port(). It works the same way as
usb_for_each_dev(), but instead of going through every USB
device in the system, it walks through the USB ports in the
system.

Acked-by: Alan Stern 
Signed-off-by: Heikki Krogerus 
---
 drivers/usb/core/usb.c | 46 ++
 include/linux/usb.h|  9 +
 2 files changed, 55 insertions(+)

diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 2ce3667ec6fae..62368c4ed37af 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -398,6 +398,52 @@ int usb_for_each_dev(void *data, int (*fn)(struct 
usb_device *, void *))
 }
 EXPORT_SYMBOL_GPL(usb_for_each_dev);
 
+struct each_hub_arg {
+   void *data;
+   int (*fn)(struct device *, void *);
+};
+
+static int __each_hub(struct usb_device *hdev, void *data)
+{
+   struct each_hub_arg *arg = (struct each_hub_arg *)data;
+   struct usb_hub *hub;
+   int ret = 0;
+   int i;
+
+   hub = usb_hub_to_struct_hub(hdev);
+   if (!hub)
+   return 0;
+
+   mutex_lock(_port_peer_mutex);
+
+   for (i = 0; i < hdev->maxchild; i++) {
+   ret = arg->fn(>ports[i]->dev, arg->data);
+   if (ret)
+   break;
+   }
+
+   mutex_unlock(_port_peer_mutex);
+
+   return ret;
+}
+
+/**
+ * usb_for_each_port - interate over all USB ports in the system
+ * @data: data pointer that will be handed to the callback function
+ * @fn: callback function to be called for each USB port
+ *
+ * Iterate over all USB ports and call @fn for each, passing it @data. If it
+ * returns anything other than 0, we break the iteration prematurely and return
+ * that value.
+ */
+int usb_for_each_port(void *data, int (*fn)(struct device *, void *))
+{
+   struct each_hub_arg arg = {data, fn};
+
+   return usb_for_each_dev(, __each_hub);
+}
+EXPORT_SYMBOL_GPL(usb_for_each_port);
+
 /**
  * usb_release_dev - free a usb device structure when all users of it are 
finished.
  * @dev: device that's been disconnected
diff --git a/include/linux/usb.h b/include/linux/usb.h
index ddd2f5b2a2827..eaae24217e8a2 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -882,6 +882,15 @@ extern struct usb_host_interface *usb_find_alt_setting(
unsigned int iface_num,
unsigned int alt_num);
 
+#if IS_REACHABLE(CONFIG_USB)
+int usb_for_each_port(void *data, int (*fn)(struct device *, void *));
+#else
+static inline int usb_for_each_port(void *data, int (*fn)(struct device *, 
void *))
+{
+   return 0;
+}
+#endif
+
 /* port claiming functions */
 int usb_hub_claim_port(struct usb_device *hdev, unsigned port1,
struct usb_dev_state *owner);
-- 
2.30.2



[PATCH v6 2/4] usb: Link the ports to the connectors they are attached to

2021-04-07 Thread Heikki Krogerus
Creating link to the USB Type-C connector for every new port
that is added when possible.

Signed-off-by: Heikki Krogerus 
---
 Documentation/ABI/testing/sysfs-bus-usb | 9 +
 drivers/usb/core/port.c | 3 +++
 2 files changed, 12 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-usb 
b/Documentation/ABI/testing/sysfs-bus-usb
index bf2c1968525f0..8b4303a0ff51d 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -255,6 +255,15 @@ Description:
is permitted, "u2" if only u2 is permitted, "u1_u2" if both u1 
and
u2 are permitted.
 
+What:  /sys/bus/usb/devices/.../(hub interface)/portX/connector
+Date:  April 2021
+Contact:   Heikki Krogerus 
+Description:
+   Link to the USB Type-C connector when available. This link is
+   only created when USB Type-C Connector Class is enabled, and
+   only if the system firmware is capable of describing the
+   connection between a port and its connector.
+
 What:  /sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout
 Date:  May 2013
 Contact:   Mathias Nyman 
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index dfcca9c876c73..3c382a4b648ec 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -9,6 +9,7 @@
 
 #include 
 #include 
+#include 
 
 #include "hub.h"
 
@@ -576,6 +577,7 @@ int usb_hub_create_port_device(struct usb_hub *hub, int 
port1)
}
 
find_and_link_peer(hub, port1);
+   typec_link_port(_dev->dev);
 
/*
 * Enable runtime pm and hold a refernce that hub_configure()
@@ -619,5 +621,6 @@ void usb_hub_remove_port_device(struct usb_hub *hub, int 
port1)
peer = port_dev->peer;
if (peer)
unlink_peers(port_dev, peer);
+   typec_unlink_port(_dev->dev);
device_unregister(_dev->dev);
 }
-- 
2.30.2



[PATCH v6 1/4] usb: typec: Port mapping utility

2021-04-07 Thread Heikki Krogerus
Adding functions that can be used to link/unlink ports -
USB ports, TBT3/USB4 ports, DisplayPorts and so on - to
the USB Type-C connectors they are attached to inside a
system. The symlink that is created for the port device is
named "connector".

Initially only ACPI is supported. ACPI port object shares
the _PLD (Physical Location of Device) with the USB Type-C
connector that it's attached to.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/Makefile  |   2 +-
 drivers/usb/typec/class.c   |   7 +-
 drivers/usb/typec/class.h   |   9 ++
 drivers/usb/typec/port-mapper.c | 219 
 include/linux/usb/typec.h   |  13 ++
 5 files changed, 248 insertions(+), 2 deletions(-)
 create mode 100644 drivers/usb/typec/port-mapper.c

diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index 1fb8b6668b1ba..a0adb8947a301 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_TYPEC)+= typec.o
-typec-y:= class.o mux.o bus.o
+typec-y:= class.o mux.o bus.o port-mapper.o
 obj-$(CONFIG_TYPEC)+= altmodes/
 obj-$(CONFIG_TYPEC_TCPM)   += tcpm/
 obj-$(CONFIG_TYPEC_UCSI)   += ucsi/
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index d3e1002386357..ff199e2d26c7b 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -18,7 +18,7 @@
 
 static DEFINE_IDA(typec_index_ida);
 
-static struct class typec_class = {
+struct class typec_class = {
.name = "typec",
.owner = THIS_MODULE,
 };
@@ -1601,6 +1601,7 @@ static void typec_release(struct device *dev)
ida_destroy(>mode_ids);
typec_switch_put(port->sw);
typec_mux_put(port->mux);
+   free_pld(port->pld);
kfree(port->cap);
kfree(port);
 }
@@ -1983,6 +1984,8 @@ struct typec_port *typec_register_port(struct device 
*parent,
 
ida_init(>mode_ids);
mutex_init(>port_type_lock);
+   mutex_init(>port_list_lock);
+   INIT_LIST_HEAD(>port_list);
 
port->id = id;
port->ops = cap->ops;
@@ -2024,6 +2027,8 @@ struct typec_port *typec_register_port(struct device 
*parent,
return ERR_PTR(ret);
}
 
+   port->pld = get_pld(>dev);
+
return port;
 }
 EXPORT_SYMBOL_GPL(typec_register_port);
diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h
index d414be58d122e..52294f7020a8b 100644
--- a/drivers/usb/typec/class.h
+++ b/drivers/usb/typec/class.h
@@ -54,6 +54,11 @@ struct typec_port {
 
const struct typec_capability   *cap;
const struct typec_operations   *ops;
+
+   struct list_headport_list;
+   struct mutexport_list_lock; /* Port list lock */
+
+   void*pld;
 };
 
 #define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
@@ -72,5 +77,9 @@ extern const struct device_type typec_port_dev_type;
 #define is_typec_port(dev) ((dev)->type == _port_dev_type)
 
 extern struct class typec_mux_class;
+extern struct class typec_class;
+
+void *get_pld(struct device *dev);
+void free_pld(void *pld);
 
 #endif /* __USB_TYPEC_CLASS__ */
diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c
new file mode 100644
index 0..5bee7a97242fe
--- /dev/null
+++ b/drivers/usb/typec/port-mapper.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Type-C Connector Class Port Mapping Utility
+ *
+ * Copyright (C) 2021, Intel Corporation
+ * Author: Heikki Krogerus 
+ */
+
+#include 
+#include 
+#include 
+
+#include "class.h"
+
+struct port_node {
+   struct list_head list;
+   struct device *dev;
+   void *pld;
+};
+
+static int acpi_pld_match(const struct acpi_pld_info *pld1,
+ const struct acpi_pld_info *pld2)
+{
+   if (!pld1 || !pld2)
+   return 0;
+
+   /*
+* To speed things up, first checking only the group_position. It seems
+* to often have the first unique value in the _PLD.
+*/
+   if (pld1->group_position == pld2->group_position)
+   return !memcmp(pld1, pld2, sizeof(struct acpi_pld_info));
+
+   return 0;
+}
+
+void *get_pld(struct device *dev)
+{
+#ifdef CONFIG_ACPI
+   struct acpi_pld_info *pld;
+   acpi_status status;
+
+   if (!has_acpi_companion(dev))
+   return NULL;
+
+   status = acpi_get_physical_device_location(ACPI_HANDLE(dev), );
+   if (ACPI_FAILURE(status))
+   return NULL;
+
+   return pld;
+#else
+   return NULL;
+#endif
+}
+
+void free_pld(void *pld)
+{
+#ifdef CONFIG_ACPI
+   ACPI_FREE(pld);
+#endif
+}
+
+static int __link_port(struct typec_port *con, struct port_node *node)
+{
+  

[PATCH v6 0/4] usb: Linking ports to their Type-C connectors

2021-04-07 Thread Heikki Krogerus
Hi,

These are the remaining four patches of the series, now rebased on top
of the latest usb-next. No other changes.


v5 cover letter:

I have to use IS_REACHABLE() instead of IS_ENABLED() also in
include/linux/usb.h. Otherwise compilation will fail if the Type-C
class is build-in while USB is a module.

I'm sorry for re-sending these so fast, immediately after v4. Normally
I would wait, but I'll be taking a short vacation starting from right
now, and I'm still hoping to get these into v5.13.


v4 cover letter:

One more version. I used #ifdef when I should have used #if
IS_DEFINED(). Thanks Guenter for pointing that out.

I'm sending this version right away because of the holidays. I'm not
changing anything else except that one fix.


v3: cover letter:

Third version: ifdefs now in the header files as they should be.


v2 cover letter:

This is the second version of this series. The "Iterator for ports"
patch is now moved to the end of the series (5/6).

I'm now using usb_for_each_dev() in usb_for_each_port like Alan
suggested, and I'm now using usb_port_peer_mutex to lock the ports
while we're dealing with them in __each_hub().


The original cover letter:

Adding a simple function typec_link_port() that can be used to create
a symlink "connector" that points to the USB Type-C connector of a
port. It is used with USB ports initially, but hopefully later also
with other things like DisplayPorts.

Being able to see which connector is connected to a port is important
in general, but it is really important when for example the data or
power role of a device needs to swapped. The user probable wants to
know which USB device is disconnected if role swap on a USB Type-C
connector is executed.

Hope these are OK.

thanks,

Heikki Krogerus (4):
  usb: typec: Port mapping utility
  usb: Link the ports to the connectors they are attached to
  usb: Iterator for ports
  usb: typec: Link all ports during connector registration

 Documentation/ABI/testing/sysfs-bus-usb |   9 +
 drivers/usb/core/port.c |   3 +
 drivers/usb/core/usb.c  |  46 
 drivers/usb/typec/Makefile  |   2 +-
 drivers/usb/typec/class.c   |  12 +-
 drivers/usb/typec/class.h   |   9 +
 drivers/usb/typec/port-mapper.c | 277 
 include/linux/usb.h |   9 +
 include/linux/usb/typec.h   |  13 ++
 9 files changed, 377 insertions(+), 3 deletions(-)
 create mode 100644 drivers/usb/typec/port-mapper.c

-- 
2.30.2



Re: [PATCH v5 3/6] usb: typec: Port mapping utility

2021-04-07 Thread Heikki Krogerus
On Mon, Apr 05, 2021 at 09:09:00AM +0200, Greg Kroah-Hartman wrote:
> On Thu, Apr 01, 2021 at 01:58:44PM +0300, Heikki Krogerus wrote:
> > Adding functions that can be used to link/unlink ports -
> > USB ports, TBT3/USB4 ports, DisplayPorts and so on - to
> > the USB Type-C connectors they are attached to inside a
> > system. The symlink that is created for the port device is
> > named "connector".
> > 
> > Initially only ACPI is supported. ACPI port object shares
> > the _PLD (Physical Location of Device) with the USB Type-C
> > connector that it's attached to.
> > 
> > Signed-off-by: Heikki Krogerus 
> > ---
> >  drivers/usb/typec/Makefile  |   2 +-
> >  drivers/usb/typec/class.c   |   7 +-
> >  drivers/usb/typec/class.h   |   9 ++
> >  drivers/usb/typec/port-mapper.c | 219 
> >  include/linux/usb/typec.h   |  13 ++
> >  5 files changed, 248 insertions(+), 2 deletions(-)
> >  create mode 100644 drivers/usb/typec/port-mapper.c
> 
> This patch failed to apply to my usb-next branch, so I stopped applying
> the patches here.  Can you rebase the rest when you resend?

Okay.

thanks,

-- 
heikki


[PATCH v5 5/6] usb: Iterator for ports

2021-04-01 Thread Heikki Krogerus
Introducing usb_for_each_port(). It works the same way as
usb_for_each_dev(), but instead of going through every USB
device in the system, it walks through the USB ports in the
system.

Signed-off-by: Heikki Krogerus 
Acked-by: Alan Stern 
---
 drivers/usb/core/usb.c | 46 ++
 include/linux/usb.h|  9 +
 2 files changed, 55 insertions(+)

diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 2ce3667ec6fae..62368c4ed37af 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -398,6 +398,52 @@ int usb_for_each_dev(void *data, int (*fn)(struct 
usb_device *, void *))
 }
 EXPORT_SYMBOL_GPL(usb_for_each_dev);
 
+struct each_hub_arg {
+   void *data;
+   int (*fn)(struct device *, void *);
+};
+
+static int __each_hub(struct usb_device *hdev, void *data)
+{
+   struct each_hub_arg *arg = (struct each_hub_arg *)data;
+   struct usb_hub *hub;
+   int ret = 0;
+   int i;
+
+   hub = usb_hub_to_struct_hub(hdev);
+   if (!hub)
+   return 0;
+
+   mutex_lock(_port_peer_mutex);
+
+   for (i = 0; i < hdev->maxchild; i++) {
+   ret = arg->fn(>ports[i]->dev, arg->data);
+   if (ret)
+   break;
+   }
+
+   mutex_unlock(_port_peer_mutex);
+
+   return ret;
+}
+
+/**
+ * usb_for_each_port - interate over all USB ports in the system
+ * @data: data pointer that will be handed to the callback function
+ * @fn: callback function to be called for each USB port
+ *
+ * Iterate over all USB ports and call @fn for each, passing it @data. If it
+ * returns anything other than 0, we break the iteration prematurely and return
+ * that value.
+ */
+int usb_for_each_port(void *data, int (*fn)(struct device *, void *))
+{
+   struct each_hub_arg arg = {data, fn};
+
+   return usb_for_each_dev(, __each_hub);
+}
+EXPORT_SYMBOL_GPL(usb_for_each_port);
+
 /**
  * usb_release_dev - free a usb device structure when all users of it are 
finished.
  * @dev: device that's been disconnected
diff --git a/include/linux/usb.h b/include/linux/usb.h
index ddd2f5b2a2827..eaae24217e8a2 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -882,6 +882,15 @@ extern struct usb_host_interface *usb_find_alt_setting(
unsigned int iface_num,
unsigned int alt_num);
 
+#if IS_REACHABLE(CONFIG_USB)
+int usb_for_each_port(void *data, int (*fn)(struct device *, void *));
+#else
+static inline int usb_for_each_port(void *data, int (*fn)(struct device *, 
void *))
+{
+   return 0;
+}
+#endif
+
 /* port claiming functions */
 int usb_hub_claim_port(struct usb_device *hdev, unsigned port1,
struct usb_dev_state *owner);
-- 
2.30.2



[PATCH v5 6/6] usb: typec: Link all ports during connector registration

2021-04-01 Thread Heikki Krogerus
The connectors may be registered after the ports, so the
"connector" links need to be created for the ports also when
ever a new connector gets registered.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/class.c   |  9 +++--
 drivers/usb/typec/class.h   |  4 +--
 drivers/usb/typec/port-mapper.c | 62 +++--
 3 files changed, 68 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index ff199e2d26c7b..f1c2d823c6509 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -1601,7 +1601,6 @@ static void typec_release(struct device *dev)
ida_destroy(>mode_ids);
typec_switch_put(port->sw);
typec_mux_put(port->mux);
-   free_pld(port->pld);
kfree(port->cap);
kfree(port);
 }
@@ -2027,7 +2026,9 @@ struct typec_port *typec_register_port(struct device 
*parent,
return ERR_PTR(ret);
}
 
-   port->pld = get_pld(>dev);
+   ret = typec_link_ports(port);
+   if (ret)
+   dev_warn(>dev, "failed to create symlinks (%d)\n", ret);
 
return port;
 }
@@ -2041,8 +2042,10 @@ EXPORT_SYMBOL_GPL(typec_register_port);
  */
 void typec_unregister_port(struct typec_port *port)
 {
-   if (!IS_ERR_OR_NULL(port))
+   if (!IS_ERR_OR_NULL(port)) {
+   typec_unlink_ports(port);
device_unregister(>dev);
+   }
 }
 EXPORT_SYMBOL_GPL(typec_unregister_port);
 
diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h
index 52294f7020a8b..aef03eb7e1523 100644
--- a/drivers/usb/typec/class.h
+++ b/drivers/usb/typec/class.h
@@ -79,7 +79,7 @@ extern const struct device_type typec_port_dev_type;
 extern struct class typec_mux_class;
 extern struct class typec_class;
 
-void *get_pld(struct device *dev);
-void free_pld(void *pld);
+int typec_link_ports(struct typec_port *connector);
+void typec_unlink_ports(struct typec_port *connector);
 
 #endif /* __USB_TYPEC_CLASS__ */
diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c
index 5bee7a97242fe..fae736eb0601e 100644
--- a/drivers/usb/typec/port-mapper.c
+++ b/drivers/usb/typec/port-mapper.c
@@ -34,7 +34,7 @@ static int acpi_pld_match(const struct acpi_pld_info *pld1,
return 0;
 }
 
-void *get_pld(struct device *dev)
+static void *get_pld(struct device *dev)
 {
 #ifdef CONFIG_ACPI
struct acpi_pld_info *pld;
@@ -53,7 +53,7 @@ void *get_pld(struct device *dev)
 #endif
 }
 
-void free_pld(void *pld)
+static void free_pld(void *pld)
 {
 #ifdef CONFIG_ACPI
ACPI_FREE(pld);
@@ -217,3 +217,61 @@ void typec_unlink_port(struct device *port)
class_for_each_device(_class, NULL, port, port_match_and_unlink);
 }
 EXPORT_SYMBOL_GPL(typec_unlink_port);
+
+static int each_port(struct device *port, void *connector)
+{
+   struct port_node *node;
+   int ret;
+
+   node = create_port_node(port);
+   if (IS_ERR(node))
+   return PTR_ERR(node);
+
+   if (!connector_match(connector, node)) {
+   remove_port_node(node);
+   return 0;
+   }
+
+   ret = link_port(to_typec_port(connector), node);
+   if (ret) {
+   remove_port_node(node->pld);
+   return ret;
+   }
+
+   get_device(connector);
+
+   return 0;
+}
+
+int typec_link_ports(struct typec_port *con)
+{
+   int ret = 0;
+
+   con->pld = get_pld(>dev);
+   if (!con->pld)
+   return 0;
+
+   ret = usb_for_each_port(>dev, each_port);
+   if (ret)
+   typec_unlink_ports(con);
+
+   return ret;
+}
+
+void typec_unlink_ports(struct typec_port *con)
+{
+   struct port_node *node;
+   struct port_node *tmp;
+
+   mutex_lock(>port_list_lock);
+
+   list_for_each_entry_safe(node, tmp, >port_list, list) {
+   __unlink_port(con, node);
+   remove_port_node(node);
+   put_device(>dev);
+   }
+
+   mutex_unlock(>port_list_lock);
+
+   free_pld(con->pld);
+}
-- 
2.30.2



[PATCH v5 3/6] usb: typec: Port mapping utility

2021-04-01 Thread Heikki Krogerus
Adding functions that can be used to link/unlink ports -
USB ports, TBT3/USB4 ports, DisplayPorts and so on - to
the USB Type-C connectors they are attached to inside a
system. The symlink that is created for the port device is
named "connector".

Initially only ACPI is supported. ACPI port object shares
the _PLD (Physical Location of Device) with the USB Type-C
connector that it's attached to.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/Makefile  |   2 +-
 drivers/usb/typec/class.c   |   7 +-
 drivers/usb/typec/class.h   |   9 ++
 drivers/usb/typec/port-mapper.c | 219 
 include/linux/usb/typec.h   |  13 ++
 5 files changed, 248 insertions(+), 2 deletions(-)
 create mode 100644 drivers/usb/typec/port-mapper.c

diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index a820e6e8c1ffc..1e1868832b8d8 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -3,7 +3,7 @@
 CFLAGS_tps6598x.o  := -I$(src)
 
 obj-$(CONFIG_TYPEC)+= typec.o
-typec-y:= class.o mux.o bus.o
+typec-y:= class.o mux.o bus.o port-mapper.o
 obj-$(CONFIG_TYPEC)+= altmodes/
 obj-$(CONFIG_TYPEC_TCPM)   += tcpm/
 obj-$(CONFIG_TYPEC_UCSI)   += ucsi/
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index d3e1002386357..ff199e2d26c7b 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -18,7 +18,7 @@
 
 static DEFINE_IDA(typec_index_ida);
 
-static struct class typec_class = {
+struct class typec_class = {
.name = "typec",
.owner = THIS_MODULE,
 };
@@ -1601,6 +1601,7 @@ static void typec_release(struct device *dev)
ida_destroy(>mode_ids);
typec_switch_put(port->sw);
typec_mux_put(port->mux);
+   free_pld(port->pld);
kfree(port->cap);
kfree(port);
 }
@@ -1983,6 +1984,8 @@ struct typec_port *typec_register_port(struct device 
*parent,
 
ida_init(>mode_ids);
mutex_init(>port_type_lock);
+   mutex_init(>port_list_lock);
+   INIT_LIST_HEAD(>port_list);
 
port->id = id;
port->ops = cap->ops;
@@ -2024,6 +2027,8 @@ struct typec_port *typec_register_port(struct device 
*parent,
return ERR_PTR(ret);
}
 
+   port->pld = get_pld(>dev);
+
return port;
 }
 EXPORT_SYMBOL_GPL(typec_register_port);
diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h
index d414be58d122e..52294f7020a8b 100644
--- a/drivers/usb/typec/class.h
+++ b/drivers/usb/typec/class.h
@@ -54,6 +54,11 @@ struct typec_port {
 
const struct typec_capability   *cap;
const struct typec_operations   *ops;
+
+   struct list_headport_list;
+   struct mutexport_list_lock; /* Port list lock */
+
+   void*pld;
 };
 
 #define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
@@ -72,5 +77,9 @@ extern const struct device_type typec_port_dev_type;
 #define is_typec_port(dev) ((dev)->type == _port_dev_type)
 
 extern struct class typec_mux_class;
+extern struct class typec_class;
+
+void *get_pld(struct device *dev);
+void free_pld(void *pld);
 
 #endif /* __USB_TYPEC_CLASS__ */
diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c
new file mode 100644
index 0..5bee7a97242fe
--- /dev/null
+++ b/drivers/usb/typec/port-mapper.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Type-C Connector Class Port Mapping Utility
+ *
+ * Copyright (C) 2021, Intel Corporation
+ * Author: Heikki Krogerus 
+ */
+
+#include 
+#include 
+#include 
+
+#include "class.h"
+
+struct port_node {
+   struct list_head list;
+   struct device *dev;
+   void *pld;
+};
+
+static int acpi_pld_match(const struct acpi_pld_info *pld1,
+ const struct acpi_pld_info *pld2)
+{
+   if (!pld1 || !pld2)
+   return 0;
+
+   /*
+* To speed things up, first checking only the group_position. It seems
+* to often have the first unique value in the _PLD.
+*/
+   if (pld1->group_position == pld2->group_position)
+   return !memcmp(pld1, pld2, sizeof(struct acpi_pld_info));
+
+   return 0;
+}
+
+void *get_pld(struct device *dev)
+{
+#ifdef CONFIG_ACPI
+   struct acpi_pld_info *pld;
+   acpi_status status;
+
+   if (!has_acpi_companion(dev))
+   return NULL;
+
+   status = acpi_get_physical_device_location(ACPI_HANDLE(dev), );
+   if (ACPI_FAILURE(status))
+   return NULL;
+
+   return pld;
+#else
+   return NULL;
+#endif
+}
+
+void free_pld(void *pld)
+{
+#ifdef CONFIG_ACPI
+   ACPI_FREE(pld);
+#endif
+}
+
+static int __link_port(struct typec_port *con, struct port_node *

[PATCH v5 2/6] usb: typec: Declare the typec_class static

2021-04-01 Thread Heikki Krogerus
This is only to make the handling of the class consistent
with the two other susbsystems - the alt mode bus and the
mux class.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/class.c | 24 +---
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 5fa279a96b6ef..d3e1002386357 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -17,7 +17,11 @@
 #include "class.h"
 
 static DEFINE_IDA(typec_index_ida);
-static struct class *typec_class;
+
+static struct class typec_class = {
+   .name = "typec",
+   .owner = THIS_MODULE,
+};
 
 /* - */
 /* Common attributes */
@@ -551,7 +555,7 @@ typec_register_altmode(struct device *parent,
 
/* Plug alt modes need a class to generate udev events. */
if (is_typec_plug(parent))
-   alt->adev.dev.class = typec_class;
+   alt->adev.dev.class = _class;
 
ret = device_register(>adev.dev);
if (ret) {
@@ -815,7 +819,7 @@ struct typec_partner *typec_register_partner(struct 
typec_port *port,
partner->identity = desc->identity;
}
 
-   partner->dev.class = typec_class;
+   partner->dev.class = _class;
partner->dev.parent = >dev;
partner->dev.type = _partner_dev_type;
dev_set_name(>dev, "%s-partner", dev_name(>dev));
@@ -967,7 +971,7 @@ struct typec_plug *typec_register_plug(struct typec_cable 
*cable,
ida_init(>mode_ids);
plug->num_altmodes = -1;
plug->index = desc->index;
-   plug->dev.class = typec_class;
+   plug->dev.class = _class;
plug->dev.parent = >dev;
plug->dev.type = _plug_dev_type;
dev_set_name(>dev, "%s-%s", dev_name(cable->dev.parent), name);
@@ -1132,7 +1136,7 @@ struct typec_cable *typec_register_cable(struct 
typec_port *port,
cable->identity = desc->identity;
}
 
-   cable->dev.class = typec_class;
+   cable->dev.class = _class;
cable->dev.parent = >dev;
cable->dev.type = _cable_dev_type;
dev_set_name(>dev, "%s-cable", dev_name(>dev));
@@ -1986,7 +1990,7 @@ struct typec_port *typec_register_port(struct device 
*parent,
port->prefer_role = cap->prefer_role;
 
device_initialize(>dev);
-   port->dev.class = typec_class;
+   port->dev.class = _class;
port->dev.parent = parent;
port->dev.fwnode = cap->fwnode;
port->dev.type = _port_dev_type;
@@ -2049,11 +2053,9 @@ static int __init typec_init(void)
if (ret)
goto err_unregister_bus;
 
-   typec_class = class_create(THIS_MODULE, "typec");
-   if (IS_ERR(typec_class)) {
-   ret = PTR_ERR(typec_class);
+   ret = class_register(_class);
+   if (ret)
goto err_unregister_mux_class;
-   }
 
return 0;
 
@@ -2069,7 +2071,7 @@ subsys_initcall(typec_init);
 
 static void __exit typec_exit(void)
 {
-   class_destroy(typec_class);
+   class_unregister(_class);
ida_destroy(_index_ida);
bus_unregister(_bus);
class_unregister(_mux_class);
-- 
2.30.2



[PATCH v5 1/6] usb: typec: Organize the private headers properly

2021-04-01 Thread Heikki Krogerus
Adding a header file for each subsystem - the connector
class, alt mode bus and the class for the muxes.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/bus.c   |  2 ++
 drivers/usb/typec/bus.h   | 19 +-
 drivers/usb/typec/class.c | 69 +++
 drivers/usb/typec/class.h | 76 +++
 drivers/usb/typec/mux.c   |  4 +--
 drivers/usb/typec/mux.h   | 21 +++
 6 files changed, 107 insertions(+), 84 deletions(-)
 create mode 100644 drivers/usb/typec/class.h
 create mode 100644 drivers/usb/typec/mux.h

diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c
index e8ddb81cb6df4..7f3c9a8e2bf08 100644
--- a/drivers/usb/typec/bus.c
+++ b/drivers/usb/typec/bus.c
@@ -9,6 +9,8 @@
 #include 
 
 #include "bus.h"
+#include "class.h"
+#include "mux.h"
 
 static inline int
 typec_altmode_set_mux(struct altmode *alt, unsigned long conf, void *data)
diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h
index 8ba8112d2740d..56dec268d4dd9 100644
--- a/drivers/usb/typec/bus.h
+++ b/drivers/usb/typec/bus.h
@@ -4,9 +4,9 @@
 #define __USB_TYPEC_ALTMODE_H__
 
 #include 
-#include 
 
 struct bus_type;
+struct typec_mux;
 
 struct altmode {
unsigned intid;
@@ -28,24 +28,7 @@ struct altmode {
 
 extern struct bus_type typec_bus;
 extern const struct device_type typec_altmode_dev_type;
-extern const struct device_type typec_port_dev_type;
 
 #define is_typec_altmode(_dev_) (_dev_->type == _altmode_dev_type)
-#define is_typec_port(_dev_) (_dev_->type == _port_dev_type)
-
-extern struct class typec_mux_class;
-
-struct typec_switch {
-   struct device dev;
-   typec_switch_set_fn_t set;
-};
-
-struct typec_mux {
-   struct device dev;
-   typec_mux_set_fn_t set;
-};
-
-#define to_typec_switch(_dev_) container_of(_dev_, struct typec_switch, dev)
-#define to_typec_mux(_dev_) container_of(_dev_, struct typec_mux, dev)
 
 #endif /* __USB_TYPEC_ALTMODE_H__ */
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 45f0bf65e9aba..5fa279a96b6ef 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -6,74 +6,15 @@
  * Author: Heikki Krogerus 
  */
 
-#include 
 #include 
 #include 
 #include 
 #include 
 #include 
+#include 
 
 #include "bus.h"
-
-struct typec_plug {
-   struct device   dev;
-   enum typec_plug_index   index;
-   struct ida  mode_ids;
-   int num_altmodes;
-};
-
-struct typec_cable {
-   struct device   dev;
-   enum typec_plug_typetype;
-   struct usb_pd_identity  *identity;
-   unsigned intactive:1;
-   u16 pd_revision; /* 0300H = "3.0" */
-};
-
-struct typec_partner {
-   struct device   dev;
-   unsigned intusb_pd:1;
-   struct usb_pd_identity  *identity;
-   enum typec_accessoryaccessory;
-   struct ida  mode_ids;
-   int num_altmodes;
-   u16 pd_revision; /* 0300H = "3.0" */
-   enum usb_pd_svdm_versvdm_version;
-};
-
-struct typec_port {
-   unsigned intid;
-   struct device   dev;
-   struct ida  mode_ids;
-
-   int prefer_role;
-   enum typec_data_roledata_role;
-   enum typec_role pwr_role;
-   enum typec_role vconn_role;
-   enum typec_pwr_opmode   pwr_opmode;
-   enum typec_port_typeport_type;
-   struct mutexport_type_lock;
-
-   enum typec_orientation  orientation;
-   struct typec_switch *sw;
-   struct typec_mux*mux;
-
-   const struct typec_capability   *cap;
-   const struct typec_operations   *ops;
-};
-
-#define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
-#define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev)
-#define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev)
-#define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev)
-
-static const struct device_type typec_partner_dev_type;
-static const struct device_type typec_cable_dev_type;
-static const struct device_type typec_plug_dev_type;
-
-#define is_typec_partner(_dev_) (_dev_->type == _partner_dev_type)
-#define is_typec_cable(_dev_) (_dev_->type == _cable_dev_type)
-#define is_typec_plug(_dev_) (_dev_->type == _plug_dev_type)
+#include "class.h"
 
 static DEFINE_IDA(typec_index_ida);
 static struct class *typec_class;
@@ -726,7 +667,7 @@ static void typec_partner_release(struct device *dev)

[PATCH v5 4/6] usb: Link the ports to the connectors they are attached to

2021-04-01 Thread Heikki Krogerus
Creating link to the USB Type-C connector for every new port
that is added when possible.

Signed-off-by: Heikki Krogerus 
---
 Documentation/ABI/testing/sysfs-bus-usb | 9 +
 drivers/usb/core/port.c | 3 +++
 2 files changed, 12 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-usb 
b/Documentation/ABI/testing/sysfs-bus-usb
index bf2c1968525f0..8b4303a0ff51d 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -255,6 +255,15 @@ Description:
is permitted, "u2" if only u2 is permitted, "u1_u2" if both u1 
and
u2 are permitted.
 
+What:  /sys/bus/usb/devices/.../(hub interface)/portX/connector
+Date:  April 2021
+Contact:   Heikki Krogerus 
+Description:
+   Link to the USB Type-C connector when available. This link is
+   only created when USB Type-C Connector Class is enabled, and
+   only if the system firmware is capable of describing the
+   connection between a port and its connector.
+
 What:  /sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout
 Date:  May 2013
 Contact:   Mathias Nyman 
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index dfcca9c876c73..3c382a4b648ec 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -9,6 +9,7 @@
 
 #include 
 #include 
+#include 
 
 #include "hub.h"
 
@@ -576,6 +577,7 @@ int usb_hub_create_port_device(struct usb_hub *hub, int 
port1)
}
 
find_and_link_peer(hub, port1);
+   typec_link_port(_dev->dev);
 
/*
 * Enable runtime pm and hold a refernce that hub_configure()
@@ -619,5 +621,6 @@ void usb_hub_remove_port_device(struct usb_hub *hub, int 
port1)
peer = port_dev->peer;
if (peer)
unlink_peers(port_dev, peer);
+   typec_unlink_port(_dev->dev);
device_unregister(_dev->dev);
 }
-- 
2.30.2



[PATCH v5 0/6] usb: Linking ports to their Type-C connectors

2021-04-01 Thread Heikki Krogerus
Hi,

I have to use IS_REACHABLE() instead of IS_ENABLED() also in
include/linux/usb.h. Otherwise compilation will fail if the Type-C
class is build-in while USB is a module.

I'm sorry for re-sending these so fast, immediately after v4. Normally
I would wait, but I'll be taking a short vacation starting from right
now, and I'm still hoping to get these into v5.13.


v4 cover letter:

One more version. I used #ifdef when I should have used #if
IS_DEFINED(). Thanks Guenter for pointing that out.

I'm sending this version right away because of the holidays. I'm not
changing anything else except that one fix.


v3: cover letter:

Third version: ifdefs now in the header files as they should be.


v2 cover letter:

This is the second version of this series. The "Iterator for ports"
patch is now moved to the end of the series (5/6).

I'm now using usb_for_each_dev() in usb_for_each_port like Alan
suggested, and I'm now using usb_port_peer_mutex to lock the ports
while we're dealing with them in __each_hub().


The original cover letter:

Adding a simple function typec_link_port() that can be used to create
a symlink "connector" that points to the USB Type-C connector of a
port. It is used with USB ports initially, but hopefully later also
with other things like DisplayPorts.

Being able to see which connector is connected to a port is important
in general, but it is really important when for example the data or
power role of a device needs to swapped. The user probable wants to
know which USB device is disconnected if role swap on a USB Type-C
connector is executed.

Hope these are OK.

thanks,

Heikki Krogerus (6):
  usb: typec: Organize the private headers properly
  usb: typec: Declare the typec_class static
  usb: typec: Port mapping utility
  usb: Link the ports to the connectors they are attached to
  usb: Iterator for ports
  usb: typec: Link all ports during connector registration

 Documentation/ABI/testing/sysfs-bus-usb |   9 +
 drivers/usb/core/port.c |   3 +
 drivers/usb/core/usb.c  |  46 
 drivers/usb/typec/Makefile  |   2 +-
 drivers/usb/typec/bus.c |   2 +
 drivers/usb/typec/bus.h |  19 +-
 drivers/usb/typec/class.c   | 101 +++--
 drivers/usb/typec/class.h   |  85 
 drivers/usb/typec/mux.c |   4 +-
 drivers/usb/typec/mux.h |  21 ++
 drivers/usb/typec/port-mapper.c | 277 
 include/linux/usb.h |   9 +
 include/linux/usb/typec.h   |  13 ++
 13 files changed, 495 insertions(+), 96 deletions(-)
 create mode 100644 drivers/usb/typec/class.h
 create mode 100644 drivers/usb/typec/mux.h
 create mode 100644 drivers/usb/typec/port-mapper.c

-- 
2.30.2



Re: [PATCH v4 0/6] usb: Linking ports to their Type-C connectors

2021-04-01 Thread Heikki Krogerus
On Thu, Apr 01, 2021 at 09:53:41AM +0300, Heikki Krogerus wrote:
> Hi,
> 
> One more version. I used #ifdef when I should have used #if
> IS_DEFINED(). Thanks Guenter for pointing that out.
> 
> I'm sending this version right away because of the holidays. I'm not
> changing anything else except that one fix.

I have to prepare one more version.

Unfortunately we can not use IS_DEFINED() either. I have to use
IS_REACHABLE() instead.

I'm sorry about this.


thanks,

-- 
heikki


[PATCH v4 5/6] usb: Iterator for ports

2021-04-01 Thread Heikki Krogerus
Introducing usb_for_each_port(). It works the same way as
usb_for_each_dev(), but instead of going through every USB
device in the system, it walks through the USB ports in the
system.

Signed-off-by: Heikki Krogerus 
Acked-by: Alan Stern 
---
 drivers/usb/core/usb.c | 46 ++
 include/linux/usb.h|  9 +
 2 files changed, 55 insertions(+)

diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 2ce3667ec6fae..62368c4ed37af 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -398,6 +398,52 @@ int usb_for_each_dev(void *data, int (*fn)(struct 
usb_device *, void *))
 }
 EXPORT_SYMBOL_GPL(usb_for_each_dev);
 
+struct each_hub_arg {
+   void *data;
+   int (*fn)(struct device *, void *);
+};
+
+static int __each_hub(struct usb_device *hdev, void *data)
+{
+   struct each_hub_arg *arg = (struct each_hub_arg *)data;
+   struct usb_hub *hub;
+   int ret = 0;
+   int i;
+
+   hub = usb_hub_to_struct_hub(hdev);
+   if (!hub)
+   return 0;
+
+   mutex_lock(_port_peer_mutex);
+
+   for (i = 0; i < hdev->maxchild; i++) {
+   ret = arg->fn(>ports[i]->dev, arg->data);
+   if (ret)
+   break;
+   }
+
+   mutex_unlock(_port_peer_mutex);
+
+   return ret;
+}
+
+/**
+ * usb_for_each_port - interate over all USB ports in the system
+ * @data: data pointer that will be handed to the callback function
+ * @fn: callback function to be called for each USB port
+ *
+ * Iterate over all USB ports and call @fn for each, passing it @data. If it
+ * returns anything other than 0, we break the iteration prematurely and return
+ * that value.
+ */
+int usb_for_each_port(void *data, int (*fn)(struct device *, void *))
+{
+   struct each_hub_arg arg = {data, fn};
+
+   return usb_for_each_dev(, __each_hub);
+}
+EXPORT_SYMBOL_GPL(usb_for_each_port);
+
 /**
  * usb_release_dev - free a usb device structure when all users of it are 
finished.
  * @dev: device that's been disconnected
diff --git a/include/linux/usb.h b/include/linux/usb.h
index ddd2f5b2a2827..0de620b207637 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -882,6 +882,15 @@ extern struct usb_host_interface *usb_find_alt_setting(
unsigned int iface_num,
unsigned int alt_num);
 
+#if IS_ENABLED(CONFIG_USB)
+int usb_for_each_port(void *data, int (*fn)(struct device *, void *));
+#else
+static inline int usb_for_each_port(void *data, int (*fn)(struct device *, 
void *))
+{
+   return 0;
+}
+#endif
+
 /* port claiming functions */
 int usb_hub_claim_port(struct usb_device *hdev, unsigned port1,
struct usb_dev_state *owner);
-- 
2.30.2



[PATCH v4 3/6] usb: typec: Port mapping utility

2021-04-01 Thread Heikki Krogerus
Adding functions that can be used to link/unlink ports -
USB ports, TBT3/USB4 ports, DisplayPorts and so on - to
the USB Type-C connectors they are attached to inside a
system. The symlink that is created for the port device is
named "connector".

Initially only ACPI is supported. ACPI port object shares
the _PLD (Physical Location of Device) with the USB Type-C
connector that it's attached to.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/Makefile  |   2 +-
 drivers/usb/typec/class.c   |   7 +-
 drivers/usb/typec/class.h   |   9 ++
 drivers/usb/typec/port-mapper.c | 219 
 include/linux/usb/typec.h   |  13 ++
 5 files changed, 248 insertions(+), 2 deletions(-)
 create mode 100644 drivers/usb/typec/port-mapper.c

diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index a820e6e8c1ffc..1e1868832b8d8 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -3,7 +3,7 @@
 CFLAGS_tps6598x.o  := -I$(src)
 
 obj-$(CONFIG_TYPEC)+= typec.o
-typec-y:= class.o mux.o bus.o
+typec-y:= class.o mux.o bus.o port-mapper.o
 obj-$(CONFIG_TYPEC)+= altmodes/
 obj-$(CONFIG_TYPEC_TCPM)   += tcpm/
 obj-$(CONFIG_TYPEC_UCSI)   += ucsi/
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index d3e1002386357..ff199e2d26c7b 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -18,7 +18,7 @@
 
 static DEFINE_IDA(typec_index_ida);
 
-static struct class typec_class = {
+struct class typec_class = {
.name = "typec",
.owner = THIS_MODULE,
 };
@@ -1601,6 +1601,7 @@ static void typec_release(struct device *dev)
ida_destroy(>mode_ids);
typec_switch_put(port->sw);
typec_mux_put(port->mux);
+   free_pld(port->pld);
kfree(port->cap);
kfree(port);
 }
@@ -1983,6 +1984,8 @@ struct typec_port *typec_register_port(struct device 
*parent,
 
ida_init(>mode_ids);
mutex_init(>port_type_lock);
+   mutex_init(>port_list_lock);
+   INIT_LIST_HEAD(>port_list);
 
port->id = id;
port->ops = cap->ops;
@@ -2024,6 +2027,8 @@ struct typec_port *typec_register_port(struct device 
*parent,
return ERR_PTR(ret);
}
 
+   port->pld = get_pld(>dev);
+
return port;
 }
 EXPORT_SYMBOL_GPL(typec_register_port);
diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h
index d414be58d122e..52294f7020a8b 100644
--- a/drivers/usb/typec/class.h
+++ b/drivers/usb/typec/class.h
@@ -54,6 +54,11 @@ struct typec_port {
 
const struct typec_capability   *cap;
const struct typec_operations   *ops;
+
+   struct list_headport_list;
+   struct mutexport_list_lock; /* Port list lock */
+
+   void*pld;
 };
 
 #define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
@@ -72,5 +77,9 @@ extern const struct device_type typec_port_dev_type;
 #define is_typec_port(dev) ((dev)->type == _port_dev_type)
 
 extern struct class typec_mux_class;
+extern struct class typec_class;
+
+void *get_pld(struct device *dev);
+void free_pld(void *pld);
 
 #endif /* __USB_TYPEC_CLASS__ */
diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c
new file mode 100644
index 0..5bee7a97242fe
--- /dev/null
+++ b/drivers/usb/typec/port-mapper.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Type-C Connector Class Port Mapping Utility
+ *
+ * Copyright (C) 2021, Intel Corporation
+ * Author: Heikki Krogerus 
+ */
+
+#include 
+#include 
+#include 
+
+#include "class.h"
+
+struct port_node {
+   struct list_head list;
+   struct device *dev;
+   void *pld;
+};
+
+static int acpi_pld_match(const struct acpi_pld_info *pld1,
+ const struct acpi_pld_info *pld2)
+{
+   if (!pld1 || !pld2)
+   return 0;
+
+   /*
+* To speed things up, first checking only the group_position. It seems
+* to often have the first unique value in the _PLD.
+*/
+   if (pld1->group_position == pld2->group_position)
+   return !memcmp(pld1, pld2, sizeof(struct acpi_pld_info));
+
+   return 0;
+}
+
+void *get_pld(struct device *dev)
+{
+#ifdef CONFIG_ACPI
+   struct acpi_pld_info *pld;
+   acpi_status status;
+
+   if (!has_acpi_companion(dev))
+   return NULL;
+
+   status = acpi_get_physical_device_location(ACPI_HANDLE(dev), );
+   if (ACPI_FAILURE(status))
+   return NULL;
+
+   return pld;
+#else
+   return NULL;
+#endif
+}
+
+void free_pld(void *pld)
+{
+#ifdef CONFIG_ACPI
+   ACPI_FREE(pld);
+#endif
+}
+
+static int __link_port(struct typec_port *con, struct port_node *

[PATCH v4 6/6] usb: typec: Link all ports during connector registration

2021-04-01 Thread Heikki Krogerus
The connectors may be registered after the ports, so the
"connector" links need to be created for the ports also when
ever a new connector gets registered.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/class.c   |  9 +++--
 drivers/usb/typec/class.h   |  4 +--
 drivers/usb/typec/port-mapper.c | 62 +++--
 3 files changed, 68 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index ff199e2d26c7b..f1c2d823c6509 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -1601,7 +1601,6 @@ static void typec_release(struct device *dev)
ida_destroy(>mode_ids);
typec_switch_put(port->sw);
typec_mux_put(port->mux);
-   free_pld(port->pld);
kfree(port->cap);
kfree(port);
 }
@@ -2027,7 +2026,9 @@ struct typec_port *typec_register_port(struct device 
*parent,
return ERR_PTR(ret);
}
 
-   port->pld = get_pld(>dev);
+   ret = typec_link_ports(port);
+   if (ret)
+   dev_warn(>dev, "failed to create symlinks (%d)\n", ret);
 
return port;
 }
@@ -2041,8 +2042,10 @@ EXPORT_SYMBOL_GPL(typec_register_port);
  */
 void typec_unregister_port(struct typec_port *port)
 {
-   if (!IS_ERR_OR_NULL(port))
+   if (!IS_ERR_OR_NULL(port)) {
+   typec_unlink_ports(port);
device_unregister(>dev);
+   }
 }
 EXPORT_SYMBOL_GPL(typec_unregister_port);
 
diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h
index 52294f7020a8b..aef03eb7e1523 100644
--- a/drivers/usb/typec/class.h
+++ b/drivers/usb/typec/class.h
@@ -79,7 +79,7 @@ extern const struct device_type typec_port_dev_type;
 extern struct class typec_mux_class;
 extern struct class typec_class;
 
-void *get_pld(struct device *dev);
-void free_pld(void *pld);
+int typec_link_ports(struct typec_port *connector);
+void typec_unlink_ports(struct typec_port *connector);
 
 #endif /* __USB_TYPEC_CLASS__ */
diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c
index 5bee7a97242fe..fae736eb0601e 100644
--- a/drivers/usb/typec/port-mapper.c
+++ b/drivers/usb/typec/port-mapper.c
@@ -34,7 +34,7 @@ static int acpi_pld_match(const struct acpi_pld_info *pld1,
return 0;
 }
 
-void *get_pld(struct device *dev)
+static void *get_pld(struct device *dev)
 {
 #ifdef CONFIG_ACPI
struct acpi_pld_info *pld;
@@ -53,7 +53,7 @@ void *get_pld(struct device *dev)
 #endif
 }
 
-void free_pld(void *pld)
+static void free_pld(void *pld)
 {
 #ifdef CONFIG_ACPI
ACPI_FREE(pld);
@@ -217,3 +217,61 @@ void typec_unlink_port(struct device *port)
class_for_each_device(_class, NULL, port, port_match_and_unlink);
 }
 EXPORT_SYMBOL_GPL(typec_unlink_port);
+
+static int each_port(struct device *port, void *connector)
+{
+   struct port_node *node;
+   int ret;
+
+   node = create_port_node(port);
+   if (IS_ERR(node))
+   return PTR_ERR(node);
+
+   if (!connector_match(connector, node)) {
+   remove_port_node(node);
+   return 0;
+   }
+
+   ret = link_port(to_typec_port(connector), node);
+   if (ret) {
+   remove_port_node(node->pld);
+   return ret;
+   }
+
+   get_device(connector);
+
+   return 0;
+}
+
+int typec_link_ports(struct typec_port *con)
+{
+   int ret = 0;
+
+   con->pld = get_pld(>dev);
+   if (!con->pld)
+   return 0;
+
+   ret = usb_for_each_port(>dev, each_port);
+   if (ret)
+   typec_unlink_ports(con);
+
+   return ret;
+}
+
+void typec_unlink_ports(struct typec_port *con)
+{
+   struct port_node *node;
+   struct port_node *tmp;
+
+   mutex_lock(>port_list_lock);
+
+   list_for_each_entry_safe(node, tmp, >port_list, list) {
+   __unlink_port(con, node);
+   remove_port_node(node);
+   put_device(>dev);
+   }
+
+   mutex_unlock(>port_list_lock);
+
+   free_pld(con->pld);
+}
-- 
2.30.2



[PATCH v4 2/6] usb: typec: Declare the typec_class static

2021-04-01 Thread Heikki Krogerus
This is only to make the handling of the class consistent
with the two other susbsystems - the alt mode bus and the
mux class.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/class.c | 24 +---
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 5fa279a96b6ef..d3e1002386357 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -17,7 +17,11 @@
 #include "class.h"
 
 static DEFINE_IDA(typec_index_ida);
-static struct class *typec_class;
+
+static struct class typec_class = {
+   .name = "typec",
+   .owner = THIS_MODULE,
+};
 
 /* - */
 /* Common attributes */
@@ -551,7 +555,7 @@ typec_register_altmode(struct device *parent,
 
/* Plug alt modes need a class to generate udev events. */
if (is_typec_plug(parent))
-   alt->adev.dev.class = typec_class;
+   alt->adev.dev.class = _class;
 
ret = device_register(>adev.dev);
if (ret) {
@@ -815,7 +819,7 @@ struct typec_partner *typec_register_partner(struct 
typec_port *port,
partner->identity = desc->identity;
}
 
-   partner->dev.class = typec_class;
+   partner->dev.class = _class;
partner->dev.parent = >dev;
partner->dev.type = _partner_dev_type;
dev_set_name(>dev, "%s-partner", dev_name(>dev));
@@ -967,7 +971,7 @@ struct typec_plug *typec_register_plug(struct typec_cable 
*cable,
ida_init(>mode_ids);
plug->num_altmodes = -1;
plug->index = desc->index;
-   plug->dev.class = typec_class;
+   plug->dev.class = _class;
plug->dev.parent = >dev;
plug->dev.type = _plug_dev_type;
dev_set_name(>dev, "%s-%s", dev_name(cable->dev.parent), name);
@@ -1132,7 +1136,7 @@ struct typec_cable *typec_register_cable(struct 
typec_port *port,
cable->identity = desc->identity;
}
 
-   cable->dev.class = typec_class;
+   cable->dev.class = _class;
cable->dev.parent = >dev;
cable->dev.type = _cable_dev_type;
dev_set_name(>dev, "%s-cable", dev_name(>dev));
@@ -1986,7 +1990,7 @@ struct typec_port *typec_register_port(struct device 
*parent,
port->prefer_role = cap->prefer_role;
 
device_initialize(>dev);
-   port->dev.class = typec_class;
+   port->dev.class = _class;
port->dev.parent = parent;
port->dev.fwnode = cap->fwnode;
port->dev.type = _port_dev_type;
@@ -2049,11 +2053,9 @@ static int __init typec_init(void)
if (ret)
goto err_unregister_bus;
 
-   typec_class = class_create(THIS_MODULE, "typec");
-   if (IS_ERR(typec_class)) {
-   ret = PTR_ERR(typec_class);
+   ret = class_register(_class);
+   if (ret)
goto err_unregister_mux_class;
-   }
 
return 0;
 
@@ -2069,7 +2071,7 @@ subsys_initcall(typec_init);
 
 static void __exit typec_exit(void)
 {
-   class_destroy(typec_class);
+   class_unregister(_class);
ida_destroy(_index_ida);
bus_unregister(_bus);
class_unregister(_mux_class);
-- 
2.30.2



[PATCH v4 4/6] usb: Link the ports to the connectors they are attached to

2021-04-01 Thread Heikki Krogerus
Creating link to the USB Type-C connector for every new port
that is added when possible.

Signed-off-by: Heikki Krogerus 
---
 Documentation/ABI/testing/sysfs-bus-usb | 9 +
 drivers/usb/core/port.c | 3 +++
 2 files changed, 12 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-usb 
b/Documentation/ABI/testing/sysfs-bus-usb
index bf2c1968525f0..8b4303a0ff51d 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -255,6 +255,15 @@ Description:
is permitted, "u2" if only u2 is permitted, "u1_u2" if both u1 
and
u2 are permitted.
 
+What:  /sys/bus/usb/devices/.../(hub interface)/portX/connector
+Date:  April 2021
+Contact:   Heikki Krogerus 
+Description:
+   Link to the USB Type-C connector when available. This link is
+   only created when USB Type-C Connector Class is enabled, and
+   only if the system firmware is capable of describing the
+   connection between a port and its connector.
+
 What:  /sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout
 Date:  May 2013
 Contact:   Mathias Nyman 
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index dfcca9c876c73..3c382a4b648ec 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -9,6 +9,7 @@
 
 #include 
 #include 
+#include 
 
 #include "hub.h"
 
@@ -576,6 +577,7 @@ int usb_hub_create_port_device(struct usb_hub *hub, int 
port1)
}
 
find_and_link_peer(hub, port1);
+   typec_link_port(_dev->dev);
 
/*
 * Enable runtime pm and hold a refernce that hub_configure()
@@ -619,5 +621,6 @@ void usb_hub_remove_port_device(struct usb_hub *hub, int 
port1)
peer = port_dev->peer;
if (peer)
unlink_peers(port_dev, peer);
+   typec_unlink_port(_dev->dev);
device_unregister(_dev->dev);
 }
-- 
2.30.2



[PATCH v4 1/6] usb: typec: Organize the private headers properly

2021-04-01 Thread Heikki Krogerus
Adding a header file for each subsystem - the connector
class, alt mode bus and the class for the muxes.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/bus.c   |  2 ++
 drivers/usb/typec/bus.h   | 19 +-
 drivers/usb/typec/class.c | 69 +++
 drivers/usb/typec/class.h | 76 +++
 drivers/usb/typec/mux.c   |  4 +--
 drivers/usb/typec/mux.h   | 21 +++
 6 files changed, 107 insertions(+), 84 deletions(-)
 create mode 100644 drivers/usb/typec/class.h
 create mode 100644 drivers/usb/typec/mux.h

diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c
index e8ddb81cb6df4..7f3c9a8e2bf08 100644
--- a/drivers/usb/typec/bus.c
+++ b/drivers/usb/typec/bus.c
@@ -9,6 +9,8 @@
 #include 
 
 #include "bus.h"
+#include "class.h"
+#include "mux.h"
 
 static inline int
 typec_altmode_set_mux(struct altmode *alt, unsigned long conf, void *data)
diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h
index 8ba8112d2740d..56dec268d4dd9 100644
--- a/drivers/usb/typec/bus.h
+++ b/drivers/usb/typec/bus.h
@@ -4,9 +4,9 @@
 #define __USB_TYPEC_ALTMODE_H__
 
 #include 
-#include 
 
 struct bus_type;
+struct typec_mux;
 
 struct altmode {
unsigned intid;
@@ -28,24 +28,7 @@ struct altmode {
 
 extern struct bus_type typec_bus;
 extern const struct device_type typec_altmode_dev_type;
-extern const struct device_type typec_port_dev_type;
 
 #define is_typec_altmode(_dev_) (_dev_->type == _altmode_dev_type)
-#define is_typec_port(_dev_) (_dev_->type == _port_dev_type)
-
-extern struct class typec_mux_class;
-
-struct typec_switch {
-   struct device dev;
-   typec_switch_set_fn_t set;
-};
-
-struct typec_mux {
-   struct device dev;
-   typec_mux_set_fn_t set;
-};
-
-#define to_typec_switch(_dev_) container_of(_dev_, struct typec_switch, dev)
-#define to_typec_mux(_dev_) container_of(_dev_, struct typec_mux, dev)
 
 #endif /* __USB_TYPEC_ALTMODE_H__ */
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 45f0bf65e9aba..5fa279a96b6ef 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -6,74 +6,15 @@
  * Author: Heikki Krogerus 
  */
 
-#include 
 #include 
 #include 
 #include 
 #include 
 #include 
+#include 
 
 #include "bus.h"
-
-struct typec_plug {
-   struct device   dev;
-   enum typec_plug_index   index;
-   struct ida  mode_ids;
-   int num_altmodes;
-};
-
-struct typec_cable {
-   struct device   dev;
-   enum typec_plug_typetype;
-   struct usb_pd_identity  *identity;
-   unsigned intactive:1;
-   u16 pd_revision; /* 0300H = "3.0" */
-};
-
-struct typec_partner {
-   struct device   dev;
-   unsigned intusb_pd:1;
-   struct usb_pd_identity  *identity;
-   enum typec_accessoryaccessory;
-   struct ida  mode_ids;
-   int num_altmodes;
-   u16 pd_revision; /* 0300H = "3.0" */
-   enum usb_pd_svdm_versvdm_version;
-};
-
-struct typec_port {
-   unsigned intid;
-   struct device   dev;
-   struct ida  mode_ids;
-
-   int prefer_role;
-   enum typec_data_roledata_role;
-   enum typec_role pwr_role;
-   enum typec_role vconn_role;
-   enum typec_pwr_opmode   pwr_opmode;
-   enum typec_port_typeport_type;
-   struct mutexport_type_lock;
-
-   enum typec_orientation  orientation;
-   struct typec_switch *sw;
-   struct typec_mux*mux;
-
-   const struct typec_capability   *cap;
-   const struct typec_operations   *ops;
-};
-
-#define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
-#define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev)
-#define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev)
-#define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev)
-
-static const struct device_type typec_partner_dev_type;
-static const struct device_type typec_cable_dev_type;
-static const struct device_type typec_plug_dev_type;
-
-#define is_typec_partner(_dev_) (_dev_->type == _partner_dev_type)
-#define is_typec_cable(_dev_) (_dev_->type == _cable_dev_type)
-#define is_typec_plug(_dev_) (_dev_->type == _plug_dev_type)
+#include "class.h"
 
 static DEFINE_IDA(typec_index_ida);
 static struct class *typec_class;
@@ -726,7 +667,7 @@ static void typec_partner_release(struct device *dev)

[PATCH v4 0/6] usb: Linking ports to their Type-C connectors

2021-04-01 Thread Heikki Krogerus
Hi,

One more version. I used #ifdef when I should have used #if
IS_DEFINED(). Thanks Guenter for pointing that out.

I'm sending this version right away because of the holidays. I'm not
changing anything else except that one fix.


v3: cover letter:

Third version: ifdefs now in the header files as they should be.


v2 cover letter:

This is the second version of this series. The "Iterator for ports"
patch is now moved to the end of the series (5/6).

I'm now using usb_for_each_dev() in usb_for_each_port like Alan
suggested, and I'm now using usb_port_peer_mutex to lock the ports
while we're dealing with them in __each_hub().


The original cover letter:

Adding a simple function typec_link_port() that can be used to create
a symlink "connector" that points to the USB Type-C connector of a
port. It is used with USB ports initially, but hopefully later also
with other things like DisplayPorts.

Being able to see which connector is connected to a port is important
in general, but it is really important when for example the data or
power role of a device needs to swapped. The user probable wants to
know which USB device is disconnected if role swap on a USB Type-C
connector is executed.

Hope these are OK.

thanks,

Heikki Krogerus (6):
  usb: typec: Organize the private headers properly
  usb: typec: Declare the typec_class static
  usb: typec: Port mapping utility
  usb: Link the ports to the connectors they are attached to
  usb: Iterator for ports
  usb: typec: Link all ports during connector registration

 Documentation/ABI/testing/sysfs-bus-usb |   9 +
 drivers/usb/core/port.c |   3 +
 drivers/usb/core/usb.c  |  46 
 drivers/usb/typec/Makefile  |   2 +-
 drivers/usb/typec/bus.c |   2 +
 drivers/usb/typec/bus.h |  19 +-
 drivers/usb/typec/class.c   | 101 +++--
 drivers/usb/typec/class.h   |  85 
 drivers/usb/typec/mux.c |   4 +-
 drivers/usb/typec/mux.h |  21 ++
 drivers/usb/typec/port-mapper.c | 277 
 include/linux/usb.h |   9 +
 include/linux/usb/typec.h   |  13 ++
 13 files changed, 495 insertions(+), 96 deletions(-)
 create mode 100644 drivers/usb/typec/class.h
 create mode 100644 drivers/usb/typec/mux.h
 create mode 100644 drivers/usb/typec/port-mapper.c

-- 
2.30.2



Re: [PATCH v3 5/6] usb: Iterator for ports

2021-04-01 Thread Heikki Krogerus
On Wed, Mar 31, 2021 at 09:41:22AM -0700, Guenter Roeck wrote:
> > diff --git a/include/linux/usb.h b/include/linux/usb.h
> > index ddd2f5b2a2827..ebcd03d835d04 100644
> > --- a/include/linux/usb.h
> > +++ b/include/linux/usb.h
> > @@ -882,6 +882,15 @@ extern struct usb_host_interface *usb_find_alt_setting(
> > unsigned int iface_num,
> > unsigned int alt_num);
> >  
> > +#ifdef CONFIG_USB
> 
> #if IS_ENABLED(CONFIG_USB)

Thanks Guenter.

> > +int usb_for_each_port(void *data, int (*fn)(struct device *, void *));
> > +#else
> > +static inline int usb_for_each_port(void *data, int (*fn)(struct device *, 
> > void *))
> > +{
> > +   return 0;
> > +}
> > +#endif
> > +
> >  /* port claiming functions */
> >  int usb_hub_claim_port(struct usb_device *hdev, unsigned port1,
> > struct usb_dev_state *owner);
> > 

-- 
heikki


Re: [PATCH v2 1/6] software node: Free resources explicitly when swnode_register() fails

2021-03-31 Thread Heikki Krogerus
On Mon, Mar 29, 2021 at 06:12:02PM +0300, Andy Shevchenko wrote:
> Currently we have a slightly twisted logic in swnode_register().
> It frees resources that it doesn't allocate on error path and
> in once case it relies on the ->release() implementation.
> 
> Untwist the logic by freeing resources explicitly when swnode_register()
> fails. Currently it happens only in fwnode_create_software_node().
> 
> Signed-off-by: Andy Shevchenko 

It all looks OK to me. FWIW, for the whole series:

Reviewed-by: Heikki Krogerus 

> ---
> v2: no changes
>  drivers/base/swnode.c | 29 +
>  1 file changed, 17 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
> index fa3719ef80e4..456f5fe58b58 100644
> --- a/drivers/base/swnode.c
> +++ b/drivers/base/swnode.c
> @@ -767,22 +767,19 @@ swnode_register(const struct software_node *node, 
> struct swnode *parent,
>   int ret;
>  
>   swnode = kzalloc(sizeof(*swnode), GFP_KERNEL);
> - if (!swnode) {
> - ret = -ENOMEM;
> - goto out_err;
> - }
> + if (!swnode)
> + return ERR_PTR(-ENOMEM);
>  
>   ret = ida_simple_get(parent ? >child_ids : _root_ids,
>0, 0, GFP_KERNEL);
>   if (ret < 0) {
>   kfree(swnode);
> - goto out_err;
> + return ERR_PTR(ret);
>   }
>  
>   swnode->id = ret;
>   swnode->node = node;
>   swnode->parent = parent;
> - swnode->allocated = allocated;
>   swnode->kobj.kset = swnode_kset;
>   fwnode_init(>fwnode, _node_ops);
>  
> @@ -803,16 +800,17 @@ swnode_register(const struct software_node *node, 
> struct swnode *parent,
>   return ERR_PTR(ret);
>   }
>  
> + /*
> +  * Assign the flag only in the successful case, so
> +  * the above kobject_put() won't mess up with properties.
> +  */
> + swnode->allocated = allocated;
> +
>   if (parent)
>   list_add_tail(>entry, >children);
>  
>   kobject_uevent(>kobj, KOBJ_ADD);
>   return >fwnode;
> -
> -out_err:
> - if (allocated)
> - property_entries_free(node->properties);
> - return ERR_PTR(ret);
>  }
>  
>  /**
> @@ -963,6 +961,7 @@ struct fwnode_handle *
>  fwnode_create_software_node(const struct property_entry *properties,
>   const struct fwnode_handle *parent)
>  {
> + struct fwnode_handle *fwnode;
>   struct software_node *node;
>   struct swnode *p = NULL;
>   int ret;
> @@ -987,7 +986,13 @@ fwnode_create_software_node(const struct property_entry 
> *properties,
>  
>   node->parent = p ? p->node : NULL;
>  
> - return swnode_register(node, p, 1);
> + fwnode = swnode_register(node, p, 1);
> + if (IS_ERR(fwnode)) {
> + property_entries_free(node->properties);
> + kfree(node);
> + }
> +
> + return fwnode;
>  }
>  EXPORT_SYMBOL_GPL(fwnode_create_software_node);
>  
> -- 
> 2.30.2

thanks,

-- 
heikki


[PATCH v3 5/6] usb: Iterator for ports

2021-03-31 Thread Heikki Krogerus
Introducing usb_for_each_port(). It works the same way as
usb_for_each_dev(), but instead of going through every USB
device in the system, it walks through the USB ports in the
system.

Signed-off-by: Heikki Krogerus 
Acked-by: Alan Stern 
---
 drivers/usb/core/usb.c | 46 ++
 include/linux/usb.h|  9 +
 2 files changed, 55 insertions(+)

diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 2ce3667ec6fae..62368c4ed37af 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -398,6 +398,52 @@ int usb_for_each_dev(void *data, int (*fn)(struct 
usb_device *, void *))
 }
 EXPORT_SYMBOL_GPL(usb_for_each_dev);
 
+struct each_hub_arg {
+   void *data;
+   int (*fn)(struct device *, void *);
+};
+
+static int __each_hub(struct usb_device *hdev, void *data)
+{
+   struct each_hub_arg *arg = (struct each_hub_arg *)data;
+   struct usb_hub *hub;
+   int ret = 0;
+   int i;
+
+   hub = usb_hub_to_struct_hub(hdev);
+   if (!hub)
+   return 0;
+
+   mutex_lock(_port_peer_mutex);
+
+   for (i = 0; i < hdev->maxchild; i++) {
+   ret = arg->fn(>ports[i]->dev, arg->data);
+   if (ret)
+   break;
+   }
+
+   mutex_unlock(_port_peer_mutex);
+
+   return ret;
+}
+
+/**
+ * usb_for_each_port - interate over all USB ports in the system
+ * @data: data pointer that will be handed to the callback function
+ * @fn: callback function to be called for each USB port
+ *
+ * Iterate over all USB ports and call @fn for each, passing it @data. If it
+ * returns anything other than 0, we break the iteration prematurely and return
+ * that value.
+ */
+int usb_for_each_port(void *data, int (*fn)(struct device *, void *))
+{
+   struct each_hub_arg arg = {data, fn};
+
+   return usb_for_each_dev(, __each_hub);
+}
+EXPORT_SYMBOL_GPL(usb_for_each_port);
+
 /**
  * usb_release_dev - free a usb device structure when all users of it are 
finished.
  * @dev: device that's been disconnected
diff --git a/include/linux/usb.h b/include/linux/usb.h
index ddd2f5b2a2827..ebcd03d835d04 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -882,6 +882,15 @@ extern struct usb_host_interface *usb_find_alt_setting(
unsigned int iface_num,
unsigned int alt_num);
 
+#ifdef CONFIG_USB
+int usb_for_each_port(void *data, int (*fn)(struct device *, void *));
+#else
+static inline int usb_for_each_port(void *data, int (*fn)(struct device *, 
void *))
+{
+   return 0;
+}
+#endif
+
 /* port claiming functions */
 int usb_hub_claim_port(struct usb_device *hdev, unsigned port1,
struct usb_dev_state *owner);
-- 
2.30.2



[PATCH v3 6/6] usb: typec: Link all ports during connector registration

2021-03-31 Thread Heikki Krogerus
The connectors may be registered after the ports, so the
"connector" links need to be created for the ports also when
ever a new connector gets registered.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/class.c   |  9 +++--
 drivers/usb/typec/class.h   |  4 +--
 drivers/usb/typec/port-mapper.c | 62 +++--
 3 files changed, 68 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index ff199e2d26c7b..f1c2d823c6509 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -1601,7 +1601,6 @@ static void typec_release(struct device *dev)
ida_destroy(>mode_ids);
typec_switch_put(port->sw);
typec_mux_put(port->mux);
-   free_pld(port->pld);
kfree(port->cap);
kfree(port);
 }
@@ -2027,7 +2026,9 @@ struct typec_port *typec_register_port(struct device 
*parent,
return ERR_PTR(ret);
}
 
-   port->pld = get_pld(>dev);
+   ret = typec_link_ports(port);
+   if (ret)
+   dev_warn(>dev, "failed to create symlinks (%d)\n", ret);
 
return port;
 }
@@ -2041,8 +2042,10 @@ EXPORT_SYMBOL_GPL(typec_register_port);
  */
 void typec_unregister_port(struct typec_port *port)
 {
-   if (!IS_ERR_OR_NULL(port))
+   if (!IS_ERR_OR_NULL(port)) {
+   typec_unlink_ports(port);
device_unregister(>dev);
+   }
 }
 EXPORT_SYMBOL_GPL(typec_unregister_port);
 
diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h
index 52294f7020a8b..aef03eb7e1523 100644
--- a/drivers/usb/typec/class.h
+++ b/drivers/usb/typec/class.h
@@ -79,7 +79,7 @@ extern const struct device_type typec_port_dev_type;
 extern struct class typec_mux_class;
 extern struct class typec_class;
 
-void *get_pld(struct device *dev);
-void free_pld(void *pld);
+int typec_link_ports(struct typec_port *connector);
+void typec_unlink_ports(struct typec_port *connector);
 
 #endif /* __USB_TYPEC_CLASS__ */
diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c
index 5bee7a97242fe..fae736eb0601e 100644
--- a/drivers/usb/typec/port-mapper.c
+++ b/drivers/usb/typec/port-mapper.c
@@ -34,7 +34,7 @@ static int acpi_pld_match(const struct acpi_pld_info *pld1,
return 0;
 }
 
-void *get_pld(struct device *dev)
+static void *get_pld(struct device *dev)
 {
 #ifdef CONFIG_ACPI
struct acpi_pld_info *pld;
@@ -53,7 +53,7 @@ void *get_pld(struct device *dev)
 #endif
 }
 
-void free_pld(void *pld)
+static void free_pld(void *pld)
 {
 #ifdef CONFIG_ACPI
ACPI_FREE(pld);
@@ -217,3 +217,61 @@ void typec_unlink_port(struct device *port)
class_for_each_device(_class, NULL, port, port_match_and_unlink);
 }
 EXPORT_SYMBOL_GPL(typec_unlink_port);
+
+static int each_port(struct device *port, void *connector)
+{
+   struct port_node *node;
+   int ret;
+
+   node = create_port_node(port);
+   if (IS_ERR(node))
+   return PTR_ERR(node);
+
+   if (!connector_match(connector, node)) {
+   remove_port_node(node);
+   return 0;
+   }
+
+   ret = link_port(to_typec_port(connector), node);
+   if (ret) {
+   remove_port_node(node->pld);
+   return ret;
+   }
+
+   get_device(connector);
+
+   return 0;
+}
+
+int typec_link_ports(struct typec_port *con)
+{
+   int ret = 0;
+
+   con->pld = get_pld(>dev);
+   if (!con->pld)
+   return 0;
+
+   ret = usb_for_each_port(>dev, each_port);
+   if (ret)
+   typec_unlink_ports(con);
+
+   return ret;
+}
+
+void typec_unlink_ports(struct typec_port *con)
+{
+   struct port_node *node;
+   struct port_node *tmp;
+
+   mutex_lock(>port_list_lock);
+
+   list_for_each_entry_safe(node, tmp, >port_list, list) {
+   __unlink_port(con, node);
+   remove_port_node(node);
+   put_device(>dev);
+   }
+
+   mutex_unlock(>port_list_lock);
+
+   free_pld(con->pld);
+}
-- 
2.30.2



[PATCH v3 4/6] usb: Link the ports to the connectors they are attached to

2021-03-31 Thread Heikki Krogerus
Creating link to the USB Type-C connector for every new port
that is added when possible.

Signed-off-by: Heikki Krogerus 
---
 Documentation/ABI/testing/sysfs-bus-usb | 9 +
 drivers/usb/core/port.c | 3 +++
 2 files changed, 12 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-usb 
b/Documentation/ABI/testing/sysfs-bus-usb
index bf2c1968525f0..8b4303a0ff51d 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -255,6 +255,15 @@ Description:
is permitted, "u2" if only u2 is permitted, "u1_u2" if both u1 
and
u2 are permitted.
 
+What:  /sys/bus/usb/devices/.../(hub interface)/portX/connector
+Date:  April 2021
+Contact:   Heikki Krogerus 
+Description:
+   Link to the USB Type-C connector when available. This link is
+   only created when USB Type-C Connector Class is enabled, and
+   only if the system firmware is capable of describing the
+   connection between a port and its connector.
+
 What:  /sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout
 Date:  May 2013
 Contact:   Mathias Nyman 
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index dfcca9c876c73..3c382a4b648ec 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -9,6 +9,7 @@
 
 #include 
 #include 
+#include 
 
 #include "hub.h"
 
@@ -576,6 +577,7 @@ int usb_hub_create_port_device(struct usb_hub *hub, int 
port1)
}
 
find_and_link_peer(hub, port1);
+   typec_link_port(_dev->dev);
 
/*
 * Enable runtime pm and hold a refernce that hub_configure()
@@ -619,5 +621,6 @@ void usb_hub_remove_port_device(struct usb_hub *hub, int 
port1)
peer = port_dev->peer;
if (peer)
unlink_peers(port_dev, peer);
+   typec_unlink_port(_dev->dev);
device_unregister(_dev->dev);
 }
-- 
2.30.2



[PATCH v3 3/6] usb: typec: Port mapping utility

2021-03-31 Thread Heikki Krogerus
Adding functions that can be used to link/unlink ports -
USB ports, TBT3/USB4 ports, DisplayPorts and so on - to
the USB Type-C connectors they are attached to inside a
system. The symlink that is created for the port device is
named "connector".

Initially only ACPI is supported. ACPI port object shares
the _PLD (Physical Location of Device) with the USB Type-C
connector that it's attached to.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/Makefile  |   2 +-
 drivers/usb/typec/class.c   |   7 +-
 drivers/usb/typec/class.h   |   9 ++
 drivers/usb/typec/port-mapper.c | 219 
 include/linux/usb/typec.h   |  13 ++
 5 files changed, 248 insertions(+), 2 deletions(-)
 create mode 100644 drivers/usb/typec/port-mapper.c

diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index a820e6e8c1ffc..1e1868832b8d8 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -3,7 +3,7 @@
 CFLAGS_tps6598x.o  := -I$(src)
 
 obj-$(CONFIG_TYPEC)+= typec.o
-typec-y:= class.o mux.o bus.o
+typec-y:= class.o mux.o bus.o port-mapper.o
 obj-$(CONFIG_TYPEC)+= altmodes/
 obj-$(CONFIG_TYPEC_TCPM)   += tcpm/
 obj-$(CONFIG_TYPEC_UCSI)   += ucsi/
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index d3e1002386357..ff199e2d26c7b 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -18,7 +18,7 @@
 
 static DEFINE_IDA(typec_index_ida);
 
-static struct class typec_class = {
+struct class typec_class = {
.name = "typec",
.owner = THIS_MODULE,
 };
@@ -1601,6 +1601,7 @@ static void typec_release(struct device *dev)
ida_destroy(>mode_ids);
typec_switch_put(port->sw);
typec_mux_put(port->mux);
+   free_pld(port->pld);
kfree(port->cap);
kfree(port);
 }
@@ -1983,6 +1984,8 @@ struct typec_port *typec_register_port(struct device 
*parent,
 
ida_init(>mode_ids);
mutex_init(>port_type_lock);
+   mutex_init(>port_list_lock);
+   INIT_LIST_HEAD(>port_list);
 
port->id = id;
port->ops = cap->ops;
@@ -2024,6 +2027,8 @@ struct typec_port *typec_register_port(struct device 
*parent,
return ERR_PTR(ret);
}
 
+   port->pld = get_pld(>dev);
+
return port;
 }
 EXPORT_SYMBOL_GPL(typec_register_port);
diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h
index d414be58d122e..52294f7020a8b 100644
--- a/drivers/usb/typec/class.h
+++ b/drivers/usb/typec/class.h
@@ -54,6 +54,11 @@ struct typec_port {
 
const struct typec_capability   *cap;
const struct typec_operations   *ops;
+
+   struct list_headport_list;
+   struct mutexport_list_lock; /* Port list lock */
+
+   void*pld;
 };
 
 #define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
@@ -72,5 +77,9 @@ extern const struct device_type typec_port_dev_type;
 #define is_typec_port(dev) ((dev)->type == _port_dev_type)
 
 extern struct class typec_mux_class;
+extern struct class typec_class;
+
+void *get_pld(struct device *dev);
+void free_pld(void *pld);
 
 #endif /* __USB_TYPEC_CLASS__ */
diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c
new file mode 100644
index 0..5bee7a97242fe
--- /dev/null
+++ b/drivers/usb/typec/port-mapper.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Type-C Connector Class Port Mapping Utility
+ *
+ * Copyright (C) 2021, Intel Corporation
+ * Author: Heikki Krogerus 
+ */
+
+#include 
+#include 
+#include 
+
+#include "class.h"
+
+struct port_node {
+   struct list_head list;
+   struct device *dev;
+   void *pld;
+};
+
+static int acpi_pld_match(const struct acpi_pld_info *pld1,
+ const struct acpi_pld_info *pld2)
+{
+   if (!pld1 || !pld2)
+   return 0;
+
+   /*
+* To speed things up, first checking only the group_position. It seems
+* to often have the first unique value in the _PLD.
+*/
+   if (pld1->group_position == pld2->group_position)
+   return !memcmp(pld1, pld2, sizeof(struct acpi_pld_info));
+
+   return 0;
+}
+
+void *get_pld(struct device *dev)
+{
+#ifdef CONFIG_ACPI
+   struct acpi_pld_info *pld;
+   acpi_status status;
+
+   if (!has_acpi_companion(dev))
+   return NULL;
+
+   status = acpi_get_physical_device_location(ACPI_HANDLE(dev), );
+   if (ACPI_FAILURE(status))
+   return NULL;
+
+   return pld;
+#else
+   return NULL;
+#endif
+}
+
+void free_pld(void *pld)
+{
+#ifdef CONFIG_ACPI
+   ACPI_FREE(pld);
+#endif
+}
+
+static int __link_port(struct typec_port *con, struct port_node *

[PATCH v3 1/6] usb: typec: Organize the private headers properly

2021-03-31 Thread Heikki Krogerus
Adding a header file for each subsystem - the connector
class, alt mode bus and the class for the muxes.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/bus.c   |  2 ++
 drivers/usb/typec/bus.h   | 19 +-
 drivers/usb/typec/class.c | 69 +++
 drivers/usb/typec/class.h | 76 +++
 drivers/usb/typec/mux.c   |  4 +--
 drivers/usb/typec/mux.h   | 21 +++
 6 files changed, 107 insertions(+), 84 deletions(-)
 create mode 100644 drivers/usb/typec/class.h
 create mode 100644 drivers/usb/typec/mux.h

diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c
index e8ddb81cb6df4..7f3c9a8e2bf08 100644
--- a/drivers/usb/typec/bus.c
+++ b/drivers/usb/typec/bus.c
@@ -9,6 +9,8 @@
 #include 
 
 #include "bus.h"
+#include "class.h"
+#include "mux.h"
 
 static inline int
 typec_altmode_set_mux(struct altmode *alt, unsigned long conf, void *data)
diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h
index 8ba8112d2740d..56dec268d4dd9 100644
--- a/drivers/usb/typec/bus.h
+++ b/drivers/usb/typec/bus.h
@@ -4,9 +4,9 @@
 #define __USB_TYPEC_ALTMODE_H__
 
 #include 
-#include 
 
 struct bus_type;
+struct typec_mux;
 
 struct altmode {
unsigned intid;
@@ -28,24 +28,7 @@ struct altmode {
 
 extern struct bus_type typec_bus;
 extern const struct device_type typec_altmode_dev_type;
-extern const struct device_type typec_port_dev_type;
 
 #define is_typec_altmode(_dev_) (_dev_->type == _altmode_dev_type)
-#define is_typec_port(_dev_) (_dev_->type == _port_dev_type)
-
-extern struct class typec_mux_class;
-
-struct typec_switch {
-   struct device dev;
-   typec_switch_set_fn_t set;
-};
-
-struct typec_mux {
-   struct device dev;
-   typec_mux_set_fn_t set;
-};
-
-#define to_typec_switch(_dev_) container_of(_dev_, struct typec_switch, dev)
-#define to_typec_mux(_dev_) container_of(_dev_, struct typec_mux, dev)
 
 #endif /* __USB_TYPEC_ALTMODE_H__ */
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 45f0bf65e9aba..5fa279a96b6ef 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -6,74 +6,15 @@
  * Author: Heikki Krogerus 
  */
 
-#include 
 #include 
 #include 
 #include 
 #include 
 #include 
+#include 
 
 #include "bus.h"
-
-struct typec_plug {
-   struct device   dev;
-   enum typec_plug_index   index;
-   struct ida  mode_ids;
-   int num_altmodes;
-};
-
-struct typec_cable {
-   struct device   dev;
-   enum typec_plug_typetype;
-   struct usb_pd_identity  *identity;
-   unsigned intactive:1;
-   u16 pd_revision; /* 0300H = "3.0" */
-};
-
-struct typec_partner {
-   struct device   dev;
-   unsigned intusb_pd:1;
-   struct usb_pd_identity  *identity;
-   enum typec_accessoryaccessory;
-   struct ida  mode_ids;
-   int num_altmodes;
-   u16 pd_revision; /* 0300H = "3.0" */
-   enum usb_pd_svdm_versvdm_version;
-};
-
-struct typec_port {
-   unsigned intid;
-   struct device   dev;
-   struct ida  mode_ids;
-
-   int prefer_role;
-   enum typec_data_roledata_role;
-   enum typec_role pwr_role;
-   enum typec_role vconn_role;
-   enum typec_pwr_opmode   pwr_opmode;
-   enum typec_port_typeport_type;
-   struct mutexport_type_lock;
-
-   enum typec_orientation  orientation;
-   struct typec_switch *sw;
-   struct typec_mux*mux;
-
-   const struct typec_capability   *cap;
-   const struct typec_operations   *ops;
-};
-
-#define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
-#define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev)
-#define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev)
-#define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev)
-
-static const struct device_type typec_partner_dev_type;
-static const struct device_type typec_cable_dev_type;
-static const struct device_type typec_plug_dev_type;
-
-#define is_typec_partner(_dev_) (_dev_->type == _partner_dev_type)
-#define is_typec_cable(_dev_) (_dev_->type == _cable_dev_type)
-#define is_typec_plug(_dev_) (_dev_->type == _plug_dev_type)
+#include "class.h"
 
 static DEFINE_IDA(typec_index_ida);
 static struct class *typec_class;
@@ -726,7 +667,7 @@ static void typec_partner_release(struct device *dev)

[PATCH v3 2/6] usb: typec: Declare the typec_class static

2021-03-31 Thread Heikki Krogerus
This is only to make the handling of the class consistent
with the two other susbsystems - the alt mode bus and the
mux class.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/class.c | 24 +---
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 5fa279a96b6ef..d3e1002386357 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -17,7 +17,11 @@
 #include "class.h"
 
 static DEFINE_IDA(typec_index_ida);
-static struct class *typec_class;
+
+static struct class typec_class = {
+   .name = "typec",
+   .owner = THIS_MODULE,
+};
 
 /* - */
 /* Common attributes */
@@ -551,7 +555,7 @@ typec_register_altmode(struct device *parent,
 
/* Plug alt modes need a class to generate udev events. */
if (is_typec_plug(parent))
-   alt->adev.dev.class = typec_class;
+   alt->adev.dev.class = _class;
 
ret = device_register(>adev.dev);
if (ret) {
@@ -815,7 +819,7 @@ struct typec_partner *typec_register_partner(struct 
typec_port *port,
partner->identity = desc->identity;
}
 
-   partner->dev.class = typec_class;
+   partner->dev.class = _class;
partner->dev.parent = >dev;
partner->dev.type = _partner_dev_type;
dev_set_name(>dev, "%s-partner", dev_name(>dev));
@@ -967,7 +971,7 @@ struct typec_plug *typec_register_plug(struct typec_cable 
*cable,
ida_init(>mode_ids);
plug->num_altmodes = -1;
plug->index = desc->index;
-   plug->dev.class = typec_class;
+   plug->dev.class = _class;
plug->dev.parent = >dev;
plug->dev.type = _plug_dev_type;
dev_set_name(>dev, "%s-%s", dev_name(cable->dev.parent), name);
@@ -1132,7 +1136,7 @@ struct typec_cable *typec_register_cable(struct 
typec_port *port,
cable->identity = desc->identity;
}
 
-   cable->dev.class = typec_class;
+   cable->dev.class = _class;
cable->dev.parent = >dev;
cable->dev.type = _cable_dev_type;
dev_set_name(>dev, "%s-cable", dev_name(>dev));
@@ -1986,7 +1990,7 @@ struct typec_port *typec_register_port(struct device 
*parent,
port->prefer_role = cap->prefer_role;
 
device_initialize(>dev);
-   port->dev.class = typec_class;
+   port->dev.class = _class;
port->dev.parent = parent;
port->dev.fwnode = cap->fwnode;
port->dev.type = _port_dev_type;
@@ -2049,11 +2053,9 @@ static int __init typec_init(void)
if (ret)
goto err_unregister_bus;
 
-   typec_class = class_create(THIS_MODULE, "typec");
-   if (IS_ERR(typec_class)) {
-   ret = PTR_ERR(typec_class);
+   ret = class_register(_class);
+   if (ret)
goto err_unregister_mux_class;
-   }
 
return 0;
 
@@ -2069,7 +2071,7 @@ subsys_initcall(typec_init);
 
 static void __exit typec_exit(void)
 {
-   class_destroy(typec_class);
+   class_unregister(_class);
ida_destroy(_index_ida);
bus_unregister(_bus);
class_unregister(_mux_class);
-- 
2.30.2



[PATCH v3 0/6] usb: Linking ports to their Type-C connectors

2021-03-31 Thread Heikki Krogerus
Hi,

Third version: ifdefs now in the header files as they should be.


v2 cover letter:

This is the second version of this series. The "Iterator for ports"
patch is now moved to the end of the series (5/6).

I'm now using usb_for_each_dev() in usb_for_each_port like Alan
suggested, and I'm now using usb_port_peer_mutex to lock the ports
while we're dealing with them in __each_hub().


The original cover letter:

Adding a simple function typec_link_port() that can be used to create
a symlink "connector" that points to the USB Type-C connector of a
port. It is used with USB ports initially, but hopefully later also
with other things like DisplayPorts.

Being able to see which connector is connected to a port is important
in general, but it is really important when for example the data or
power role of a device needs to swapped. The user probable wants to
know which USB device is disconnected if role swap on a USB Type-C
connector is executed.

Hope these are OK.

thanks,

Heikki Krogerus (6):
  usb: typec: Organize the private headers properly
  usb: typec: Declare the typec_class static
  usb: typec: Port mapping utility
  usb: Link the ports to the connectors they are attached to
  usb: Iterator for ports
  usb: typec: Link all ports during connector registration

 Documentation/ABI/testing/sysfs-bus-usb |   9 +
 drivers/usb/core/port.c |   3 +
 drivers/usb/core/usb.c  |  46 
 drivers/usb/typec/Makefile  |   2 +-
 drivers/usb/typec/bus.c |   2 +
 drivers/usb/typec/bus.h |  19 +-
 drivers/usb/typec/class.c   | 101 +++--
 drivers/usb/typec/class.h   |  85 
 drivers/usb/typec/mux.c |   4 +-
 drivers/usb/typec/mux.h |  21 ++
 drivers/usb/typec/port-mapper.c | 277 
 include/linux/usb.h |   9 +
 include/linux/usb/typec.h   |  13 ++
 13 files changed, 495 insertions(+), 96 deletions(-)
 create mode 100644 drivers/usb/typec/class.h
 create mode 100644 drivers/usb/typec/mux.h
 create mode 100644 drivers/usb/typec/port-mapper.c

-- 
2.30.2



Re: [PATCH 00/12] i2c: Adding support for software nodes

2021-03-31 Thread Heikki Krogerus
On Wed, Mar 31, 2021 at 11:22:32AM +0200, Wolfram Sang wrote:
> 
> > The old device property API (device_add_properties()) is going to be
> > removed. These prepare the i2c subsystem and drivers for the change.
> > The change is fairly trivial in case of i2c. All we need to do is add
> > complete software nodes to the devices instead of only the device
> > properties in those nodes.
> 
> This looks like a nice cleanup!
> 
> Reviewed-by: Wolfram Sang  # for the I2C parts
> 
> Which tree should this go into? I can offer I2C but am also fine with
> another one...

I think these go via I2C tree.

thanks,

-- 
heikki


Re: [RFC PATCH v2 2/2] usb: typec: sama7g5_tcpc: add driver for Microchip sama7g5 tcpc

2021-03-31 Thread Heikki Krogerus
Hi Cristian,

On Tue, Mar 30, 2021 at 11:54:42PM +0300, cristian.bir...@microchip.com wrote:
> From: Cristian Birsan 
> 
> This patch adds initial driver support for the new Microchip USB
> Type-C Port Controller (TCPC) embedded in sama7g5 SoC.
> 
> Signed-off-by: Cristian Birsan 
> ---
>  drivers/usb/typec/tcpm/Kconfig|   8 +
>  drivers/usb/typec/tcpm/Makefile   |   1 +
>  drivers/usb/typec/tcpm/sama7g5_tcpc.c | 610 ++
>  3 files changed, 619 insertions(+)
>  create mode 100644 drivers/usb/typec/tcpm/sama7g5_tcpc.c
> 
> diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig
> index 557f392fe24d..8ba0fd85741f 100644
> --- a/drivers/usb/typec/tcpm/Kconfig
> +++ b/drivers/usb/typec/tcpm/Kconfig
> @@ -52,6 +52,14 @@ config TYPEC_FUSB302
> Type-C Port Controller Manager to provide USB PD and USB
> Type-C functionalities.
>  
> +config TYPEC_SAMA7G5
> + tristate "Microchip SAMA7G5 Type-C Port Controller driver"
> + select REGMAP_MMIO
> + help
> +   Say Y or M here if your system has SAMA7G5 TCPC controller.
> +   It works with Type-C Port Controller Manager to provide USB
> +   Type-C functionalities.
> +
>  config TYPEC_WCOVE
>   tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver"
>   depends on ACPI
> diff --git a/drivers/usb/typec/tcpm/Makefile b/drivers/usb/typec/tcpm/Makefile
> index 7d499f3569fd..9abe8a7ae1cc 100644
> --- a/drivers/usb/typec/tcpm/Makefile
> +++ b/drivers/usb/typec/tcpm/Makefile
> @@ -1,6 +1,7 @@
>  # SPDX-License-Identifier: GPL-2.0
>  obj-$(CONFIG_TYPEC_TCPM) += tcpm.o
>  obj-$(CONFIG_TYPEC_FUSB302)  += fusb302.o
> +obj-$(CONFIG_TYPEC_SAMA7G5)  += sama7g5_tcpc.o
>  obj-$(CONFIG_TYPEC_WCOVE)+= typec_wcove.o
>  typec_wcove-y:= wcove.o
>  obj-$(CONFIG_TYPEC_TCPCI)+= tcpci.o
> diff --git a/drivers/usb/typec/tcpm/sama7g5_tcpc.c 
> b/drivers/usb/typec/tcpm/sama7g5_tcpc.c
> new file mode 100644
> index ..2986c0fcc8a3
> --- /dev/null
> +++ b/drivers/usb/typec/tcpm/sama7g5_tcpc.c
> @@ -0,0 +1,610 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Microchip SAMA7G5 Type-C Port Controller Driver
> + *
> + * Copyright (C) 2021 Microchip Technology, Inc. and its subsidiaries
> + */
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#define SAMA7G5_TCPC_GCLK32000
> +
> +/* TCPC registers offsets */
> +#define TCPC_CR  0x80/* TCPC Control 
> Register */
> +#define TCPC_UPC 0xA0/* TCPC PHY Control Register */
> +#define TCPC_UPS 0xA4/* TCPC PHY Status Register */
> +
> +#define TCPC_CR_RESET0x54434301  /* Magic value */
> +
> +/* TCPC PHY Control Register */
> +#define TCPC_UPC_BCDETE  BIT(29)
> +#define TCPC_UPC_BCVSRCE BIT(28)
> +#define  TCPC_UPC_BCDETSEL   BIT(27)

Why do you have a tab right after "#define" above?

> +#define  TCPC_UPC_BCIDPSRCE  BIT(26)

And here?

> +#define TCPC_UPC_DMPDFE  BIT(25)
> +#define TCPC_UPC_DMPDFD  BIT(24)
> +#define TCPC_UPC_IP_OFF  (0 << 12)
> +#define TCPC_UPC_IP_0P5  (1 << 12)
> +#define TCPC_UPC_IP_1P5  (2 << 12)
> +#define TCPC_UPC_IP_3P0  (3 << 12)
> +#define TCPC_UPC_THRESHOLD0  (0 << 8)
> +#define TCPC_UPC_THRESHOLD2  (2 << 8)
> +#define TCPC_UPC_THRESHOLD4  (4 << 8)
> +#define TCPC_UPC_THRESHOLD6  (6 << 8)
> +
> +/* TCPC PHY  Status Register */
> +#define TCPC_UPS_CC2RDT  BIT(4)
> +#define TCPC_UPS_CC1ID   BIT(3)
> +#define TCPC_UPS_CC_MASK GENMASK(4, 3)
> +#define TCPC_UPS_CHGDCP  BIT(2)
> +#define TCPC_UPS_DM  BIT(1)
> +#define TCPC_UPS_DP  BIT(0)
> +
> +#define TCPC_VERSION 0xFC
> +
> +/* USB Type-C measurement timings */
> +#define T_CC_MEASURE 100 /* 100 ms */
> +
> +#define SAMA7G5_TCPC_VBUS_IRQFLAGS (IRQF_ONESHOT \
> +| IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING)
> +
> +struct sama7g5_tcpc {
> + struct device *dev;
> +
> + struct workqueue_struct *wq;
> + struct delayed_work measure_work;
> +
> + struct regmap *regmap;
> + void __iomem *base;
> +
> + struct clk *pclk;
> + struct clk *gclk;
> +
> + struct gpio_desc *vbus_pin;
> + struct regulator *vbus;
> +
> + /* lock for sharing states */
> + struct mutex lock;
> +
> + /* port status */
> + enum typec_cc_polarity cc_polarity;
> + enum typec_cc_status cc1_status;
> + enum typec_cc_status cc2_status;
> + enum typec_cc_status cc1_status_prev;
> + enum typec_cc_status cc2_status_prev;
> +
> + /* mutex used for VBUS detection */
> + struct mutex vbus_mutex;
> + int 

Re: [PATCH v2 5/6] usb: Iterator for ports

2021-03-30 Thread Heikki Krogerus
On Mon, Mar 29, 2021 at 02:49:46PM -0400, Alan Stern wrote:
> On Mon, Mar 29, 2021 at 11:44:25AM +0300, Heikki Krogerus wrote:
> > Introducing usb_for_each_port(). It works the same way as
> > usb_for_each_dev(), but instead of going through every USB
> > device in the system, it walks through the USB ports in the
> > system.
> > 
> > Signed-off-by: Heikki Krogerus 
> > ---
> >  drivers/usb/core/usb.c | 46 ++
> >  include/linux/usb.h|  1 +
> >  2 files changed, 47 insertions(+)
> > 
> > diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
> > index 2ce3667ec6fae..62368c4ed37af 100644
> > --- a/drivers/usb/core/usb.c
> > +++ b/drivers/usb/core/usb.c
> > @@ -398,6 +398,52 @@ int usb_for_each_dev(void *data, int (*fn)(struct 
> > usb_device *, void *))
> >  }
> >  EXPORT_SYMBOL_GPL(usb_for_each_dev);
> >  
> > +struct each_hub_arg {
> > +   void *data;
> > +   int (*fn)(struct device *, void *);
> > +};
> > +
> > +static int __each_hub(struct usb_device *hdev, void *data)
> > +{
> > +   struct each_hub_arg *arg = (struct each_hub_arg *)data;
> > +   struct usb_hub *hub;
> > +   int ret = 0;
> > +   int i;
> > +
> > +   hub = usb_hub_to_struct_hub(hdev);
> > +   if (!hub)
> > +   return 0;
> 
> What happens if the hub is removed exactly now?  Although hdev is 
> reference-counted (and the loop iterator does take a reference to it), 
> usb_hub_to_struct_hub doesn't take a reference to hub.  And hub->ports 
> isn't refcounted at all.

If the hub is removed right now, and if hub_disconnect() also manages
to remove the ports before we have time to take the lock below, then
hdev->maxchild will be 0 by the time we can take the lock. In that
case nothing happens here.

If on the other hand we manage to acquire the usb_port_peer_mutex
before hub_disconnect(), then hub_disconnect() will simply have to
wait until we are done, and only after that remove the ports.

> > +   mutex_lock(_port_peer_mutex);
> > +
> > +   for (i = 0; i < hdev->maxchild; i++) {
> > +   ret = arg->fn(>ports[i]->dev, arg->data);
> > +   if (ret)
> > +   break;
> > +   }
> > +
> > +   mutex_unlock(_port_peer_mutex);
> 
> I have a feeling that it would be better to take and release this mutex 
> in usb_for_each_port (or its caller), so that it is held over the whole 
> loop.

I disagree. The lock is for the ports, not the hubs. We should take
the lock when we are going through the ports of a hub, but release it
between the hubs. Otherwise we will be only keeping things on hold for
a long period of time for no good reason (I for example have to
evaluate the _PLD of every single port which takes a lot of time). We
don't need to prevent other things from happening to the hubs at the
same time.


thanks,

-- 
heikki


Re: [PATCH 05/12] ARM: s3c: mini2440: Constify the software node

2021-03-29 Thread Heikki Krogerus
On Mon, Mar 29, 2021 at 12:58:41PM +0200, Krzysztof Kozlowski wrote:
> On 29/03/2021 12:50, Heikki Krogerus wrote:
> > Additional device properties are always just a part of a
> > software fwnode. If the device properties are constant, the
> > software node can also be constant.
> > 
> Hi,
> 
> Thanks for your work.
> 
> I did not get the cover letter nor other patches from this set and I
> don't see how the i2c uses the swnode. This makes difficult to judge
> whether this looks reasonable. At least without the context the title
> looks misleading - you add software_node or change to use software_node
> instead of constifying it.

OK, I'll try to open this up somehow...

Whenever additional device properties are added to devices by using
the old device property API (device_add_properties()) that also i2c
core code uses, in reality a software node is always created to hold
those properties. It's just always dynamically allocated.

The goal of this series is to prepare the i2c subsystem and drivers
for the removal of that old device property API, but I did not see
that as relevant info for this patch, because even if we did not in
the end remove that old API, this change is still useful.

The patch does exactly what the subject says. After this we supply the
device a constant software node instead of a dynamically allocated one.


thanks,

-- 
heikki


[PATCH 12/12] i2c: Remove support for dangling device properties

2021-03-29 Thread Heikki Krogerus
>From now on only accepting complete software nodes.

Signed-off-by: Heikki Krogerus 
---
 drivers/i2c/i2c-boardinfo.c | 11 ---
 drivers/i2c/i2c-core-base.c | 15 +--
 include/linux/i2c.h |  2 --
 3 files changed, 1 insertion(+), 27 deletions(-)

diff --git a/drivers/i2c/i2c-boardinfo.c b/drivers/i2c/i2c-boardinfo.c
index 8bc51d4e69df3..4df8ad092df38 100644
--- a/drivers/i2c/i2c-boardinfo.c
+++ b/drivers/i2c/i2c-boardinfo.c
@@ -47,7 +47,6 @@ EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bus_num);
  *
  * The board info passed can safely be __initdata, but be careful of embedded
  * pointers (for platform_data, functions, etc) since that won't be copied.
- * Device properties are deep-copied though.
  */
 int i2c_register_board_info(int busnum, struct i2c_board_info const *info, 
unsigned len)
 {
@@ -72,16 +71,6 @@ int i2c_register_board_info(int busnum, struct 
i2c_board_info const *info, unsig
devinfo->busnum = busnum;
devinfo->board_info = *info;
 
-   if (info->properties) {
-   devinfo->board_info.properties =
-   property_entries_dup(info->properties);
-   if (IS_ERR(devinfo->board_info.properties)) {
-   status = 
PTR_ERR(devinfo->board_info.properties);
-   kfree(devinfo);
-   break;
-   }
-   }
-
if (info->resources) {
devinfo->board_info.resources =
kmemdup(info->resources,
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index a6a68081f54e1..916899ee1115f 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -910,23 +910,13 @@ i2c_new_client_device(struct i2c_adapter *adap, struct 
i2c_board_info const *inf
 
i2c_dev_set_name(adap, client, info);
 
-   if (info->properties) {
-   status = device_add_properties(>dev, info->properties);
-   if (status) {
-   dev_err(>dev,
-   "Failed to add properties to client %s: %d\n",
-   client->name, status);
-   goto out_err_put_of_node;
-   }
-   }
-
if (info->swnode) {
status = device_add_software_node(>dev, info->swnode);
if (status) {
dev_err(>dev,
"Failed to add software node to client %s: 
%d\n",
client->name, status);
-   goto out_free_props;
+   goto out_err_put_of_node;
}
}
 
@@ -941,9 +931,6 @@ i2c_new_client_device(struct i2c_adapter *adap, struct 
i2c_board_info const *inf
 
 out_remove_swnode:
device_remove_software_node(>dev);
-out_free_props:
-   if (info->properties)
-   device_remove_properties(>dev);
 out_err_put_of_node:
of_node_put(info->of_node);
 out_err:
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index cb1f882a3e88e..54b3ccc71e372 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -391,7 +391,6 @@ static inline bool i2c_detect_slave_mode(struct device 
*dev) { return false; }
  * @platform_data: stored in i2c_client.dev.platform_data
  * @of_node: pointer to OpenFirmware device node
  * @fwnode: device node supplied by the platform firmware
- * @properties: Deprecated - use swnode instead
  * @swnode: software node for the device
  * @resources: resources associated with the device
  * @num_resources: number of resources in the @resources array
@@ -416,7 +415,6 @@ struct i2c_board_info {
void*platform_data;
struct device_node *of_node;
struct fwnode_handle *fwnode;
-   const struct property_entry *properties;
const struct software_node *swnode;
const struct resource *resources;
unsigned intnum_resources;
-- 
2.30.2



[PATCH 03/12] ARM: omap1: osk: Constify the software node

2021-03-29 Thread Heikki Krogerus
Additional device properties are always just a part of a
software fwnode. If the device properties are constant, the
software node can also be constant.

Signed-off-by: Heikki Krogerus 
Cc: Aaro Koskinen 
Cc: Tony Lindgren 
---
 arch/arm/mach-omap1/board-osk.c | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-omap1/board-osk.c b/arch/arm/mach-omap1/board-osk.c
index 0a4c9b0b13b0c..e18b6f13300eb 100644
--- a/arch/arm/mach-omap1/board-osk.c
+++ b/arch/arm/mach-omap1/board-osk.c
@@ -332,11 +332,15 @@ static const struct property_entry 
mistral_at24_properties[] = {
{ }
 };
 
+static const struct software_node mistral_at24_node = {
+   .properties = mistral_at24_properties,
+};
+
 static struct i2c_board_info __initdata mistral_i2c_board_info[] = {
{
/* NOTE:  powered from LCD supply */
I2C_BOARD_INFO("24c04", 0x50),
-   .properties = mistral_at24_properties,
+   .swnode = _at24_node,
},
/* TODO when driver support is ready:
 *  - optionally ov9640 camera sensor at 0x30
-- 
2.30.2



[PATCH 06/12] platform/x86: intel_cht_int33fe_microb: Constify the software node

2021-03-29 Thread Heikki Krogerus
Additional device properties are always just a part of a
software fwnode. If the device properties are constant, the
software node can also be constant.

Signed-off-by: Heikki Krogerus 
Cc: Hans de Goede 
---
 drivers/platform/x86/intel_cht_int33fe_microb.c | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/x86/intel_cht_int33fe_microb.c 
b/drivers/platform/x86/intel_cht_int33fe_microb.c
index 20b11e0d9a758..673f41cd14b52 100644
--- a/drivers/platform/x86/intel_cht_int33fe_microb.c
+++ b/drivers/platform/x86/intel_cht_int33fe_microb.c
@@ -35,6 +35,10 @@ static const struct property_entry bq27xxx_props[] = {
{ }
 };
 
+static const struct software_node bq27xxx_node = {
+   .properties = bq27xxx_props,
+};
+
 int cht_int33fe_microb_probe(struct cht_int33fe_data *data)
 {
struct device *dev = data->dev;
@@ -43,7 +47,7 @@ int cht_int33fe_microb_probe(struct cht_int33fe_data *data)
memset(_info, 0, sizeof(board_info));
strscpy(board_info.type, "bq27542", ARRAY_SIZE(board_info.type));
board_info.dev_name = "bq27542";
-   board_info.properties = bq27xxx_props;
+   board_info.swnode = _node;
data->battery_fg = i2c_acpi_new_device(dev, 1, _info);
 
return PTR_ERR_OR_ZERO(data->battery_fg);
-- 
2.30.2



[PATCH 11/12] Input: elantech - Prepare a complete software node for the device

2021-03-29 Thread Heikki Krogerus
Creating a software node and supplying that for the device
instead of only the device properties in it. A software
node was always created in any case to hold the additional
device properties, so this change does not have any real
effect.

This change makes it possible to remove support for the
problematic "dangling" device properties from i2c subsystem,
i.e. the "properties" member from struct i2c_board_info. The
problems caused by them are not related to this driver.

Signed-off-by: Heikki Krogerus 
Cc: Dmitry Torokhov 
---
 drivers/input/mouse/elantech.c | 6 --
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 97381e2e03bae..2d0bc029619ff 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1885,8 +1885,6 @@ static int elantech_create_smbus(struct psmouse *psmouse,
};
unsigned int idx = 0;
 
-   smbus_board.properties = i2c_props;
-
i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-x",
   info->x_max + 1);
i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-y",
@@ -1918,6 +1916,10 @@ static int elantech_create_smbus(struct psmouse *psmouse,
if (elantech_is_buttonpad(info))
i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,clickpad");
 
+   smbus_board.fwnode = fwnode_create_software_node(i2c_props, NULL);
+   if (IS_ERR(smbus_board.fwnode))
+   return PTR_ERR(smbus_board.fwnode);
+
return psmouse_smbus_init(psmouse, _board, NULL, 0, false,
  leave_breadcrumbs);
 }
-- 
2.30.2



[PATCH 05/12] ARM: s3c: mini2440: Constify the software node

2021-03-29 Thread Heikki Krogerus
Additional device properties are always just a part of a
software fwnode. If the device properties are constant, the
software node can also be constant.

Signed-off-by: Heikki Krogerus 
Cc: Krzysztof Kozlowski 
---
 arch/arm/mach-s3c/mach-mini2440.c | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-s3c/mach-mini2440.c 
b/arch/arm/mach-s3c/mach-mini2440.c
index 4100905dfbd0c..551ec660ab599 100644
--- a/arch/arm/mach-s3c/mach-mini2440.c
+++ b/arch/arm/mach-s3c/mach-mini2440.c
@@ -542,10 +542,14 @@ static const struct property_entry 
mini2440_at24_properties[] = {
{ }
 };
 
+static const struct software_node mini2440_at24_node = {
+   .properties = mini2440_at24_properties,
+};
+
 static struct i2c_board_info mini2440_i2c_devs[] __initdata = {
{
I2C_BOARD_INFO("24c08", 0x50),
-   .properties = mini2440_at24_properties,
+   .swnode = _at24_node,
},
 };
 
-- 
2.30.2



[PATCH 04/12] ARM: pxa: stargate2: Constify the software node

2021-03-29 Thread Heikki Krogerus
Additional device properties are always just a part of a
software fwnode. If the device properties are constant, the
software node can also be constant.

Signed-off-by: Heikki Krogerus 
Cc: Jonathan Cameron 
Cc: Daniel Mack 
Cc: Haojian Zhuang 
Cc: Robert Jarzmik 
---
 arch/arm/mach-pxa/stargate2.c | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-pxa/stargate2.c b/arch/arm/mach-pxa/stargate2.c
index e2353f7dcf01a..7ad6274657686 100644
--- a/arch/arm/mach-pxa/stargate2.c
+++ b/arch/arm/mach-pxa/stargate2.c
@@ -794,6 +794,10 @@ static const struct property_entry 
pca9500_eeprom_properties[] = {
{ }
 };
 
+static const struct software_node pca9500_eeprom_node = {
+   .properties = pca9500_eeprom_properties,
+};
+
 /**
  * stargate2_reset_bluetooth() reset the bluecore to ensure consistent state
  **/
@@ -929,7 +933,7 @@ static struct i2c_board_info __initdata 
stargate2_i2c_board_info[] = {
}, {
.type = "24c02",
.addr = 0x57,
-   .properties = pca9500_eeprom_properties,
+   .swnode = _eeprom_node,
}, {
.type = "max1238",
.addr = 0x35,
-- 
2.30.2



[PATCH 02/12] ARM: davinci: Constify the software nodes

2021-03-29 Thread Heikki Krogerus
Additional device properties are always just a part of a
software fwnode. If the device properties are constant, the
software node can also be constant.

Signed-off-by: Heikki Krogerus 
Cc: Sekhar Nori 
Cc: Bartosz Golaszewski 
---
 arch/arm/mach-davinci/board-da830-evm.c| 6 +-
 arch/arm/mach-davinci/board-dm365-evm.c| 6 +-
 arch/arm/mach-davinci/board-dm644x-evm.c   | 6 +-
 arch/arm/mach-davinci/board-dm646x-evm.c   | 6 +-
 arch/arm/mach-davinci/board-mityomapl138.c | 6 +-
 arch/arm/mach-davinci/board-sffsdr.c   | 6 +-
 6 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/arch/arm/mach-davinci/board-da830-evm.c 
b/arch/arm/mach-davinci/board-da830-evm.c
index a20ba12d876c6..823c9cc98f18b 100644
--- a/arch/arm/mach-davinci/board-da830-evm.c
+++ b/arch/arm/mach-davinci/board-da830-evm.c
@@ -454,6 +454,10 @@ static const struct property_entry 
da830_evm_i2c_eeprom_properties[] = {
{ }
 };
 
+static const struct software_node da830_evm_i2c_eeprom_node = {
+   .properties = da830_evm_i2c_eeprom_properties,
+};
+
 static int __init da830_evm_ui_expander_setup(struct i2c_client *client,
int gpio, unsigned ngpio, void *context)
 {
@@ -485,7 +489,7 @@ static struct pcf857x_platform_data __initdata 
da830_evm_ui_expander_info = {
 static struct i2c_board_info __initdata da830_evm_i2c_devices[] = {
{
I2C_BOARD_INFO("24c256", 0x50),
-   .properties = da830_evm_i2c_eeprom_properties,
+   .swnode = _evm_i2c_eeprom_node,
},
{
I2C_BOARD_INFO("tlv320aic3x", 0x18),
diff --git a/arch/arm/mach-davinci/board-dm365-evm.c 
b/arch/arm/mach-davinci/board-dm365-evm.c
index bdf31eb776204..b3bef74c982a3 100644
--- a/arch/arm/mach-davinci/board-dm365-evm.c
+++ b/arch/arm/mach-davinci/board-dm365-evm.c
@@ -232,10 +232,14 @@ static const struct property_entry eeprom_properties[] = {
{ }
 };
 
+static const struct software_node eeprom_node = {
+   .properties = eeprom_properties,
+};
+
 static struct i2c_board_info i2c_info[] = {
{
I2C_BOARD_INFO("24c256", 0x50),
-   .properties = eeprom_properties,
+   .swnode = _node,
},
{
I2C_BOARD_INFO("tlv320aic3x", 0x18),
diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c 
b/arch/arm/mach-davinci/board-dm644x-evm.c
index 7755cccec550c..cce3a621eb20b 100644
--- a/arch/arm/mach-davinci/board-dm644x-evm.c
+++ b/arch/arm/mach-davinci/board-dm644x-evm.c
@@ -541,6 +541,10 @@ static const struct property_entry eeprom_properties[] = {
{ }
 };
 
+static const struct software_node eeprom_node = {
+   .properties = eeprom_properties,
+};
+
 /*
  * MSP430 supports RTC, card detection, input from IR remote, and
  * a bit more.  It triggers interrupts on GPIO(7) from pressing
@@ -647,7 +651,7 @@ static struct i2c_board_info __initdata i2c_info[] =  {
},
{
I2C_BOARD_INFO("24c256", 0x50),
-   .properties = eeprom_properties,
+   .swnode = _node,
},
{
I2C_BOARD_INFO("tlv320aic33", 0x1b),
diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c 
b/arch/arm/mach-davinci/board-dm646x-evm.c
index 952ddabc743e0..ee91d81ebbfdf 100644
--- a/arch/arm/mach-davinci/board-dm646x-evm.c
+++ b/arch/arm/mach-davinci/board-dm646x-evm.c
@@ -362,6 +362,10 @@ static const struct property_entry eeprom_properties[] = {
PROPERTY_ENTRY_U32("pagesize", 64),
{ }
 };
+
+static const struct software_node eeprom_node = {
+   .properties = eeprom_properties,
+};
 #endif
 
 static u8 dm646x_iis_serializer_direction[] = {
@@ -430,7 +434,7 @@ static void evm_init_cpld(void)
 static struct i2c_board_info __initdata i2c_info[] =  {
{
I2C_BOARD_INFO("24c256", 0x50),
-   .properties  = eeprom_properties,
+   .swnode = _node,
},
{
I2C_BOARD_INFO("pcf8574a", 0x38),
diff --git a/arch/arm/mach-davinci/board-mityomapl138.c 
b/arch/arm/mach-davinci/board-mityomapl138.c
index 5205008c8061b..2127969beb965 100644
--- a/arch/arm/mach-davinci/board-mityomapl138.c
+++ b/arch/arm/mach-davinci/board-mityomapl138.c
@@ -197,6 +197,10 @@ static const struct property_entry 
mityomapl138_fd_chip_properties[] = {
{ }
 };
 
+static const struct software_node mityomapl138_fd_chip_node = {
+   .properties = mityomapl138_fd_chip_properties,
+};
+
 static struct davinci_i2c_platform_data mityomap_i2c_0_pdata = {
.bus_freq   = 100,  /* kHz */
.bus_delay  = 0,/* usec */
@@ -323,7 +327,7 @@ static struct i2c_board_info __initdata 
mityomap_tps65023_info[] = {
},
{
I2C_BOARD_INFO("24c02", 0x50),
-   .properties = mityomapl138_fd_chip_properties,
+ 

[PATCH 07/12] i2c: cht-wc: Constify the software node

2021-03-29 Thread Heikki Krogerus
Additional device properties are always just a part of a
software fwnode. If the device properties are constant, the
software node can also be constant.

Signed-off-by: Heikki Krogerus 
Cc: Hans de Goede 
---
 drivers/i2c/busses/i2c-cht-wc.c | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/i2c/busses/i2c-cht-wc.c b/drivers/i2c/busses/i2c-cht-wc.c
index f80d79e973cd2..08f491ea21ac9 100644
--- a/drivers/i2c/busses/i2c-cht-wc.c
+++ b/drivers/i2c/busses/i2c-cht-wc.c
@@ -280,6 +280,10 @@ static const struct property_entry bq24190_props[] = {
{ }
 };
 
+static const struct software_node bq24190_node = {
+   .properties = bq24190_props,
+};
+
 static struct regulator_consumer_supply fusb302_consumer = {
.supply = "vbus",
/* Must match fusb302 dev_name in intel_cht_int33fe.c */
@@ -308,7 +312,7 @@ static int cht_wc_i2c_adap_i2c_probe(struct platform_device 
*pdev)
.type = "bq24190",
.addr = 0x6b,
.dev_name = "bq24190",
-   .properties = bq24190_props,
+   .swnode = _node,
.platform_data = _pdata,
};
int ret, reg, irq;
-- 
2.30.2



[PATCH 10/12] platform/chrome: chromeos_laptop - Prepare complete software nodes

2021-03-29 Thread Heikki Krogerus
The older device property API is going to be removed soon
and that will affect also I2C subystem. Supplying complete
software nodes instead of only the properties in them for
the I2C devices.

Signed-off-by: Heikki Krogerus 
Cc: Benson Leung 
Cc: Enric Balletbo i Serra 
---
 drivers/platform/chrome/chromeos_laptop.c | 100 +-
 1 file changed, 60 insertions(+), 40 deletions(-)

diff --git a/drivers/platform/chrome/chromeos_laptop.c 
b/drivers/platform/chrome/chromeos_laptop.c
index 472a03daa8693..4e14b4d6635d7 100644
--- a/drivers/platform/chrome/chromeos_laptop.c
+++ b/drivers/platform/chrome/chromeos_laptop.c
@@ -52,12 +52,15 @@ struct i2c_peripheral {
enum i2c_adapter_type type;
u32 pci_devid;
 
+   const struct property_entry *properties;
+
struct i2c_client *client;
 };
 
 struct acpi_peripheral {
char hid[ACPI_ID_LEN];
-   const struct property_entry *properties;
+   struct software_node swnode;
+   struct i2c_client *client;
 };
 
 struct chromeos_laptop {
@@ -68,7 +71,7 @@ struct chromeos_laptop {
struct i2c_peripheral *i2c_peripherals;
unsigned int num_i2c_peripherals;
 
-   const struct acpi_peripheral *acpi_peripherals;
+   struct acpi_peripheral *acpi_peripherals;
unsigned int num_acpi_peripherals;
 };
 
@@ -161,7 +164,7 @@ static void chromeos_laptop_check_adapter(struct 
i2c_adapter *adapter)
 
 static bool chromeos_laptop_adjust_client(struct i2c_client *client)
 {
-   const struct acpi_peripheral *acpi_dev;
+   struct acpi_peripheral *acpi_dev;
struct acpi_device_id acpi_ids[2] = { };
int i;
int error;
@@ -175,8 +178,7 @@ static bool chromeos_laptop_adjust_client(struct i2c_client 
*client)
memcpy(acpi_ids[0].id, acpi_dev->hid, ACPI_ID_LEN);
 
if (acpi_match_device(acpi_ids, >dev)) {
-   error = device_add_properties(>dev,
- acpi_dev->properties);
+   error = device_add_software_node(>dev, 
_dev->swnode);
if (error) {
dev_err(>dev,
"failed to add properties: %d\n",
@@ -184,6 +186,8 @@ static bool chromeos_laptop_adjust_client(struct i2c_client 
*client)
break;
}
 
+   acpi_dev->client = client;
+
return true;
}
}
@@ -193,15 +197,28 @@ static bool chromeos_laptop_adjust_client(struct 
i2c_client *client)
 
 static void chromeos_laptop_detach_i2c_client(struct i2c_client *client)
 {
+   struct acpi_peripheral *acpi_dev;
struct i2c_peripheral *i2c_dev;
int i;
 
-   for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
-   i2c_dev = _laptop->i2c_peripherals[i];
+   if (has_acpi_companion(>dev))
+   for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) {
+   acpi_dev = _laptop->acpi_peripherals[i];
 
-   if (i2c_dev->client == client)
-   i2c_dev->client = NULL;
-   }
+   if (acpi_dev->client == client) {
+   acpi_dev->client = NULL;
+   return;
+   }
+   }
+   else
+   for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
+   i2c_dev = _laptop->i2c_peripherals[i];
+
+   if (i2c_dev->client == client) {
+   i2c_dev->client = NULL;
+   return;
+   }
+   }
 }
 
 static int chromeos_laptop_i2c_notifier_call(struct notifier_block *nb,
@@ -302,28 +319,26 @@ static struct i2c_peripheral 
chromebook_pixel_peripherals[] __initdata = {
.board_info = {
I2C_BOARD_INFO("atmel_mxt_ts",
ATMEL_TS_I2C_ADDR),
-   .properties =
-   chromebook_atmel_touchscreen_props,
.flags  = I2C_CLIENT_WAKE,
},
.dmi_name   = "touchscreen",
.irqflags   = IRQF_TRIGGER_FALLING,
.type   = I2C_ADAPTER_PANEL,
.alt_addr   = ATMEL_TS_I2C_BL_ADDR,
+   .properties = chromebook_atmel_touchscreen_props,
},
/* Touchpad. */
{
.board_info = {
I2C_BOARD_INFO("atmel_mxt_tp",
ATMEL_TP_I2C_ADDR),
-   .properties =
-   chromebook_pixel_trackpad_props,
 

[PATCH 08/12] i2c: nvidia-gpu: Constify the software node

2021-03-29 Thread Heikki Krogerus
Additional device properties are always just a part of a
software fwnode. If the device properties are constant, the
software node can also be constant.

Signed-off-by: Heikki Krogerus 
Cc: Ajay Gupta 
---
 drivers/i2c/busses/i2c-nvidia-gpu.c | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c 
b/drivers/i2c/busses/i2c-nvidia-gpu.c
index 6b20601ffb139..b5055a3cbd935 100644
--- a/drivers/i2c/busses/i2c-nvidia-gpu.c
+++ b/drivers/i2c/busses/i2c-nvidia-gpu.c
@@ -262,6 +262,10 @@ static const struct property_entry ccgx_props[] = {
{ }
 };
 
+static const struct software_node ccgx_node = {
+   .properties = ccgx_props,
+};
+
 static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq)
 {
i2cd->gpu_ccgx_ucsi = devm_kzalloc(i2cd->dev,
@@ -274,7 +278,7 @@ static int gpu_populate_client(struct gpu_i2c_dev *i2cd, 
int irq)
sizeof(i2cd->gpu_ccgx_ucsi->type));
i2cd->gpu_ccgx_ucsi->addr = 0x8;
i2cd->gpu_ccgx_ucsi->irq = irq;
-   i2cd->gpu_ccgx_ucsi->properties = ccgx_props;
+   i2cd->gpu_ccgx_ucsi->swnode = _node;
i2cd->ccgx_client = i2c_new_client_device(>adapter, 
i2cd->gpu_ccgx_ucsi);
return PTR_ERR_OR_ZERO(i2cd->ccgx_client);
 }
-- 
2.30.2



[PATCH 09/12] i2c: icy: Constify the software node

2021-03-29 Thread Heikki Krogerus
Complete software node can now be supplied to the device
with struct i2c_board_info.

Signed-off-by: Heikki Krogerus 
Cc: Max Staudt 
---
 drivers/i2c/busses/i2c-icy.c | 32 +---
 1 file changed, 9 insertions(+), 23 deletions(-)

diff --git a/drivers/i2c/busses/i2c-icy.c b/drivers/i2c/busses/i2c-icy.c
index 66c9923fc7665..c8c422e9dda43 100644
--- a/drivers/i2c/busses/i2c-icy.c
+++ b/drivers/i2c/busses/i2c-icy.c
@@ -54,7 +54,6 @@ struct icy_i2c {
 
void __iomem *reg_s0;
void __iomem *reg_s1;
-   struct fwnode_handle *ltc2990_fwnode;
struct i2c_client *ltc2990_client;
 };
 
@@ -115,6 +114,10 @@ static const struct property_entry icy_ltc2990_props[] = {
{ }
 };
 
+static const struct software_node icy_ltc2990_node = {
+   .properties = icy_ltc2990_props,
+};
+
 static int icy_probe(struct zorro_dev *z,
 const struct zorro_device_id *ent)
 {
@@ -123,6 +126,7 @@ static int icy_probe(struct zorro_dev *z,
struct fwnode_handle *new_fwnode;
struct i2c_board_info ltc2990_info = {
.type   = "ltc2990",
+   .swnode = _ltc2990_node,
};
 
i2c = devm_kzalloc(>dev, sizeof(*i2c), GFP_KERNEL);
@@ -174,26 +178,10 @@ static int icy_probe(struct zorro_dev *z,
 *
 * See property_entry above for in1, in2, temp3.
 */
-   new_fwnode = fwnode_create_software_node(icy_ltc2990_props, NULL);
-   if (IS_ERR(new_fwnode)) {
-   dev_info(>dev, "Failed to create fwnode for LTC2990, error: 
%ld\n",
-PTR_ERR(new_fwnode));
-   } else {
-   /*
-* Store the fwnode so we can destroy it on .remove().
-* Only store it on success, as fwnode_remove_software_node()
-* is NULL safe, but not PTR_ERR safe.
-*/
-   i2c->ltc2990_fwnode = new_fwnode;
-   ltc2990_info.fwnode = new_fwnode;
-
-   i2c->ltc2990_client =
-   i2c_new_scanned_device(>adapter,
-  _info,
-  icy_ltc2990_addresses,
-  NULL);
-   }
-
+   i2c->ltc2990_client = i2c_new_scanned_device(>adapter,
+_info,
+icy_ltc2990_addresses,
+NULL);
return 0;
 }
 
@@ -202,8 +190,6 @@ static void icy_remove(struct zorro_dev *z)
struct icy_i2c *i2c = dev_get_drvdata(>dev);
 
i2c_unregister_device(i2c->ltc2990_client);
-   fwnode_remove_software_node(i2c->ltc2990_fwnode);
-
i2c_del_adapter(>adapter);
 }
 
-- 
2.30.2



[PATCH 00/12] i2c: Adding support for software nodes

2021-03-29 Thread Heikki Krogerus
Hi,

The old device property API (device_add_properties()) is going to be
removed. These prepare the i2c subsystem and drivers for the change.
The change is fairly trivial in case of i2c. All we need to do is add
complete software nodes to the devices instead of only the device
properties in those nodes.

thanks,

Heikki Krogerus (12):
  i2c: Add support for software nodes
  ARM: davinci: Constify the software nodes
  ARM: omap1: osk: Constify the software node
  ARM: pxa: stargate2: Constify the software node
  ARM: s3c: mini2440: Constify the software node
  platform/x86: intel_cht_int33fe_microb: Constify the software node
  i2c: cht-wc: Constify the software node
  i2c: nvidia-gpu: Constify the software node
  i2c: icy: Constify the software node
  platform/chrome: chromeos_laptop - Prepare complete software nodes
  Input: elantech - Prepare a complete software node for the device
  i2c: Remove support for dangling device properties

 arch/arm/mach-davinci/board-da830-evm.c   |   6 +-
 arch/arm/mach-davinci/board-dm365-evm.c   |   6 +-
 arch/arm/mach-davinci/board-dm644x-evm.c  |   6 +-
 arch/arm/mach-davinci/board-dm646x-evm.c  |   6 +-
 arch/arm/mach-davinci/board-mityomapl138.c|   6 +-
 arch/arm/mach-davinci/board-sffsdr.c  |   6 +-
 arch/arm/mach-omap1/board-osk.c   |   6 +-
 arch/arm/mach-pxa/stargate2.c |   6 +-
 arch/arm/mach-s3c/mach-mini2440.c |   6 +-
 drivers/i2c/busses/i2c-cht-wc.c   |   6 +-
 drivers/i2c/busses/i2c-icy.c  |  32 ++
 drivers/i2c/busses/i2c-nvidia-gpu.c   |   6 +-
 drivers/i2c/i2c-boardinfo.c   |  11 --
 drivers/i2c/i2c-core-base.c   |  14 +--
 drivers/input/mouse/elantech.c|   6 +-
 drivers/platform/chrome/chromeos_laptop.c | 100 +++---
 .../platform/x86/intel_cht_int33fe_microb.c   |   6 +-
 include/linux/i2c.h   |   4 +-
 18 files changed, 142 insertions(+), 97 deletions(-)

-- 
2.30.2



[PATCH 01/12] i2c: Add support for software nodes

2021-03-29 Thread Heikki Krogerus
This makes it possible for the drivers to assign complete
software fwnodes to the devices instead of only the device
properties in those nodes.

Signed-off-by: Heikki Krogerus 
---
 drivers/i2c/i2c-core-base.c | 15 ++-
 include/linux/i2c.h |  4 +++-
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index f21362355973e..a6a68081f54e1 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -920,15 +920,27 @@ i2c_new_client_device(struct i2c_adapter *adap, struct 
i2c_board_info const *inf
}
}
 
+   if (info->swnode) {
+   status = device_add_software_node(>dev, info->swnode);
+   if (status) {
+   dev_err(>dev,
+   "Failed to add software node to client %s: 
%d\n",
+   client->name, status);
+   goto out_free_props;
+   }
+   }
+
status = device_register(>dev);
if (status)
-   goto out_free_props;
+   goto out_remove_swnode;
 
dev_dbg(>dev, "client [%s] registered with bus id %s\n",
client->name, dev_name(>dev));
 
return client;
 
+out_remove_swnode:
+   device_remove_software_node(>dev);
 out_free_props:
if (info->properties)
device_remove_properties(>dev);
@@ -961,6 +973,7 @@ void i2c_unregister_device(struct i2c_client *client)
 
if (ACPI_COMPANION(>dev))
acpi_device_clear_enumerated(ACPI_COMPANION(>dev));
+   device_remove_software_node(>dev);
device_unregister(>dev);
 }
 EXPORT_SYMBOL_GPL(i2c_unregister_device);
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 56622658b2158..cb1f882a3e88e 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -391,7 +391,8 @@ static inline bool i2c_detect_slave_mode(struct device 
*dev) { return false; }
  * @platform_data: stored in i2c_client.dev.platform_data
  * @of_node: pointer to OpenFirmware device node
  * @fwnode: device node supplied by the platform firmware
- * @properties: additional device properties for the device
+ * @properties: Deprecated - use swnode instead
+ * @swnode: software node for the device
  * @resources: resources associated with the device
  * @num_resources: number of resources in the @resources array
  * @irq: stored in i2c_client.irq
@@ -416,6 +417,7 @@ struct i2c_board_info {
struct device_node *of_node;
struct fwnode_handle *fwnode;
const struct property_entry *properties;
+   const struct software_node *swnode;
const struct resource *resources;
unsigned intnum_resources;
int irq;
-- 
2.30.2



Re: [PATCH v2 6/6] usb: typec: Link all ports during connector registration

2021-03-29 Thread Heikki Krogerus
> I could make a stub for the usb_for_each_port() function in case
> CONFIG_USB is not enable. Would that work?

Ah, I think that's what you meant :-)

I'll fix it.

thaks,

-- 
heikki


Re: [PATCH v2 6/6] usb: typec: Link all ports during connector registration

2021-03-29 Thread Heikki Krogerus
On Mon, Mar 29, 2021 at 10:48:19AM +0200, Greg Kroah-Hartman wrote:
> On Mon, Mar 29, 2021 at 11:44:26AM +0300, Heikki Krogerus wrote:
> > +#ifdef CONFIG_USB
> 
> This feels odd in a file under drivers/usb/ is it still relevant?  Will
> this code get built for non-USB systems (i.e. gadget only?)

Yes, later. The typec connector class can not depend on CONFIG_USB for
sure.

> > +static int each_port(struct device *port, void *connector)
> > +{
> > +   struct port_node *node;
> > +   int ret;
> > +
> > +   node = create_port_node(port);
> > +   if (IS_ERR(node))
> > +   return PTR_ERR(node);
> > +
> > +   if (!connector_match(connector, node)) {
> > +   remove_port_node(node);
> > +   return 0;
> > +   }
> > +
> > +   ret = link_port(to_typec_port(connector), node);
> > +   if (ret) {
> > +   remove_port_node(node->pld);
> > +   return ret;
> > +   }
> > +
> > +   get_device(connector);
> > +
> > +   return 0;
> > +}
> > +#endif
> > +
> > +int typec_link_ports(struct typec_port *con)
> > +{
> > +   int ret = 0;
> > +
> > +   con->pld = get_pld(>dev);
> > +   if (!con->pld)
> > +   return 0;
> > +
> > +#ifdef CONFIG_USB
> > +   ret = usb_for_each_port(>dev, each_port);
> > +   if (ret)
> > +   typec_unlink_ports(con);
> 
> If you have proper #ifdef for CONFIG_USB in the .h file, then there's no
> need for the #ifdef in the .c file.

We could do that now, but we will have to move the ifdef back to the
C file the moment we add support for Thunderbolt ports and/or
DisplayPorts.

I could make a stub for the usb_for_each_port() function in case
CONFIG_USB is not enable. Would that work?


thanks,

-- 
heikki


[PATCH v2 6/6] usb: typec: Link all ports during connector registration

2021-03-29 Thread Heikki Krogerus
The connectors may be registered after the ports, so the
"connector" links need to be created for the ports also when
ever a new connector gets registered.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/class.c   |  9 +++--
 drivers/usb/typec/class.h   |  4 +-
 drivers/usb/typec/port-mapper.c | 65 -
 3 files changed, 71 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index ff199e2d26c7b..f1c2d823c6509 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -1601,7 +1601,6 @@ static void typec_release(struct device *dev)
ida_destroy(>mode_ids);
typec_switch_put(port->sw);
typec_mux_put(port->mux);
-   free_pld(port->pld);
kfree(port->cap);
kfree(port);
 }
@@ -2027,7 +2026,9 @@ struct typec_port *typec_register_port(struct device 
*parent,
return ERR_PTR(ret);
}
 
-   port->pld = get_pld(>dev);
+   ret = typec_link_ports(port);
+   if (ret)
+   dev_warn(>dev, "failed to create symlinks (%d)\n", ret);
 
return port;
 }
@@ -2041,8 +2042,10 @@ EXPORT_SYMBOL_GPL(typec_register_port);
  */
 void typec_unregister_port(struct typec_port *port)
 {
-   if (!IS_ERR_OR_NULL(port))
+   if (!IS_ERR_OR_NULL(port)) {
+   typec_unlink_ports(port);
device_unregister(>dev);
+   }
 }
 EXPORT_SYMBOL_GPL(typec_unregister_port);
 
diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h
index 52294f7020a8b..aef03eb7e1523 100644
--- a/drivers/usb/typec/class.h
+++ b/drivers/usb/typec/class.h
@@ -79,7 +79,7 @@ extern const struct device_type typec_port_dev_type;
 extern struct class typec_mux_class;
 extern struct class typec_class;
 
-void *get_pld(struct device *dev);
-void free_pld(void *pld);
+int typec_link_ports(struct typec_port *connector);
+void typec_unlink_ports(struct typec_port *connector);
 
 #endif /* __USB_TYPEC_CLASS__ */
diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c
index 5bee7a97242fe..fafd98de89df5 100644
--- a/drivers/usb/typec/port-mapper.c
+++ b/drivers/usb/typec/port-mapper.c
@@ -34,7 +34,7 @@ static int acpi_pld_match(const struct acpi_pld_info *pld1,
return 0;
 }
 
-void *get_pld(struct device *dev)
+static void *get_pld(struct device *dev)
 {
 #ifdef CONFIG_ACPI
struct acpi_pld_info *pld;
@@ -53,7 +53,7 @@ void *get_pld(struct device *dev)
 #endif
 }
 
-void free_pld(void *pld)
+static void free_pld(void *pld)
 {
 #ifdef CONFIG_ACPI
ACPI_FREE(pld);
@@ -217,3 +217,64 @@ void typec_unlink_port(struct device *port)
class_for_each_device(_class, NULL, port, port_match_and_unlink);
 }
 EXPORT_SYMBOL_GPL(typec_unlink_port);
+
+#ifdef CONFIG_USB
+static int each_port(struct device *port, void *connector)
+{
+   struct port_node *node;
+   int ret;
+
+   node = create_port_node(port);
+   if (IS_ERR(node))
+   return PTR_ERR(node);
+
+   if (!connector_match(connector, node)) {
+   remove_port_node(node);
+   return 0;
+   }
+
+   ret = link_port(to_typec_port(connector), node);
+   if (ret) {
+   remove_port_node(node->pld);
+   return ret;
+   }
+
+   get_device(connector);
+
+   return 0;
+}
+#endif
+
+int typec_link_ports(struct typec_port *con)
+{
+   int ret = 0;
+
+   con->pld = get_pld(>dev);
+   if (!con->pld)
+   return 0;
+
+#ifdef CONFIG_USB
+   ret = usb_for_each_port(>dev, each_port);
+   if (ret)
+   typec_unlink_ports(con);
+#endif
+   return ret;
+}
+
+void typec_unlink_ports(struct typec_port *con)
+{
+   struct port_node *node;
+   struct port_node *tmp;
+
+   mutex_lock(>port_list_lock);
+
+   list_for_each_entry_safe(node, tmp, >port_list, list) {
+   __unlink_port(con, node);
+   remove_port_node(node);
+   put_device(>dev);
+   }
+
+   mutex_unlock(>port_list_lock);
+
+   free_pld(con->pld);
+}
-- 
2.30.2



[PATCH v2 5/6] usb: Iterator for ports

2021-03-29 Thread Heikki Krogerus
Introducing usb_for_each_port(). It works the same way as
usb_for_each_dev(), but instead of going through every USB
device in the system, it walks through the USB ports in the
system.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/core/usb.c | 46 ++
 include/linux/usb.h|  1 +
 2 files changed, 47 insertions(+)

diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 2ce3667ec6fae..62368c4ed37af 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -398,6 +398,52 @@ int usb_for_each_dev(void *data, int (*fn)(struct 
usb_device *, void *))
 }
 EXPORT_SYMBOL_GPL(usb_for_each_dev);
 
+struct each_hub_arg {
+   void *data;
+   int (*fn)(struct device *, void *);
+};
+
+static int __each_hub(struct usb_device *hdev, void *data)
+{
+   struct each_hub_arg *arg = (struct each_hub_arg *)data;
+   struct usb_hub *hub;
+   int ret = 0;
+   int i;
+
+   hub = usb_hub_to_struct_hub(hdev);
+   if (!hub)
+   return 0;
+
+   mutex_lock(_port_peer_mutex);
+
+   for (i = 0; i < hdev->maxchild; i++) {
+   ret = arg->fn(>ports[i]->dev, arg->data);
+   if (ret)
+   break;
+   }
+
+   mutex_unlock(_port_peer_mutex);
+
+   return ret;
+}
+
+/**
+ * usb_for_each_port - interate over all USB ports in the system
+ * @data: data pointer that will be handed to the callback function
+ * @fn: callback function to be called for each USB port
+ *
+ * Iterate over all USB ports and call @fn for each, passing it @data. If it
+ * returns anything other than 0, we break the iteration prematurely and return
+ * that value.
+ */
+int usb_for_each_port(void *data, int (*fn)(struct device *, void *))
+{
+   struct each_hub_arg arg = {data, fn};
+
+   return usb_for_each_dev(, __each_hub);
+}
+EXPORT_SYMBOL_GPL(usb_for_each_port);
+
 /**
  * usb_release_dev - free a usb device structure when all users of it are 
finished.
  * @dev: device that's been disconnected
diff --git a/include/linux/usb.h b/include/linux/usb.h
index ddd2f5b2a2827..e4d2eb703cf89 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -871,6 +871,7 @@ extern int usb_match_one_id(struct usb_interface *interface,
const struct usb_device_id *id);
 
 extern int usb_for_each_dev(void *data, int (*fn)(struct usb_device *, void 
*));
+int usb_for_each_port(void *data, int (*fn)(struct device *, void *));
 extern struct usb_interface *usb_find_interface(struct usb_driver *drv,
int minor);
 extern struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev,
-- 
2.30.2



[PATCH v2 4/6] usb: Link the ports to the connectors they are attached to

2021-03-29 Thread Heikki Krogerus
Creating link to the USB Type-C connector for every new port
that is added when possible.

Signed-off-by: Heikki Krogerus 
---
 Documentation/ABI/testing/sysfs-bus-usb | 9 +
 drivers/usb/core/port.c | 3 +++
 2 files changed, 12 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-usb 
b/Documentation/ABI/testing/sysfs-bus-usb
index bf2c1968525f0..8b4303a0ff51d 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -255,6 +255,15 @@ Description:
is permitted, "u2" if only u2 is permitted, "u1_u2" if both u1 
and
u2 are permitted.
 
+What:  /sys/bus/usb/devices/.../(hub interface)/portX/connector
+Date:  April 2021
+Contact:   Heikki Krogerus 
+Description:
+   Link to the USB Type-C connector when available. This link is
+   only created when USB Type-C Connector Class is enabled, and
+   only if the system firmware is capable of describing the
+   connection between a port and its connector.
+
 What:  /sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout
 Date:  May 2013
 Contact:   Mathias Nyman 
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index dfcca9c876c73..3c382a4b648ec 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -9,6 +9,7 @@
 
 #include 
 #include 
+#include 
 
 #include "hub.h"
 
@@ -576,6 +577,7 @@ int usb_hub_create_port_device(struct usb_hub *hub, int 
port1)
}
 
find_and_link_peer(hub, port1);
+   typec_link_port(_dev->dev);
 
/*
 * Enable runtime pm and hold a refernce that hub_configure()
@@ -619,5 +621,6 @@ void usb_hub_remove_port_device(struct usb_hub *hub, int 
port1)
peer = port_dev->peer;
if (peer)
unlink_peers(port_dev, peer);
+   typec_unlink_port(_dev->dev);
device_unregister(_dev->dev);
 }
-- 
2.30.2



[PATCH v2 3/6] usb: typec: Port mapping utility

2021-03-29 Thread Heikki Krogerus
Adding functions that can be used to link/unlink ports -
USB ports, TBT3/USB4 ports, DisplayPorts and so on - to
the USB Type-C connectors they are attached to inside a
system. The symlink that is created for the port device is
named "connector".

Initially only ACPI is supported. ACPI port object shares
the _PLD (Physical Location of Device) with the USB Type-C
connector that it's attached to.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/Makefile  |   2 +-
 drivers/usb/typec/class.c   |   7 +-
 drivers/usb/typec/class.h   |   9 ++
 drivers/usb/typec/port-mapper.c | 219 
 include/linux/usb/typec.h   |  13 ++
 5 files changed, 248 insertions(+), 2 deletions(-)
 create mode 100644 drivers/usb/typec/port-mapper.c

diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index a820e6e8c1ffc..1e1868832b8d8 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -3,7 +3,7 @@
 CFLAGS_tps6598x.o  := -I$(src)
 
 obj-$(CONFIG_TYPEC)+= typec.o
-typec-y:= class.o mux.o bus.o
+typec-y:= class.o mux.o bus.o port-mapper.o
 obj-$(CONFIG_TYPEC)+= altmodes/
 obj-$(CONFIG_TYPEC_TCPM)   += tcpm/
 obj-$(CONFIG_TYPEC_UCSI)   += ucsi/
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index d3e1002386357..ff199e2d26c7b 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -18,7 +18,7 @@
 
 static DEFINE_IDA(typec_index_ida);
 
-static struct class typec_class = {
+struct class typec_class = {
.name = "typec",
.owner = THIS_MODULE,
 };
@@ -1601,6 +1601,7 @@ static void typec_release(struct device *dev)
ida_destroy(>mode_ids);
typec_switch_put(port->sw);
typec_mux_put(port->mux);
+   free_pld(port->pld);
kfree(port->cap);
kfree(port);
 }
@@ -1983,6 +1984,8 @@ struct typec_port *typec_register_port(struct device 
*parent,
 
ida_init(>mode_ids);
mutex_init(>port_type_lock);
+   mutex_init(>port_list_lock);
+   INIT_LIST_HEAD(>port_list);
 
port->id = id;
port->ops = cap->ops;
@@ -2024,6 +2027,8 @@ struct typec_port *typec_register_port(struct device 
*parent,
return ERR_PTR(ret);
}
 
+   port->pld = get_pld(>dev);
+
return port;
 }
 EXPORT_SYMBOL_GPL(typec_register_port);
diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h
index d414be58d122e..52294f7020a8b 100644
--- a/drivers/usb/typec/class.h
+++ b/drivers/usb/typec/class.h
@@ -54,6 +54,11 @@ struct typec_port {
 
const struct typec_capability   *cap;
const struct typec_operations   *ops;
+
+   struct list_headport_list;
+   struct mutexport_list_lock; /* Port list lock */
+
+   void*pld;
 };
 
 #define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
@@ -72,5 +77,9 @@ extern const struct device_type typec_port_dev_type;
 #define is_typec_port(dev) ((dev)->type == _port_dev_type)
 
 extern struct class typec_mux_class;
+extern struct class typec_class;
+
+void *get_pld(struct device *dev);
+void free_pld(void *pld);
 
 #endif /* __USB_TYPEC_CLASS__ */
diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c
new file mode 100644
index 0..5bee7a97242fe
--- /dev/null
+++ b/drivers/usb/typec/port-mapper.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Type-C Connector Class Port Mapping Utility
+ *
+ * Copyright (C) 2021, Intel Corporation
+ * Author: Heikki Krogerus 
+ */
+
+#include 
+#include 
+#include 
+
+#include "class.h"
+
+struct port_node {
+   struct list_head list;
+   struct device *dev;
+   void *pld;
+};
+
+static int acpi_pld_match(const struct acpi_pld_info *pld1,
+ const struct acpi_pld_info *pld2)
+{
+   if (!pld1 || !pld2)
+   return 0;
+
+   /*
+* To speed things up, first checking only the group_position. It seems
+* to often have the first unique value in the _PLD.
+*/
+   if (pld1->group_position == pld2->group_position)
+   return !memcmp(pld1, pld2, sizeof(struct acpi_pld_info));
+
+   return 0;
+}
+
+void *get_pld(struct device *dev)
+{
+#ifdef CONFIG_ACPI
+   struct acpi_pld_info *pld;
+   acpi_status status;
+
+   if (!has_acpi_companion(dev))
+   return NULL;
+
+   status = acpi_get_physical_device_location(ACPI_HANDLE(dev), );
+   if (ACPI_FAILURE(status))
+   return NULL;
+
+   return pld;
+#else
+   return NULL;
+#endif
+}
+
+void free_pld(void *pld)
+{
+#ifdef CONFIG_ACPI
+   ACPI_FREE(pld);
+#endif
+}
+
+static int __link_port(struct typec_port *con, struct port_node *

[PATCH v2 1/6] usb: typec: Organize the private headers properly

2021-03-29 Thread Heikki Krogerus
Adding a header file for each subsystem - the connector
class, alt mode bus and the class for the muxes.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/bus.c   |  2 ++
 drivers/usb/typec/bus.h   | 19 +-
 drivers/usb/typec/class.c | 69 +++
 drivers/usb/typec/class.h | 76 +++
 drivers/usb/typec/mux.c   |  4 +--
 drivers/usb/typec/mux.h   | 21 +++
 6 files changed, 107 insertions(+), 84 deletions(-)
 create mode 100644 drivers/usb/typec/class.h
 create mode 100644 drivers/usb/typec/mux.h

diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c
index e8ddb81cb6df4..7f3c9a8e2bf08 100644
--- a/drivers/usb/typec/bus.c
+++ b/drivers/usb/typec/bus.c
@@ -9,6 +9,8 @@
 #include 
 
 #include "bus.h"
+#include "class.h"
+#include "mux.h"
 
 static inline int
 typec_altmode_set_mux(struct altmode *alt, unsigned long conf, void *data)
diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h
index 8ba8112d2740d..56dec268d4dd9 100644
--- a/drivers/usb/typec/bus.h
+++ b/drivers/usb/typec/bus.h
@@ -4,9 +4,9 @@
 #define __USB_TYPEC_ALTMODE_H__
 
 #include 
-#include 
 
 struct bus_type;
+struct typec_mux;
 
 struct altmode {
unsigned intid;
@@ -28,24 +28,7 @@ struct altmode {
 
 extern struct bus_type typec_bus;
 extern const struct device_type typec_altmode_dev_type;
-extern const struct device_type typec_port_dev_type;
 
 #define is_typec_altmode(_dev_) (_dev_->type == _altmode_dev_type)
-#define is_typec_port(_dev_) (_dev_->type == _port_dev_type)
-
-extern struct class typec_mux_class;
-
-struct typec_switch {
-   struct device dev;
-   typec_switch_set_fn_t set;
-};
-
-struct typec_mux {
-   struct device dev;
-   typec_mux_set_fn_t set;
-};
-
-#define to_typec_switch(_dev_) container_of(_dev_, struct typec_switch, dev)
-#define to_typec_mux(_dev_) container_of(_dev_, struct typec_mux, dev)
 
 #endif /* __USB_TYPEC_ALTMODE_H__ */
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 45f0bf65e9aba..5fa279a96b6ef 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -6,74 +6,15 @@
  * Author: Heikki Krogerus 
  */
 
-#include 
 #include 
 #include 
 #include 
 #include 
 #include 
+#include 
 
 #include "bus.h"
-
-struct typec_plug {
-   struct device   dev;
-   enum typec_plug_index   index;
-   struct ida  mode_ids;
-   int num_altmodes;
-};
-
-struct typec_cable {
-   struct device   dev;
-   enum typec_plug_typetype;
-   struct usb_pd_identity  *identity;
-   unsigned intactive:1;
-   u16 pd_revision; /* 0300H = "3.0" */
-};
-
-struct typec_partner {
-   struct device   dev;
-   unsigned intusb_pd:1;
-   struct usb_pd_identity  *identity;
-   enum typec_accessoryaccessory;
-   struct ida  mode_ids;
-   int num_altmodes;
-   u16 pd_revision; /* 0300H = "3.0" */
-   enum usb_pd_svdm_versvdm_version;
-};
-
-struct typec_port {
-   unsigned intid;
-   struct device   dev;
-   struct ida  mode_ids;
-
-   int prefer_role;
-   enum typec_data_roledata_role;
-   enum typec_role pwr_role;
-   enum typec_role vconn_role;
-   enum typec_pwr_opmode   pwr_opmode;
-   enum typec_port_typeport_type;
-   struct mutexport_type_lock;
-
-   enum typec_orientation  orientation;
-   struct typec_switch *sw;
-   struct typec_mux*mux;
-
-   const struct typec_capability   *cap;
-   const struct typec_operations   *ops;
-};
-
-#define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
-#define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev)
-#define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev)
-#define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev)
-
-static const struct device_type typec_partner_dev_type;
-static const struct device_type typec_cable_dev_type;
-static const struct device_type typec_plug_dev_type;
-
-#define is_typec_partner(_dev_) (_dev_->type == _partner_dev_type)
-#define is_typec_cable(_dev_) (_dev_->type == _cable_dev_type)
-#define is_typec_plug(_dev_) (_dev_->type == _plug_dev_type)
+#include "class.h"
 
 static DEFINE_IDA(typec_index_ida);
 static struct class *typec_class;
@@ -726,7 +667,7 @@ static void typec_partner_release(struct device *dev)

[PATCH v2 2/6] usb: typec: Declare the typec_class static

2021-03-29 Thread Heikki Krogerus
This is only to make the handling of the class consistent
with the two other susbsystems - the alt mode bus and the
mux class.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/class.c | 24 +---
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 5fa279a96b6ef..d3e1002386357 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -17,7 +17,11 @@
 #include "class.h"
 
 static DEFINE_IDA(typec_index_ida);
-static struct class *typec_class;
+
+static struct class typec_class = {
+   .name = "typec",
+   .owner = THIS_MODULE,
+};
 
 /* - */
 /* Common attributes */
@@ -551,7 +555,7 @@ typec_register_altmode(struct device *parent,
 
/* Plug alt modes need a class to generate udev events. */
if (is_typec_plug(parent))
-   alt->adev.dev.class = typec_class;
+   alt->adev.dev.class = _class;
 
ret = device_register(>adev.dev);
if (ret) {
@@ -815,7 +819,7 @@ struct typec_partner *typec_register_partner(struct 
typec_port *port,
partner->identity = desc->identity;
}
 
-   partner->dev.class = typec_class;
+   partner->dev.class = _class;
partner->dev.parent = >dev;
partner->dev.type = _partner_dev_type;
dev_set_name(>dev, "%s-partner", dev_name(>dev));
@@ -967,7 +971,7 @@ struct typec_plug *typec_register_plug(struct typec_cable 
*cable,
ida_init(>mode_ids);
plug->num_altmodes = -1;
plug->index = desc->index;
-   plug->dev.class = typec_class;
+   plug->dev.class = _class;
plug->dev.parent = >dev;
plug->dev.type = _plug_dev_type;
dev_set_name(>dev, "%s-%s", dev_name(cable->dev.parent), name);
@@ -1132,7 +1136,7 @@ struct typec_cable *typec_register_cable(struct 
typec_port *port,
cable->identity = desc->identity;
}
 
-   cable->dev.class = typec_class;
+   cable->dev.class = _class;
cable->dev.parent = >dev;
cable->dev.type = _cable_dev_type;
dev_set_name(>dev, "%s-cable", dev_name(>dev));
@@ -1986,7 +1990,7 @@ struct typec_port *typec_register_port(struct device 
*parent,
port->prefer_role = cap->prefer_role;
 
device_initialize(>dev);
-   port->dev.class = typec_class;
+   port->dev.class = _class;
port->dev.parent = parent;
port->dev.fwnode = cap->fwnode;
port->dev.type = _port_dev_type;
@@ -2049,11 +2053,9 @@ static int __init typec_init(void)
if (ret)
goto err_unregister_bus;
 
-   typec_class = class_create(THIS_MODULE, "typec");
-   if (IS_ERR(typec_class)) {
-   ret = PTR_ERR(typec_class);
+   ret = class_register(_class);
+   if (ret)
goto err_unregister_mux_class;
-   }
 
return 0;
 
@@ -2069,7 +2071,7 @@ subsys_initcall(typec_init);
 
 static void __exit typec_exit(void)
 {
-   class_destroy(typec_class);
+   class_unregister(_class);
ida_destroy(_index_ida);
bus_unregister(_bus);
class_unregister(_mux_class);
-- 
2.30.2



[PATCH v2 0/6] usb: Linking ports to their Type-C connectors

2021-03-29 Thread Heikki Krogerus
Hi,

This is the second version of this series. The "Iterator for ports"
patch is now moved to the end of the series (5/6).

I'm now using usb_for_each_dev() in usb_for_each_port like Alan
suggested, and I'm now using usb_port_peer_mutex to lock the ports
while we're dealing with them in __each_hub().


The original cover letter:

Adding a simple function typec_link_port() that can be used to create
a symlink "connector" that points to the USB Type-C connector of a
port. It is used with USB ports initially, but hopefully later also
with other things like DisplayPorts.

Being able to see which connector is connected to a port is important
in general, but it is really important when for example the data or
power role of a device needs to swapped. The user probable wants to
know which USB device is disconnected if role swap on a USB Type-C
connector is executed.

Hope these are OK.

thanks,

Heikki Krogerus (6):
  usb: typec: Organize the private headers properly
  usb: typec: Declare the typec_class static
  usb: typec: Port mapping utility
  usb: Link the ports to the connectors they are attached to
  usb: Iterator for ports
  usb: typec: Link all ports during connector registration

 Documentation/ABI/testing/sysfs-bus-usb |   9 +
 drivers/usb/core/port.c |   3 +
 drivers/usb/core/usb.c  |  46 
 drivers/usb/typec/Makefile  |   2 +-
 drivers/usb/typec/bus.c |   2 +
 drivers/usb/typec/bus.h |  19 +-
 drivers/usb/typec/class.c   | 101 +++--
 drivers/usb/typec/class.h   |  85 +++
 drivers/usb/typec/mux.c |   4 +-
 drivers/usb/typec/mux.h |  21 ++
 drivers/usb/typec/port-mapper.c | 280 
 include/linux/usb.h |   1 +
 include/linux/usb/typec.h   |  13 ++
 13 files changed, 490 insertions(+), 96 deletions(-)
 create mode 100644 drivers/usb/typec/class.h
 create mode 100644 drivers/usb/typec/mux.h
 create mode 100644 drivers/usb/typec/port-mapper.c

-- 
2.30.2



Re: [PATCH 1/6] usb: Iterator for ports

2021-03-25 Thread Heikki Krogerus
On Thu, Mar 25, 2021 at 04:20:15PM +0100, Greg Kroah-Hartman wrote:
> On Thu, Mar 25, 2021 at 05:14:42PM +0200, Heikki Krogerus wrote:
> > On Thu, Mar 25, 2021 at 10:41:09AM -0400, Alan Stern wrote:
> > > On Thu, Mar 25, 2021 at 03:29:21PM +0300, Heikki Krogerus wrote:
> > > > Introducing usb_for_each_port(). It works the same way as
> > > > usb_for_each_dev(), but instead of going through every USB
> > > > device in the system, it walks through the USB ports in the
> > > > system.
> > > > 
> > > > Signed-off-by: Heikki Krogerus 
> > > 
> > > This has a couple of nasty errors.
> > > 
> > > > ---
> > > >  drivers/usb/core/usb.c | 43 ++
> > > >  include/linux/usb.h|  1 +
> > > >  2 files changed, 44 insertions(+)
> > > > 
> > > > diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
> > > > index 2ce3667ec6fae..6d49db9a1b208 100644
> > > > --- a/drivers/usb/core/usb.c
> > > > +++ b/drivers/usb/core/usb.c
> > > > @@ -398,6 +398,49 @@ int usb_for_each_dev(void *data, int (*fn)(struct 
> > > > usb_device *, void *))
> > > >  }
> > > >  EXPORT_SYMBOL_GPL(usb_for_each_dev);
> > > >  
> > > > +struct each_hub_arg {
> > > > +   void *data;
> > > > +   int (*fn)(struct device *, void *);
> > > > +};
> > > > +
> > > > +static int __each_hub(struct device *dev, void *data)
> > > > +{
> > > > +   struct each_hub_arg *arg = (struct each_hub_arg *)data;
> > > > +   struct usb_device *hdev = to_usb_device(dev);
> > > 
> > > to_usb_device() won't work properly if the struct device isn't embedded 
> > > in an actual usb_device structure.  And that will happen, since the USB 
> > > bus type holds usb_interface structures as well as usb_devices.
> > 
> > OK, so I need to filter them out.
> > 
> > > In fact, you should use usb_for_each_dev here; it already does what you 
> > > want.
> > 
> > Unfortunately I can't use usb_for_each_dev here, because it deals with
> > struct usb_device while I need to deal with struct device in the
> > callback.
> 
> Why do you need 'struct device' in the callback?  All you really want is
> the hub devices, right?

I need the ports, not the hubs.

> > > > +   struct usb_hub *hub;
> > > > +   int ret;
> > > > +   int i;
> > > > +
> > > > +   hub = usb_hub_to_struct_hub(hdev);
> > > > +   if (!hub)
> > > > +   return 0;
> > > > +
> > > > +   for (i = 0; i < hdev->maxchild; i++) {
> > > > +   ret = arg->fn(>ports[i]->dev, arg->data);
> > > > +   if (ret)
> > > > +   return ret;
> > > > +   }
> > > > +
> > > > +   return 0;
> > > > +}
> > > 
> > > Don't you need some sort of locking or refcounting here?  What would 
> > > happen if this hub got removed while the routine was running?
> > 
> > I'll use a lock then.
> 
> That's not going to work to be held over a callback.  Just properly
> increment the reference count.

I though we have done that already. Does bus_for_each_dev() do that
for the device that it passes to the callback until that callback
returns?

thanks,

-- 
heikki


Re: [PATCH 1/6] usb: Iterator for ports

2021-03-25 Thread Heikki Krogerus
On Thu, Mar 25, 2021 at 05:14:45PM +0200, Heikki Krogerus wrote:
> On Thu, Mar 25, 2021 at 10:41:09AM -0400, Alan Stern wrote:
> > On Thu, Mar 25, 2021 at 03:29:21PM +0300, Heikki Krogerus wrote:
> > > Introducing usb_for_each_port(). It works the same way as
> > > usb_for_each_dev(), but instead of going through every USB
> > > device in the system, it walks through the USB ports in the
> > > system.
> > > 
> > > Signed-off-by: Heikki Krogerus 
> > 
> > This has a couple of nasty errors.
> > 
> > > ---
> > >  drivers/usb/core/usb.c | 43 ++
> > >  include/linux/usb.h|  1 +
> > >  2 files changed, 44 insertions(+)
> > > 
> > > diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
> > > index 2ce3667ec6fae..6d49db9a1b208 100644
> > > --- a/drivers/usb/core/usb.c
> > > +++ b/drivers/usb/core/usb.c
> > > @@ -398,6 +398,49 @@ int usb_for_each_dev(void *data, int (*fn)(struct 
> > > usb_device *, void *))
> > >  }
> > >  EXPORT_SYMBOL_GPL(usb_for_each_dev);
> > >  
> > > +struct each_hub_arg {
> > > + void *data;
> > > + int (*fn)(struct device *, void *);
> > > +};
> > > +
> > > +static int __each_hub(struct device *dev, void *data)
> > > +{
> > > + struct each_hub_arg *arg = (struct each_hub_arg *)data;
> > > + struct usb_device *hdev = to_usb_device(dev);
> > 
> > to_usb_device() won't work properly if the struct device isn't embedded 
> > in an actual usb_device structure.  And that will happen, since the USB 
> > bus type holds usb_interface structures as well as usb_devices.
> 
> OK, so I need to filter them out.
> 
> > In fact, you should use usb_for_each_dev here; it already does what you 
> > want.
> 
> Unfortunately I can't use usb_for_each_dev here, because it deals with
> struct usb_device while I need to deal with struct device in the
> callback.

Ah, I can use it instead of bus_for_each_dev() in usb_for_each_port().
I'll fix these in v2.

For the lock I guess I can just use the peer lock (usb_port_peer_mutex)?

thanks,

-- 
heikki


Re: [PATCH 1/6] usb: Iterator for ports

2021-03-25 Thread Heikki Krogerus
On Thu, Mar 25, 2021 at 10:41:09AM -0400, Alan Stern wrote:
> On Thu, Mar 25, 2021 at 03:29:21PM +0300, Heikki Krogerus wrote:
> > Introducing usb_for_each_port(). It works the same way as
> > usb_for_each_dev(), but instead of going through every USB
> > device in the system, it walks through the USB ports in the
> > system.
> > 
> > Signed-off-by: Heikki Krogerus 
> 
> This has a couple of nasty errors.
> 
> > ---
> >  drivers/usb/core/usb.c | 43 ++
> >  include/linux/usb.h|  1 +
> >  2 files changed, 44 insertions(+)
> > 
> > diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
> > index 2ce3667ec6fae..6d49db9a1b208 100644
> > --- a/drivers/usb/core/usb.c
> > +++ b/drivers/usb/core/usb.c
> > @@ -398,6 +398,49 @@ int usb_for_each_dev(void *data, int (*fn)(struct 
> > usb_device *, void *))
> >  }
> >  EXPORT_SYMBOL_GPL(usb_for_each_dev);
> >  
> > +struct each_hub_arg {
> > +   void *data;
> > +   int (*fn)(struct device *, void *);
> > +};
> > +
> > +static int __each_hub(struct device *dev, void *data)
> > +{
> > +   struct each_hub_arg *arg = (struct each_hub_arg *)data;
> > +   struct usb_device *hdev = to_usb_device(dev);
> 
> to_usb_device() won't work properly if the struct device isn't embedded 
> in an actual usb_device structure.  And that will happen, since the USB 
> bus type holds usb_interface structures as well as usb_devices.

OK, so I need to filter them out.

> In fact, you should use usb_for_each_dev here; it already does what you 
> want.

Unfortunately I can't use usb_for_each_dev here, because it deals with
struct usb_device while I need to deal with struct device in the
callback.

> > +   struct usb_hub *hub;
> > +   int ret;
> > +   int i;
> > +
> > +   hub = usb_hub_to_struct_hub(hdev);
> > +   if (!hub)
> > +   return 0;
> > +
> > +   for (i = 0; i < hdev->maxchild; i++) {
> > +   ret = arg->fn(>ports[i]->dev, arg->data);
> > +   if (ret)
> > +   return ret;
> > +   }
> > +
> > +   return 0;
> > +}
> 
> Don't you need some sort of locking or refcounting here?  What would 
> happen if this hub got removed while the routine was running?

I'll use a lock then.

thanks,

-- 
heikki


[PATCH 6/6] usb: typec: Link all ports during connector registration

2021-03-25 Thread Heikki Krogerus
The connectors may be registered after the ports, so the
"connector" links need to be created for the ports also when
ever a new connector gets registered.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/class.c   |  9 +++--
 drivers/usb/typec/class.h   | 10 +++---
 drivers/usb/typec/port-mapper.c | 62 +++--
 3 files changed, 71 insertions(+), 10 deletions(-)

diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index ff199e2d26c7b..f1c2d823c6509 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -1601,7 +1601,6 @@ static void typec_release(struct device *dev)
ida_destroy(>mode_ids);
typec_switch_put(port->sw);
typec_mux_put(port->mux);
-   free_pld(port->pld);
kfree(port->cap);
kfree(port);
 }
@@ -2027,7 +2026,9 @@ struct typec_port *typec_register_port(struct device 
*parent,
return ERR_PTR(ret);
}
 
-   port->pld = get_pld(>dev);
+   ret = typec_link_ports(port);
+   if (ret)
+   dev_warn(>dev, "failed to create symlinks (%d)\n", ret);
 
return port;
 }
@@ -2041,8 +2042,10 @@ EXPORT_SYMBOL_GPL(typec_register_port);
  */
 void typec_unregister_port(struct typec_port *port)
 {
-   if (!IS_ERR_OR_NULL(port))
+   if (!IS_ERR_OR_NULL(port)) {
+   typec_unlink_ports(port);
device_unregister(>dev);
+   }
 }
 EXPORT_SYMBOL_GPL(typec_unregister_port);
 
diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h
index f3bc4d175d79c..a6a034f49c228 100644
--- a/drivers/usb/typec/class.h
+++ b/drivers/usb/typec/class.h
@@ -80,15 +80,15 @@ extern struct class typec_mux_class;
 extern struct class typec_class;
 
 #ifdef CONFIG_ACPI
-void *get_pld(struct device *dev);
-void free_pld(void *pld);
+int typec_link_ports(struct typec_port *connector);
+void typec_unlink_ports(struct typec_port *connector);
 #else
-static inline void *get_pld(struct device *dev)
+static inline int typec_link_ports(struct typec_port *connector)
 {
-   return NULL;
+   return 0;
 }
 
-static inline void free_pld(void *pld) { }
+static inline void typec_unlink_ports(struct typec_port *connector) { }
 #endif
 
 #endif /* __USB_TYPEC_CLASS__ */
diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c
index 97264a4f11967..98eda37d99117 100644
--- a/drivers/usb/typec/port-mapper.c
+++ b/drivers/usb/typec/port-mapper.c
@@ -34,7 +34,7 @@ static int acpi_pld_match(const struct acpi_pld_info *pld1,
return 0;
 }
 
-void *get_pld(struct device *dev)
+static void *get_pld(struct device *dev)
 {
struct acpi_pld_info *pld;
acpi_status status;
@@ -49,7 +49,7 @@ void *get_pld(struct device *dev)
return pld;
 }
 
-void free_pld(void *pld)
+static void free_pld(void *pld)
 {
ACPI_FREE(pld);
 }
@@ -223,3 +223,61 @@ void typec_unlink_port(struct device *port)
put_device(data.connector);
 }
 EXPORT_SYMBOL_GPL(typec_unlink_port);
+
+static int connector_match(struct device *port, void *connector)
+{
+   struct port_node *node;
+   int ret;
+
+   node = create_port_node(port);
+   if (IS_ERR(node))
+   return PTR_ERR(node);
+
+   if (!node->pld || !connector_pld_match(connector, node->pld)) {
+   remove_port_node(node);
+   return 0;
+   }
+
+   ret = link_port(to_typec_port(connector), node);
+   if (ret) {
+   remove_port_node(node->pld);
+   return ret;
+   }
+
+   get_device(connector);
+
+   return 0;
+}
+
+int typec_link_ports(struct typec_port *con)
+{
+   int ret;
+
+   con->pld = get_pld(>dev);
+   if (!con->pld)
+   return 0;
+
+   ret = usb_for_each_port(>dev, connector_match);
+   if (ret)
+   typec_unlink_ports(con);
+
+   return ret;
+}
+
+void typec_unlink_ports(struct typec_port *con)
+{
+   struct port_node *node;
+   struct port_node *tmp;
+
+   mutex_lock(>port_list_lock);
+
+   list_for_each_entry_safe(node, tmp, >port_list, list) {
+   __unlink_port(con, node);
+   remove_port_node(node);
+   put_device(>dev);
+   }
+
+   mutex_unlock(>port_list_lock);
+
+   free_pld(con->pld);
+}
-- 
2.30.2



[PATCH 1/6] usb: Iterator for ports

2021-03-25 Thread Heikki Krogerus
Introducing usb_for_each_port(). It works the same way as
usb_for_each_dev(), but instead of going through every USB
device in the system, it walks through the USB ports in the
system.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/core/usb.c | 43 ++
 include/linux/usb.h|  1 +
 2 files changed, 44 insertions(+)

diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 2ce3667ec6fae..6d49db9a1b208 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -398,6 +398,49 @@ int usb_for_each_dev(void *data, int (*fn)(struct 
usb_device *, void *))
 }
 EXPORT_SYMBOL_GPL(usb_for_each_dev);
 
+struct each_hub_arg {
+   void *data;
+   int (*fn)(struct device *, void *);
+};
+
+static int __each_hub(struct device *dev, void *data)
+{
+   struct each_hub_arg *arg = (struct each_hub_arg *)data;
+   struct usb_device *hdev = to_usb_device(dev);
+   struct usb_hub *hub;
+   int ret;
+   int i;
+
+   hub = usb_hub_to_struct_hub(hdev);
+   if (!hub)
+   return 0;
+
+   for (i = 0; i < hdev->maxchild; i++) {
+   ret = arg->fn(>ports[i]->dev, arg->data);
+   if (ret)
+   return ret;
+   }
+
+   return 0;
+}
+
+/**
+ * usb_for_each_port - interate over all USB ports in the system
+ * @data: data pointer that will be handed to the callback function
+ * @fn: callback function to be called for each USB port
+ *
+ * Iterate over all USB ports and call @fn for each, passing it @data. If it
+ * returns anything other than 0, we break the iteration prematurely and return
+ * that value.
+ */
+int usb_for_each_port(void *data, int (*fn)(struct device *, void *))
+{
+   struct each_hub_arg arg = {data, fn};
+
+   return bus_for_each_dev(_bus_type, NULL, , __each_hub);
+}
+EXPORT_SYMBOL_GPL(usb_for_each_port);
+
 /**
  * usb_release_dev - free a usb device structure when all users of it are 
finished.
  * @dev: device that's been disconnected
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 57c1e0ce5eba4..06ed5779fb4d8 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -869,6 +869,7 @@ extern int usb_match_one_id(struct usb_interface *interface,
const struct usb_device_id *id);
 
 extern int usb_for_each_dev(void *data, int (*fn)(struct usb_device *, void 
*));
+int usb_for_each_port(void *data, int (*fn)(struct device *, void *));
 extern struct usb_interface *usb_find_interface(struct usb_driver *drv,
int minor);
 extern struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev,
-- 
2.30.2



[PATCH 4/6] usb: typec: Port mapping utility

2021-03-25 Thread Heikki Krogerus
Adding functions that can be used to link/unlink ports -
USB ports, TBT3/USB4 ports, DisplayPorts and so on - to
the USB Type-C connectors they are attached to inside a
system. The symlink that is created for the port device is
named "connector".

Initially only ACPI is supported. ACPI port object shares
the _PLD (Physical Location of Device) with the USB Type-C
connector that it's attached to.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/Makefile  |   1 +
 drivers/usb/typec/class.c   |   7 +-
 drivers/usb/typec/class.h   |  18 +++
 drivers/usb/typec/port-mapper.c | 225 
 include/linux/usb/typec.h   |  13 ++
 5 files changed, 263 insertions(+), 1 deletion(-)
 create mode 100644 drivers/usb/typec/port-mapper.c

diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index a820e6e8c1ffc..ef790cb72d8c3 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -4,6 +4,7 @@ CFLAGS_tps6598x.o   := -I$(src)
 
 obj-$(CONFIG_TYPEC)+= typec.o
 typec-y:= class.o mux.o bus.o
+typec-$(CONFIG_ACPI)   += port-mapper.o
 obj-$(CONFIG_TYPEC)+= altmodes/
 obj-$(CONFIG_TYPEC_TCPM)   += tcpm/
 obj-$(CONFIG_TYPEC_UCSI)   += ucsi/
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index d3e1002386357..ff199e2d26c7b 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -18,7 +18,7 @@
 
 static DEFINE_IDA(typec_index_ida);
 
-static struct class typec_class = {
+struct class typec_class = {
.name = "typec",
.owner = THIS_MODULE,
 };
@@ -1601,6 +1601,7 @@ static void typec_release(struct device *dev)
ida_destroy(>mode_ids);
typec_switch_put(port->sw);
typec_mux_put(port->mux);
+   free_pld(port->pld);
kfree(port->cap);
kfree(port);
 }
@@ -1983,6 +1984,8 @@ struct typec_port *typec_register_port(struct device 
*parent,
 
ida_init(>mode_ids);
mutex_init(>port_type_lock);
+   mutex_init(>port_list_lock);
+   INIT_LIST_HEAD(>port_list);
 
port->id = id;
port->ops = cap->ops;
@@ -2024,6 +2027,8 @@ struct typec_port *typec_register_port(struct device 
*parent,
return ERR_PTR(ret);
}
 
+   port->pld = get_pld(>dev);
+
return port;
 }
 EXPORT_SYMBOL_GPL(typec_register_port);
diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h
index d414be58d122e..f3bc4d175d79c 100644
--- a/drivers/usb/typec/class.h
+++ b/drivers/usb/typec/class.h
@@ -54,6 +54,11 @@ struct typec_port {
 
const struct typec_capability   *cap;
const struct typec_operations   *ops;
+
+   struct list_headport_list;
+   struct mutexport_list_lock; /* Port list lock */
+
+   void*pld;
 };
 
 #define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
@@ -72,5 +77,18 @@ extern const struct device_type typec_port_dev_type;
 #define is_typec_port(dev) ((dev)->type == _port_dev_type)
 
 extern struct class typec_mux_class;
+extern struct class typec_class;
+
+#ifdef CONFIG_ACPI
+void *get_pld(struct device *dev);
+void free_pld(void *pld);
+#else
+static inline void *get_pld(struct device *dev)
+{
+   return NULL;
+}
+
+static inline void free_pld(void *pld) { }
+#endif
 
 #endif /* __USB_TYPEC_CLASS__ */
diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c
new file mode 100644
index 0..97264a4f11967
--- /dev/null
+++ b/drivers/usb/typec/port-mapper.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Type-C Connector Class Port Mapping Utility
+ *
+ * Copyright (C) 2021, Intel Corporation
+ * Author: Heikki Krogerus 
+ */
+
+#include 
+#include 
+#include 
+
+#include "class.h"
+
+struct port_node {
+   struct list_head list;
+   struct device *dev;
+   void *pld;
+};
+
+static int acpi_pld_match(const struct acpi_pld_info *pld1,
+ const struct acpi_pld_info *pld2)
+{
+   if (!pld1 || !pld2)
+   return 0;
+
+   /*
+* To speed things up, first checking only the group_position. It seems
+* to often have the first unique value in the _PLD.
+*/
+   if (pld1->group_position == pld2->group_position)
+   return !memcmp(pld1, pld2, sizeof(struct acpi_pld_info));
+
+   return 0;
+}
+
+void *get_pld(struct device *dev)
+{
+   struct acpi_pld_info *pld;
+   acpi_status status;
+
+   if (!has_acpi_companion(dev))
+   return NULL;
+
+   status = acpi_get_physical_device_location(ACPI_HANDLE(dev), );
+   if (ACPI_FAILURE(status))
+   return NULL;
+
+   return pld;
+}
+
+void free_pld(void *pld)
+{
+   ACPI_FREE(pld);
+}
+
+static int __link_port(

[PATCH 3/6] usb: typec: Declare the typec_class static

2021-03-25 Thread Heikki Krogerus
This is only to make the handling of the class consistent
with the two other susbsystems - the alt mode bus and the
mux class.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/class.c | 24 +---
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 5fa279a96b6ef..d3e1002386357 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -17,7 +17,11 @@
 #include "class.h"
 
 static DEFINE_IDA(typec_index_ida);
-static struct class *typec_class;
+
+static struct class typec_class = {
+   .name = "typec",
+   .owner = THIS_MODULE,
+};
 
 /* - */
 /* Common attributes */
@@ -551,7 +555,7 @@ typec_register_altmode(struct device *parent,
 
/* Plug alt modes need a class to generate udev events. */
if (is_typec_plug(parent))
-   alt->adev.dev.class = typec_class;
+   alt->adev.dev.class = _class;
 
ret = device_register(>adev.dev);
if (ret) {
@@ -815,7 +819,7 @@ struct typec_partner *typec_register_partner(struct 
typec_port *port,
partner->identity = desc->identity;
}
 
-   partner->dev.class = typec_class;
+   partner->dev.class = _class;
partner->dev.parent = >dev;
partner->dev.type = _partner_dev_type;
dev_set_name(>dev, "%s-partner", dev_name(>dev));
@@ -967,7 +971,7 @@ struct typec_plug *typec_register_plug(struct typec_cable 
*cable,
ida_init(>mode_ids);
plug->num_altmodes = -1;
plug->index = desc->index;
-   plug->dev.class = typec_class;
+   plug->dev.class = _class;
plug->dev.parent = >dev;
plug->dev.type = _plug_dev_type;
dev_set_name(>dev, "%s-%s", dev_name(cable->dev.parent), name);
@@ -1132,7 +1136,7 @@ struct typec_cable *typec_register_cable(struct 
typec_port *port,
cable->identity = desc->identity;
}
 
-   cable->dev.class = typec_class;
+   cable->dev.class = _class;
cable->dev.parent = >dev;
cable->dev.type = _cable_dev_type;
dev_set_name(>dev, "%s-cable", dev_name(>dev));
@@ -1986,7 +1990,7 @@ struct typec_port *typec_register_port(struct device 
*parent,
port->prefer_role = cap->prefer_role;
 
device_initialize(>dev);
-   port->dev.class = typec_class;
+   port->dev.class = _class;
port->dev.parent = parent;
port->dev.fwnode = cap->fwnode;
port->dev.type = _port_dev_type;
@@ -2049,11 +2053,9 @@ static int __init typec_init(void)
if (ret)
goto err_unregister_bus;
 
-   typec_class = class_create(THIS_MODULE, "typec");
-   if (IS_ERR(typec_class)) {
-   ret = PTR_ERR(typec_class);
+   ret = class_register(_class);
+   if (ret)
goto err_unregister_mux_class;
-   }
 
return 0;
 
@@ -2069,7 +2071,7 @@ subsys_initcall(typec_init);
 
 static void __exit typec_exit(void)
 {
-   class_destroy(typec_class);
+   class_unregister(_class);
ida_destroy(_index_ida);
bus_unregister(_bus);
class_unregister(_mux_class);
-- 
2.30.2



[PATCH 5/6] usb: Link the ports to the connectors they are attached to

2021-03-25 Thread Heikki Krogerus
Creating link to the USB Type-C connector for every new port
that is added when possible.

Signed-off-by: Heikki Krogerus 
---
 Documentation/ABI/testing/sysfs-bus-usb | 9 +
 drivers/usb/core/port.c | 3 +++
 2 files changed, 12 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-usb 
b/Documentation/ABI/testing/sysfs-bus-usb
index bf2c1968525f0..8b4303a0ff51d 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -255,6 +255,15 @@ Description:
is permitted, "u2" if only u2 is permitted, "u1_u2" if both u1 
and
u2 are permitted.
 
+What:  /sys/bus/usb/devices/.../(hub interface)/portX/connector
+Date:  April 2021
+Contact:   Heikki Krogerus 
+Description:
+   Link to the USB Type-C connector when available. This link is
+   only created when USB Type-C Connector Class is enabled, and
+   only if the system firmware is capable of describing the
+   connection between a port and its connector.
+
 What:  /sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout
 Date:  May 2013
 Contact:   Mathias Nyman 
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index dfcca9c876c73..3c382a4b648ec 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -9,6 +9,7 @@
 
 #include 
 #include 
+#include 
 
 #include "hub.h"
 
@@ -576,6 +577,7 @@ int usb_hub_create_port_device(struct usb_hub *hub, int 
port1)
}
 
find_and_link_peer(hub, port1);
+   typec_link_port(_dev->dev);
 
/*
 * Enable runtime pm and hold a refernce that hub_configure()
@@ -619,5 +621,6 @@ void usb_hub_remove_port_device(struct usb_hub *hub, int 
port1)
peer = port_dev->peer;
if (peer)
unlink_peers(port_dev, peer);
+   typec_unlink_port(_dev->dev);
device_unregister(_dev->dev);
 }
-- 
2.30.2



[PATCH 2/6] usb: typec: Organize the private headers properly

2021-03-25 Thread Heikki Krogerus
Adding a header file for each subsystem - the connector
class, alt mode bus and the class for the muxes.

Signed-off-by: Heikki Krogerus 
---
 drivers/usb/typec/bus.c   |  2 ++
 drivers/usb/typec/bus.h   | 19 +-
 drivers/usb/typec/class.c | 69 +++
 drivers/usb/typec/class.h | 76 +++
 drivers/usb/typec/mux.c   |  4 +--
 drivers/usb/typec/mux.h   | 21 +++
 6 files changed, 107 insertions(+), 84 deletions(-)
 create mode 100644 drivers/usb/typec/class.h
 create mode 100644 drivers/usb/typec/mux.h

diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c
index e8ddb81cb6df4..7f3c9a8e2bf08 100644
--- a/drivers/usb/typec/bus.c
+++ b/drivers/usb/typec/bus.c
@@ -9,6 +9,8 @@
 #include 
 
 #include "bus.h"
+#include "class.h"
+#include "mux.h"
 
 static inline int
 typec_altmode_set_mux(struct altmode *alt, unsigned long conf, void *data)
diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h
index 8ba8112d2740d..56dec268d4dd9 100644
--- a/drivers/usb/typec/bus.h
+++ b/drivers/usb/typec/bus.h
@@ -4,9 +4,9 @@
 #define __USB_TYPEC_ALTMODE_H__
 
 #include 
-#include 
 
 struct bus_type;
+struct typec_mux;
 
 struct altmode {
unsigned intid;
@@ -28,24 +28,7 @@ struct altmode {
 
 extern struct bus_type typec_bus;
 extern const struct device_type typec_altmode_dev_type;
-extern const struct device_type typec_port_dev_type;
 
 #define is_typec_altmode(_dev_) (_dev_->type == _altmode_dev_type)
-#define is_typec_port(_dev_) (_dev_->type == _port_dev_type)
-
-extern struct class typec_mux_class;
-
-struct typec_switch {
-   struct device dev;
-   typec_switch_set_fn_t set;
-};
-
-struct typec_mux {
-   struct device dev;
-   typec_mux_set_fn_t set;
-};
-
-#define to_typec_switch(_dev_) container_of(_dev_, struct typec_switch, dev)
-#define to_typec_mux(_dev_) container_of(_dev_, struct typec_mux, dev)
 
 #endif /* __USB_TYPEC_ALTMODE_H__ */
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 45f0bf65e9aba..5fa279a96b6ef 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -6,74 +6,15 @@
  * Author: Heikki Krogerus 
  */
 
-#include 
 #include 
 #include 
 #include 
 #include 
 #include 
+#include 
 
 #include "bus.h"
-
-struct typec_plug {
-   struct device   dev;
-   enum typec_plug_index   index;
-   struct ida  mode_ids;
-   int num_altmodes;
-};
-
-struct typec_cable {
-   struct device   dev;
-   enum typec_plug_typetype;
-   struct usb_pd_identity  *identity;
-   unsigned intactive:1;
-   u16 pd_revision; /* 0300H = "3.0" */
-};
-
-struct typec_partner {
-   struct device   dev;
-   unsigned intusb_pd:1;
-   struct usb_pd_identity  *identity;
-   enum typec_accessoryaccessory;
-   struct ida  mode_ids;
-   int num_altmodes;
-   u16 pd_revision; /* 0300H = "3.0" */
-   enum usb_pd_svdm_versvdm_version;
-};
-
-struct typec_port {
-   unsigned intid;
-   struct device   dev;
-   struct ida  mode_ids;
-
-   int prefer_role;
-   enum typec_data_roledata_role;
-   enum typec_role pwr_role;
-   enum typec_role vconn_role;
-   enum typec_pwr_opmode   pwr_opmode;
-   enum typec_port_typeport_type;
-   struct mutexport_type_lock;
-
-   enum typec_orientation  orientation;
-   struct typec_switch *sw;
-   struct typec_mux*mux;
-
-   const struct typec_capability   *cap;
-   const struct typec_operations   *ops;
-};
-
-#define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
-#define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev)
-#define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev)
-#define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev)
-
-static const struct device_type typec_partner_dev_type;
-static const struct device_type typec_cable_dev_type;
-static const struct device_type typec_plug_dev_type;
-
-#define is_typec_partner(_dev_) (_dev_->type == _partner_dev_type)
-#define is_typec_cable(_dev_) (_dev_->type == _cable_dev_type)
-#define is_typec_plug(_dev_) (_dev_->type == _plug_dev_type)
+#include "class.h"
 
 static DEFINE_IDA(typec_index_ida);
 static struct class *typec_class;
@@ -726,7 +667,7 @@ static void typec_partner_release(struct device *dev)

[PATCH 0/6] usb: Linking ports to their Type-C connectors

2021-03-25 Thread Heikki Krogerus
Hi,

Adding a simple function typec_link_port() that can be used to create
a symlink "connector" that points to the USB Type-C connector of a
port. It is used with USB ports initially, but hopefully later also
with other things like DisplayPorts.

Being able to see which connector is connected to a port is important
in general, but it is really important when for example the data or
power role of a device needs to swapped. The user probable wants to
know which USB device is disconnected if role swap on a USB Type-C
connector is executed.

Hope these are OK.

thanks,

Heikki Krogerus (6):
  usb: Iterator for ports
  usb: typec: Organize the private headers properly
  usb: typec: Declare the typec_class static
  usb: typec: Port mapping utility
  usb: Link the ports to the connectors they are attached to
  usb: typec: Link all ports during connector registration

 Documentation/ABI/testing/sysfs-bus-usb |   9 +
 drivers/usb/core/port.c |   3 +
 drivers/usb/core/usb.c  |  43 
 drivers/usb/typec/Makefile  |   1 +
 drivers/usb/typec/bus.c |   2 +
 drivers/usb/typec/bus.h |  19 +-
 drivers/usb/typec/class.c   | 101 +++--
 drivers/usb/typec/class.h   |  94 
 drivers/usb/typec/mux.c |   4 +-
 drivers/usb/typec/mux.h |  21 ++
 drivers/usb/typec/port-mapper.c | 283 
 include/linux/usb.h |   1 +
 include/linux/usb/typec.h   |  13 ++
 13 files changed, 499 insertions(+), 95 deletions(-)
 create mode 100644 drivers/usb/typec/class.h
 create mode 100644 drivers/usb/typec/mux.h
 create mode 100644 drivers/usb/typec/port-mapper.c

-- 
2.30.2



Re: [PATCH] usb: typec: Fix a typo

2021-03-25 Thread Heikki Krogerus
On Thu, Mar 25, 2021 at 10:40:23AM +0530, Bhaskar Chowdhury wrote:
> 
> s/Acknowlege/Acknowledge/
> 
> Signed-off-by: Bhaskar Chowdhury 

Reviewed-by: Heikki Krogerus 

> ---
>  drivers/usb/typec/ucsi/ucsi.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
> index 244270755ae6..282c3c825c13 100644
> --- a/drivers/usb/typec/ucsi/ucsi.c
> +++ b/drivers/usb/typec/ucsi/ucsi.c
> @@ -63,7 +63,7 @@ static int ucsi_read_error(struct ucsi *ucsi)
>   u16 error;
>   int ret;
> 
> - /* Acknowlege the command that failed */
> + /* Acknowledge the command that failed */
>   ret = ucsi_acknowledge_command(ucsi);
>   if (ret)
>   return ret;
> --
> 2.30.1

-- 
heikki


Re: [PATCH] ASoC: Intel: Handle device properties with software node API

2021-03-23 Thread Heikki Krogerus
On Mon, Mar 22, 2021 at 10:02:40AM -0500, Pierre-Louis Bossart wrote:
> 
> 
> On 3/22/21 6:06 AM, Heikki Krogerus wrote:
> > The function device_add_properties() is going to be removed.
> > Replacing it with software node API equivalents.
> > 
> > Signed-off-by: Heikki Krogerus 
> > ---
> > Hi,
> > 
> > This patch depends on a fix from mainline, available in v5.12-rc4:
> > 
> > 2a92c90f2ecc ("software node: Fix device_add_software_node()")
> > 
> > Cheers,
> > ---
> >   sound/soc/intel/boards/bytcht_es8316.c  |  2 +-
> >   sound/soc/intel/boards/bytcr_rt5640.c   |  2 +-
> >   sound/soc/intel/boards/bytcr_rt5651.c   |  2 +-
> >   sound/soc/intel/boards/sof_sdw_rt711.c  | 20 +++-
> >   sound/soc/intel/boards/sof_sdw_rt711_sdca.c | 20 +++-
> >   5 files changed, 33 insertions(+), 13 deletions(-)
> > 
> > diff --git a/sound/soc/intel/boards/bytcht_es8316.c 
> > b/sound/soc/intel/boards/bytcht_es8316.c
> > index 06df2d46d910b..4a9817a95928c 100644
> > --- a/sound/soc/intel/boards/bytcht_es8316.c
> > +++ b/sound/soc/intel/boards/bytcht_es8316.c
> > @@ -544,7 +544,7 @@ static int snd_byt_cht_es8316_mc_probe(struct 
> > platform_device *pdev)
> > props[cnt++] = 
> > PROPERTY_ENTRY_BOOL("everest,jack-detect-inverted");
> > if (cnt) {
> > -   ret = device_add_properties(codec_dev, props);
> > +   ret = device_create_managed_software_node(codec_dev, props, 
> > NULL);
> 
> I don't think this is correct. This is using the codec_dev device, but this
> property is created in the machine driver - different device. I think the
> proper fix is to remove the property in the machine driver .remove()
> callback, as done below for the SoundWire case, and not to rely on devm_
> with the wrong device.
> 
> there was a thread between July and October on this in
> https://github.com/thesofproject/linux/pull/2306/
> 
> It seems that we need to restart this work.
> 
> Heikki, do you mind if I take your patches (keeping you as the author) and
> rework+test them with the SOF tree + CI ?

OK by me (though, I'm not sure about the author part after that).


thanks,

-- 
heikki


  1   2   3   4   5   6   7   8   9   10   >