Devicetree
 help / color / mirror / Atom feed
* [PATCH 2/4] ARM: dts: sd-600eval: add hdmi support
From: Srinivas Kandagatla @ 2017-01-04 13:34 UTC (permalink / raw)
  To: Andy Gross
  Cc: David Brown, Rob Herring, Mark Rutland, linux-arm-msm, linux-soc,
	devicetree, linux-arm-kernel, linux-kernel, Srinivas Kandagatla
In-Reply-To: <1483536854-21389-1-git-send-email-srinivas.kandagatla@linaro.org>

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 .../arm/boot/dts/qcom-apq8064-arrow-sd-600eval.dts | 44 ++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/arch/arm/boot/dts/qcom-apq8064-arrow-sd-600eval.dts b/arch/arm/boot/dts/qcom-apq8064-arrow-sd-600eval.dts
index 39ae2bc..4e908af 100644
--- a/arch/arm/boot/dts/qcom-apq8064-arrow-sd-600eval.dts
+++ b/arch/arm/boot/dts/qcom-apq8064-arrow-sd-600eval.dts
@@ -39,6 +39,17 @@
 
 	};
 
+	hdmi-out {
+		compatible = "hdmi-connector";
+		type = "a";
+
+		port {
+			hdmi_con: endpoint {
+				remote-endpoint = <&hdmi_out>;
+			};
+		};
+	};
+
 	soc {
 		rpm@108000 {
 			regulators {
@@ -347,5 +358,38 @@
 				cd-gpios	= <&tlmm_pinmux 26 GPIO_ACTIVE_HIGH>;
 			};
 		};
+
+		hdmi-tx@4a00000 {
+			status = "okay";
+			core-vdda-supply = <&pm8921_hdmi_switch>;
+			hdmi-mux-supply = <&vcc3v3>;
+
+			hpd-gpio = <&tlmm_pinmux 72 GPIO_ACTIVE_HIGH>;
+
+			ports {
+				port@1 {
+					endpoint {
+						remote-endpoint = <&hdmi_con>;
+					};
+				};
+			};
+		};
+
+		hdmi-phy@4a00400 {
+			status = "okay";
+			core-vdda-supply = <&pm8921_hdmi_switch>;
+		};
+
+		mdp@5100000 {
+			status = "okay";
+
+			ports {
+				port@3 {
+					endpoint {
+						remote-endpoint = <&hdmi_in>;
+					};
+				};
+			};
+		};
 	};
 };
-- 
2.10.1

^ permalink raw reply related

* [PATCH 1/4] ARM: dts: move hdmi pinctrl out of board file.
From: Srinivas Kandagatla @ 2017-01-04 13:34 UTC (permalink / raw)
  To: Andy Gross
  Cc: David Brown, Rob Herring, Mark Rutland, linux-arm-msm, linux-soc,
	devicetree, linux-arm-kernel, linux-kernel, Srinivas Kandagatla

This patch moves hdmi pinctrl defination from board file to soc level
pinctrl file. If not this pinctrl setup will be duplicated across all
the apq8064 based board files.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 arch/arm/boot/dts/qcom-apq8064-ifc6410.dts | 22 ----------------------
 arch/arm/boot/dts/qcom-apq8064-pins.dtsi   | 19 +++++++++++++++++++
 arch/arm/boot/dts/qcom-apq8064.dtsi        |  2 ++
 3 files changed, 21 insertions(+), 22 deletions(-)

diff --git a/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts b/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts
index 3d37cab..881ce70 100644
--- a/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts
+++ b/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts
@@ -75,25 +75,6 @@
 					bias-disable;
 				};
 			};
-
-			hdmi_pinctrl: hdmi-pinctrl {
-				mux {
-					pins = "gpio70", "gpio71", "gpio72";
-					function = "hdmi";
-				};
-
-				pinconf_ddc {
-					pins = "gpio70", "gpio71";
-					bias-pull-up;
-					drive-strength = <2>;
-				};
-
-				pinconf_hpd {
-					pins = "gpio72";
-					bias-pull-down;
-					drive-strength = <16>;
-				};
-			};
 		};
 
 		rpm@108000 {
@@ -368,9 +349,6 @@
 
 			hpd-gpios = <&tlmm_pinmux 72 GPIO_ACTIVE_HIGH>;
 
-			pinctrl-names = "default";
-			pinctrl-0 = <&hdmi_pinctrl>;
-
 			ports {
 				port@0 {
 					endpoint {
diff --git a/arch/arm/boot/dts/qcom-apq8064-pins.dtsi b/arch/arm/boot/dts/qcom-apq8064-pins.dtsi
index 6b801e7..cba4450 100644
--- a/arch/arm/boot/dts/qcom-apq8064-pins.dtsi
+++ b/arch/arm/boot/dts/qcom-apq8064-pins.dtsi
@@ -284,4 +284,23 @@
 			bias-disable = <0>;
 		};
 	};
+
+	hdmi_pinctrl: hdmi-pinctrl {
+		mux {
+			pins = "gpio70", "gpio71", "gpio72";
+			function = "hdmi";
+		};
+
+		pinconf_ddc {
+			pins = "gpio70", "gpio71";
+			bias-pull-up;
+			drive-strength = <2>;
+		};
+
+		pinconf_hpd {
+			pins = "gpio72";
+			bias-pull-down;
+			drive-strength = <16>;
+		};
+	};
 };
diff --git a/arch/arm/boot/dts/qcom-apq8064.dtsi b/arch/arm/boot/dts/qcom-apq8064.dtsi
index 407a461..e68a8a1 100644
--- a/arch/arm/boot/dts/qcom-apq8064.dtsi
+++ b/arch/arm/boot/dts/qcom-apq8064.dtsi
@@ -1327,6 +1327,8 @@
 
 		hdmi: hdmi-tx@4a00000 {
 			compatible = "qcom,hdmi-tx-8960";
+			pinctrl-names = "default";
+			pinctrl-0 = <&hdmi_pinctrl>;
 			reg = <0x04a00000 0x2f0>;
 			reg-names = "core_physical";
 			interrupts = <GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>;
-- 
2.10.1

^ permalink raw reply related

* Re: [PATCH v2 10/19] media: Add i.MX media core driver
From: Vladimir Zapolskiy @ 2017-01-04 13:33 UTC (permalink / raw)
  To: Steve Longerbeam, shawnguo, kernel, fabio.estevam, robh+dt,
	mark.rutland, linux, mchehab, gregkh, p.zabel
  Cc: devel, devicetree, Steve Longerbeam, linux-kernel,
	linux-arm-kernel, linux-media
In-Reply-To: <1483477049-19056-11-git-send-email-steve_longerbeam@mentor.com>

Hi Steve,

On 01/03/2017 10:57 PM, Steve Longerbeam wrote:
> Add the core media driver for i.MX SOC.
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>  Documentation/devicetree/bindings/media/imx.txt   | 205 +++++

v2 was sent before getting Rob's review comments, but still they
should be addressed in v3.

Also I would suggest to separate device tree binding documentation
change and place it as the first patch in the series, this should
make the following DTS changes valid.

>  Documentation/media/v4l-drivers/imx.rst           | 430 ++++++++++
>  drivers/staging/media/Kconfig                     |   2 +
>  drivers/staging/media/Makefile                    |   1 +
>  drivers/staging/media/imx/Kconfig                 |   8 +
>  drivers/staging/media/imx/Makefile                |   6 +
>  drivers/staging/media/imx/TODO                    |  18 +
>  drivers/staging/media/imx/imx-media-common.c      | 985 ++++++++++++++++++++++
>  drivers/staging/media/imx/imx-media-dev.c         | 479 +++++++++++
>  drivers/staging/media/imx/imx-media-fim.c         | 509 +++++++++++
>  drivers/staging/media/imx/imx-media-internal-sd.c | 457 ++++++++++
>  drivers/staging/media/imx/imx-media-of.c          | 291 +++++++
>  drivers/staging/media/imx/imx-media-of.h          |  25 +
>  drivers/staging/media/imx/imx-media.h             | 299 +++++++
>  include/media/imx.h                               |  15 +
>  include/uapi/Kbuild                               |   1 +
>  include/uapi/linux/v4l2-controls.h                |   4 +
>  include/uapi/media/Kbuild                         |   2 +
>  include/uapi/media/imx.h                          |  30 +

Probably Greg should ack the UAPI changes, you may consider
to split them into a separate patch.

>  19 files changed, 3767 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/imx.txt
>  create mode 100644 Documentation/media/v4l-drivers/imx.rst
>  create mode 100644 drivers/staging/media/imx/Kconfig
>  create mode 100644 drivers/staging/media/imx/Makefile
>  create mode 100644 drivers/staging/media/imx/TODO
>  create mode 100644 drivers/staging/media/imx/imx-media-common.c
>  create mode 100644 drivers/staging/media/imx/imx-media-dev.c
>  create mode 100644 drivers/staging/media/imx/imx-media-fim.c
>  create mode 100644 drivers/staging/media/imx/imx-media-internal-sd.c
>  create mode 100644 drivers/staging/media/imx/imx-media-of.c
>  create mode 100644 drivers/staging/media/imx/imx-media-of.h
>  create mode 100644 drivers/staging/media/imx/imx-media.h
>  create mode 100644 include/media/imx.h
>  create mode 100644 include/uapi/media/Kbuild
>  create mode 100644 include/uapi/media/imx.h
> 

[snip]

> +
> +struct imx_media_subdev *
> +imx_media_find_subdev_by_sd(struct imx_media_dev *imxmd,
> +			    struct v4l2_subdev *sd)
> +{
> +	struct imx_media_subdev *imxsd;
> +	int i, ret = -ENODEV;
> +
> +	for (i = 0; i < imxmd->num_subdevs; i++) {
> +		imxsd = &imxmd->subdev[i];
> +		if (sd == imxsd->sd) {

This can be simplifed:

...

	if (sd == imxsd->sd)
		return imxsd;
}

return ERR_PTR(-ENODEV);

> +			ret = 0;
> +			break;
> +		}
> +	}
> +
> +	return ret ? ERR_PTR(ret) : imxsd;
> +}
> +EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_sd);
> +
> +struct imx_media_subdev *
> +imx_media_find_subdev_by_id(struct imx_media_dev *imxmd, u32 grp_id)
> +{
> +	struct imx_media_subdev *imxsd;
> +	int i, ret = -ENODEV;
> +
> +	for (i = 0; i < imxmd->num_subdevs; i++) {
> +		imxsd = &imxmd->subdev[i];
> +		if (imxsd->sd && imxsd->sd->grp_id == grp_id) {
> +			ret = 0;
> +			break;

This can be simplifed:

...

	if (imxsd->sd && imxsd->sd->grp_id == grp_i)
		return imxsd;
}

return ERR_PTR(-ENODEV);

> +		}
> +	}
> +
> +	return ret ? ERR_PTR(ret) : imxsd;
> +}
> +EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_id);
> +

[snip]

> diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c
> new file mode 100644
> index 0000000..8d22730
> --- /dev/null
> +++ b/drivers/staging/media/imx/imx-media-dev.c
> @@ -0,0 +1,479 @@
> +/*
> + * V4L2 Media Controller Driver for Freescale i.MX5/6 SOC
> + *
> + * Copyright (c) 2016 Mentor Graphics Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/fs.h>
> +#include <linux/timer.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/platform_device.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/of_platform.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-mc.h>

Please sort out the list of headers alphabetically.

> +#include <video/imx-ipu-v3.h>
> +#include <media/imx.h>
> +#include "imx-media.h"
> +#include "imx-media-of.h"
> +
> +#define DEVICE_NAME "imx-media"

I suppose you don't need this macro.

[snip]

> + */
> +static int imx_media_create_links(struct imx_media_dev *imxmd)
> +{
> +	struct imx_media_subdev *local_sd;
> +	struct imx_media_subdev *remote_sd;
> +	struct v4l2_subdev *source, *sink;
> +	struct imx_media_link *link;
> +	struct imx_media_pad *pad;
> +	u16 source_pad, sink_pad;
> +	int num_pads, i, j, k;
> +	int ret = 0;
> +
> +	for (i = 0; i < imxmd->num_subdevs; i++) {
> +		local_sd = &imxmd->subdev[i];
> +		num_pads = local_sd->num_sink_pads + local_sd->num_src_pads;
> +
> +		for (j = 0; j < num_pads; j++) {
> +			pad = &local_sd->pad[j];
> +
> +			for (k = 0; k < pad->num_links; k++) {
> +				link = &pad->link[k];
> +
> +				remote_sd = imx_media_find_async_subdev(
> +					imxmd, link->remote_sd_node,
> +					link->remote_devname);
> +				if (!remote_sd) {
> +					v4l2_warn(&imxmd->v4l2_dev,
> +						  "%s: no remote for %s:%d\n",
> +						  __func__, local_sd->sd->name,
> +						  link->local_pad);
> +					continue;
> +				}
> +
> +				/* only create the source->sink links */
> +				if (pad->pad.flags & MEDIA_PAD_FL_SINK)
> +					continue;
> +
> +				source = local_sd->sd;
> +				sink = remote_sd->sd;
> +				source_pad = link->local_pad;
> +				sink_pad = link->remote_pad;
> +
> +				v4l2_info(&imxmd->v4l2_dev,
> +					  "%s: %s:%d -> %s:%d\n", __func__,
> +					  source->name, source_pad,
> +					  sink->name, sink_pad);
> +
> +				ret = media_create_pad_link(&source->entity,
> +							    source_pad,
> +							    &sink->entity,
> +							    sink_pad,
> +							    0);
> +				if (ret) {
> +					v4l2_err(&imxmd->v4l2_dev,
> +						 "create_pad_link failed: %d\n",
> +						 ret);
> +					goto out;

Indentation depth is quite terrific.

> +				}
> +			}
> +		}
> +	}
> +
> +out:
> +	return ret;
>
[snip]

> +
> +static const struct of_device_id imx_media_dt_ids[] = {
> +	{ .compatible = "fsl,imx-media" },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, imx_media_dt_ids);
> +
> +static struct platform_driver imx_media_pdrv = {
> +	.probe		= imx_media_probe,
> +	.remove		= imx_media_remove,
> +	.driver		= {
> +		.name	= DEVICE_NAME,
> +		.owner	= THIS_MODULE,

Setting of .owner is not needed nowadays IIRC.

> +		.of_match_table	= imx_media_dt_ids,
> +	},
> +};
> +
> +module_platform_driver(imx_media_pdrv);
> +
> +MODULE_DESCRIPTION("i.MX5/6 v4l2 media controller driver");
> +MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/staging/media/imx/imx-media-fim.c b/drivers/staging/media/imx/imx-media-fim.c
> new file mode 100644
> index 0000000..52bfa8d
> --- /dev/null
> +++ b/drivers/staging/media/imx/imx-media-fim.c
> @@ -0,0 +1,509 @@
> +/*
> + * Frame Interval Monitor.
> + *
> + * Copyright (c) 2016 Mentor Graphics Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/slab.h>
> +#include <linux/platform_device.h>
> +#ifdef CONFIG_IMX_GPT_ICAP
> +#include <linux/mxc_icap.h>
> +#endif

This looks clumsy. Include it unconditionally, if needed
do #ifdef's inside the header file.

> +#include <media/v4l2-subdev.h>
> +#include <media/v4l2-of.h>
> +#include <media/v4l2-ctrls.h>

Please sort out the list alphabetically.

> +#include <media/imx.h>
> +#include "imx-media.h"
> +

[snip]

> +
> +static int of_parse_fim(struct imx_media_fim *fim, struct device_node *np)
> +{
> +	struct device_node *fim_np;
> +	u32 val, tol[2], icap[2];
> +	int ret;
> +
> +	fim_np = of_get_child_by_name(np, "fim");
> +	if (!fim_np) {
> +		/* set to the default defaults */
> +		fim->of_defaults[FIM_CL_ENABLE] = FIM_CL_ENABLE_DEF;
> +		fim->of_defaults[FIM_CL_NUM] = FIM_CL_NUM_DEF;
> +		fim->of_defaults[FIM_CL_NUM_SKIP] = FIM_CL_NUM_SKIP_DEF;
> +		fim->of_defaults[FIM_CL_TOLERANCE_MIN] =
> +			FIM_CL_TOLERANCE_MIN_DEF;
> +		fim->of_defaults[FIM_CL_TOLERANCE_MAX] =
> +			FIM_CL_TOLERANCE_MAX_DEF;
> +		fim->icap_channel = -1;
> +		return 0;
> +	}
> +
> +	ret = of_property_read_u32(fim_np, "enable", &val);
> +	if (ret)
> +		val = FIM_CL_ENABLE_DEF;
> +	fim->of_defaults[FIM_CL_ENABLE] = val;
> +
> +	ret = of_property_read_u32(fim_np, "num-avg", &val);
> +	if (ret)
> +		val = FIM_CL_NUM_DEF;
> +	fim->of_defaults[FIM_CL_NUM] = val;
> +
> +	ret = of_property_read_u32(fim_np, "num-skip", &val);
> +	if (ret)
> +		val = FIM_CL_NUM_SKIP_DEF;
> +	fim->of_defaults[FIM_CL_NUM_SKIP] = val;
> +
> +	ret = of_property_read_u32_array(fim_np, "tolerance-range", tol, 2);
> +	if (ret) {
> +		tol[0] = FIM_CL_TOLERANCE_MIN_DEF;
> +		tol[1] = FIM_CL_TOLERANCE_MAX_DEF;
> +	}
> +	fim->of_defaults[FIM_CL_TOLERANCE_MIN] = tol[0];
> +	fim->of_defaults[FIM_CL_TOLERANCE_MAX] = tol[1];
> +
> +	fim->icap_channel = -1;
> +	if (IS_ENABLED(CONFIG_IMX_GPT_ICAP)) {
> +		ret = of_property_read_u32_array(fim_np,
> +						 "input-capture-channel",
> +						 icap, 2);
> +		if (!ret) {
> +			fim->icap_channel = icap[0];
> +			fim->icap_flags = icap[1];
> +		}

Should you return error otherwise?

> +	}
> +
> +	of_node_put(fim_np);
> +	return 0;
> +}

[snip]

> diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c
> new file mode 100644
> index 0000000..018d05a
> --- /dev/null
> +++ b/drivers/staging/media/imx/imx-media-of.c
> @@ -0,0 +1,291 @@
> +/*
> + * Media driver for Freescale i.MX5/6 SOC
> + *
> + * Open Firmware parsing.
> + *
> + * Copyright (c) 2016 Mentor Graphics Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#include <linux/of_platform.h>
> +#include <media/v4l2-device.h>
> +#include <media/videobuf2-dma-contig.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/v4l2-of.h>
> +#include <media/v4l2-ctrls.h>

Please sort out the list alphabetically.

> +#include <video/imx-ipu-v3.h>
> +#include "imx-media.h"
> +
> +static int of_add_pad_link(struct imx_media_dev *imxmd,
> +			   struct imx_media_pad *pad,
> +			   struct device_node *local_sd_node,
> +			   struct device_node *remote_sd_node,
> +			   int local_pad, int remote_pad)
> +{
> +	dev_dbg(imxmd->dev, "%s: adding %s:%d -> %s:%d\n", __func__,
> +		local_sd_node->name, local_pad,
> +		remote_sd_node->name, remote_pad);
> +
> +	return imx_media_add_pad_link(imxmd, pad, remote_sd_node, NULL,
> +				      local_pad, remote_pad);
> +}
> +
> +/* parse inputs property from a sensor node */
> +static void of_parse_sensor_inputs(struct imx_media_dev *imxmd,
> +				   struct imx_media_subdev *sensor,
> +				   struct device_node *sensor_np)
> +{
> +	struct imx_media_sensor_input *sinput = &sensor->input;
> +	int ret, i;
> +
> +	for (i = 0; i < IMX_MEDIA_MAX_SENSOR_INPUTS; i++) {
> +		const char *input_name;
> +		u32 val;
> +
> +		ret = of_property_read_u32_index(sensor_np, "inputs", i, &val);
> +		if (ret)
> +			break;
> +
> +		sinput->value[i] = val;
> +
> +		ret = of_property_read_string_index(sensor_np, "input-names",
> +						    i, &input_name);
> +		/*
> +		 * if input-names not provided, they will be set using
> +		 * the subdev name once the sensor is known during
> +		 * async bind
> +		 */
> +		if (!ret)
> +			strncpy(sinput->name[i], input_name,
> +				sizeof(sinput->name[i]));
> +	}
> +
> +	sinput->num = i;
> +
> +	/* if no inputs provided just assume a single input */
> +	if (sinput->num == 0)
> +		sinput->num = 1;
> +}
> +
> +static void of_parse_sensor(struct imx_media_dev *imxmd,
> +			    struct imx_media_subdev *sensor,
> +			    struct device_node *sensor_np)
> +{
> +	struct device_node *endpoint;
> +
> +	of_parse_sensor_inputs(imxmd, sensor, sensor_np);
> +
> +	endpoint = of_graph_get_next_endpoint(sensor_np, NULL);
> +	if (endpoint) {
> +		v4l2_of_parse_endpoint(endpoint, &sensor->sensor_ep);
> +		of_node_put(endpoint);
> +	}
> +}
> +
> +static int of_get_port_count(const struct device_node *np)
> +{
> +	struct device_node *child;
> +	int num = 0;
> +
> +	/* if this node is itself a port, return 1 */
> +	if (of_node_cmp(np->name, "port") == 0)
> +		return 1;
> +
> +	for_each_child_of_node(np, child) {
> +		if (of_node_cmp(child->name, "port") == 0)
> +			num++;
> +	}

Unneeded bracers.

> +	return num;
> +}
> +
> +/*
> + * find the remote device node and remote port id (remote pad #)
> + * given local endpoint node
> + */
> +static void of_get_remote_pad(struct device_node *epnode,
> +			      struct device_node **remote_node,
> +			      int *remote_pad)
> +{
> +	struct device_node *rp, *rpp;
> +	struct device_node *remote;
> +
> +	rp = of_graph_get_remote_port(epnode);
> +	rpp = of_graph_get_remote_port_parent(epnode);
> +
> +	if (of_device_is_compatible(rpp, "fsl,imx6q-ipu")) {
> +		/* the remote is one of the CSI ports */
> +		remote = rp;
> +		*remote_pad = 0;
> +		of_node_put(rpp);
> +	} else {
> +		remote = rpp;
> +		of_property_read_u32(rp, "reg", remote_pad);
> +		of_node_put(rp);
> +	}
> +
> +	if (!remote || !of_device_is_available(remote)) {
> +		of_node_put(remote);
> +		*remote_node = NULL;
> +	} else {
> +		*remote_node = remote;
> +	}
> +}
> +
> +static struct imx_media_subdev *
> +of_parse_subdev(struct imx_media_dev *imxmd, struct device_node *sd_np,
> +		bool is_csi_port)
> +{
> +	struct imx_media_subdev *imxsd;
> +	int i, num_pads, ret;
> +
> +	if (!of_device_is_available(sd_np)) {
> +		dev_dbg(imxmd->dev, "%s: %s not enabled\n", __func__,
> +			sd_np->name);
> +		return NULL;
> +	}
> +
> +	/* register this subdev with async notifier */
> +	imxsd = imx_media_add_async_subdev(imxmd, sd_np, NULL);
> +	if (!imxsd)
> +		return NULL;
> +	if (IS_ERR(imxsd))
> +		return imxsd;

if (IS_ERR_OR_NULL(imxsd))
	return imxsd;

> +
> +	if (is_csi_port) {
> +		/*
> +		 * the ipu-csi has one sink port and one source port.
> +		 * The source port is not represented in the device tree,
> +		 * but is described by the internal pads and links later.
> +		 */
> +		num_pads = 2;
> +		imxsd->num_sink_pads = 1;
> +	} else if (of_device_is_compatible(sd_np, "fsl,imx-mipi-csi2")) {
> +		num_pads = of_get_port_count(sd_np);
> +		/* the mipi csi2 receiver has only one sink port */
> +		imxsd->num_sink_pads = 1;
> +	} else if (of_device_is_compatible(sd_np, "imx-video-mux")) {
> +		num_pads = of_get_port_count(sd_np);
> +		/* for the video mux, all but the last port are sinks */
> +		imxsd->num_sink_pads = num_pads - 1;
> +	} else {
> +		/* must be a sensor */
> +		num_pads = 1;
> +		imxsd->num_sink_pads = 0;
> +	}
> +
> +	if (imxsd->num_sink_pads >= num_pads)
> +		return ERR_PTR(-EINVAL);
> +
> +	imxsd->num_src_pads = num_pads - imxsd->num_sink_pads;
> +
> +	dev_dbg(imxmd->dev, "%s: %s has %d pads (%d sink, %d src)\n",
> +		__func__, sd_np->name, num_pads,
> +		imxsd->num_sink_pads, imxsd->num_src_pads);
> +
> +	if (imxsd->num_sink_pads == 0) {
> +		/* this might be a sensor */
> +		of_parse_sensor(imxmd, imxsd, sd_np);
> +	}

Unneeded bracers.

> +
> +	for (i = 0; i < num_pads; i++) {
> +		struct device_node *epnode = NULL, *port, *remote_np;
> +		struct imx_media_subdev *remote_imxsd;
> +		struct imx_media_pad *pad;
> +		int remote_pad;

Too deep indentation, may be move the cycle body into a separate function?

> +
> +		/* init this pad */
> +		pad = &imxsd->pad[i];
> +		pad->pad.flags = (i < imxsd->num_sink_pads) ?
> +			MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
> +
> +		if (is_csi_port)
> +			port = (i < imxsd->num_sink_pads) ? sd_np : NULL;
> +		else
> +			port = of_graph_get_port_by_id(sd_np, i);
> +		if (!port)
> +			continue;
> +
> +		while ((epnode = of_get_next_child(port, epnode))) {

Please reuse for_each_child_of_node() here.

> +			of_get_remote_pad(epnode, &remote_np, &remote_pad);
> +			if (!remote_np) {
> +				of_node_put(epnode);

Please remove of_node_put() here, of_get_next_child() does it.

> +				continue;
> +			}
> +
> +			ret = of_add_pad_link(imxmd, pad, sd_np, remote_np,
> +					      i, remote_pad);
> +			if (ret) {
> +				imxsd = ERR_PTR(ret);
> +				break;
> +			}
> +
> +			if (i < imxsd->num_sink_pads) {
> +				/* follow sink endpoints upstream */
> +				remote_imxsd = of_parse_subdev(imxmd,
> +							       remote_np,
> +							       false);
> +				if (IS_ERR(remote_imxsd)) {
> +					imxsd = remote_imxsd;
> +					break;
> +				}
> +			}
> +
> +			of_node_put(remote_np);
> +			of_node_put(epnode);
> +		}
> +
> +		if (port != sd_np)
> +			of_node_put(port);
> +		if (IS_ERR(imxsd)) {
> +			of_node_put(remote_np);
> +			of_node_put(epnode);
> +			break;
> +		}
> +	}
> +
> +	return imxsd;
> +}
> +
> +int imx_media_of_parse(struct imx_media_dev *imxmd,
> +		       struct imx_media_subdev *(*csi)[4],
> +		       struct device_node *np)
> +{
> +	struct device_node *csi_np;
> +	struct imx_media_subdev *lcsi;

Please swap two lines above to get the reverse christmas tree ordering.

> +	u32 ipu_id, csi_id;
> +	int i, ret;
> +
> +	for (i = 0; ; i++) {
> +		csi_np = of_parse_phandle(np, "ports", i);
> +		if (!csi_np)
> +			break;
> +
> +		lcsi = of_parse_subdev(imxmd, csi_np, true);
> +		if (IS_ERR(lcsi)) {
> +			ret = PTR_ERR(lcsi);
> +			goto err_put;
> +		}
> +
> +		of_property_read_u32(csi_np, "reg", &csi_id);

Not sure if it is safe enough to ignore return value and potentially
left csi_id uninitialized.

> +		ipu_id = of_alias_get_id(csi_np->parent, "ipu");
> +
> +		if (ipu_id > 1 || csi_id > 1) {
> +			dev_err(imxmd->dev, "%s: invalid ipu/csi id (%u/%u)\n",
> +				__func__, ipu_id, csi_id);
> +			ret = -EINVAL;
> +			goto err_put;
> +		}
> +
> +		of_node_put(csi_np);

You can put the node right after of_alias_get_id() call, then in case
of error return right from the if block and remove the goto label.

> +
> +		(*csi)[ipu_id * 2 + csi_id] = lcsi;
> +	}
> +
> +	return 0;
> +err_put:
> +	of_node_put(csi_np);
> +	return ret;
> +}
> diff --git a/drivers/staging/media/imx/imx-media-of.h b/drivers/staging/media/imx/imx-media-of.h
> new file mode 100644
> index 0000000..0c61b05
> --- /dev/null
> +++ b/drivers/staging/media/imx/imx-media-of.h
> @@ -0,0 +1,25 @@
> +/*
> + * V4L2 Media Controller Driver for Freescale i.MX5/6 SOC
> + *
> + * Open Firmware parsing.
> + *
> + * Copyright (c) 2016 Mentor Graphics Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#ifndef _IMX_MEDIA_OF_H
> +#define _IMX_MEDIA_OF_H
> +

I do believe you should include some headers or add declarations
of "struct imx_media_dev", "struct imx_media_subdev", "struct device_node".

> +struct imx_media_subdev *
> +imx_media_of_find_subdev(struct imx_media_dev *imxmd,
> +			 struct device_node *np,
> +			 const char *name);
> +
> +int imx_media_of_parse(struct imx_media_dev *dev,
> +		       struct imx_media_subdev *(*csi)[4],
> +		       struct device_node *np);
> +
> +#endif
> diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h
> new file mode 100644
> index 0000000..6a018a9
> --- /dev/null
> +++ b/drivers/staging/media/imx/imx-media.h
> @@ -0,0 +1,299 @@
> +/*
> + * V4L2 Media Controller Driver for Freescale i.MX5/6 SOC
> + *
> + * Copyright (c) 2016 Mentor Graphics Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#ifndef _IMX_MEDIA_H
> +#define _IMX_MEDIA_H

Please insert here an empty line to improve readability.

> +#include <media/v4l2-device.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-of.h>

Please sort out the list alphabetically.

> +#include <media/videobuf2-dma-contig.h>
> +#include <video/imx-ipu-v3.h>
> +
> +/*
> + * This is somewhat arbitrary, but we need at least:
> + * - 2 camera interface subdevs
> + * - 3 IC subdevs
> + * - 2 CSI subdevs
> + * - 1 mipi-csi2 receiver subdev
> + * - 2 video-mux subdevs
> + * - 3 camera sensor subdevs (2 parallel, 1 mipi-csi2)
> + *
> + * And double the above numbers for quad i.mx!
> + */

[snip]

--
With best wishes,
Vladimir

^ permalink raw reply

* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver
From: Rob Herring @ 2017-01-04 13:32 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Linus Walleij, Gary Bisson, Grygorii Strashko, Mark Rutland,
	Nishanth Menon, devicetree, linux-gpio, linux-omap, Lokesh Vutla
In-Reply-To: <20170102221228.GH9325@atomide.com>

On Mon, Jan 02, 2017 at 02:12:28PM -0800, Tony Lindgren wrote:
> * Tony Lindgren <tony@atomide.com> [161230 10:38]:
> > +static struct pinctrl_ops ti_iodelay_pinctrl_ops = {
> > +	.get_groups_count = pinctrl_generic_get_group_count,
> > +	.get_group_name = pinctrl_generic_get_group_name,
> > +	.get_group_pins = pinctrl_generic_get_group_pins,
> > +	.pin_dbg_show = ti_iodelay_pin_dbg_show,
> > +	.dt_node_to_map = ti_iodelay_dt_node_to_map,
> > +};
> > +
> > +static struct pinconf_ops ti_iodelay_pinctrl_pinconf_ops = {
> > +	.pin_config_group_get = ti_iodelay_pinconf_group_get,
> > +	.pin_config_group_set = ti_iodelay_pinconf_group_set,
> > +#ifdef CONFIG_DEBUG_FS
> > +	.pin_config_group_dbg_show = ti_iodelay_pinconf_group_dbg_show,
> > +#endif
> 
> I noticed that .pin_dbg_show above needs ifdef CONFIG_DEBUGFS.
> Updated patch below.
> 
> Regards,
> 
> Tony
> 
> 8< --------------------
> From tony Mon Sep 17 00:00:00 2001
> From: Nishanth Menon <nm@ti.com>
> Date: Tue, 27 Dec 2016 08:03:43 -0800
> Subject: [PATCH] pinctrl: Introduce TI IOdelay configuration driver
> 
> SoC family such as DRA7 family of processors have, in addition
> to the regular muxing of pins (as done by pinctrl-single), a separate
> hardware module called IODelay which is also expected to be configured.
> The "IODelay" module has it's own register space that is independent
> of the control module and the padconf register area.
> 
> With recent changes to the pinctrl framework, we can now support
> this hardware with a reasonably minimal driver by using #pinctrl-cells,
> GENERIC_PINCTRL_GROUPS and GENERIC_PINMUX_FUNCTIONS.
> 
> It is advocated strongly in TI's official documentation considering
> the existing design of the DRA7 family of processors during mux or
> IODelay reconfiguration, there is a potential for a significant glitch
> which may cause functional impairment to certain hardware. It is
> hence recommended to do as little of muxing as absolutely necessary
> without I/O isolation (which can only be done in initial stages of
> bootloader).
> 
> NOTE: with the system wide I/O isolation scheme present in DRA7 SoC
> family, it is not reasonable to do stop all I/O operations for every
> such pad configuration scheme. So, we will let it glitch when used in
> this mode.
> 
> Even with the above limitation, certain functionality such as MMC has
> mandatory need for IODelay reconfiguration requirements, depending on
> speed of transfer. In these cases, with careful examination of usecase
> involved, the expected glitch can be controlled such that it does not
> impact functionality.
> 
> In short, IODelay module support as a padconf driver being introduced
> here is not expected to do SoC wide I/O Isolation and is meant for
> a limited subset of IODelay configuration requirements that need to
> be dynamic and whose glitchy behavior will not cause functionality
> failure for that interface.
> 
> IMPORTANT NOTE: we take the approach of keeping LOCK_BITs cleared
> to 0x0 at all times, even when configuring Manual IO Timing Modes.
> This is done by eliminating the LOCK_BIT=1 setting from Step
> of the Manual IO timing Mode configuration procedure. This option
> leaves the CFG_* registers unprotected from unintended writes to the
> CTRL_CORE_PAD_* registers while Manual IO Timing Modes are configured.
> 
> This approach is taken to allow for a generic driver to exist in kernel
> world that has to be used carefully in required usecases.
> 
> Signed-off-by: Nishanth Menon <nm@ti.com>
> Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com>
> [tony@atomide.com: updated to use generic pinctrl functions, added
>  binding documentation, updated comments]
> Signed-off-by: Tony Lindgren <tony@atomide.com>
> ---
>  .../devicetree/bindings/pinctrl/ti,iodelay.txt     |  47 +

Acked-by: Rob Herring <robh@kernel.org>

>  drivers/pinctrl/Kconfig                            |   1 +
>  drivers/pinctrl/Makefile                           |   1 +
>  drivers/pinctrl/ti/Kconfig                         |  10 +
>  drivers/pinctrl/ti/Makefile                        |   1 +
>  drivers/pinctrl/ti/pinctrl-ti-iodelay.c            | 947 +++++++++++++++++++++
>  6 files changed, 1007 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt
>  create mode 100644 drivers/pinctrl/ti/Kconfig
>  create mode 100644 drivers/pinctrl/ti/Makefile
>  create mode 100644 drivers/pinctrl/ti/pinctrl-ti-iodelay.c

^ permalink raw reply

* Re: [RFC PATCH] usb: dwc3: host: add support for OTG in DWC3 host driver
From: Felipe Balbi @ 2017-01-04 13:32 UTC (permalink / raw)
  To: Manish Narani, robh+dt, mark.rutland, catalin.marinas,
	will.deacon, michal.simek, soren.brinkmann, gregkh, mathias.nyman,
	agraf, bharatku, punnaiah.choudary.kalluri, dhdang, marc.zyngier,
	mnarani, devicetree, linux-arm-kernel, linux-kernel, linux-usb
  Cc: anirudh, anuragku
In-Reply-To: <1483536181-22356-5-git-send-email-mnarani@xilinx.com>

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


Hi,

Manish Narani <manish.narani@xilinx.com> writes:
> This patch adds support for OTG host mode initialization in DWC3
> host driver. Before the host initialization sequence begins. The
> driver has to make sure the no OTG peripheral mode is enabled.
>
> Signed-off-by: Manish Narani <mnarani@xilinx.com>
> ---
>  drivers/usb/dwc3/host.c | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)
>
> diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
> index 487f0ff..4caa3fe 100644
> --- a/drivers/usb/dwc3/host.c
> +++ b/drivers/usb/dwc3/host.c
> @@ -16,6 +16,8 @@
>   */
>  
>  #include <linux/platform_device.h>
> +#include <linux/usb.h>
> +#include <linux/usb/hcd.h>
>  
>  #include "core.h"
>  
> @@ -111,6 +113,18 @@ int dwc3_host_init(struct dwc3 *dwc)
>  	phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
>  			  dev_name(dwc->dev));
>  
> +	if (dwc->dr_mode == USB_DR_MODE_OTG) {
> +		struct usb_phy *phy;
> +		/* Switch otg to host mode */
> +		phy = usb_get_phy(USB_PHY_TYPE_USB3);
> +		if (!IS_ERR(phy)) {
> +			if (phy && phy->otg)
> +				otg_set_host(phy->otg,
> +						(struct usb_bus *)(long)1);
> +			usb_put_phy(phy);
> +		}
> +	}

NAK. Don't change default mode for everybody. Default mode should
actually be peripheral, but let's not touch whatever HW designer has
set; at least for now.

-- 
balbi

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

^ permalink raw reply

* Re: [RFC PATCH] usb: dwc3: add support for OTG driver compilation
From: Felipe Balbi @ 2017-01-04 13:31 UTC (permalink / raw)
  To: Manish Narani, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, catalin.marinas-5wv7dgnIgG8,
	will.deacon-5wv7dgnIgG8, michal.simek-gjFFaj9aHVfQT0dZR+AlfA,
	soren.brinkmann-gjFFaj9aHVfQT0dZR+AlfA,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	mathias.nyman-ral2JQCrhuEAvxtiuMwx3w, agraf-l3A5Bk7waGM,
	bharatku-gjFFaj9aHVfQT0dZR+AlfA,
	punnaiah.choudary.kalluri-gjFFaj9aHVfQT0dZR+AlfA,
	dhdang-qTEPVZfXA3Y, marc.zyngier-5wv7dgnIgG8,
	mnarani-gjFFaj9aHVfQT0dZR+AlfA, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA
  Cc: anirudh-gjFFaj9aHVfQT0dZR+AlfA, anuragku-gjFFaj9aHVfQT0dZR+AlfA
In-Reply-To: <1483536181-22356-2-git-send-email-mnarani-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org>

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


Hi,

Manish Narani <manish.narani-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org> writes:
> This patch adds support for OTG driver compilation and object
> file creation
>
> Signed-off-by: Manish Narani <mnarani-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org>
> ---
>  drivers/usb/dwc3/Makefile | 4 ++++
>  1 file changed, 4 insertions(+)
>
> diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
> index ffca340..2d269ad 100644
> --- a/drivers/usb/dwc3/Makefile
> +++ b/drivers/usb/dwc3/Makefile
> @@ -17,6 +17,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
>  	dwc3-y				+= gadget.o ep0.o
>  endif
>  
> +ifneq ($(CONFIG_USB_DWC3_DUAL_ROLE),)
> +	dwc3-y				+= otg.o
> +endif

you just broke compilation

-- 
balbi

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

^ permalink raw reply

* Re: [RFC PATCH] arch: arm64: dts: add USB OTG interrupts support in ZynqMP device tree
From: Felipe Balbi @ 2017-01-04 13:30 UTC (permalink / raw)
  To: Manish Narani, robh+dt, mark.rutland, catalin.marinas,
	will.deacon, michal.simek, soren.brinkmann, gregkh, mathias.nyman,
	agraf, bharatku, punnaiah.choudary.kalluri, dhdang, marc.zyngier,
	mnarani, devicetree, linux-arm-kernel, linux-kernel, linux-usb
  Cc: anirudh, anuragku
In-Reply-To: <1483536181-22356-1-git-send-email-mnarani@xilinx.com>

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


Hi,

Manish Narani <manish.narani@xilinx.com> writes:
> This patch adds OTG interrupt support in device tree. It will add
> an extra interrupt line number dedicated to OTG events. This will
> enable OTG interrupts to serve in DWC3 OTG driver.
>
> Signed-off-by: Manish Narani <mnarani@xilinx.com>
> ---
>  arch/arm64/boot/dts/xilinx/zynqmp.dtsi | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
> index 68a90833..ce9ad02 100644
> --- a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
> +++ b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
> @@ -351,7 +351,7 @@
>  			compatible = "snps,dwc3";
>  			status = "disabled";
>  			interrupt-parent = <&gic>;
> -			interrupts = <0 65 4>;
> +			interrupts = <0 65 4>, <0 69 4>;

help the driver by passing names to these IRQ lines.

-- 
balbi

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

^ permalink raw reply

* RE: [PATCH v3 2/3] dmaeninge: xilinx_dma: Fix bug in multiple frame stores scenario in vdma
From: Appana Durga Kedareswara Rao @ 2017-01-04 13:30 UTC (permalink / raw)
  To: Jose Abreu, robh+dt@kernel.org, mark.rutland@arm.com,
	dan.j.williams@intel.com, vinod.koul@intel.com,
	michal.simek@xilinx.com, Soren Brinkmann,
	moritz.fischer@ettus.com, laurent.pinchart@ideasonboard.com,
	luis@debethencourt.com
  Cc: dmaengine@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, devicetree@vger.kernel.org
In-Reply-To: <df3662de-fa3b-b33e-ebf5-88802aea5c49@synopsys.com>

Hi 

	Thanks for the review...
> 
> Hi Kedar,
> 
> 
> On 04-01-2017 06:54, Kedareswara rao Appana wrote:
> > When VDMA is configured for more than one frame in the h/w for example
> > h/w is configured for n number of frames and user Submits n number of
> > frames and triggered the DMA using issue_pending API.
> > In the current driver flow we are submitting one frame at a time but
> > we should submit all the n number of frames at one time as the h/w Is
> > configured for n number of frames.
> >
> > This patch fixes this issue.
> >
> > Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
> 
> Looks fine. I have a couple of minor comments, if you address them you can add
> "Reviewed-by: Jose Abreu <joabreu@synopsys.com>"
> in next version.

Thanks...
Sure will fix the comments and will add your' s Reviewed-by....

[snip]
> > +	/*
> > +	 * Note: When VDMA is built with default h/w configuration
> > +	 * On the S2MM(recv) side user should submit frames upto
> > +	 * H/W configured. If users submits less than h/w configured
> > +	 * VDMA engine tries to write to a invalid location
> > +	 * Results undefined behaviour/memory corruption.
> > +	 *
> > +	 * If user would like to submit frames less than h/w capable
> > +	 * On S2MM side please enable debug info 13 at the h/w level
> > +	 * It will allows the frame buffers numbers to be modified at runtime.
> > +	 */
> > +	if (!chan->has_fstoreconfig && chan->direction == DMA_DEV_TO_MEM
> &&
> > +	    chan->desc_pendingcount < chan->num_frms) {
> > +		dev_dbg(chan->dev, "Frame Store Configuration is not enabled
> at the");
> > +		dev_dbg(chan->dev, " H/w level enable Debug info 13 at the
> h/w level");
> > +		dev_dbg(chan->dev, " OR Submit the frames upto h/w
> Capable\n\r");
> > +
> > +		return;
> > +	}
> 
> Hmm, may dev_warn would be more suitable because with dev_dbg and no
> dynamic debug enabled user will not know what happened. Also, I am aware
> that in direction DMA_MEM_TO_DEV there will be no corruption in PC side but it
> will be corruption in VDMA side because it will read from invalid memory
> locations. Maybe drop the check for channel direction.

Sure will fix it in the next version....

> 
> I am also not fancy about dropping prints that are not grep'able (you do not
> break line in each print so a user searching for the whole string will not find it).
> Try to do a line break in each print or change the string to be smaller.
> 

Sure will add a line break in each print in the next version...

Regards,
Kedar.

> Best regards,
> Jose Miguel Abreu
> 

^ permalink raw reply

* Re: [PATCH V5 3/3] cfg80211: support ieee80211-freq-limit DT property
From: Johannes Berg @ 2017-01-04 13:26 UTC (permalink / raw)
  To: Rafał Miłecki, linux-wireless-u79uwXL29TY76Z2rM5mHXA
  Cc: Martin Blumenstingl, Felix Fietkau, Arend van Spriel,
	Arnd Bergmann, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Rafał Miłecki
In-Reply-To: <20170103225715.14072-3-zajec5-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>


> V4: Move code to of.c
>     Use one helper called at init time (no runtime hooks)
>     Modify orig_flags


> +/**
> + * wiphy_read_of_freq_limits - read frequency limits from device
> tree
> + *
> + * @wiphy: the wireless device to get extra limits for
> + *
> + * Some devices may have extra limitations specified in DT. This may
> be useful
> + * for chipsets that normally support more bands but are limited due
> to board
> + * design (e.g. by antennas or extermal power amplifier).
> + *
> + * This function reads info from DT and uses it to *modify* channels
> (disable
> + * unavailable ones). It's usually a *bad* idea to use it in drivers
> with
> + * shared channel data as DT limitations are device specific.
> + *
> + * As this function access device node it has to be called after
> set_wiphy_dev.
> + * It also modifies channels so they have to be set first.
> + */

It should also be called before wiphy_register(), I think? And I
suppose you should add a comment about not being able to use shared
channels.

> +				pr_debug("Disabling freq %d MHz as
> it's out of OF limits\n",
> +					 chan->center_freq);
> +				chan->orig_flags |=
> IEEE80211_CHAN_DISABLED;
> 
But just setting orig_flags also won't work, since it'd be overwritten
again by wiphy_register(), no?

johannes

^ permalink raw reply

* Re: [PATCH v3] net: ethernet: faraday: To support device tree usage.
From: Arnd Bergmann @ 2017-01-04 13:23 UTC (permalink / raw)
  To: Greentime Hu
  Cc: Florian Fainelli, netdev, devicetree, Andrew Lunn, linux-kernel,
	Jiri Pirko, jonas.jensen, davem
In-Reply-To: <CAEbi=3f7pf59YjNha-bpK+GZw4BSdevngi=fekd9uNLMVXDVnQ@mail.gmail.com>

On Wednesday, January 4, 2017 9:49:51 AM CET Greentime Hu wrote:
> On Tue, Jan 3, 2017 at 9:24 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> 
> > On Tuesday, January 3, 2017 2:05:47 PM CET Greentime Hu wrote:
> > > ​I am not sure if atmac and moxa-art are exactly hardware compatible
> > though
> > > they are based on faraday ftmac.
> > > It may be better if we use 2 different device tree binding documents to
> > > describe for these 2 different drivers to use.
> >
> > They are probably slightly different, but close enough to have the same
> > binding document, as there is no technical reason to have two separate
> > drivers for them. The binding should be about the hardware type, not the
> > way that Linux currently implements the drivers.
> >
> >         Arnd
> >
> >
> OK.
> 
> How about this?
> 
> rename
> Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
> to
> Documentation/devicetree/bindings/net/faraday,ftmac.txt
> 
> and the content to
> Faraday Ethernet Controller

Sounds good. Note that you can use 'git patch -M' to produce
this as a renaming patch.

> 
> Required properties:
> 
> - compatible : Must be "moxa,moxart-mac" or "andestech,atmac" or
> "faraday,ftmac"

I'd write this as

	compatible: Must contain "faraday,ftmac", as well as one of
	            the SoC specific identifiers:
			"andestec,atmac"
			"moxa,moxart-mac"

This makes it easier to extend, plus it makes the generic string
mandatory.

	Arnd

^ permalink raw reply

* [RFC PATCH] usb: host: xhci: plat: add support for otg_set_host() call
From: Manish Narani @ 2017-01-04 13:23 UTC (permalink / raw)
  To: robh+dt, mark.rutland, catalin.marinas, will.deacon, michal.simek,
	soren.brinkmann, balbi, gregkh, mathias.nyman, agraf, bharatku,
	punnaiah.choudary.kalluri, dhdang, marc.zyngier, mnarani,
	devicetree, linux-arm-kernel, linux-kernel, linux-usb
  Cc: anirudh, anuragku
In-Reply-To: <1483536181-22356-1-git-send-email-mnarani@xilinx.com>

This patch will add support for OTG host initialization. This will
help OTG drivers to populate their host subsystem.

Signed-off-by: Manish Narani <mnarani@xilinx.com>
---
 drivers/usb/host/xhci-plat.c | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index ddfab30..aa08bdd 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -19,6 +19,7 @@
 #include <linux/usb/phy.h>
 #include <linux/slab.h>
 #include <linux/acpi.h>
+#include <linux/usb/otg.h>
 
 #include "xhci.h"
 #include "xhci-plat.h"
@@ -144,6 +145,37 @@ static const struct of_device_id usb_xhci_of_match[] = {
 MODULE_DEVICE_TABLE(of, usb_xhci_of_match);
 #endif
 
+static int usb_otg_set_host(struct device *dev, struct usb_hcd *hcd, bool yes)
+{
+	int ret = 0;
+
+	hcd->usb_phy = usb_get_phy(USB_PHY_TYPE_USB3);
+	if (!IS_ERR_OR_NULL(hcd->usb_phy) && hcd->usb_phy->otg) {
+		dev_dbg(dev, "%s otg support available\n", __func__);
+		if (yes) {
+			if (otg_set_host(hcd->usb_phy->otg, &hcd->self)) {
+				dev_err(dev, "%s otg_set_host failed\n",
+						__func__);
+				usb_put_phy(hcd->usb_phy);
+				goto disable_phy;
+			}
+		} else {
+			ret = otg_set_host(hcd->usb_phy->otg, NULL);
+			usb_put_phy(hcd->usb_phy);
+			goto disable_phy;
+		}
+
+	} else
+		goto disable_phy;
+
+	return 0;
+
+disable_phy:
+	hcd->usb_phy = NULL;
+
+	return ret;
+}
+
 static int xhci_plat_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *match;
@@ -255,6 +287,11 @@ static int xhci_plat_probe(struct platform_device *pdev)
 	if (ret)
 		goto dealloc_usb2_hcd;
 
+	ret = usb_otg_set_host(&pdev->dev, hcd, 1);
+	if (ret)
+		goto dealloc_usb2_hcd;
+
+
 	return 0;
 
 
@@ -283,6 +320,8 @@ static int xhci_plat_remove(struct platform_device *dev)
 	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
 	struct clk *clk = xhci->clk;
 
+	usb_otg_set_host(&dev->dev, hcd, 0);
+
 	usb_remove_hcd(xhci->shared_hcd);
 	usb_phy_shutdown(hcd->usb_phy);
 
-- 
2.1.1

^ permalink raw reply related

* [RFC PATCH] usb: dwc3: otg: Adding OTG driver for DWC3 controller
From: Manish Narani @ 2017-01-04 13:23 UTC (permalink / raw)
  To: robh+dt, mark.rutland, catalin.marinas, will.deacon, michal.simek,
	soren.brinkmann, balbi, gregkh, mathias.nyman, agraf, bharatku,
	punnaiah.choudary.kalluri, dhdang, marc.zyngier, mnarani,
	devicetree, linux-arm-kernel, linux-kernel, linux-usb
  Cc: anirudh, anuragku
In-Reply-To: <1483536181-22356-1-git-send-email-mnarani@xilinx.com>

This driver will add support for USB 2.0 OTG and USB 3.0 DRD in
DWC3 framework.
This OTG driver supports host/peripheral modes on run time. This
driver will enable HNP and SRP support in High-Speed mode.

Signed-off-by: Manish Narani <mnarani@xilinx.com>
---
 drivers/usb/dwc3/otg.c | 2064 ++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/dwc3/otg.h |  247 ++++++
 2 files changed, 2311 insertions(+)
 create mode 100644 drivers/usb/dwc3/otg.c
 create mode 100644 drivers/usb/dwc3/otg.h

diff --git a/drivers/usb/dwc3/otg.c b/drivers/usb/dwc3/otg.c
new file mode 100644
index 0000000..4f2aa891
--- /dev/null
+++ b/drivers/usb/dwc3/otg.c
@@ -0,0 +1,2064 @@
+/**
+ * otg.c - DesignWare USB3 DRD Controller OTG file
+ *
+ * Copyright (C) 2016 Xilinx, Inc. All rights reserved.
+ *
+ * Author:  Manish Narani <mnarani@xilinx.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2  of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include <linux/version.h>
+#include <linux/sysfs.h>
+
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/phy.h>
+
+#include <../drivers/usb/host/xhci.h>
+#include "core.h"
+#include "gadget.h"
+#include "io.h"
+#include "otg.h"
+
+#include <linux/ulpi/regs.h>
+#include <linux/ulpi/driver.h>
+#include "debug.h"
+
+/* Print the hardware registers' value for debugging purpose */
+static void print_debug_regs(struct dwc3_otg *otg)
+{
+	u32 gctl = otg_read(otg, DWC3_GCTL);
+	u32 gsts = otg_read(otg, DWC3_GSTS);
+	u32 gdbgltssm = otg_read(otg, DWC3_GDBGLTSSM);
+	u32 gusb2phycfg0 = otg_read(otg, DWC3_GUSB2PHYCFG(0));
+	u32 gusb3pipectl0 = otg_read(otg, DWC3_GUSB3PIPECTL(0));
+	u32 dcfg = otg_read(otg, DWC3_DCFG);
+	u32 dctl = otg_read(otg, DWC3_DCTL);
+	u32 dsts = otg_read(otg, DWC3_DSTS);
+	u32 ocfg = otg_read(otg, OCFG);
+	u32 octl = otg_read(otg, OCTL);
+	u32 oevt = otg_read(otg, OEVT);
+	u32 oevten = otg_read(otg, OEVTEN);
+	u32 osts = otg_read(otg, OSTS);
+
+	otg_info(otg, "gctl = %08x\n", gctl);
+	otg_info(otg, "gsts = %08x\n", gsts);
+	otg_info(otg, "gdbgltssm = %08x\n", gdbgltssm);
+	otg_info(otg, "gusb2phycfg0 = %08x\n", gusb2phycfg0);
+	otg_info(otg, "gusb3pipectl0 = %08x\n", gusb3pipectl0);
+	otg_info(otg, "dcfg = %08x\n", dcfg);
+	otg_info(otg, "dctl = %08x\n", dctl);
+	otg_info(otg, "dsts = %08x\n", dsts);
+	otg_info(otg, "ocfg = %08x\n", ocfg);
+	otg_info(otg, "octl = %08x\n", octl);
+	otg_info(otg, "oevt = %08x\n", oevt);
+	otg_info(otg, "oevten = %08x\n", oevten);
+	otg_info(otg, "osts = %08x\n", osts);
+}
+
+/* Check whether the hardware supports HNP or not */
+static int hnp_capable(struct dwc3_otg *otg)
+{
+	if (otg->hwparams6 & GHWPARAMS6_HNP_SUPPORT_ENABLED)
+		return 1;
+	return 0;
+}
+
+/* Check whether the hardware supports SRP or not */
+static int srp_capable(struct dwc3_otg *otg)
+{
+	if (otg->hwparams6 & GHWPARAMS6_SRP_SUPPORT_ENABLED)
+		return 1;
+	return 0;
+}
+
+/* Wakeup main thread to execute the OTG flow after an event */
+static void wakeup_main_thread(struct dwc3_otg *otg)
+{
+	if (!otg->main_thread)
+		return;
+
+	otg_vdbg(otg, "\n");
+	/* Tell the main thread that something has happened */
+	otg->main_wakeup_needed = 1;
+	wake_up_interruptible(&otg->main_wq);
+}
+
+/* Sleep main thread for 'msecs' to wait for an event to occur */
+static int sleep_main_thread_timeout(struct dwc3_otg *otg, int msecs)
+{
+	signed long jiffies;
+	int rc = msecs;
+
+	if (signal_pending(current)) {
+		otg_dbg(otg, "Main thread signal pending\n");
+		rc = -EINTR;
+		goto done;
+	}
+	if (otg->main_wakeup_needed) {
+		otg_dbg(otg, "Main thread wakeup needed\n");
+		rc = msecs;
+		goto done;
+	}
+
+	jiffies = msecs_to_jiffies(msecs);
+	rc = wait_event_freezable_timeout(otg->main_wq,
+					  otg->main_wakeup_needed,
+					  jiffies);
+
+	if (rc > 0)
+		rc = jiffies_to_msecs(rc);
+
+done:
+	otg->main_wakeup_needed = 0;
+	return rc;
+}
+
+/* Sleep main thread to wait for an event to occur */
+static int sleep_main_thread(struct dwc3_otg *otg)
+{
+	int rc;
+
+	do {
+		rc = sleep_main_thread_timeout(otg, 5000);
+	} while (rc == 0);
+
+	return rc;
+}
+
+static void get_events(struct dwc3_otg *otg, u32 *otg_events, u32 *user_events)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&otg->lock, flags);
+
+	if (otg_events)
+		*otg_events = otg->otg_events;
+
+	if (user_events)
+		*user_events = otg->user_events;
+
+	spin_unlock_irqrestore(&otg->lock, flags);
+}
+
+static void get_and_clear_events(struct dwc3_otg *otg, u32 *otg_events,
+		u32 *user_events)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&otg->lock, flags);
+
+	if (otg_events)
+		*otg_events = otg->otg_events;
+
+	if (user_events)
+		*user_events = otg->user_events;
+
+	otg->otg_events = 0;
+	otg->user_events = 0;
+
+	spin_unlock_irqrestore(&otg->lock, flags);
+}
+
+static int check_event(struct dwc3_otg *otg, u32 otg_mask, u32 user_mask)
+{
+	u32 otg_events;
+	u32 user_events;
+
+	get_events(otg, &otg_events, &user_events);
+	if ((otg_events & otg_mask) || (user_events & user_mask)) {
+		otg_dbg(otg, "Event occurred: otg_events=%x, otg_mask=%x, ",
+				otg_events, otg_mask);
+		otg_dbg(otg, "user_events=%x, user_mask=%x\n",
+				user_events, user_mask);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int sleep_until_event(struct dwc3_otg *otg, u32 otg_mask, u32 user_mask,
+		u32 *otg_events, u32 *user_events, int timeout)
+{
+	int rc;
+
+	/* Enable the events */
+	if (otg_mask)
+		otg_write(otg, OEVTEN, otg_mask);
+
+	/* Wait until it occurs, or timeout, or interrupt. */
+	if (timeout) {
+		otg_vdbg(otg, "Waiting for event (timeout=%d)...\n", timeout);
+		rc = sleep_main_thread_until_condition_timeout(otg,
+			check_event(otg, otg_mask, user_mask), timeout);
+	} else {
+		otg_vdbg(otg, "Waiting for event (no timeout)...\n");
+		rc = sleep_main_thread_until_condition(otg,
+			check_event(otg, otg_mask, user_mask));
+	}
+
+	/* Disable the events */
+	otg_write(otg, OEVTEN, 0);
+
+	otg_vdbg(otg, "Woke up rc=%d\n", rc);
+	if (rc >= 0)
+		get_and_clear_events(otg, otg_events, user_events);
+
+	return rc;
+}
+
+static void set_capabilities(struct dwc3_otg *otg)
+{
+	u32 ocfg = 0;
+
+	otg_dbg(otg, "\n");
+	if (srp_capable(otg))
+		ocfg |= OCFG_SRP_CAP;
+
+	if (hnp_capable(otg))
+		ocfg |= OCFG_HNP_CAP;
+
+	otg_write(otg, OCFG, ocfg);
+
+	otg_dbg(otg, "Enabled SRP and HNP capabilities in OCFG\n");
+}
+
+static int otg3_handshake(struct dwc3_otg *otg, u32 reg, u32 mask, u32 done,
+		u32 msec)
+{
+	u32 result;
+	u32 usec = msec * 1000;
+
+	otg_vdbg(otg, "reg=%08x, mask=%08x, value=%08x\n", reg, mask, done);
+	do {
+		result = otg_read(otg, reg);
+		if ((result & mask) == done)
+			return 1;
+		udelay(1);
+		usec -= 1;
+	} while (usec > 0);
+
+	return 0;
+}
+
+static int reset_port(struct dwc3_otg *otg)
+{
+	otg_dbg(otg, "\n");
+	if (!otg->otg.host)
+		return -ENODEV;
+	return usb_bus_start_enum(otg->otg.host, 1);
+}
+
+static int set_peri_mode(struct dwc3_otg *otg, int mode)
+{
+	u32 octl;
+
+	/* Set peri_mode */
+	octl = otg_read(otg, OCTL);
+	if (mode)
+		octl |= OCTL_PERI_MODE;
+	else
+		octl &= ~OCTL_PERI_MODE;
+
+	otg_write(otg, OCTL, octl);
+	otg_dbg(otg, "set OCTL PERI_MODE = %d in OCTL\n", mode);
+
+	if (mode)
+		return otg3_handshake(otg, OSTS, OSTS_PERIP_MODE,
+				OSTS_PERIP_MODE, 100);
+	else
+		return otg3_handshake(otg, OSTS, OSTS_PERIP_MODE, 0, 100);
+
+	msleep(20);
+}
+
+static int start_host(struct dwc3_otg *otg)
+{
+	int ret = -ENODEV;
+	int flg;
+	u32 octl;
+	u32 osts;
+	u32 dctl;
+	u32 event_addr;
+	struct usb_hcd *hcd;
+	struct xhci_hcd *xhci;
+
+	otg_dbg(otg, "\n");
+
+	if (!otg->otg.host)
+		return -ENODEV;
+
+	dctl = otg_read(otg, DCTL);
+	if (dctl & DWC3_DCTL_RUN_STOP) {
+		otg_dbg(otg, "Disabling the RUN/STOP bit\n");
+		dctl &= ~DWC3_DCTL_RUN_STOP;
+		otg_write(otg, DCTL, dctl);
+	}
+
+	event_addr = dwc3_readl(otg->dwc->regs, DWC3_GEVNTADRLO(0));
+	if (event_addr != 0x0) {
+		otg_dbg(otg, "Freeing the device event buffers\n");
+		dwc3_free_event_buffers(otg->dwc);
+	}
+
+	if (!set_peri_mode(otg, PERI_MODE_HOST)) {
+		otg_err(otg, "Failed to start host\n");
+		return -EINVAL;
+	}
+
+	hcd = container_of(otg->otg.host, struct usb_hcd, self);
+	xhci = hcd_to_xhci(hcd);
+	otg_dbg(otg, "hcd=%p xhci=%p\n", hcd, xhci);
+
+	if (otg->host_started) {
+		otg_info(otg, "Host already started\n");
+		goto skip;
+	}
+
+	/* Start host driver */
+
+	*(struct xhci_hcd **)hcd->hcd_priv = xhci;
+	otg_dbg(otg, "1- calling usb_add_hcd() irq=%d\n", otg->hcd_irq);
+	ret = usb_add_hcd(hcd, otg->hcd_irq, IRQF_SHARED);
+	if (ret) {
+		otg_err(otg, "%s: failed to start primary hcd, ret=%d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	*(struct xhci_hcd **)xhci->shared_hcd->hcd_priv = xhci;
+	if (xhci->shared_hcd) {
+		otg_dbg(otg, "2- calling usb_add_hcd() irq=%d\n", otg->hcd_irq);
+		ret = usb_add_hcd(xhci->shared_hcd, otg->hcd_irq, IRQF_SHARED);
+		if (ret) {
+			otg_err(otg,
+				"%s: failed to start secondary hcd, ret=%d\n",
+				__func__, ret);
+			usb_remove_hcd(hcd);
+			return ret;
+		}
+	}
+
+	otg->host_started = 1;
+skip:
+	hcd->self.otg_port = 1;
+	if (xhci->shared_hcd)
+		xhci->shared_hcd->self.otg_port = 1;
+
+	set_capabilities(otg);
+
+	/* Power the port only for A-host */
+	if (otg->otg.state == OTG_STATE_A_WAIT_VRISE) {
+		/* Spin on xhciPrtPwr bit until it becomes 1 */
+		osts = otg_read(otg, OSTS);
+		flg = otg3_handshake(otg, OSTS,
+				OSTS_XHCI_PRT_PWR,
+				OSTS_XHCI_PRT_PWR,
+				1000);
+		if (flg) {
+			otg_dbg(otg, "Port is powered by xhci-hcd\n");
+			/* Set port power control bit */
+			octl = otg_read(otg, OCTL);
+			octl |= OCTL_PRT_PWR_CTL;
+			otg_write(otg, OCTL, octl);
+		} else {
+			otg_dbg(otg, "Port is not powered by xhci-hcd\n");
+		}
+	}
+
+	return ret;
+}
+
+static int stop_host(struct dwc3_otg *otg)
+{
+	struct usb_hcd *hcd;
+	struct xhci_hcd *xhci;
+
+	otg_dbg(otg, "\n");
+
+	if (!otg->host_started) {
+		otg_info(otg, "Host already stopped\n");
+		return 1;
+	}
+
+	if (!otg->otg.host)
+		return -ENODEV;
+
+	otg_dbg(otg, "%s: turn off host %s\n",
+		__func__, otg->otg.host->bus_name);
+
+	hcd = container_of(otg->otg.host, struct usb_hcd, self);
+	xhci = hcd_to_xhci(hcd);
+
+	if (xhci->shared_hcd)
+		usb_remove_hcd(xhci->shared_hcd);
+	usb_remove_hcd(hcd);
+
+	otg->host_started = 0;
+	otg->dev_enum = 0;
+	return 0;
+}
+
+int dwc3_otg_host_release(struct usb_hcd *hcd)
+{
+	struct usb_bus *bus;
+	struct usb_device *rh;
+	struct usb_device *udev;
+
+	if (!hcd)
+		return -EINVAL;
+
+	bus = &hcd->self;
+	if (!bus->otg_port)
+		return 0;
+
+	rh = bus->root_hub;
+	udev = usb_hub_find_child(rh, bus->otg_port);
+	if (!udev)
+		return 0;
+
+	if (udev->config && udev->parent == udev->bus->root_hub) {
+		struct usb_otg20_descriptor *desc;
+
+		if (__usb_get_extra_descriptor(udev->rawdescriptors[0],
+				le16_to_cpu(udev->config[0].desc.wTotalLength),
+				USB_DT_OTG, (void **) &desc) == 0) {
+			int err;
+
+			dev_info(&udev->dev, "found OTG descriptor\n");
+			if ((desc->bcdOTG >= 0x0200) &&
+			    (udev->speed == USB_SPEED_HIGH)) {
+				err = usb_control_msg(udev,
+						usb_sndctrlpipe(udev, 0),
+						USB_REQ_SET_FEATURE, 0,
+						USB_DEVICE_TEST_MODE,
+						7 << 8,
+						NULL, 0, USB_CTRL_SET_TIMEOUT);
+				if (err < 0) {
+					dev_info(&udev->dev,
+						"can't initiate HNP from host: %d\n",
+						err);
+					return -1;
+				}
+			}
+		} else {
+			dev_info(&udev->dev, "didn't find OTG descriptor\n");
+		}
+	} else {
+		dev_info(&udev->dev,
+			 "udev->config NULL or udev->parent != udev->bus->root_hub\n");
+	}
+
+	return 0;
+}
+
+/* Sends the host release set feature request */
+static void host_release(struct dwc3_otg *otg)
+{
+	struct usb_hcd *hcd;
+	struct xhci_hcd *xhci;
+
+	otg_dbg(otg, "\n");
+	if (!otg->otg.host)
+		return;
+	hcd = container_of(otg->otg.host, struct usb_hcd, self);
+	xhci = hcd_to_xhci(hcd);
+	dwc3_otg_host_release(hcd);
+	if (xhci->shared_hcd)
+		dwc3_otg_host_release(xhci->shared_hcd);
+}
+
+static void start_peripheral(struct dwc3_otg *otg)
+{
+	struct usb_gadget *gadget = otg->otg.gadget;
+	u32 event_addr;
+
+	otg_dbg(otg, "\n");
+	if (!gadget)
+		return;
+
+	if (!set_peri_mode(otg, PERI_MODE_PERIPHERAL))
+		otg_err(otg, "Failed to set peripheral mode\n");
+
+	if (otg->peripheral_started) {
+		otg_info(otg, "Peripheral already started\n");
+		return;
+	}
+
+	event_addr = dwc3_readl(otg->dwc->regs, DWC3_GEVNTADRLO(0));
+	if (event_addr == 0x0) {
+		int ret;
+
+		otg_dbg(otg, "allocating the event buffer\n");
+		ret = dwc3_alloc_event_buffers(otg->dwc,
+				DWC3_EVENT_BUFFERS_SIZE);
+		if (ret) {
+			dev_err(otg->dwc->dev,
+					"failed to allocate event buffers\n");
+		}
+		otg_dbg(otg, "setting up event buffers\n");
+		dwc3_event_buffers_setup(otg->dwc);
+	}
+
+	otg->peripheral_started = 1;
+
+	gadget->ops->udc_start(gadget, NULL);
+
+	gadget->b_hnp_enable = 0;
+	gadget->host_request_flag = 0;
+
+	otg_write(otg, DCTL, otg_read(otg, DCTL) | DCTL_RUN_STOP);
+	otg_dbg(otg, "Setting DCTL_RUN_STOP to 1 in DCTL\n");
+
+	msleep(20);
+}
+
+static void stop_peripheral(struct dwc3_otg *otg)
+{
+	struct usb_gadget *gadget = otg->otg.gadget;
+
+	otg_dbg(otg, "\n");
+
+	if (!otg->peripheral_started) {
+		otg_info(otg, "Peripheral already stopped\n");
+		return;
+	}
+
+	if (!gadget)
+		return;
+
+	gadget->ops->udc_stop(gadget);
+	otg->peripheral_started = 0;
+	msleep(20);
+}
+
+static void set_b_host(struct dwc3_otg *otg, int val)
+{
+	otg->otg.host->is_b_host = val;
+}
+
+static enum usb_otg_state do_b_idle(struct dwc3_otg *otg);
+
+static int init_b_device(struct dwc3_otg *otg)
+{
+	otg_dbg(otg, "\n");
+	set_capabilities(otg);
+
+	if (!set_peri_mode(otg, PERI_MODE_PERIPHERAL))
+		otg_err(otg, "Failed to start peripheral\n");
+
+	return do_b_idle(otg);
+}
+
+static int init_a_device(struct dwc3_otg *otg)
+{
+	otg_write(otg, OCFG, 0);
+	otg_write(otg, OCTL, 0);
+
+	otg_dbg(otg, "Write 0 to OCFG and OCTL\n");
+	return OTG_STATE_A_IDLE;
+}
+
+static enum usb_otg_state do_connector_id_status(struct dwc3_otg *otg)
+{
+	enum usb_otg_state state;
+	u32 osts;
+
+	otg_dbg(otg, "\n");
+
+	otg_write(otg, OCFG, 0);
+	otg_write(otg, OEVTEN, 0);
+	otg_write(otg, OEVT, 0xffffffff);
+	otg_write(otg, OEVTEN, OEVT_CONN_ID_STS_CHNG_EVNT);
+
+	msleep(60);
+
+	osts = otg_read(otg, OSTS);
+	if (!(osts & OSTS_CONN_ID_STS)) {
+		otg_dbg(otg, "Connector ID is A\n");
+		state = init_a_device(otg);
+	} else {
+		otg_dbg(otg, "Connector ID is B\n");
+		stop_host(otg);
+		state = init_b_device(otg);
+	}
+
+	/* TODO: This is a workaround for latest hibernation-enabled bitfiles
+	 * which have problems before initializing SRP.
+	 */
+	msleep(50);
+
+	return state;
+}
+
+static void reset_hw(struct dwc3_otg *otg)
+{
+	u32 temp;
+
+	otg_dbg(otg, "\n");
+
+	otg_write(otg, OEVTEN, 0);
+	temp = otg_read(otg, OCTL);
+	temp &= OCTL_PERI_MODE;
+	otg_write(otg, OCTL, temp);
+	temp = otg_read(otg, GCTL);
+	temp |= GCTL_PRT_CAP_DIR_OTG << GCTL_PRT_CAP_DIR_SHIFT;
+	otg_write(otg, GCTL, temp);
+}
+
+#define SRP_TIMEOUT			6000
+
+static void start_srp(struct dwc3_otg *otg)
+{
+	u32 octl;
+
+	octl = otg_read(otg, OCTL);
+	octl |= OCTL_SES_REQ;
+	otg_write(otg, OCTL, octl);
+	otg_dbg(otg, "set OCTL_SES_REQ in OCTL\n");
+}
+
+static void start_b_hnp(struct dwc3_otg *otg)
+{
+	u32 octl;
+
+	octl = otg_read(otg, OCTL);
+	octl |= OCTL_HNP_REQ | OCTL_DEV_SET_HNP_EN;
+	otg_write(otg, OCTL, octl);
+	otg_dbg(otg, "set (OCTL_HNP_REQ | OCTL_DEV_SET_HNP_EN) in OCTL\n");
+}
+
+static void stop_b_hnp(struct dwc3_otg *otg)
+{
+	u32 octl;
+
+	octl = otg_read(otg, OCTL);
+	octl &= ~(OCTL_HNP_REQ | OCTL_DEV_SET_HNP_EN);
+	otg_write(otg, OCTL, octl);
+	otg_dbg(otg, "Clear ~(OCTL_HNP_REQ | OCTL_DEV_SET_HNP_EN) in OCTL\n");
+}
+
+static void start_a_hnp(struct dwc3_otg *otg)
+{
+	u32 octl;
+
+	octl = otg_read(otg, OCTL);
+	octl |= OCTL_HST_SET_HNP_EN;
+	otg_write(otg, OCTL, octl);
+	otg_dbg(otg, "set OCTL_HST_SET_HNP_EN in OCTL\n");
+}
+
+static void stop_a_hnp(struct dwc3_otg *otg)
+{
+	u32 octl;
+
+	octl = otg_read(otg, OCTL);
+	octl &= ~OCTL_HST_SET_HNP_EN;
+	otg_write(otg, OCTL, octl);
+	otg_dbg(otg, "clear OCTL_HST_SET_HNP_EN in OCTL\n");
+}
+
+static enum usb_otg_state do_a_hnp_init(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 otg_events = 0;
+
+	otg_dbg(otg, "");
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_A_DEV_HNP_CHNG_EVNT;
+
+	start_a_hnp(otg);
+	rc = 3000;
+
+again:
+	rc = sleep_until_event(otg,
+			otg_mask, 0,
+			&otg_events, NULL, rc);
+	stop_a_hnp(otg);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	/* Higher priority first */
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+
+	} else if (otg_events & OEVT_A_DEV_HNP_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_A_DEV_HNP_CHNG_EVNT\n");
+		if (otg_events & OEVT_HST_NEG_SCS) {
+			otg_dbg(otg, "A-HNP Success\n");
+			return OTG_STATE_A_PERIPHERAL;
+
+		} else {
+			otg_dbg(otg, "A-HNP Failed\n");
+			return OTG_STATE_A_WAIT_VFALL;
+		}
+
+	} else if (rc == 0) {
+		otg_dbg(otg, "A-HNP Failed (Timed out)\n");
+		return OTG_STATE_A_WAIT_VFALL;
+
+	} else {
+		goto again;
+	}
+
+	/* Invalid state */
+	return OTG_STATE_UNDEFINED;
+}
+
+static enum usb_otg_state do_a_host(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 user_mask;
+	u32 otg_events = 0;
+	u32 user_events = 0;
+
+	otg_dbg(otg, "");
+
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_A_DEV_SESS_END_DET_EVNT;
+	user_mask = USER_SRP_EVENT |
+		USER_HNP_EVENT;
+
+	rc = sleep_until_event(otg,
+			otg_mask, user_mask,
+			&otg_events, &user_events, 0);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	/* Higher priority first */
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+
+	} else if (otg_events & OEVT_A_DEV_SESS_END_DET_EVNT) {
+		otg_dbg(otg, "OEVT_A_DEV_SESS_END_DET_EVNT\n");
+		return OTG_STATE_A_WAIT_VFALL;
+
+	} else if (user_events & USER_HNP_EVENT) {
+		otg_dbg(otg, "USER_HNP_EVENT\n");
+		return OTG_STATE_A_SUSPEND;
+	}
+
+	/* Invalid state */
+	return OTG_STATE_UNDEFINED;
+}
+
+#define A_WAIT_VFALL_TIMEOUT 1000
+
+static enum usb_otg_state do_a_wait_vfall(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 otg_events = 0;
+
+	otg_dbg(otg, "");
+
+	otg_mask = OEVT_A_DEV_IDLE_EVNT;
+
+	rc = A_WAIT_VFALL_TIMEOUT;
+	rc = sleep_until_event(otg,
+			otg_mask, 0,
+			&otg_events, NULL, rc);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	if (otg_events & OEVT_A_DEV_IDLE_EVNT) {
+		otg_dbg(otg, "OEVT_A_DEV_IDLE_EVNT\n");
+		return OTG_STATE_A_IDLE;
+
+	} else if (rc == 0) {
+		otg_dbg(otg, "A_WAIT_VFALL_TIMEOUT\n");
+		return OTG_STATE_A_IDLE;
+	}
+
+	/* Invalid state */
+	return OTG_STATE_UNDEFINED;
+
+}
+
+#define A_WAIT_BCON_TIMEOUT 1000
+
+static enum usb_otg_state do_a_wait_bconn(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 otg_events = 0;
+
+	otg_dbg(otg, "");
+
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_A_DEV_SESS_END_DET_EVNT |
+		OEVT_A_DEV_HOST_EVNT;
+
+	rc = A_WAIT_BCON_TIMEOUT;
+	rc = sleep_until_event(otg,
+			otg_mask, 0,
+			&otg_events, NULL, rc);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	/* Higher priority first */
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+
+	} else if (otg_events & OEVT_A_DEV_SESS_END_DET_EVNT) {
+		otg_dbg(otg, "OEVT_A_DEV_SESS_END_DET_EVNT\n");
+		return OTG_STATE_A_WAIT_VFALL;
+
+	} else if (otg_events & OEVT_A_DEV_HOST_EVNT) {
+		otg_dbg(otg, "OEVT_A_DEV_HOST_EVNT\n");
+		return OTG_STATE_A_HOST;
+
+	} else if (rc == 0) {
+		if (otg_read(otg, OCTL) & OCTL_PRT_PWR_CTL)
+			return OTG_STATE_A_HOST;
+		else
+			return OTG_STATE_A_WAIT_VFALL;
+	}
+
+	/* Invalid state */
+	return OTG_STATE_UNDEFINED;
+}
+
+#define A_WAIT_VRISE_TIMEOUT 100
+
+static enum usb_otg_state do_a_wait_vrise(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 otg_events = 0;
+	struct usb_hcd *hcd;
+	struct xhci_hcd *xhci;
+
+	otg_dbg(otg, "");
+	set_b_host(otg, 0);
+	start_host(otg);
+	hcd = container_of(otg->otg.host, struct usb_hcd, self);
+	xhci = hcd_to_xhci(hcd);
+	usb_kick_hub_wq(hcd->self.root_hub);
+	if (xhci->shared_hcd)
+		usb_kick_hub_wq(xhci->shared_hcd->self.root_hub);
+
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_A_DEV_SESS_END_DET_EVNT;
+
+	rc = A_WAIT_VRISE_TIMEOUT;
+
+	rc = sleep_until_event(otg,
+			otg_mask, 0,
+			&otg_events, NULL, rc);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	/* Higher priority first */
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+
+	} else if (otg_events & OEVT_A_DEV_SESS_END_DET_EVNT) {
+		otg_dbg(otg, "OEVT_A_DEV_SESS_END_DET_EVNT\n");
+		return OTG_STATE_A_WAIT_VFALL;
+
+	} else if (rc == 0) {
+		if (otg_read(otg, OCTL) & OCTL_PRT_PWR_CTL)
+			return OTG_STATE_A_WAIT_BCON;
+		else
+			return OTG_STATE_A_WAIT_VFALL;
+	}
+
+	/* Invalid state */
+	return OTG_STATE_UNDEFINED;
+}
+
+static enum usb_otg_state do_a_idle(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 user_mask;
+	u32 otg_events = 0;
+	u32 user_events = 0;
+
+	otg_dbg(otg, "");
+
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT | OEVT_A_DEV_SRP_DET_EVNT;
+	user_mask = USER_SRP_EVENT;
+
+	rc = sleep_until_event(otg,
+			otg_mask, user_mask,
+			&otg_events, &user_events,
+			0);
+
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+	} else if (otg_events & OEVT_A_DEV_SRP_DET_EVNT) {
+		otg_dbg(otg, "OEVT_A_DEV_SRP_DET_EVNT\n");
+		return OTG_STATE_A_WAIT_VRISE;
+	} else if (user_events & USER_SRP_EVENT) {
+		otg_dbg(otg, "User initiated VBUS\n");
+		return OTG_STATE_A_WAIT_VRISE;
+	}
+
+	return OTG_STATE_UNDEFINED;
+}
+
+static enum usb_otg_state do_a_peripheral(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 otg_events = 0;
+
+	otg_dbg(otg, "");
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_A_DEV_SESS_END_DET_EVNT |
+		OEVT_A_DEV_B_DEV_HOST_END_EVNT;
+
+	rc = sleep_until_event(otg,
+			otg_mask, 0,
+			&otg_events, NULL, 0);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+
+	} else if (otg_events & OEVT_A_DEV_SESS_END_DET_EVNT) {
+		otg_dbg(otg, "OEVT_A_DEV_SESS_END_DET_EVNT\n");
+		return OTG_STATE_A_WAIT_VFALL;
+
+	} else if (otg_events & OEVT_A_DEV_B_DEV_HOST_END_EVNT) {
+		otg_dbg(otg, "OEVT_A_DEV_B_DEV_HOST_END_EVNT\n");
+		return OTG_STATE_A_WAIT_VRISE;
+	}
+
+	return OTG_STATE_UNDEFINED;
+}
+
+#define HNP_TIMEOUT	4000
+
+static enum usb_otg_state do_b_hnp_init(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 events = 0;
+
+	otg_dbg(otg, "");
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_B_DEV_HNP_CHNG_EVNT |
+		OEVT_B_DEV_VBUS_CHNG_EVNT;
+
+	start_b_hnp(otg);
+	rc = HNP_TIMEOUT;
+
+again:
+	rc = sleep_until_event(otg,
+			otg_mask, 0,
+			&events, NULL, rc);
+	stop_b_hnp(otg);
+
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	if (events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+	} else if (events & OEVT_B_DEV_VBUS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_B_DEV_VBUS_CHNG_EVNT\n");
+		return OTG_STATE_B_IDLE;
+	} else if (events & OEVT_B_DEV_HNP_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_B_DEV_HNP_CHNG_EVNT\n");
+		if (events & OEVT_HST_NEG_SCS) {
+			otg_dbg(otg, "B-HNP Success\n");
+			return OTG_STATE_B_WAIT_ACON;
+
+		} else {
+			otg_err(otg, "B-HNP Failed\n");
+			return OTG_STATE_B_PERIPHERAL;
+		}
+	} else if (rc == 0) {
+		/* Timeout */
+		otg_err(otg, "HNP timed out!\n");
+		return OTG_STATE_B_PERIPHERAL;
+
+	} else {
+		goto again;
+	}
+
+	return OTG_STATE_UNDEFINED;
+}
+
+static enum usb_otg_state do_b_peripheral(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 user_mask;
+	u32 otg_events = 0;
+	u32 user_events = 0;
+
+	otg_dbg(otg, "");
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT | OEVT_B_DEV_VBUS_CHNG_EVNT;
+	user_mask = USER_HNP_EVENT | USER_END_SESSION |
+		USER_SRP_EVENT | INITIAL_SRP;
+
+again:
+	rc = sleep_until_event(otg,
+			otg_mask, user_mask,
+			&otg_events, &user_events, 0);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+	} else if (otg_events & OEVT_B_DEV_VBUS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_B_DEV_VBUS_CHNG_EVNT\n");
+
+		if (otg_events & OEVT_B_SES_VLD_EVT) {
+			otg_dbg(otg, "Session valid\n");
+			goto again;
+		} else {
+			otg_dbg(otg, "Session not valid\n");
+			return OTG_STATE_B_IDLE;
+		}
+
+	} else if (user_events & USER_HNP_EVENT) {
+		otg_dbg(otg, "USER_HNP_EVENT\n");
+		return do_b_hnp_init(otg);
+	} else if (user_events & USER_END_SESSION) {
+		otg_dbg(otg, "USER_END_SESSION\n");
+		return OTG_STATE_B_IDLE;
+	}
+
+	return OTG_STATE_UNDEFINED;
+}
+
+static enum usb_otg_state do_b_wait_acon(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 user_mask = 0;
+	u32 otg_events = 0;
+	u32 user_events = 0;
+	struct usb_hcd *hcd;
+	struct xhci_hcd *xhci;
+
+	otg_dbg(otg, "");
+	set_b_host(otg, 1);
+	start_host(otg);
+	otg_mask = OEVT_B_DEV_B_HOST_END_EVNT;
+	otg_write(otg, OEVTEN, otg_mask);
+	reset_port(otg);
+
+	hcd = container_of(otg->otg.host, struct usb_hcd, self);
+	xhci = hcd_to_xhci(hcd);
+	usb_kick_hub_wq(hcd->self.root_hub);
+	if (xhci->shared_hcd)
+		usb_kick_hub_wq(xhci->shared_hcd->self.root_hub);
+
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_B_DEV_B_HOST_END_EVNT |
+		OEVT_B_DEV_VBUS_CHNG_EVNT |
+		OEVT_HOST_ROLE_REQ_INIT_EVNT;
+	user_mask = USER_A_CONN_EVENT;
+
+again:
+	rc = sleep_until_event(otg,
+			otg_mask, user_mask,
+			&otg_events, &user_events, 0);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	/* Higher priority first */
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+	} else if (otg_events & OEVT_B_DEV_B_HOST_END_EVNT) {
+		otg_dbg(otg, "OEVT_B_DEV_B_HOST_END_EVNT\n");
+		return OTG_STATE_B_PERIPHERAL;
+	} else if (otg_events & OEVT_B_DEV_VBUS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_B_DEV_VBUS_CHNG_EVNT\n");
+		if (otg_events & OEVT_B_SES_VLD_EVT) {
+			otg_dbg(otg, "Session valid\n");
+			goto again;
+		} else {
+			otg_dbg(otg, "Session not valid\n");
+			return OTG_STATE_B_IDLE;
+		}
+	} else if (user_events & USER_A_CONN_EVENT) {
+		otg_dbg(otg, "A-device connected\n");
+		return OTG_STATE_B_HOST;
+	}
+
+	/* Invalid state */
+	return OTG_STATE_UNDEFINED;
+}
+
+static enum usb_otg_state do_b_host(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 user_mask = 0;
+	u32 otg_events = 0;
+	u32 user_events = 0;
+
+	otg_dbg(otg, "");
+
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_B_DEV_B_HOST_END_EVNT |
+		OEVT_B_DEV_VBUS_CHNG_EVNT |
+		OEVT_HOST_ROLE_REQ_INIT_EVNT;
+
+again:
+	rc = sleep_until_event(otg,
+			otg_mask, user_mask,
+			&otg_events, &user_events, 0);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	/* Higher priority first */
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+	} else if (otg_events & OEVT_B_DEV_B_HOST_END_EVNT) {
+		otg_dbg(otg, "OEVT_B_DEV_B_HOST_END_EVNT\n");
+		return OTG_STATE_B_PERIPHERAL;
+	} else if (otg_events & OEVT_B_DEV_VBUS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_B_DEV_VBUS_CHNG_EVNT\n");
+		if (otg_events & OEVT_B_SES_VLD_EVT) {
+			otg_dbg(otg, "Session valid\n");
+			goto again;
+		} else {
+			otg_dbg(otg, "Session not valid\n");
+			return OTG_STATE_B_IDLE;
+		}
+	}
+
+	/* Invalid state */
+	return OTG_STATE_UNDEFINED;
+}
+
+static enum usb_otg_state do_b_idle(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 user_mask;
+	u32 otg_events = 0;
+	u32 user_events = 0;
+
+	otg_dbg(otg, "");
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_B_DEV_SES_VLD_DET_EVNT |
+		OEVT_B_DEV_VBUS_CHNG_EVNT;
+	user_mask = USER_SRP_EVENT;
+
+again:
+	rc = sleep_until_event(otg,
+			otg_mask, user_mask,
+			&otg_events, &user_events, 0);
+
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+	} else if ((otg_events & OEVT_B_DEV_VBUS_CHNG_EVNT) ||
+		(otg_events & OEVT_B_DEV_SES_VLD_DET_EVNT)) {
+		otg_dbg(otg, "OEVT_B_DEV_VBUS_CHNG_EVNT\n");
+		if (otg_events & OEVT_B_SES_VLD_EVT) {
+			otg_dbg(otg, "Session valid\n");
+			return OTG_STATE_B_PERIPHERAL;
+
+		} else {
+			otg_dbg(otg, "Session not valid\n");
+			goto again;
+		}
+	} else if (user_events & USER_SRP_EVENT) {
+		otg_dbg(otg, "USER_SRP_EVENT\n");
+		return OTG_STATE_B_SRP_INIT;
+	}
+
+	return OTG_STATE_UNDEFINED;
+}
+
+static enum usb_otg_state do_b_srp_init(struct dwc3_otg *otg)
+{
+	int rc;
+	u32 otg_mask;
+	u32 events = 0;
+
+	otg_dbg(otg, "");
+	otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT |
+		OEVT_B_DEV_SES_VLD_DET_EVNT |
+		OEVT_B_DEV_VBUS_CHNG_EVNT;
+
+	otg_write(otg, OEVTEN, otg_mask);
+	start_srp(otg);
+
+	rc = SRP_TIMEOUT;
+
+again:
+	rc = sleep_until_event(otg,
+			otg_mask, 0,
+			&events, NULL, rc);
+	if (rc < 0)
+		return OTG_STATE_UNDEFINED;
+
+	if (events & OEVT_CONN_ID_STS_CHNG_EVNT) {
+		otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n");
+		return OTG_STATE_UNDEFINED;
+	} else if (events & OEVT_B_DEV_SES_VLD_DET_EVNT) {
+		otg_dbg(otg, "OEVT_B_DEV_SES_VLD_DET_EVNT\n");
+		return OTG_STATE_B_PERIPHERAL;
+	} else if (rc == 0) {
+		otg_dbg(otg, "SRP Timeout (rc=%d)\n", rc);
+		otg_info(otg, "DEVICE NO RESPONSE FOR SRP\n");
+		return OTG_STATE_B_IDLE;
+
+	} else {
+		goto again;
+	}
+
+	return OTG_STATE_UNDEFINED;
+}
+
+int otg_main_thread(void *data)
+{
+	struct dwc3_otg *otg = (struct dwc3_otg *)data;
+	enum usb_otg_state prev = OTG_STATE_UNDEFINED;
+
+#ifdef VERBOSE_DEBUG
+	u32 snpsid = otg_read(otg, 0xc120);
+
+	otg_vdbg(otg, "io_priv=%p\n", otg->regs);
+	otg_vdbg(otg, "c120: %x\n", snpsid);
+#endif
+
+	/* Allow the thread to be killed by a signal, but set the signal mask
+	 * to block everything but INT, TERM, KILL, and USR1.
+	 */
+	allow_signal(SIGINT);
+	allow_signal(SIGTERM);
+	allow_signal(SIGKILL);
+	allow_signal(SIGUSR1);
+
+	/* Allow the thread to be frozen */
+	set_freezable();
+
+	/* Allow host/peripheral driver load to finish */
+	msleep(100);
+
+	reset_hw(otg);
+
+	stop_host(otg);
+	stop_peripheral(otg);
+
+	otg_dbg(otg, "Thread running\n");
+	while (1) {
+		enum usb_otg_state next = OTG_STATE_UNDEFINED;
+
+		otg_vdbg(otg, "\n\n\nMain thread entering state\n");
+
+		switch (otg->otg.state) {
+		case OTG_STATE_UNDEFINED:
+			otg_dbg(otg, "OTG_STATE_UNDEFINED\n");
+			next = do_connector_id_status(otg);
+			break;
+
+		case OTG_STATE_A_IDLE:
+			otg_dbg(otg, "OTG_STATE_A_IDLE\n");
+			stop_peripheral(otg);
+
+			if (prev == OTG_STATE_UNDEFINED)
+				next = OTG_STATE_A_WAIT_VRISE;
+			else
+				next = do_a_idle(otg);
+			break;
+
+		case OTG_STATE_A_WAIT_VRISE:
+			otg_dbg(otg, "OTG_STATE_A_WAIT_VRISE\n");
+			next = do_a_wait_vrise(otg);
+			break;
+
+		case OTG_STATE_A_WAIT_BCON:
+			otg_dbg(otg, "OTG_STATE_A_WAIT_BCON\n");
+			next = do_a_wait_bconn(otg);
+			break;
+
+		case OTG_STATE_A_HOST:
+			otg_dbg(otg, "OTG_STATE_A_HOST\n");
+			stop_peripheral(otg);
+			next = do_a_host(otg);
+			/* Don't stop the host here if we are going into
+			 * A_SUSPEND. We need to delay that until later. It
+			 * will be stopped when coming out of A_SUSPEND
+			 * state.
+			 */
+			if (next != OTG_STATE_A_SUSPEND)
+				stop_host(otg);
+			break;
+
+		case OTG_STATE_A_SUSPEND:
+			otg_dbg(otg, "OTG_STATE_A_SUSPEND\n");
+			next = do_a_hnp_init(otg);
+
+			/* Stop the host. */
+			stop_host(otg);
+			break;
+
+		case OTG_STATE_A_WAIT_VFALL:
+			otg_dbg(otg, "OTG_STATE_A_WAIT_VFALL\n");
+			next = do_a_wait_vfall(otg);
+			stop_host(otg);
+			break;
+
+		case OTG_STATE_A_PERIPHERAL:
+			otg_dbg(otg, "OTG_STATE_A_PERIPHERAL\n");
+			stop_host(otg);
+			start_peripheral(otg);
+			next = do_a_peripheral(otg);
+			stop_peripheral(otg);
+			break;
+
+		case OTG_STATE_B_IDLE:
+			otg_dbg(otg, "OTG_STATE_B_IDLE\n");
+			next = do_b_idle(otg);
+			break;
+
+		case OTG_STATE_B_PERIPHERAL:
+			otg_dbg(otg, "OTG_STATE_B_PERIPHERAL\n");
+			stop_host(otg);
+			start_peripheral(otg);
+			next = do_b_peripheral(otg);
+			stop_peripheral(otg);
+			break;
+
+		case OTG_STATE_B_SRP_INIT:
+			otg_dbg(otg, "OTG_STATE_B_SRP_INIT\n");
+			otg_read(otg, OSTS);
+			next = do_b_srp_init(otg);
+			break;
+
+		case OTG_STATE_B_WAIT_ACON:
+			otg_dbg(otg, "OTG_STATE_B_WAIT_ACON\n");
+			next = do_b_wait_acon(otg);
+			break;
+
+		case OTG_STATE_B_HOST:
+			otg_dbg(otg, "OTG_STATE_B_HOST\n");
+			next = do_b_host(otg);
+			stop_host(otg);
+			break;
+
+		default:
+			otg_err(otg, "Unknown state %d, sleeping...\n",
+					otg->state);
+			sleep_main_thread(otg);
+			break;
+		}
+
+		prev = otg->otg.state;
+		otg->otg.state = next;
+		if (kthread_should_stop())
+			break;
+	}
+
+	otg->main_thread = NULL;
+	otg_dbg(otg, "OTG main thread exiting....\n");
+
+	return 0;
+}
+
+static void start_main_thread(struct dwc3_otg *otg)
+{
+	if (!otg->main_thread && otg->otg.gadget && otg->otg.host) {
+		otg_dbg(otg, "Starting OTG main thread\n");
+		otg->main_thread = kthread_create(otg_main_thread, otg, "otg");
+		wake_up_process(otg->main_thread);
+	}
+}
+
+static inline struct dwc3_otg *otg_to_dwc3_otg(struct usb_otg *x)
+{
+	return container_of(x, struct dwc3_otg, otg);
+}
+
+static irqreturn_t dwc3_otg_irq(int irq, void *_otg)
+{
+	struct dwc3_otg *otg;
+	u32 oevt;
+	u32 osts;
+	u32 octl;
+	u32 ocfg;
+	u32 oevten;
+	u32 otg_mask = OEVT_ALL;
+
+	if (!_otg)
+		return 0;
+
+	otg = (struct dwc3_otg *)_otg;
+
+	oevt = otg_read(otg, OEVT);
+	osts = otg_read(otg, OSTS);
+	octl = otg_read(otg, OCTL);
+	ocfg = otg_read(otg, OCFG);
+	oevten = otg_read(otg, OEVTEN);
+
+	/* Clear handled events */
+	otg_write(otg, OEVT, oevt);
+
+	otg_vdbg(otg, "\n");
+	otg_vdbg(otg, "    oevt = %08x\n", oevt);
+	otg_vdbg(otg, "    osts = %08x\n", osts);
+	otg_vdbg(otg, "    octl = %08x\n", octl);
+	otg_vdbg(otg, "    ocfg = %08x\n", ocfg);
+	otg_vdbg(otg, "  oevten = %08x\n", oevten);
+
+	otg_vdbg(otg, "oevt[DeviceMode] = %s\n",
+			oevt & OEVT_DEV_MOD_EVNT ? "Device" : "Host");
+
+	if (oevt & OEVT_CONN_ID_STS_CHNG_EVNT)
+		otg_dbg(otg, "Connector ID Status Change Event\n");
+	if (oevt & OEVT_HOST_ROLE_REQ_INIT_EVNT)
+		otg_dbg(otg, "Host Role Request Init Notification Event\n");
+	if (oevt & OEVT_HOST_ROLE_REQ_CONFIRM_EVNT)
+		otg_dbg(otg, "Host Role Request Confirm Notification Event\n");
+	if (oevt & OEVT_A_DEV_B_DEV_HOST_END_EVNT)
+		otg_dbg(otg, "A-Device B-Host End Event\n");
+	if (oevt & OEVT_A_DEV_HOST_EVNT)
+		otg_dbg(otg, "A-Device Host Event\n");
+	if (oevt & OEVT_A_DEV_HNP_CHNG_EVNT)
+		otg_dbg(otg, "A-Device HNP Change Event\n");
+	if (oevt & OEVT_A_DEV_SRP_DET_EVNT)
+		otg_dbg(otg, "A-Device SRP Detect Event\n");
+	if (oevt & OEVT_A_DEV_SESS_END_DET_EVNT)
+		otg_dbg(otg, "A-Device Session End Detected Event\n");
+	if (oevt & OEVT_B_DEV_B_HOST_END_EVNT)
+		otg_dbg(otg, "B-Device B-Host End Event\n");
+	if (oevt & OEVT_B_DEV_HNP_CHNG_EVNT)
+		otg_dbg(otg, "B-Device HNP Change Event\n");
+	if (oevt & OEVT_B_DEV_SES_VLD_DET_EVNT)
+		otg_dbg(otg, "B-Device Session Valid Detect Event\n");
+	if (oevt & OEVT_B_DEV_VBUS_CHNG_EVNT)
+		otg_dbg(otg, "B-Device VBUS Change Event\n");
+
+	if (oevt & otg_mask) {
+		/* Pass event to main thread */
+		spin_lock(&otg->lock);
+		otg->otg_events |= oevt;
+		wakeup_main_thread(otg);
+		spin_unlock(&otg->lock);
+		return 1;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void hnp_polling_work(struct work_struct *w)
+{
+	struct dwc3_otg *otg = container_of(w, struct dwc3_otg,
+			hp_work.work);
+	struct usb_bus *bus;
+	struct usb_device *udev;
+	struct usb_hcd *hcd;
+	u8 *otgstatus;
+	int ret;
+	int err;
+
+	hcd = container_of(otg->otg.host, struct usb_hcd, self);
+	if (!hcd)
+		return;
+
+	bus = &hcd->self;
+	if (!bus->otg_port)
+		return;
+
+	udev = usb_hub_find_child(bus->root_hub, bus->otg_port);
+	if (!udev)
+		return;
+
+	otgstatus = kmalloc(sizeof(*otgstatus), GFP_NOIO);
+	if (!otgstatus)
+		return;
+
+	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RECIP_DEVICE,
+			0, 0xf000, otgstatus, sizeof(*otgstatus),
+			USB_CTRL_GET_TIMEOUT);
+
+	if (ret == sizeof(*otgstatus) && (*otgstatus & 0x1)) {
+		/* enable HNP before suspend, it's simpler */
+
+		udev->bus->b_hnp_enable = 1;
+		err = usb_control_msg(udev,
+				usb_sndctrlpipe(udev, 0),
+				USB_REQ_SET_FEATURE, 0,
+				udev->bus->b_hnp_enable
+				? USB_DEVICE_B_HNP_ENABLE
+				: USB_DEVICE_A_ALT_HNP_SUPPORT,
+				0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+
+		if (err < 0) {
+			/* OTG MESSAGE: report errors here,
+			 * customize to match your product.
+			 */
+			otg_info(otg, "ERROR : Device no response\n");
+			dev_info(&udev->dev, "can't set HNP mode: %d\n",
+					err);
+			udev->bus->b_hnp_enable = 0;
+			if (le16_to_cpu(udev->descriptor.idVendor) == 0x1a0a) {
+				if (usb_port_suspend(udev, PMSG_AUTO_SUSPEND)
+						< 0)
+					dev_dbg(&udev->dev, "HNP fail, %d\n",
+							err);
+			}
+		} else {
+			/* Device wants role-switch, suspend the bus. */
+			static struct usb_phy *phy;
+
+			phy = usb_get_phy(USB_PHY_TYPE_USB3);
+			otg_start_hnp(phy->otg);
+			usb_put_phy(phy);
+
+			if (usb_port_suspend(udev, PMSG_AUTO_SUSPEND) < 0)
+				dev_dbg(&udev->dev, "HNP fail, %d\n", err);
+		}
+	} else if (ret < 0) {
+		udev->bus->b_hnp_enable = 1;
+		err = usb_control_msg(udev,
+				usb_sndctrlpipe(udev, 0),
+				USB_REQ_SET_FEATURE, 0,
+				USB_DEVICE_B_HNP_ENABLE,
+				0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+		if (usb_port_suspend(udev, PMSG_AUTO_SUSPEND) < 0)
+			dev_dbg(&udev->dev, "HNP fail, %d\n", err);
+	} else {
+		schedule_delayed_work(&otg->hp_work, 1 * HZ);
+	}
+
+	kfree(otgstatus);
+}
+
+static int dwc3_otg_notify_connect(struct usb_phy *phy,
+		enum usb_device_speed speed)
+{
+	struct usb_bus *bus;
+	struct usb_device *udev;
+	struct usb_hcd *hcd;
+	struct dwc3_otg *otg;
+	int err = 0;
+
+	otg = otg_to_dwc3_otg(phy->otg);
+
+	hcd = container_of(phy->otg->host, struct usb_hcd, self);
+	if (!hcd)
+		return -EINVAL;
+
+	bus = &hcd->self;
+	if (!bus->otg_port)
+		return 0;
+
+	udev = usb_hub_find_child(bus->root_hub, bus->otg_port);
+	if (!udev)
+		return 0;
+
+	/*
+	 * OTG-aware devices on OTG-capable root hubs may be able to use SRP,
+	 * to wake us after we've powered off VBUS; and HNP, switching roles
+	 * "host" to "peripheral".  The OTG descriptor helps figure this out.
+	 */
+	if (udev->config && udev->parent == udev->bus->root_hub) {
+		struct usb_otg20_descriptor	*desc = NULL;
+
+		/* descriptor may appear anywhere in config */
+		err = __usb_get_extra_descriptor(udev->rawdescriptors[0],
+				le16_to_cpu(udev->config[0].desc.wTotalLength),
+				USB_DT_OTG, (void **) &desc);
+		if (err || !(desc->bmAttributes & USB_OTG_HNP))
+			return 0;
+
+		if (udev->portnum == udev->bus->otg_port) {
+			INIT_DELAYED_WORK(&otg->hp_work,
+					hnp_polling_work);
+			schedule_delayed_work(&otg->hp_work, HZ);
+		}
+
+	}
+
+	return err;
+}
+
+static int dwc3_otg_notify_disconnect(struct usb_phy *phy,
+		enum usb_device_speed speed)
+{
+	struct dwc3_otg *otg;
+
+	otg = otg_to_dwc3_otg(phy->otg);
+
+	if (work_pending(&otg->hp_work.work)) {
+		while (!cancel_delayed_work(&otg->hp_work))
+			msleep(20);
+	}
+	return 0;
+}
+
+static void dwc3_otg_set_peripheral(struct usb_otg *_otg, int yes)
+{
+	struct dwc3_otg *otg;
+
+	if (!_otg)
+		return;
+
+	otg = otg_to_dwc3_otg(_otg);
+	otg_dbg(otg, "\n");
+
+	if (yes) {
+		if (otg->hwparams6 == 0xdeadbeef)
+			otg->hwparams6 = otg_read(otg, GHWPARAMS6);
+		stop_host(otg);
+	} else {
+		stop_peripheral(otg);
+	}
+
+	set_peri_mode(otg, yes);
+}
+EXPORT_SYMBOL(dwc3_otg_set_peripheral);
+
+static int dwc3_otg_set_periph(struct usb_otg *_otg, struct usb_gadget *gadget)
+{
+	struct dwc3_otg *otg;
+
+	if (!_otg)
+		return -ENODEV;
+
+	otg = otg_to_dwc3_otg(_otg);
+	otg_dbg(otg, "\n");
+
+	if ((long)gadget == 1) {
+		dwc3_otg_set_peripheral(_otg, 1);
+		return 0;
+	}
+
+	if (!gadget) {
+		otg->otg.gadget = NULL;
+		return -ENODEV;
+	}
+
+	otg->otg.gadget = gadget;
+	otg->otg.gadget->hnp_polling_support = 1;
+	otg->otg.state = OTG_STATE_B_IDLE;
+
+	start_main_thread(otg);
+	return 0;
+}
+
+static int dwc3_otg_set_host(struct usb_otg *_otg, struct usb_bus *host)
+{
+	struct dwc3_otg *otg;
+	struct usb_hcd *hcd;
+	struct xhci_hcd *xhci;
+
+	if (!_otg)
+		return -ENODEV;
+
+	otg = otg_to_dwc3_otg(_otg);
+	otg_dbg(otg, "\n");
+
+	if ((long)host == 1) {
+		dwc3_otg_set_peripheral(_otg, 0);
+		return 0;
+	}
+
+	if (!host) {
+		otg->otg.host = NULL;
+		otg->hcd_irq = 0;
+		return -ENODEV;
+	}
+
+	hcd = container_of(host, struct usb_hcd, self);
+	xhci = hcd_to_xhci(hcd);
+	otg_dbg(otg, "hcd=%p xhci=%p\n", hcd, xhci);
+
+	hcd->self.otg_port = 1;
+	if (xhci->shared_hcd) {
+		xhci->shared_hcd->self.otg_port = 1;
+		otg_dbg(otg, "shared_hcd=%p\n", xhci->shared_hcd);
+	}
+
+	otg->otg.host = host;
+	otg->hcd_irq = hcd->irq;
+	otg_dbg(otg, "host=%p irq=%d\n", otg->otg.host, otg->hcd_irq);
+
+
+	otg->host_started = 1;
+	otg->dev_enum = 0;
+	start_main_thread(otg);
+	return 0;
+}
+
+static int dwc3_otg_start_srp(struct usb_otg *x)
+{
+	unsigned long flags;
+	struct dwc3_otg *otg;
+
+	if (!x)
+		return -ENODEV;
+
+	otg = otg_to_dwc3_otg(x);
+	otg_dbg(otg, "\n");
+
+	if (!otg->otg.host || !otg->otg.gadget)
+		return -ENODEV;
+
+	spin_lock_irqsave(&otg->lock, flags);
+	otg->user_events |= USER_SRP_EVENT;
+	wakeup_main_thread(otg);
+	spin_unlock_irqrestore(&otg->lock, flags);
+	return 0;
+}
+
+static int dwc3_otg_start_hnp(struct usb_otg *x)
+{
+	unsigned long flags;
+	struct dwc3_otg *otg;
+
+	if (!x)
+		return -ENODEV;
+
+	otg = otg_to_dwc3_otg(x);
+	otg_dbg(otg, "\n");
+
+	if (!otg->otg.host || !otg->otg.gadget)
+		return -ENODEV;
+
+	spin_lock_irqsave(&otg->lock, flags);
+	otg->user_events |= USER_HNP_EVENT;
+	wakeup_main_thread(otg);
+	spin_unlock_irqrestore(&otg->lock, flags);
+	return 0;
+}
+
+static int dwc3_otg_end_session(struct usb_otg *x)
+{
+	unsigned long flags;
+	struct dwc3_otg *otg;
+
+	if (!x)
+		return -ENODEV;
+
+	otg = otg_to_dwc3_otg(x);
+	otg_dbg(otg, "\n");
+
+	if (!otg->otg.host || !otg->otg.gadget)
+		return -ENODEV;
+
+	spin_lock_irqsave(&otg->lock, flags);
+	otg->user_events |= USER_END_SESSION;
+	wakeup_main_thread(otg);
+	spin_unlock_irqrestore(&otg->lock, flags);
+	return 0;
+}
+
+static int otg_end_session(struct usb_otg *otg)
+{
+	return dwc3_otg_end_session(otg);
+}
+EXPORT_SYMBOL(otg_end_session);
+
+static int dwc3_otg_received_host_release(struct usb_otg *x)
+{
+	struct dwc3_otg *otg;
+	unsigned long flags;
+
+	if (!x)
+		return -ENODEV;
+
+	otg = otg_to_dwc3_otg(x);
+	otg_dbg(otg, "\n");
+
+	if (!otg->otg.host || !otg->otg.gadget)
+		return -ENODEV;
+
+	spin_lock_irqsave(&otg->lock, flags);
+	otg->user_events |= PCD_RECEIVED_HOST_RELEASE_EVENT;
+	wakeup_main_thread(otg);
+	spin_unlock_irqrestore(&otg->lock, flags);
+	return 0;
+}
+
+int otg_host_release(struct usb_otg *otg)
+{
+	return dwc3_otg_received_host_release(otg);
+}
+EXPORT_SYMBOL(otg_host_release);
+
+static void dwc3_otg_enable_irq(struct dwc3_otg *otg)
+{
+	u32			reg;
+
+	/* Enable OTG IRQs */
+	reg = OEVT_ALL;
+
+	otg_write(otg, OEVTEN, reg);
+}
+
+static ssize_t store_srp(struct device *dev, struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	struct usb_phy *phy;
+	struct usb_otg *otg;
+
+	phy = usb_get_phy(USB_PHY_TYPE_USB3);
+	if (IS_ERR(phy) || !phy) {
+		if (!IS_ERR(phy))
+			usb_put_phy(phy);
+		return count;
+	}
+
+	otg = phy->otg;
+	if (!otg) {
+		usb_put_phy(phy);
+		return count;
+	}
+
+	otg_start_srp(otg);
+	usb_put_phy(phy);
+	return count;
+}
+static DEVICE_ATTR(srp, 0220, NULL, store_srp);
+
+static ssize_t store_end(struct device *dev, struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	struct usb_phy *phy;
+	struct usb_otg *otg;
+
+	phy = usb_get_phy(USB_PHY_TYPE_USB3);
+	if (IS_ERR(phy) || !phy) {
+		if (!IS_ERR(phy))
+			usb_put_phy(phy);
+		return count;
+	}
+
+	otg = phy->otg;
+	if (!otg) {
+		usb_put_phy(phy);
+		return count;
+	}
+
+	otg_end_session(otg);
+	usb_put_phy(phy);
+	return count;
+}
+static DEVICE_ATTR(end, 0220, NULL, store_end);
+
+static ssize_t store_hnp(struct device *dev, struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	struct dwc3 *dwc = dev_get_drvdata(dev);
+	struct usb_phy *phy = usb_get_phy(USB_PHY_TYPE_USB3);
+	struct usb_otg *otg;
+
+	dev_dbg(dwc->dev, "%s()\n", __func__);
+
+	if (IS_ERR(phy) || !phy) {
+		dev_info(dwc->dev, "NO PHY!!\n");
+		if (!IS_ERR(phy))
+			usb_put_phy(phy);
+		return count;
+	}
+
+	otg = phy->otg;
+	if (!otg) {
+		dev_info(dwc->dev, "NO OTG!!\n");
+		usb_put_phy(phy);
+		return count;
+	}
+
+	dev_info(dev, "b_hnp_enable is FALSE\n");
+	dwc->gadget.host_request_flag = 1;
+
+	usb_put_phy(phy);
+	return count;
+}
+static DEVICE_ATTR(hnp, 0220, NULL, store_hnp);
+
+static ssize_t store_a_hnp_reqd(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct dwc3 *dwc = dev_get_drvdata(dev);
+	struct dwc3_otg *otg;
+
+	otg = dwc->otg;
+	host_release(otg);
+	return count;
+}
+static DEVICE_ATTR(a_hnp_reqd, 0220, NULL, store_a_hnp_reqd);
+
+static ssize_t store_print_dbg(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct dwc3 *dwc = dev_get_drvdata(dev);
+	struct dwc3_otg *otg;
+
+	otg = dwc->otg;
+	print_debug_regs(otg);
+
+	return count;
+}
+static DEVICE_ATTR(print_dbg, 0220, NULL, store_print_dbg);
+
+void dwc_usb3_remove_dev_files(struct device *dev)
+{
+	device_remove_file(dev, &dev_attr_print_dbg);
+	device_remove_file(dev, &dev_attr_a_hnp_reqd);
+	device_remove_file(dev, &dev_attr_end);
+	device_remove_file(dev, &dev_attr_srp);
+	device_remove_file(dev, &dev_attr_hnp);
+}
+
+int dwc3_otg_create_dev_files(struct device *dev)
+{
+	int retval;
+
+	retval = device_create_file(dev, &dev_attr_hnp);
+	if (retval)
+		goto fail;
+
+	retval = device_create_file(dev, &dev_attr_srp);
+	if (retval)
+		goto fail;
+
+	retval = device_create_file(dev, &dev_attr_end);
+	if (retval)
+		goto fail;
+
+	retval = device_create_file(dev, &dev_attr_a_hnp_reqd);
+	if (retval)
+		goto fail;
+
+	retval = device_create_file(dev, &dev_attr_print_dbg);
+	if (retval)
+		goto fail;
+
+	return 0;
+
+fail:
+	dev_err(dev, "Failed to create one or more sysfs files!!\n");
+	return retval;
+}
+
+int dwc3_otg_init(struct dwc3 *dwc)
+{
+	struct dwc3_otg *otg;
+	int err;
+	u32 reg;
+
+	dev_dbg(dwc->dev, "dwc3_otg_init\n");
+
+	/*
+	 * GHWPARAMS6[10] bit is SRPSupport.
+	 * This bit also reflects DWC_USB3_EN_OTG
+	 */
+	reg = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6);
+	if (!(reg & GHWPARAMS6_SRP_SUPPORT_ENABLED)) {
+		/*
+		 * No OTG support in the HW core.
+		 * We return 0 to indicate no error, since this is acceptable
+		 * situation, just continue probe the dwc3 driver without otg.
+		 */
+		dev_dbg(dwc->dev, "dwc3_otg address space is not supported\n");
+		return 0;
+	}
+
+	otg = kzalloc(sizeof(*otg), GFP_KERNEL);
+	if (!otg)
+		return -ENOMEM;
+
+	dwc->otg = otg;
+	otg->dev = dwc->dev;
+	otg->dwc = dwc;
+
+	otg->regs = dwc->regs - DWC3_GLOBALS_REGS_START;
+	otg->otg.usb_phy = kzalloc(sizeof(struct usb_phy), GFP_KERNEL);
+	otg->otg.usb_phy->dev = otg->dev;
+	otg->otg.usb_phy->label = "dwc3_otg";
+	otg->otg.state = OTG_STATE_UNDEFINED;
+	otg->otg.usb_phy->otg = &otg->otg;
+	otg->otg.usb_phy->notify_connect = dwc3_otg_notify_connect;
+	otg->otg.usb_phy->notify_disconnect = dwc3_otg_notify_disconnect;
+
+	otg->otg.start_srp = dwc3_otg_start_srp;
+	otg->otg.start_hnp = dwc3_otg_start_hnp;
+	otg->otg.set_host = dwc3_otg_set_host;
+	otg->otg.set_peripheral = dwc3_otg_set_periph;
+
+	otg->hwparams6 = reg;
+	otg->state = OTG_STATE_UNDEFINED;
+
+	spin_lock_init(&otg->lock);
+	init_waitqueue_head(&otg->main_wq);
+
+	err = usb_add_phy(otg->otg.usb_phy, USB_PHY_TYPE_USB3);
+	if (err) {
+		dev_err(otg->dev, "can't register transceiver, err: %d\n",
+			err);
+		goto exit;
+	}
+
+	otg->irq = platform_get_irq(to_platform_device(otg->dev), 1);
+
+	dwc3_otg_create_dev_files(otg->dev);
+
+	/* Set irq handler */
+	err = request_irq(otg->irq, dwc3_otg_irq, IRQF_SHARED, "dwc3_otg", otg);
+	if (err) {
+		dev_err(otg->otg.usb_phy->dev, "failed to request irq #%d --> %d\n",
+				otg->irq, err);
+		goto exit;
+	}
+
+	dwc3_otg_enable_irq(otg);
+
+	return 0;
+exit:
+	kfree(otg->otg.usb_phy);
+	kfree(otg);
+	return err;
+}
+
+void dwc3_otg_exit(struct dwc3 *dwc)
+{
+	struct dwc3_otg *otg = dwc->otg;
+
+	otg_dbg(otg, "\n");
+	usb_remove_phy(otg->otg.usb_phy);
+	kfree(otg->otg.usb_phy);
+	kfree(otg);
+}
diff --git a/drivers/usb/dwc3/otg.h b/drivers/usb/dwc3/otg.h
new file mode 100644
index 0000000..e2eb4ca
--- /dev/null
+++ b/drivers/usb/dwc3/otg.h
@@ -0,0 +1,247 @@
+/**
+ * otg.h - DesignWare USB3 DRD OTG Header
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ *	    Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2  of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define otg_dbg(d, fmt, args...)  dev_dbg((d)->dev, "%s(): " fmt,\
+		__func__, ## args)
+#define otg_vdbg(d, fmt, args...) dev_vdbg((d)->dev, "%s(): " fmt,\
+		__func__, ## args)
+#define otg_err(d, fmt, args...)  dev_err((d)->dev, "%s(): ERROR: " fmt,\
+		__func__, ## args)
+#define otg_warn(d, fmt, args...) dev_warn((d)->dev, "%s(): WARN: " fmt,\
+		__func__, ## args)
+#define otg_info(d, fmt, args...) dev_info((d)->dev, "%s(): INFO: " fmt,\
+		__func__, ## args)
+
+#ifdef VERBOSE_DEBUG
+#define otg_write(o, reg, val)	do {					\
+		otg_vdbg(o, "OTG_WRITE: reg=0x%05x, val=0x%08x\n", reg, val); \
+		writel(val, ((void *)((o)->regs)) + reg);	\
+	} while (0)
+
+#define otg_read(o, reg) ({						\
+		u32 __r = readl(((void *)((o)->regs)) + reg);	\
+		otg_vdbg(o, "OTG_READ: reg=0x%05x, val=0x%08x\n", reg, __r); \
+		__r;							\
+	})
+#else
+#define otg_write(o, reg, val)	writel(val, ((void *)((o)->regs)) + reg)
+#define otg_read(o, reg)	readl(((void *)((o)->regs)) + reg)
+#endif
+
+#define sleep_main_thread_until_condition_timeout(otg, condition, msecs) ({ \
+		int __timeout = msecs;				\
+		while (!(condition)) {				\
+			otg_dbg(otg, "  ... sleeping for %d\n", __timeout); \
+			__timeout = sleep_main_thread_timeout(otg, __timeout); \
+			if (__timeout <= 0) {			\
+				break;				\
+			}					\
+		}						\
+		__timeout;					\
+	})
+
+#define sleep_main_thread_until_condition(otg, condition) ({	\
+		int __rc;					\
+		do {						\
+			__rc = sleep_main_thread_until_condition_timeout(otg, \
+					condition, 50000);	\
+		} while (__rc == 0);				\
+		__rc;						\
+	})
+
+#define GHWPARAMS6				0xc158
+#define GHWPARAMS6_SRP_SUPPORT_ENABLED		0x0400
+#define GHWPARAMS6_HNP_SUPPORT_ENABLED		0x0800
+
+#define GCTL					0xc110
+#define GCTL_PRT_CAP_DIR			0x3000
+#define GCTL_PRT_CAP_DIR_SHIFT			12
+#define GCTL_PRT_CAP_DIR_HOST			1
+#define GCTL_PRT_CAP_DIR_DEV			2
+#define GCTL_PRT_CAP_DIR_OTG			3
+#define GCTL_GBL_HIBERNATION_EN			0x2
+
+#define OCFG					0xcc00
+#define OCFG_SRP_CAP				0x01
+#define OCFG_SRP_CAP_SHIFT			0
+#define OCFG_HNP_CAP				0x02
+#define OCFG_HNP_CAP_SHIFT			1
+#define OCFG_OTG_VERSION			0x04
+#define OCFG_OTG_VERSION_SHIFT			2
+
+#define OCTL					0xcc04
+#define OCTL_HST_SET_HNP_EN			0x01
+#define OCTL_HST_SET_HNP_EN_SHIFT		0
+#define OCTL_DEV_SET_HNP_EN			0x02
+#define OCTL_DEV_SET_HNP_EN_SHIFT		1
+#define OCTL_TERM_SEL_DL_PULSE			0x04
+#define OCTL_TERM_SEL_DL_PULSE_SHIFT		2
+#define OCTL_SES_REQ				0x08
+#define OCTL_SES_REQ_SHIFT			3
+#define OCTL_HNP_REQ				0x10
+#define OCTL_HNP_REQ_SHIFT			4
+#define OCTL_PRT_PWR_CTL			0x20
+#define OCTL_PRT_PWR_CTL_SHIFT			5
+#define OCTL_PERI_MODE				0x40
+#define OCTL_PERI_MODE_SHIFT			6
+
+#define OEVT					0xcc08
+#define OEVT_ERR				0x00000001
+#define OEVT_ERR_SHIFT				0
+#define OEVT_SES_REQ_SCS			0x00000002
+#define OEVT_SES_REQ_SCS_SHIFT			1
+#define OEVT_HST_NEG_SCS			0x00000004
+#define OEVT_HST_NEG_SCS_SHIFT			2
+#define OEVT_B_SES_VLD_EVT			0x00000008
+#define OEVT_B_SES_VLD_EVT_SHIFT		3
+#define OEVT_B_DEV_VBUS_CHNG_EVNT		0x00000100
+#define OEVT_B_DEV_VBUS_CHNG_EVNT_SHIFT		8
+#define OEVT_B_DEV_SES_VLD_DET_EVNT		0x00000200
+#define OEVT_B_DEV_SES_VLD_DET_EVNT_SHIFT	9
+#define OEVT_B_DEV_HNP_CHNG_EVNT		0x00000400
+#define OEVT_B_DEV_HNP_CHNG_EVNT_SHIFT		10
+#define OEVT_B_DEV_B_HOST_END_EVNT		0x00000800
+#define OEVT_B_DEV_B_HOST_END_EVNT_SHIFT	11
+#define OEVT_A_DEV_SESS_END_DET_EVNT		0x00010000
+#define OEVT_A_DEV_SESS_END_DET_EVNT_SHIFT	16
+#define OEVT_A_DEV_SRP_DET_EVNT			0x00020000
+#define OEVT_A_DEV_SRP_DET_EVNT_SHIFT		17
+#define OEVT_A_DEV_HNP_CHNG_EVNT		0x00040000
+#define OEVT_A_DEV_HNP_CHNG_EVNT_SHIFT		18
+#define OEVT_A_DEV_HOST_EVNT			0x00080000
+#define OEVT_A_DEV_HOST_EVNT_SHIFT		19
+#define OEVT_A_DEV_B_DEV_HOST_END_EVNT		0x00100000
+#define OEVT_A_DEV_B_DEV_HOST_END_EVNT_SHIFT	20
+#define OEVT_A_DEV_IDLE_EVNT			0x00200000
+#define OEVT_A_DEV_IDLE_EVNT_SHIFT		21
+#define OEVT_HOST_ROLE_REQ_INIT_EVNT		0x00400000
+#define OEVT_HOST_ROLE_REQ_INIT_EVNT_SHIFT	22
+#define OEVT_HOST_ROLE_REQ_CONFIRM_EVNT		0x00800000
+#define OEVT_HOST_ROLE_REQ_CONFIRM_EVNT_SHIFT	23
+#define OEVT_CONN_ID_STS_CHNG_EVNT		0x01000000
+#define OEVT_CONN_ID_STS_CHNG_EVNT_SHIFT	24
+#define OEVT_DEV_MOD_EVNT			0x80000000
+#define OEVT_DEV_MOD_EVNT_SHIFT			31
+
+#define OEVTEN					0xcc0c
+
+#define OEVT_ALL (OEVT_CONN_ID_STS_CHNG_EVNT | \
+		OEVT_HOST_ROLE_REQ_INIT_EVNT | \
+		OEVT_HOST_ROLE_REQ_CONFIRM_EVNT | \
+		OEVT_A_DEV_B_DEV_HOST_END_EVNT | \
+		OEVT_A_DEV_HOST_EVNT | \
+		OEVT_A_DEV_HNP_CHNG_EVNT | \
+		OEVT_A_DEV_SRP_DET_EVNT | \
+		OEVT_A_DEV_SESS_END_DET_EVNT | \
+		OEVT_B_DEV_B_HOST_END_EVNT | \
+		OEVT_B_DEV_HNP_CHNG_EVNT | \
+		OEVT_B_DEV_SES_VLD_DET_EVNT | \
+		OEVT_B_DEV_VBUS_CHNG_EVNT)
+
+#define OSTS					0xcc10
+#define OSTS_CONN_ID_STS			0x0001
+#define OSTS_CONN_ID_STS_SHIFT			0
+#define OSTS_A_SES_VLD				0x0002
+#define OSTS_A_SES_VLD_SHIFT			1
+#define OSTS_B_SES_VLD				0x0004
+#define OSTS_B_SES_VLD_SHIFT			2
+#define OSTS_XHCI_PRT_PWR			0x0008
+#define OSTS_XHCI_PRT_PWR_SHIFT			3
+#define OSTS_PERIP_MODE				0x0010
+#define OSTS_PERIP_MODE_SHIFT			4
+#define OSTS_OTG_STATES				0x0f00
+#define OSTS_OTG_STATE_SHIFT			8
+
+#define DCTL					0xc704
+#define DCTL_RUN_STOP				0x80000000
+
+#define OTG_STATE_INVALID			-1
+#define OTG_STATE_EXIT				14
+#define OTG_STATE_TERMINATED			15
+
+#define PERI_MODE_HOST		0
+#define PERI_MODE_PERIPHERAL	1
+
+/** The main structure to keep track of OTG driver state. */
+struct dwc3_otg {
+
+	/** OTG PHY */
+	struct usb_otg otg;
+	struct device *dev;
+	struct dwc3 *dwc;
+
+	void __iomem *regs;
+
+	int main_wakeup_needed;
+	struct task_struct *main_thread;
+	wait_queue_head_t main_wq;
+
+	spinlock_t lock;
+
+	int otg_srp_reqd;
+
+	/* Events */
+	u32 otg_events;
+
+	u32 user_events;
+
+	/** User initiated SRP.
+	 *
+	 * Valid in B-device during sensing/probing. Initiates SRP signalling
+	 * across the bus.
+	 *
+	 * Also valid as an A-device during probing. This causes the A-device to
+	 * apply V-bus manually and check for a device. Can be used if the
+	 * device does not support SRP and the host does not support ADP.
+	 */
+#define USER_SRP_EVENT			0x1
+	/** User initiated HNP (only valid in B-peripheral) */
+#define USER_HNP_EVENT			0x2
+	/** User has ended the session (only valid in B-peripheral) */
+#define USER_END_SESSION		0x4
+	/** User initiated VBUS. This will cause the A-device to turn on the
+	 * VBUS and see if a device will connect (only valid in A-device during
+	 * sensing/probing)
+	 */
+#define USER_VBUS_ON			0x8
+	/** User has initiated RSP */
+#define USER_RSP_EVENT			0x10
+	/** Host release event */
+#define PCD_RECEIVED_HOST_RELEASE_EVENT	0x20
+	/** Initial SRP */
+#define INITIAL_SRP			0x40
+	/** A-device connected event*/
+#define USER_A_CONN_EVENT		0x80
+
+	/* States */
+	enum usb_otg_state prev;
+	enum usb_otg_state state;
+
+	u32 hwparams6;
+	int hcd_irq;
+	int irq;
+	int host_started;
+	int peripheral_started;
+	int dev_enum;
+
+	struct delayed_work hp_work;	/* drives HNP polling */
+
+};
+
+extern int usb_port_suspend(struct usb_device *udev, pm_message_t msg);
+extern void usb_kick_hub_wq(struct usb_device *dev);
-- 
2.1.1

^ permalink raw reply related

* [RFC PATCH] usb: dwc3: host: add support for OTG in DWC3 host driver
From: Manish Narani @ 2017-01-04 13:22 UTC (permalink / raw)
  To: robh+dt, mark.rutland, catalin.marinas, will.deacon, michal.simek,
	soren.brinkmann, balbi, gregkh, mathias.nyman, agraf, bharatku,
	punnaiah.choudary.kalluri, dhdang, marc.zyngier, mnarani,
	devicetree, linux-arm-kernel, linux-kernel, linux-usb
  Cc: anirudh, anuragku
In-Reply-To: <1483536181-22356-1-git-send-email-mnarani@xilinx.com>

This patch adds support for OTG host mode initialization in DWC3
host driver. Before the host initialization sequence begins. The
driver has to make sure the no OTG peripheral mode is enabled.

Signed-off-by: Manish Narani <mnarani@xilinx.com>
---
 drivers/usb/dwc3/host.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index 487f0ff..4caa3fe 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -16,6 +16,8 @@
  */
 
 #include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
 
 #include "core.h"
 
@@ -111,6 +113,18 @@ int dwc3_host_init(struct dwc3 *dwc)
 	phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
 			  dev_name(dwc->dev));
 
+	if (dwc->dr_mode == USB_DR_MODE_OTG) {
+		struct usb_phy *phy;
+		/* Switch otg to host mode */
+		phy = usb_get_phy(USB_PHY_TYPE_USB3);
+		if (!IS_ERR(phy)) {
+			if (phy && phy->otg)
+				otg_set_host(phy->otg,
+						(struct usb_bus *)(long)1);
+			usb_put_phy(phy);
+		}
+	}
+
 	ret = platform_device_add(xhci);
 	if (ret) {
 		dev_err(dwc->dev, "failed to register xHCI device\n");
-- 
2.1.1

^ permalink raw reply related

* [RFC PATCH] usb: dwc3: gadget: add support for OTG in gadget framework
From: Manish Narani @ 2017-01-04 13:22 UTC (permalink / raw)
  To: robh+dt, mark.rutland, catalin.marinas, will.deacon, michal.simek,
	soren.brinkmann, balbi, gregkh, mathias.nyman, agraf, bharatku,
	punnaiah.choudary.kalluri, dhdang, marc.zyngier, mnarani,
	devicetree, linux-arm-kernel, linux-kernel, linux-usb
  Cc: anirudh, anuragku
In-Reply-To: <1483536181-22356-1-git-send-email-mnarani@xilinx.com>

This patch adds support for OTG in DWC3 gadget framework. This
also adds support for HNP polling by host while in OTG mode.

Modifications in couple of functions to make it in sync with
OTG driver while keeping its original functionality intact.

Signed-off-by: Manish Narani <mnarani@xilinx.com>
---
 drivers/usb/dwc3/ep0.c    | 49 +++++++++++++++++++++++---
 drivers/usb/dwc3/gadget.c | 87 +++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 116 insertions(+), 20 deletions(-)

diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 4878d18..aa78c1b 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -334,6 +334,8 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
 				usb_status |= 1 << USB_DEV_STAT_U2_ENABLED;
 		}
 
+		usb_status |= dwc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
+
 		break;
 
 	case USB_RECIP_INTERFACE:
@@ -448,11 +450,45 @@ static int dwc3_ep0_handle_device(struct dwc3 *dwc,
 
 	switch (wValue) {
 	case USB_DEVICE_REMOTE_WAKEUP:
+		if (set)
+			dwc->remote_wakeup = 1;
+		else
+			dwc->remote_wakeup = 0;
 		break;
-	/*
-	 * 9.4.1 says only only for SS, in AddressState only for
-	 * default control pipe
-	 */
+	case USB_DEVICE_B_HNP_ENABLE:
+		dev_dbg(dwc->dev,
+			"SET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n");
+		if (set && dwc->gadget.is_otg) {
+			if (dwc->gadget.host_request_flag) {
+				struct usb_phy *phy =
+					usb_get_phy(USB_PHY_TYPE_USB3);
+
+				dwc->gadget.b_hnp_enable = 0;
+				dwc->gadget.host_request_flag = 0;
+				otg_start_hnp(phy->otg);
+				usb_put_phy(phy);
+			} else {
+				dwc->gadget.b_hnp_enable = 1;
+			}
+		} else
+			return -EINVAL;
+		break;
+
+	case USB_DEVICE_A_HNP_SUPPORT:
+		/* RH port supports HNP */
+		dev_dbg(dwc->dev,
+			"SET_FEATURE: USB_DEVICE_A_HNP_SUPPORT\n");
+		break;
+
+	case USB_DEVICE_A_ALT_HNP_SUPPORT:
+		/* other RH port does */
+		dev_dbg(dwc->dev,
+			"SET_FEATURE: USB_DEVICE_A_ALT_HNP_SUPPORT\n");
+		break;
+		/*
+		 * 9.4.1 says only only for SS, in AddressState only for
+		 * default control pipe
+		 */
 	case USB_DEVICE_U1_ENABLE:
 		ret = dwc3_ep0_handle_u1(dwc, state, set);
 		break;
@@ -759,7 +795,10 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
 
 	switch (ctrl->bRequest) {
 	case USB_REQ_GET_STATUS:
-		ret = dwc3_ep0_handle_status(dwc, ctrl);
+		if (le16_to_cpu(ctrl->wIndex) == OTG_STS_SELECTOR)
+			ret = dwc3_ep0_delegate_req(dwc, ctrl);
+		else
+			ret = dwc3_ep0_handle_status(dwc, ctrl);
 		break;
 	case USB_REQ_CLEAR_FEATURE:
 		ret = dwc3_ep0_handle_feature(dwc, ctrl, 0);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 6785595..3b0b771 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -34,6 +34,7 @@
 #include "core.h"
 #include "gadget.h"
 #include "io.h"
+#include "otg.h"
 
 /**
  * dwc3_gadget_set_test_mode - Enables USB2 Test Modes
@@ -1792,25 +1793,51 @@ static int dwc3_gadget_start(struct usb_gadget *g,
 	int			ret = 0;
 	int			irq;
 
-	irq = dwc->irq_gadget;
-	ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
-			IRQF_SHARED, "dwc3", dwc->ev_buf);
-	if (ret) {
-		dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
-				irq, ret);
-		goto err0;
-	}
-
 	spin_lock_irqsave(&dwc->lock, flags);
 	if (dwc->gadget_driver) {
 		dev_err(dwc->dev, "%s is already bound to %s\n",
 				dwc->gadget.name,
 				dwc->gadget_driver->driver.name);
 		ret = -EBUSY;
-		goto err1;
+		goto err0;
 	}
 
-	dwc->gadget_driver	= driver;
+	if (g->is_otg) {
+		static struct usb_gadget_driver *prev_driver;
+		/* There are two instances where OTG functionality is enabled :
+		 * 1. This function will be called from OTG driver to start the
+		 * gadget
+		 * 2. This function will be called by the Linux Class Driver to
+		 * start the gadget
+		 * Below code will keep synchronization between these calls. The
+		 * "driver" argument will be NULL when it is called from the OTG
+		 * driver, so we are maintaning a global variable "prev_driver"
+		 * to assign value of argument "driver" (from class driver) to
+		 * dwc->gadget_driver when it is called from OTG.
+		 */
+		if (driver) {
+			prev_driver	= driver;
+			if (dwc->otg) {
+				struct dwc3_otg		*otg = dwc->otg;
+
+				if ((otg->host_started ||
+						(!otg->peripheral_started)))
+					goto err0;
+			}
+			dwc->gadget_driver	= driver;
+		} else
+			dwc->gadget_driver	= prev_driver;
+	} else
+		dwc->gadget_driver	= driver;
+
+	irq = dwc->irq_gadget;
+	ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
+			IRQF_SHARED, "dwc3", dwc->ev_buf);
+	if (ret) {
+		dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
+				irq, ret);
+		goto err0;
+	}
 
 	if (pm_runtime_active(dwc->dev))
 		__dwc3_gadget_start(dwc);
@@ -1819,11 +1846,9 @@ static int dwc3_gadget_start(struct usb_gadget *g,
 
 	return 0;
 
-err1:
-	spin_unlock_irqrestore(&dwc->lock, flags);
-	free_irq(irq, dwc);
-
 err0:
+	dwc->gadget_driver = NULL;
+	spin_unlock_irqrestore(&dwc->lock, flags);
 	return ret;
 }
 
@@ -2977,6 +3002,18 @@ int dwc3_gadget_init(struct dwc3 *dwc)
 
 	dwc->irq_gadget = irq;
 
+	if (dwc->dr_mode == USB_DR_MODE_OTG) {
+		struct usb_phy *phy;
+		/* Switch otg to peripheral mode */
+		phy = usb_get_phy(USB_PHY_TYPE_USB3);
+		if (!IS_ERR(phy)) {
+			if (phy && phy->otg)
+				otg_set_peripheral(phy->otg,
+						(struct usb_gadget *)(long)1);
+			usb_put_phy(phy);
+		}
+	}
+
 	dwc->ctrl_req = dma_alloc_coherent(dwc->sysdev, sizeof(*dwc->ctrl_req),
 			&dwc->ctrl_req_addr, GFP_KERNEL);
 	if (!dwc->ctrl_req) {
@@ -3066,6 +3103,26 @@ int dwc3_gadget_init(struct dwc3 *dwc)
 		goto err5;
 	}
 
+	if (dwc->dr_mode == USB_DR_MODE_OTG) {
+		struct usb_phy *phy;
+
+		phy = usb_get_phy(USB_PHY_TYPE_USB3);
+		if (!IS_ERR(phy)) {
+			if (phy && phy->otg) {
+				ret = otg_set_peripheral(phy->otg,
+						&dwc->gadget);
+				if (ret) {
+					usb_put_phy(phy);
+					phy = NULL;
+					goto err5;
+				}
+			} else {
+				usb_put_phy(phy);
+				phy = NULL;
+			}
+		}
+	}
+
 	return 0;
 
 err5:
-- 
2.1.1

^ permalink raw reply related

* [RFC PATCH] usb: dwc3: core: add OTG support function calls and modifications
From: Manish Narani @ 2017-01-04 13:22 UTC (permalink / raw)
  To: robh+dt, mark.rutland, catalin.marinas, will.deacon, michal.simek,
	soren.brinkmann, balbi, gregkh, mathias.nyman, agraf, bharatku,
	punnaiah.choudary.kalluri, dhdang, marc.zyngier, mnarani,
	devicetree, linux-arm-kernel, linux-kernel, linux-usb
  Cc: anirudh, anuragku
In-Reply-To: <1483536181-22356-1-git-send-email-mnarani@xilinx.com>

This patch adds function call to initialize OTG driver. This patch
also adds support for OTG device structure in DWC3 device.

Modifications to event buffer related functions which are called
from OTG driver upon requirement.

Signed-off-by: Manish Narani <mnarani@xilinx.com>
---
 drivers/usb/dwc3/core.c | 17 ++++++++++++-----
 drivers/usb/dwc3/core.h | 14 ++++++++++++++
 2 files changed, 26 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 369bab1..9ab9c5b 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -240,7 +240,7 @@ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc,
  * dwc3_free_event_buffers - frees all allocated event buffers
  * @dwc: Pointer to our controller context structure
  */
-static void dwc3_free_event_buffers(struct dwc3 *dwc)
+void dwc3_free_event_buffers(struct dwc3 *dwc)
 {
 	struct dwc3_event_buffer	*evt;
 
@@ -257,7 +257,7 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc)
  * Returns 0 on success otherwise negative errno. In the error case, dwc
  * may contain some buffers allocated but not all which were requested.
  */
-static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
+int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
 {
 	struct dwc3_event_buffer *evt;
 
@@ -277,7 +277,7 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
  *
  * Returns 0 on success otherwise negative errno.
  */
-static int dwc3_event_buffers_setup(struct dwc3 *dwc)
+int dwc3_event_buffers_setup(struct dwc3 *dwc)
 {
 	struct dwc3_event_buffer	*evt;
 
@@ -862,10 +862,10 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
 		}
 		break;
 	case USB_DR_MODE_OTG:
-		ret = dwc3_host_init(dwc);
+		ret = dwc3_otg_init(dwc);
 		if (ret) {
 			if (ret != -EPROBE_DEFER)
-				dev_err(dev, "failed to initialize host\n");
+				dev_err(dev, "failed to initialize otg\n");
 			return ret;
 		}
 
@@ -875,6 +875,13 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
 				dev_err(dev, "failed to initialize gadget\n");
 			return ret;
 		}
+
+		ret = dwc3_host_init(dwc);
+		if (ret) {
+			if (ret != -EPROBE_DEFER)
+				dev_err(dev, "failed to initialize host\n");
+			return ret;
+		}
 		break;
 	default:
 		dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index de5a857..6b92064 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -887,6 +887,8 @@ struct dwc3 {
 	struct usb_gadget	gadget;
 	struct usb_gadget_driver *gadget_driver;
 
+	struct dwc3_otg		*otg;
+
 	struct usb_phy		*usb2_phy;
 	struct usb_phy		*usb3_phy;
 
@@ -987,6 +989,7 @@ struct dwc3 {
 	unsigned		setup_packet_pending:1;
 	unsigned		three_stage_setup:1;
 	unsigned		usb3_lpm_capable:1;
+	unsigned		remote_wakeup:1;
 
 	unsigned		disable_scramble_quirk:1;
 	unsigned		u2exit_lfps_quirk:1;
@@ -1220,6 +1223,13 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
 { return 0; }
 #endif
 
+#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
+int dwc3_otg_init(struct dwc3 *dwc);
+#else
+static inline int dwc3_otg_init(struct dwc3 *dwc)
+{ return 0; }
+#endif
+
 /* power management interface */
 #if !IS_ENABLED(CONFIG_USB_DWC3_HOST)
 int dwc3_gadget_suspend(struct dwc3 *dwc);
@@ -1251,4 +1261,8 @@ static inline void dwc3_ulpi_exit(struct dwc3 *dwc)
 { }
 #endif
 
+int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length);
+void dwc3_free_event_buffers(struct dwc3 *dwc);
+int dwc3_event_buffers_setup(struct dwc3 *dwc);
+
 #endif /* __DRIVERS_USB_DWC3_CORE_H */
-- 
2.1.1

^ permalink raw reply related

* [RFC PATCH] usb: dwc3: add support for OTG driver compilation
From: Manish Narani @ 2017-01-04 13:22 UTC (permalink / raw)
  To: robh+dt, mark.rutland, catalin.marinas, will.deacon, michal.simek,
	soren.brinkmann, balbi, gregkh, mathias.nyman, agraf, bharatku,
	punnaiah.choudary.kalluri, dhdang, marc.zyngier, mnarani,
	devicetree, linux-arm-kernel, linux-kernel, linux-usb
  Cc: anirudh, anuragku
In-Reply-To: <1483536181-22356-1-git-send-email-mnarani@xilinx.com>

This patch adds support for OTG driver compilation and object
file creation

Signed-off-by: Manish Narani <mnarani@xilinx.com>
---
 drivers/usb/dwc3/Makefile | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index ffca340..2d269ad 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -17,6 +17,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
 	dwc3-y				+= gadget.o ep0.o
 endif
 
+ifneq ($(CONFIG_USB_DWC3_DUAL_ROLE),)
+	dwc3-y				+= otg.o
+endif
+
 ifneq ($(CONFIG_USB_DWC3_ULPI),)
 	dwc3-y				+= ulpi.o
 endif
-- 
2.1.1

^ permalink raw reply related

* [RFC PATCH] arch: arm64: dts: add USB OTG interrupts support in ZynqMP device tree
From: Manish Narani @ 2017-01-04 13:22 UTC (permalink / raw)
  To: robh+dt, mark.rutland, catalin.marinas, will.deacon, michal.simek,
	soren.brinkmann, balbi, gregkh, mathias.nyman, agraf, bharatku,
	punnaiah.choudary.kalluri, dhdang, marc.zyngier, mnarani,
	devicetree, linux-arm-kernel, linux-kernel, linux-usb
  Cc: anirudh, anuragku

This patch adds OTG interrupt support in device tree. It will add
an extra interrupt line number dedicated to OTG events. This will
enable OTG interrupts to serve in DWC3 OTG driver.

Signed-off-by: Manish Narani <mnarani@xilinx.com>
---
 arch/arm64/boot/dts/xilinx/zynqmp.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
index 68a90833..ce9ad02 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
+++ b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
@@ -351,7 +351,7 @@
 			compatible = "snps,dwc3";
 			status = "disabled";
 			interrupt-parent = <&gic>;
-			interrupts = <0 65 4>;
+			interrupts = <0 65 4>, <0 69 4>;
 			reg = <0x0 0xfe200000 0x0 0x40000>;
 			clock-names = "clk_xin", "clk_ahb";
 		};
-- 
2.1.1

^ permalink raw reply related

* Re: [PATCH] gpio: pca953x: Add optional reset gpio control
From: Rob Herring @ 2017-01-04 13:22 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: linus.walleij, gnurou, mark.rutland, linux-gpio, devicetree,
	linux-kernel, Steve Longerbeam
In-Reply-To: <1483391271-17304-2-git-send-email-steve_longerbeam@mentor.com>

On Mon, Jan 02, 2017 at 01:07:51PM -0800, Steve Longerbeam wrote:
> Add optional reset-gpios pin control. If present, de-assert the
> specified reset gpio pin to bring the chip out of reset.
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> Cc: Linus Walleij <linus.walleij@linaro.org>
> Cc: Alexandre Courbot <gnurou@gmail.com>
> Cc: linux-gpio@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> ---
>  Documentation/devicetree/bindings/gpio/gpio-pca953x.txt |  4 ++++
>  drivers/gpio/gpio-pca953x.c                             | 11 +++++++++++
>  2 files changed, 15 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt b/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt
> index 08dd15f..da54f4c 100644
> --- a/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt
> +++ b/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt
> @@ -29,6 +29,10 @@ Required properties:
>  	onsemi,pca9654
>  	exar,xra1202
>  
> +Optional properties:
> + - reset-gpios: GPIO specification for the RESET input

Need to specify active high or low.

> +
> +
>  Example:
>  
>  

^ permalink raw reply

* Re: [PATCH 14/22] dt-bindings: power: supply: add AXP20X/AXP22X battery DT binding
From: Rob Herring @ 2017-01-04 13:21 UTC (permalink / raw)
  To: Quentin Schulz
  Cc: jic23, knaack.h, lars, pmeerw, mark.rutland, wens, sre, linux,
	maxime.ripard, lee.jones, linux-iio, devicetree, linux-kernel,
	linux-pm, linux-arm-kernel, thomas.petazzoni, icenowy, bonbons
In-Reply-To: <20170102163723.7939-15-quentin.schulz@free-electrons.com>

On Mon, Jan 02, 2017 at 05:37:14PM +0100, Quentin Schulz wrote:
> The X-Powers AXP20X and AXP22X PMICs can have a battery as power supply.
> 
> This patch adds the DT binding documentation for the battery power
> supply which gets various data from the PMIC, such as the battery status
> (charging, discharging, full, dead), current max limit, current current,
> battery capacity (in percentage), voltage max and min limits, current
> voltage and battery capacity (in Ah).
> 
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> ---
>  .../bindings/power/supply/axp20x_battery.txt       | 27 ++++++++++++++++++++++
>  1 file changed, 27 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/power/supply/axp20x_battery.txt
> 
> diff --git a/Documentation/devicetree/bindings/power/supply/axp20x_battery.txt b/Documentation/devicetree/bindings/power/supply/axp20x_battery.txt
> new file mode 100644
> index 0000000..5489d0d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/supply/axp20x_battery.txt
> @@ -0,0 +1,27 @@
> +AXP20x and AXP22x battery power supply
> +
> +Required Properties:
> + - compatible, one of:
> +			"x-powers,axp209-battery-power-supply"
> +			"x-powers,axp221-battery-power-supply"
> + - io-channels: phandles to battery voltage, charge and discharge
> + currents ADC channels
> + - io-channel-names = "batt_v", "batt_chrg_i", "batt_dischrg_i";
> +
> +This node is a subnode of the axp20x/axp22x PMIC.
> +
> +The AXP20X and AXP22X can read the battery voltage, charge and discharge
> +currents of the battery by reading ADC channels from the AXP20X/AXP22X
> +ADC.
> +
> +Example:
> +
> +&axp209 {
> +	battery_power_supply: battery_power_supply {

Humm, I guess you power-supply is not sufficient, so 
'battery-power-supply' and similar for ac.

> +		compatible = "x-powers,axp209-battery-power-supply";
> +		io-channels = <&axp209_adc 7>, <&axp209_adc 8>,
> +			<&axp209_adc 9>;
> +		io-channel-names = "batt_v", "batt_chrg_i",
> +			"batt_dischrg_i";
> +	}
> +};
> -- 
> 2.9.3
> 

^ permalink raw reply

* Re: [PATCH 07/22] dt-bindings: power: supply: add AXP20X/AXP22X AC power supply
From: Rob Herring @ 2017-01-04 13:14 UTC (permalink / raw)
  To: Quentin Schulz
  Cc: jic23, knaack.h, lars, pmeerw, mark.rutland, wens, sre, linux,
	maxime.ripard, lee.jones, linux-iio, devicetree, linux-kernel,
	linux-pm, linux-arm-kernel, thomas.petazzoni, icenowy, bonbons
In-Reply-To: <20170102163723.7939-8-quentin.schulz@free-electrons.com>

On Mon, Jan 02, 2017 at 05:37:07PM +0100, Quentin Schulz wrote:
> The X-Powers AXP20X and AXP22X PMICs have an AC entry to supply power to
> the board. They have a few registers dedicated to the status of the AC
> power supply.
> 
> This adds the DT binding documentation for the AC power supply for
> AXP20X and AXP22X PMICs.
> 
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> ---
>  .../bindings/power/supply/axp20x_ac_power.txt      | 28 ++++++++++++++++++++++
>  1 file changed, 28 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/power/supply/axp20x_ac_power.txt
> 
> diff --git a/Documentation/devicetree/bindings/power/supply/axp20x_ac_power.txt b/Documentation/devicetree/bindings/power/supply/axp20x_ac_power.txt
> new file mode 100644
> index 0000000..16d0de4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/supply/axp20x_ac_power.txt
> @@ -0,0 +1,28 @@
> +AXP20X and AXP22X PMICs' AC power supply
> +
> +Required Properties:
> + - compatible: One of:
> +			"x-powers,axp202-ac-power-supply"
> +			"x-powers,axp221-ac-power-supply"
> +
> +More Required Properties for AXP20X PMICs:
> + - io-channels: phandles to ACIN voltage and current ADC channels
> + - io-channel-names = "acin_v", "acin_i";
> +
> +This node is a subnode of the axp20x PMIC.
> +
> +The AXP20X can read the current current and voltage supplied by AC by
> +reading ADC channels from the AXP20X ADC.
> +
> +The AXP22X is only able to tell if an AC power supply is present and
> +usable.
> +
> +Example:
> +
> +&axp209 {
> +	ac_power_supply: ac_power_supply {

power-supply {

> +		compatible = "x-powers,axp202-ac-power-supply";
> +		io-channels = <&axp209_adc 0>, <&axp209_adc 1>;

Is this assignment fixed? If so, then it doesn't need to be in DT.

> +		io-channel-names = "acin_v", "acin_i";
> +	};
> +};
> -- 
> 2.9.3
> 

^ permalink raw reply

* Re: [RFC 1/4] Documentation: devicetree: add multiple cpu port DSA binding
From: Andrew Lunn @ 2017-01-04 12:58 UTC (permalink / raw)
  To: John Crispin
  Cc: David S. Miller, Florian Fainelli, Vivien Didelot, netdev,
	Rob Herring, devicetree
In-Reply-To: <1483515484-21793-2-git-send-email-john@phrozen.org>

On Wed, Jan 04, 2017 at 08:38:01AM +0100, John Crispin wrote:
> From: Andrew Lunn <andrew@lunn.ch>
> 
> Extend the DSA binding documentation, adding the new properties required
> when there is more than one CPU port attached to the switch.

Hi John

Thanks for picking up my old patches.

> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: devicetree@vger.kernel.org
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>
> ---
>  Documentation/devicetree/bindings/net/dsa/dsa.txt |   67 ++++++++++++++++++++-
>  1 file changed, 66 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/net/dsa/dsa.txt b/Documentation/devicetree/bindings/net/dsa/dsa.txt
> index a4a570f..fc901cf 100644
> --- a/Documentation/devicetree/bindings/net/dsa/dsa.txt
> +++ b/Documentation/devicetree/bindings/net/dsa/dsa.txt
> @@ -337,13 +337,25 @@ Optional property:
>  			  This mii-bus will be used in preference to the
>  			  global dsa,mii-bus defined above, for this switch.
>  
> +- ethernet		: Optional for "cpu" ports. A phandle to an ethernet
> +                          device which will be used by this CPU port for
> +			  passing packets to/from the host. If not present,
> +			  the port will use the "dsa,ethernet" property
> +			  defined above.

This appears to be for the old binding. The new binding has this
already.  I only want to support multiple CPU ports with the new
binding, since Florian is in the process of removing the old one.

> +
> +- cpu			: Option for non "cpu"/"dsa" ports. A phandle to a
> +			  "cpu" port, which will be used for passing packets
> +			  from this port to the host. If not present, the first
> +			  "cpu" port will be used.
> +
> +
>  Optional subnodes:
>  - fixed-link		: Fixed-link subnode describing a link to a non-MDIO
>  			  managed entity. See
>  			  Documentation/devicetree/bindings/net/fixed-link.txt
>  			  for details.
>  
> -Example:
> +Examples:
>  
>  	dsa@0 {
>  		compatible = "marvell,dsa";
> @@ -416,3 +428,56 @@ Example:
>  			};
>  		};
>  	};
> +
> +	dsa@1 {
> +		compatible = "marvell,dsa";
> +		#address-cells = <2>;
> +		#size-cells = <0>;
> +
> +		dsa,ethernet = <&eth0port>;
> +		dsa,mii-bus = <&mdio>;
> +
> +		switch@0 {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +			reg = <0 0>;	/* MDIO address 0, switch 0 in tree */
> +
> +			port@0 {
> +				reg = <0>;
> +				label = "lan4";
> +			};
> +
> +			port@1 {
> +				reg = <1>;
> +				label = "lan3";
> +				cpu = <&cpu1>;
> +			};

Again, this is the old binding. The example should use the new
binding.

Thanks
	Andrew

^ permalink raw reply

* Re: [PATCH v2 09/19] ARM: dts: imx6-sabreauto: add the ADV7180 video decoder
From: Vladimir Zapolskiy @ 2017-01-04 12:41 UTC (permalink / raw)
  To: Steve Longerbeam, shawnguo, kernel, fabio.estevam, robh+dt,
	mark.rutland, linux, mchehab, gregkh, p.zabel
  Cc: devel, devicetree, Steve Longerbeam, linux-kernel,
	linux-arm-kernel, linux-media
In-Reply-To: <1483477049-19056-10-git-send-email-steve_longerbeam@mentor.com>

On 01/03/2017 10:57 PM, Steve Longerbeam wrote:
> Enables the ADV7180 decoder sensor. The ADV7180 connects to the
> parallel-bus mux input on ipu1_csi0_mux.
> 
> On the sabreauto, two analog video inputs are routed to the ADV7180,
> composite on Ain1, and composite on Ain3. Those inputs are defined
> via inputs and input-names under the ADV7180 node. The ADV7180 power
> pin is via max7310_b port expander.
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>  arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 56 ++++++++++++++++++++++++++++++++
>  1 file changed, 56 insertions(+)
> 
> diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
> index 83ac2ff..30ee378 100644
> --- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
> +++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
> @@ -147,10 +147,42 @@
>  				gpio-controller;
>  				#gpio-cells = <2>;
>  			};
> +
> +			camera: adv7180@21 {

adv7180: camera@21

> +				compatible = "adi,adv7180";
> +				reg = <0x21>;
> +				powerdown-gpios = <&max7310_b 2 GPIO_ACTIVE_LOW>;
> +				interrupt-parent = <&gpio1>;
> +				interrupts = <27 0x8>;
> +				inputs = <0x00 0x02>;
> +				input-names = "ADV7180 Composite on Ain1",
> +						"ADV7180 Composite on Ain3";
> +
> +				port {
> +					adv7180_to_ipu1_csi0_mux: endpoint {
> +						remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
> +						bus-width = <8>;
> +					};
> +				};
> +			};
>  		};
>  	};
>  };
>  
> +&ipu1_csi0_from_ipu1_csi0_mux {
> +	bus-width = <8>;
> +};
> +
> +&ipu1_csi0_mux_from_parallel_sensor {
> +	remote-endpoint = <&adv7180_to_ipu1_csi0_mux>;
> +	bus-width = <8>;
> +};
> +
> +&ipu1_csi0 {
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&pinctrl_ipu1_csi0>;
> +};
> +
>  &clks {
>  	assigned-clocks = <&clks IMX6QDL_PLL4_BYPASS_SRC>,
>  			  <&clks IMX6QDL_PLL4_BYPASS>,
> @@ -451,6 +483,30 @@
>  			>;
>  		};
>  
> +		pinctrl_ipu1_csi0: ipu1grp-csi0 {

Please rename node name to ipu1csi0grp.

> +			fsl,pins = <
> +				MX6QDL_PAD_CSI0_DAT4__IPU1_CSI0_DATA04   0x80000000
> +				MX6QDL_PAD_CSI0_DAT5__IPU1_CSI0_DATA05   0x80000000
> +				MX6QDL_PAD_CSI0_DAT6__IPU1_CSI0_DATA06   0x80000000
> +				MX6QDL_PAD_CSI0_DAT7__IPU1_CSI0_DATA07   0x80000000
> +				MX6QDL_PAD_CSI0_DAT8__IPU1_CSI0_DATA08   0x80000000
> +				MX6QDL_PAD_CSI0_DAT9__IPU1_CSI0_DATA09   0x80000000
> +				MX6QDL_PAD_CSI0_DAT10__IPU1_CSI0_DATA10  0x80000000
> +				MX6QDL_PAD_CSI0_DAT11__IPU1_CSI0_DATA11  0x80000000
> +				MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12  0x80000000
> +				MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13  0x80000000
> +				MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14  0x80000000
> +				MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15  0x80000000
> +				MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16  0x80000000
> +				MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17  0x80000000
> +				MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18  0x80000000
> +				MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19  0x80000000
> +				MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK 0x80000000
> +				MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC    0x80000000
> +				MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC   0x80000000
> +			>;
> +		};
> +
>  		pinctrl_pwm3: pwm1grp {
>  			fsl,pins = <
>  				MX6QDL_PAD_SD4_DAT1__PWM3_OUT		0x1b0b1
> 

--
With best wishes,
Vladimir

^ permalink raw reply

* [PATCH V2 5/5] ARM: dts: exynos5440: support the phy-pcie node for pcie
From: Jaehoon Chung @ 2017-01-04 12:34 UTC (permalink / raw)
  To: linux-pci
  Cc: devicetree, linux-kernel, linux-samsung-soc, bhelgaas, robh+dt,
	mark.rutland, kgene, krzk, kishon, jingoohan1, vivek.gautam,
	pankaj.dubey, alim.akhtar, cpgs, Jaehoon Chung
In-Reply-To: <20170104123435.30740-1-jh80.chung@samsung.com>

Add phy-pcie node for using Exynos5440 pcie.
And use the reg-names as "elbi" and "config".
Because the getting configuratioin space address from ranges is old way.
It also is helpful to distinguish more clearly.

Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
Changelog on V2:
- Removes the child-node
- Fixes the typo
- Removes the unnecessary comments

 arch/arm/boot/dts/exynos5440.dtsi | 34 ++++++++++++++++++++++------------
 1 file changed, 22 insertions(+), 12 deletions(-)

diff --git a/arch/arm/boot/dts/exynos5440.dtsi b/arch/arm/boot/dts/exynos5440.dtsi
index 2a2e570..feb074d 100644
--- a/arch/arm/boot/dts/exynos5440.dtsi
+++ b/arch/arm/boot/dts/exynos5440.dtsi
@@ -290,11 +290,22 @@
 		clock-names = "usbhost";
 	};
 
+	pcie_phy0: pcie-phy@270000 {
+		#phy-cells = <0>;
+		compatible = "samsung,exynos5440-pcie-phy";
+		reg = <0x270000 0x1000>, <0x271000 0x40>;
+	};
+
+	pcie_phy1: pcie-phy@272000 {
+		#phy-cells = <0>;
+		compatible = "samsung,exynos5440-pcie-phy";
+		reg = <0x272000 0x1000>, <0x271040 0x40>;
+	};
+
 	pcie_0: pcie@290000 {
 		compatible = "samsung,exynos5440-pcie", "snps,dw-pcie";
-		reg = <0x290000 0x1000
-			0x270000 0x1000
-			0x271000 0x40>;
+		reg = <0x290000 0x1000>, <0x40000000 0x1000>;
+		reg-names = "elbi", "config";
 		interrupts = <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>,
 			     <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>,
 			     <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>;
@@ -303,9 +314,9 @@
 		#address-cells = <3>;
 		#size-cells = <2>;
 		device_type = "pci";
-		ranges = <0x00000800 0 0x40000000 0x40000000 0 0x00001000   /* configuration space */
-			  0x81000000 0 0	  0x40001000 0 0x00010000   /* downstream I/O */
-			  0x82000000 0 0x40011000 0x40011000 0 0x1ffef000>; /* non-prefetchable memory */
+		phys = <&pcie_phy0>;
+		ranges = <0x81000000 0 0	  0x40001000 0 0x00010000
+			  0x82000000 0 0x40011000 0x40011000 0 0x1ffef000>;
 		#interrupt-cells = <1>;
 		interrupt-map-mask = <0 0 0 0>;
 		interrupt-map = <0x0 0 &gic 53>;
@@ -315,9 +326,8 @@
 
 	pcie_1: pcie@2a0000 {
 		compatible = "samsung,exynos5440-pcie", "snps,dw-pcie";
-		reg = <0x2a0000 0x1000
-			0x272000 0x1000
-			0x271040 0x40>;
+		reg = <0x2a0000 0x1000>, <0x60000000 0x1000>;
+		reg-names = "elbi", "config";
 		interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>,
 			     <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>,
 			     <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
@@ -326,9 +336,9 @@
 		#address-cells = <3>;
 		#size-cells = <2>;
 		device_type = "pci";
-		ranges = <0x00000800 0 0x60000000 0x60000000 0 0x00001000   /* configuration space */
-			  0x81000000 0 0	  0x60001000 0 0x00010000   /* downstream I/O */
-			  0x82000000 0 0x60011000 0x60011000 0 0x1ffef000>; /* non-prefetchable memory */
+		phys = <&pcie_phy1>;
+		ranges = <0x81000000 0 0	  0x60001000 0 0x00010000
+			  0x82000000 0 0x60011000 0x60011000 0 0x1ffef000>;
 		#interrupt-cells = <1>;
 		interrupt-map-mask = <0 0 0 0>;
 		interrupt-map = <0x0 0 &gic 56>;
-- 
2.10.2

^ permalink raw reply related

* [PATCH V2 4/5] PCI: exynos: support the using PHY generic framework
From: Jaehoon Chung @ 2017-01-04 12:34 UTC (permalink / raw)
  To: linux-pci
  Cc: devicetree, linux-kernel, linux-samsung-soc, bhelgaas, robh+dt,
	mark.rutland, kgene, krzk, kishon, jingoohan1, vivek.gautam,
	pankaj.dubey, alim.akhtar, cpgs, Jaehoon Chung
In-Reply-To: <20170104123435.30740-1-jh80.chung@samsung.com>

This patch is for using PHY generic framework.
To maintain backward compatibility, check whether phy is supported or
not with 'using_phy'.

And if someone use the old dt-file, display the "deprecated" message.
But it's still working fine with it.

Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
Changelog on V2:
- This patch is split from previous PATCH[1/4]
- Maintain the backward compatibility
- Adds 'using_phy' for cheching whether phy framework is used or not
- Adds 'DEPRECATED' message for old dt-binding way

 drivers/pci/host/pci-exynos.c | 61 +++++++++++++++++++++++++++++++++++--------
 1 file changed, 50 insertions(+), 11 deletions(-)

diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
index feed0fd..34f2eed 100644
--- a/drivers/pci/host/pci-exynos.c
+++ b/drivers/pci/host/pci-exynos.c
@@ -21,6 +21,7 @@
 #include <linux/of_gpio.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
+#include <linux/phy/phy.h>
 #include <linux/resource.h>
 #include <linux/signal.h>
 #include <linux/types.h>
@@ -110,6 +111,10 @@ struct exynos_pcie {
 	struct exynos_pcie_clk_res	*clk_res;
 	const struct exynos_pcie_ops	*ops;
 	int				reset_gpio;
+
+	/* For Generic PHY Framework */
+	bool				using_phy;
+	struct phy			*phy;
 };
 
 struct exynos_pcie_ops {
@@ -135,6 +140,10 @@ static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev,
 	if (IS_ERR(ep->mem_res->elbi_base))
 		return PTR_ERR(ep->mem_res->elbi_base);
 
+	/* If using the PHY framework, doesn't need to get other resource */
+	if (ep->using_phy)
+		return 0;
+
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 	ep->mem_res->phy_base = devm_ioremap_resource(dev, res);
 	if (IS_ERR(ep->mem_res->phy_base))
@@ -396,17 +405,28 @@ static int exynos_pcie_establish_link(struct exynos_pcie *exynos_pcie)
 	}
 
 	exynos_pcie_assert_core_reset(exynos_pcie);
-	exynos_pcie_assert_phy_reset(exynos_pcie);
-	exynos_pcie_deassert_phy_reset(exynos_pcie);
-	exynos_pcie_power_on_phy(exynos_pcie);
-	exynos_pcie_init_phy(exynos_pcie);
-
-	/* pulse for common reset */
-	exynos_pcie_writel(exynos_pcie->mem_res->block_base, 1,
-				PCIE_PHY_COMMON_RESET);
-	udelay(500);
-	exynos_pcie_writel(exynos_pcie->mem_res->block_base, 0,
-				PCIE_PHY_COMMON_RESET);
+
+	if (exynos_pcie->using_phy) {
+		phy_reset(exynos_pcie->phy);
+
+		exynos_pcie_writel(exynos_pcie->mem_res->elbi_base, 1,
+				PCIE_PWR_RESET);
+
+		phy_power_on(exynos_pcie->phy);
+		phy_init(exynos_pcie->phy);
+	} else {
+		exynos_pcie_assert_phy_reset(exynos_pcie);
+		exynos_pcie_deassert_phy_reset(exynos_pcie);
+		exynos_pcie_power_on_phy(exynos_pcie);
+		exynos_pcie_init_phy(exynos_pcie);
+
+		/* pulse for common reset */
+		exynos_pcie_writel(exynos_pcie->mem_res->block_base, 1,
+					PCIE_PHY_COMMON_RESET);
+		udelay(500);
+		exynos_pcie_writel(exynos_pcie->mem_res->block_base, 0,
+					PCIE_PHY_COMMON_RESET);
+	}
 
 	exynos_pcie_deassert_core_reset(exynos_pcie);
 	dw_pcie_setup_rc(pp);
@@ -420,6 +440,11 @@ static int exynos_pcie_establish_link(struct exynos_pcie *exynos_pcie)
 	if (!dw_pcie_wait_for_link(pp))
 		return 0;
 
+	if (exynos_pcie->using_phy) {
+		phy_power_off(exynos_pcie->phy);
+		return -ETIMEDOUT;
+	}
+
 	while (exynos_pcie_readl(exynos_pcie->mem_res->phy_base,
 				PCIE_PHY_PLL_LOCKED) == 0) {
 		val = exynos_pcie_readl(exynos_pcie->mem_res->block_base,
@@ -633,6 +658,17 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
 
 	exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
 
+	/* Assume that controller doesn't use the PHY framework */
+	exynos_pcie->using_phy = false;
+
+	exynos_pcie->phy = devm_of_phy_get(dev, np, NULL);
+	if (IS_ERR(exynos_pcie->phy)) {
+		if (PTR_ERR(exynos_pcie->phy) == -EPROBE_DEFER)
+			return PTR_ERR(exynos_pcie->phy);
+		dev_warn(dev, "Use the 'phy' property. Current DT of pci-exynos was deprecated!!\n");
+	} else
+		exynos_pcie->using_phy = true;
+
 	if (exynos_pcie->ops && exynos_pcie->ops->get_mem_resources) {
 		ret = exynos_pcie->ops->get_mem_resources(pdev, exynos_pcie);
 		if (ret)
@@ -657,6 +693,9 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
 	return 0;
 
 fail_probe:
+	if (exynos_pcie->using_phy)
+		phy_exit(exynos_pcie->phy);
+
 	if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
 		exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
 	return ret;
-- 
2.10.2

^ permalink raw reply related

* [PATCH V2 3/5] Documetation: binding: modify the exynos5440 pcie binding
From: Jaehoon Chung @ 2017-01-04 12:34 UTC (permalink / raw)
  To: linux-pci
  Cc: devicetree, linux-kernel, linux-samsung-soc, bhelgaas, robh+dt,
	mark.rutland, kgene, krzk, kishon, jingoohan1, vivek.gautam,
	pankaj.dubey, alim.akhtar, cpgs, Jaehoon Chung
In-Reply-To: <20170104123435.30740-1-jh80.chung@samsung.com>

According to using PHY framework, updates the exynos5440-pcie binding.
For maintaining backward compatibility, leaves the current dt-binding.
(It should be deprecated.)

Recommends to use the Phy Framework and "config" property to follow
the designware-pcie binding.
If you use the old way, can see "mssing *config* reg space" message.
Because the getting configuration space address from range is old way.

NOTE: When use the "config" property, first name of 'reg-names' must be
set to "elbi". Otherwise driver can't maintain the backward capability.

Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
Changelog on V2:
- Describes more commit message
- Fixes the typos
- Adds the new example for using PHY framework
- Deprecated the old dt-binding description
- Removes 'phy-names'

 .../bindings/pci/samsung,exynos5440-pcie.txt       | 29 ++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/Documentation/devicetree/bindings/pci/samsung,exynos5440-pcie.txt b/Documentation/devicetree/bindings/pci/samsung,exynos5440-pcie.txt
index 4f9d23d..1d0af0e 100644
--- a/Documentation/devicetree/bindings/pci/samsung,exynos5440-pcie.txt
+++ b/Documentation/devicetree/bindings/pci/samsung,exynos5440-pcie.txt
@@ -7,8 +7,19 @@ Required properties:
 - compatible: "samsung,exynos5440-pcie"
 - reg: base addresses and lengths of the pcie controller,
 	the phy controller, additional register for the phy controller.
+	(Registers for the phy controller are DEPRECATED.
+	 Use the PHY framework.)
+- reg-names : First name should be set to "elbi".
+	And use the "config" instead of getting the confgiruation address space
+	from "ranges".
+	NOTE: When use the "config" property, reg-names must be set.
 - interrupts: A list of interrupt outputs for level interrupt,
 	pulse interrupt, special interrupt.
+- phys: From PHY binding. Phandle for the Generic PHY.
+	Refer to Documentation/devicetree/bindings/phy/samsung-phy.txt
+
+Other common properties refer to
+	Documentation/devicetree/binding/pci/designware-pcie.txt
 
 Example:
 
@@ -54,6 +65,24 @@ SoC specific DT Entry:
 		num-lanes = <4>;
 	};
 
+With using PHY framework:
+	pcie_phy0: pcie-phy@270000 {
+		...
+		reg = <0x270000 0x1000>, <0x271000 0x40>;
+		regn-names = "phy", "block";
+		...
+	};
+
+	pcie@290000 {
+		...
+		reg = <0x290000 0x1000>, <0x40000000 0x1000>;
+		reg-names = "elbi", "config";
+		phys = <&pcie_phy0>;
+		ranges = <0x81000000 0 0	  0x60001000 0 0x00010000
+			  0x82000000 0 0x60011000 0x60011000 0 0x1ffef000>;
+		...
+	};
+
 Board specific DT Entry:
 
 	pcie@290000 {
-- 
2.10.2

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox