Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] rtc: armada38x: add __ro_after_init to armada38x_rtc_ops
From: Russell King - ARM Linux @ 2017-01-04 12:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <alpine.DEB.2.20.1701041242080.3073@hadrien>

On Wed, Jan 04, 2017 at 12:43:32PM +0100, Julia Lawall wrote:
> > The question was whether the point to the rtc_class_ops could be made
> > __ro_after_init. And Russell is right, it is pointed to by the ops
> > pointer in a struct rtc_device and that struct is dynamically allocated
> > in rtc_device_register().
> 
> OK, I think it's a terminology issue.  You mean the structure that
> contains the pointer, and not the pointer itself, which is already const.

That statement is really ambiguous, and really doesn't help the cause -
we have several structures here which contain pointers and it's far from
clear which you're talking about:

- The armada38x_rtc_ops and armada38x_rtc_ops_noirq structures contain
  pointers to functions.
- The dynamically allocated struct rtc_device contains an ops pointer,
  which will point at one or other of these two structures.

Now, as soon as we make armada38x_rtc_ops and armada38x_rtc_ops_noirq
const, if the pointer is passed through any function call where the
argument is not also marked const, or is assigned to a pointer that is
not marked const (without an explicit cast), the compiler will complain.
Remember that a const pointer (iow, const void *ptr) is just a hint to
the compiler that "ptr" _may_ point at read-only data, and dereferences
of the pointer are not allowed to write - it's just syntactic checking.

Given that this is stuff we should all know, I'm not quite sure what
people are getting in a tiz over... I'm finding it worrying that I'm
even writing this mail, reviewing this stuff!  _Really_ worried that
Kees even brought it up in the first place - I suspect that came from
a misunderstanding of my suggestion which is why I later provided the
suggestion in patch form.

What I suggested, and what my patch does is:

1. It places both the armada38x_rtc_ops and armada38x_rtc_ops_noirq
   structures into the .rodata section, which will be protected from
   writes by hardware when appropriate kernel options are enabled.

2. The driver does _not_ store a local pointer to this memory at a
   static address which could be subsequently modified (*).

3. The only pointer to this memory is during driver initialisation
   (which may well reside in a CPU register only) before being passed
   to the RTC subsystem.

4. The RTC subsystem dynamically allocates a struct rtc_device
   structure (in rtc_device_register()) where it eventually stores
   this pointer.  This pointer is already marked const.  This structure
   contains read/write data, and can't be marked read-only, just in the
   same way as "struct file" can't be.

The whole __ro_after_init thing is completely irrelevant and a total
distraction at this point - there is nothing that you could add a
__ro_after_init annotation to after my patch in regard of these ops
structures.

* - however, a compiler may decide to store the addresses of these
structures as a literal constant near the function, but with RONX
protection for the .text section, this memory is also read-only, and
so can't be modified.

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply

* [PATCH 1/2] mm: don't dereference struct page fields of invalid pages
From: Will Deacon @ 2017-01-04 12:16 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1481706707-6211-2-git-send-email-ard.biesheuvel@linaro.org>

On Wed, Dec 14, 2016 at 09:11:46AM +0000, Ard Biesheuvel wrote:
> The VM_BUG_ON() check in move_freepages() checks whether the node
> id of a page matches the node id of its zone. However, it does this
> before having checked whether the struct page pointer refers to a
> valid struct page to begin with. This is guaranteed in most cases,
> but may not be the case if CONFIG_HOLES_IN_ZONE=y.
> 
> So reorder the VM_BUG_ON() with the pfn_valid_within() check.
> 
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  mm/page_alloc.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/mm/page_alloc.c b/mm/page_alloc.c
> index f64e7bcb43b7..4e298e31fa86 100644
> --- a/mm/page_alloc.c
> +++ b/mm/page_alloc.c
> @@ -1864,14 +1864,14 @@ int move_freepages(struct zone *zone,
>  #endif
>  
>  	for (page = start_page; page <= end_page;) {
> -		/* Make sure we are not inadvertently changing nodes */
> -		VM_BUG_ON_PAGE(page_to_nid(page) != zone_to_nid(zone), page);
> -
>  		if (!pfn_valid_within(page_to_pfn(page))) {
>  			page++;
>  			continue;
>  		}
>  
> +		/* Make sure we are not inadvertently changing nodes */
> +		VM_BUG_ON_PAGE(page_to_nid(page) != zone_to_nid(zone), page);
> +
>  		if (!PageBuddy(page)) {
>  			page++;
>  			continue;

Acked-by: Will Deacon <will.deacon@arm.com>

I'm guessing akpm can pick this up as a non-urgent fix.

Will

^ permalink raw reply

* arm64: virt_to_page() does not return right page for a kernel image address
From: Pratyush Anand @ 2017-01-04 12:23 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170104120602.GG8329@leverpostej>



On Wednesday 04 January 2017 05:36 PM, Mark Rutland wrote:
> So it seems we need to fix the crypto test.
>
> Looking at crypto/testmgr.c, I can't spot the kmap_atomic() or the
> page_address()/virt_to_page(). I guess that's hidden behind helpers,
> which might also be used elsewhere?
>
> Could you elaborate on where exactly the problem is in the crypto test?

We have in test_acomp() -> crypto_acomp_decompress() -> 
tfm->decompress() -> scomp_acomp_decompress() -> 
scomp_acomp_comp_decomp() -> scatterwalk_map_and_copy() -> 
scatterwalk_copychunks()

  41                 if (out != 2) {
  42                         vaddr = scatterwalk_map(walk);
  43                         memcpy_dir(buf, vaddr, len_this_page, out);
  44                         scatterwalk_unmap(vaddr);
  45                 }


scatterwalk_map() gets vaddr from kmap_atomic().

test_acomp() initializes sg:
sg_init_one(&src, ctemplate[i].input, ilen);

ctemplate is a kernel address (like lzo_comp_tv_template), which was 
assigned to walk->sg latter and passed to kmap_atomic().


~Pratyush

^ permalink raw reply

* [PATCH] rtc: armada38x: add __ro_after_init to armada38x_rtc_ops
From: Julia Lawall @ 2017-01-04 12:23 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170104121425.GP14217@n2100.armlinux.org.uk>



On Wed, 4 Jan 2017, Russell King - ARM Linux wrote:

> On Wed, Jan 04, 2017 at 12:43:32PM +0100, Julia Lawall wrote:
> > > The question was whether the point to the rtc_class_ops could be made
> > > __ro_after_init. And Russell is right, it is pointed to by the ops
> > > pointer in a struct rtc_device and that struct is dynamically allocated
> > > in rtc_device_register().
> >
> > OK, I think it's a terminology issue.  You mean the structure that
> > contains the pointer, and not the pointer itself, which is already const.
>
> That statement is really ambiguous, and really doesn't help the cause -
> we have several structures here which contain pointers and it's far from
> clear which you're talking about:
>
> - The armada38x_rtc_ops and armada38x_rtc_ops_noirq structures contain
>   pointers to functions.
> - The dynamically allocated struct rtc_device contains an ops pointer,
>   which will point at one or other of these two structures.
>
> Now, as soon as we make armada38x_rtc_ops and armada38x_rtc_ops_noirq
> const, if the pointer is passed through any function call where the
> argument is not also marked const, or is assigned to a pointer that is
> not marked const (without an explicit cast), the compiler will complain.
> Remember that a const pointer (iow, const void *ptr) is just a hint to
> the compiler that "ptr" _may_ point at read-only data, and dereferences
> of the pointer are not allowed to write - it's just syntactic checking.
>
> Given that this is stuff we should all know, I'm not quite sure what
> people are getting in a tiz over... I'm finding it worrying that I'm
> even writing this mail, reviewing this stuff!  _Really_ worried that
> Kees even brought it up in the first place - I suspect that came from
> a misunderstanding of my suggestion which is why I later provided the
> suggestion in patch form.
>
> What I suggested, and what my patch does is:
>
> 1. It places both the armada38x_rtc_ops and armada38x_rtc_ops_noirq
>    structures into the .rodata section, which will be protected from
>    writes by hardware when appropriate kernel options are enabled.
>
> 2. The driver does _not_ store a local pointer to this memory at a
>    static address which could be subsequently modified (*).
>
> 3. The only pointer to this memory is during driver initialisation
>    (which may well reside in a CPU register only) before being passed
>    to the RTC subsystem.
>
> 4. The RTC subsystem dynamically allocates a struct rtc_device
>    structure (in rtc_device_register()) where it eventually stores
>    this pointer.  This pointer is already marked const.  This structure
>    contains read/write data, and can't be marked read-only, just in the
>    same way as "struct file" can't be.
>
> The whole __ro_after_init thing is completely irrelevant and a total
> distraction at this point - there is nothing that you could add a
> __ro_after_init annotation to after my patch in regard of these ops
> structures.
>
> * - however, a compiler may decide to store the addresses of these
> structures as a literal constant near the function, but with RONX
> protection for the .text section, this memory is also read-only, and
> so can't be modified.

Thanks for the explanation.  I understood the patch, just not Kees's
question.

Basically, the strategy of the patch is that one may consider it
preferable to duplicate the structure for the different alternatives,
rather than use __ro_after_init.  Perhaps if the structure were larger,
then __ro_after_init would be a better choice?

thanks,
julia

^ permalink raw reply

* [PATCH v2 04/19] ARM: dts: imx6-sabrelite: add OV5642 and OV5640 camera sensors
From: Vladimir Zapolskiy @ 2017-01-04 12:25 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483477049-19056-5-git-send-email-steve_longerbeam@mentor.com>

Hi Steve,

On 01/03/2017 10:57 PM, Steve Longerbeam wrote:
> Enables the OV5642 parallel-bus sensor, and the OV5640 MIPI CSI-2 sensor.
> Both hang off the same i2c2 bus, so they require different (and non-
> default) i2c slave addresses.
> 
> The OV5642 connects to the parallel-bus mux input port on ipu1_csi0_mux.
> 
> The OV5640 connects to the input port on the MIPI CSI-2 receiver on
> mipi_csi. It is set to transmit over MIPI virtual channel 1.
> 
> Note there is a pin conflict with GPIO6. This pin functions as a power
> input pin to the OV5642, but ENET uses it as the h/w workaround for
> erratum ERR006687, to wake-up the ARM cores on normal RX and TX packet
> done events (see 6261c4c8). So workaround 6261c4c8 is reverted here to
> support the OV5642, and the "fsl,err006687-workaround-present" boolean
> also must be removed. The result is that the CPUidle driver will no longer
> allow entering the deep idle states on the sabrelite.

For me it sounds like a candidate of its own separate change.

> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---

[snip]

>  &audmux {
>  	pinctrl-names = "default";
>  	pinctrl-0 = <&pinctrl_audmux>;
> @@ -271,9 +298,6 @@
>  	txd1-skew-ps = <0>;
>  	txd2-skew-ps = <0>;
>  	txd3-skew-ps = <0>;
> -	interrupts-extended = <&gpio1 6 IRQ_TYPE_LEVEL_HIGH>,
> -			      <&intc 0 119 IRQ_TYPE_LEVEL_HIGH>;

Like you say in the commit message this is a partial revert of 6261c4c8f13e
("ARM: dts: imx6qdl-sabrelite: use GPIO_6 for FEC interrupt.")

> -	fsl,err006687-workaround-present;

This is a partial revert of a28eeb43ee57 ("ARM: dts: imx6: tag boards that
have the HW workaround for ERR006687").

The change should be split and reviewed separately in my opinion, also
cc Gary Bisson <gary.bisson@boundarydevices.com> for SabreLite changes.

>  	status = "okay";
>  };
>  
> @@ -302,6 +326,52 @@
>  	pinctrl-names = "default";
>  	pinctrl-0 = <&pinctrl_i2c2>;
>  	status = "okay";
> +
> +	camera: ov5642 at 42 {
> +		compatible = "ovti,ov5642";
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&pinctrl_ov5642>;
> +		clocks = <&clks IMX6QDL_CLK_CKO2>;
> +		clock-names = "xclk";
> +		reg = <0x42>;
> +		xclk = <24000000>;
> +		reset-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;
> +		pwdn-gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>;
> +		gp-gpios = <&gpio1 16 GPIO_ACTIVE_HIGH>;
> +
> +		port {
> +			ov5642_to_ipu1_csi0_mux: endpoint {
> +				remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
> +				bus-width = <8>;
> +				hsync-active = <1>;
> +				vsync-active = <1>;
> +			};
> +		};
> +	};
> +
> +	mipi_camera: ov5640 at 40 {

Please reorder device nodes by address value, also according to ePAPR
node names should be generic, labels can be specific:

	ov5640: camera at 40 {
		...
	};

	ov5642: camera at 42 {
		...
	};

> +		compatible = "ovti,ov5640_mipi";
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&pinctrl_ov5640>;
> +		clocks = <&mipi_xclk>;
> +		clock-names = "xclk";
> +		reg = <0x40>;
> +		xclk = <22000000>;
> +		reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; /* NANDF_D5 */
> +		pwdn-gpios = <&gpio6 9 GPIO_ACTIVE_HIGH>; /* NANDF_WP_B */
> +
> +		port {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +
> +			ov5640_to_mipi_csi: endpoint at 1 {
> +				reg = <1>;
> +				remote-endpoint = <&mipi_csi_from_mipi_sensor>;
> +				data-lanes = <0 1>;
> +				clock-lanes = <2>;
> +			};
> +		};
> +	};
>  };
>  
>  &i2c3 {
> @@ -374,7 +444,6 @@
>  				MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL	0x1b030
>  				/* Phy reset */
>  				MX6QDL_PAD_EIM_D23__GPIO3_IO23		0x000b0
> -				MX6QDL_PAD_GPIO_6__ENET_IRQ		0x000b1

Yep.

>  			>;
>  		};
>  
> @@ -449,6 +518,39 @@
>  			>;
>  		};
>  
> +		pinctrl_ov5642: ov5642grp {
> +			fsl,pins = <
> +				MX6QDL_PAD_SD1_DAT0__GPIO1_IO16 0x80000000
> +				MX6QDL_PAD_GPIO_6__GPIO1_IO06   0x80000000
> +				MX6QDL_PAD_GPIO_8__GPIO1_IO08   0x80000000
> +				MX6QDL_PAD_GPIO_3__CCM_CLKO2    0x80000000
> +			>;
> +		};
> +
> +		pinctrl_ipu1_csi0: ipu1grp-csi0 {

Please rename node name to ipu1csi0grp.

> +			fsl,pins = <
> +				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
> +				MX6QDL_PAD_CSI0_DATA_EN__IPU1_CSI0_DATA_EN 0x80000000
> +			>;
> +		};
> +
> +                pinctrl_ov5640: ov5640grp {
> +                        fsl,pins = <
> +				MX6QDL_PAD_NANDF_D5__GPIO2_IO05 0x000b0
> +				MX6QDL_PAD_NANDF_WP_B__GPIO6_IO09 0x0b0b0
> +                        >;
> +                };
> +

Indentation issues above, please use tabs instead of spaces.

Also please add new pin control groups preserving the alphanimerical order.

--
With best wishes,
Vladimir

^ permalink raw reply

* [PATCH v2 05/19] ARM: dts: imx6-sabresd: add OV5642 and OV5640 camera sensors
From: Vladimir Zapolskiy @ 2017-01-04 12:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483477049-19056-6-git-send-email-steve_longerbeam@mentor.com>

On 01/03/2017 10:57 PM, Steve Longerbeam wrote:
> Enables the OV5642 parallel-bus sensor, and the OV5640 MIPI CSI-2 sensor.
> 
> The OV5642 connects to the parallel-bus mux input port on ipu1_csi0_mux.
> 
> The OV5640 connects to the input port on the MIPI CSI-2 receiver on
> mipi_csi. It is set to transmit over MIPI virtual channel 1.
> 
> Until the OV5652 sensor module compatible with the SabreSD becomes
> available for testing, the ov5642 node is currently disabled.
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---

[snip]

> +
> +	camera: ov5642 at 3c {

ov5642: camera at 3c

> +		compatible = "ovti,ov5642";
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&pinctrl_ov5642>;
> +		clocks = <&clks IMX6QDL_CLK_CKO>;
> +		clock-names = "xclk";
> +		reg = <0x3c>;
> +		xclk = <24000000>;
> +		DOVDD-supply = <&vgen4_reg>; /* 1.8v */
> +		AVDD-supply = <&vgen5_reg>;  /* 2.8v, rev C board is VGEN3
> +						rev B board is VGEN5 */
> +		DVDD-supply = <&vgen2_reg>;  /* 1.5v*/
> +		pwdn-gpios = <&gpio1 16 GPIO_ACTIVE_HIGH>; /* SD1_DAT0 */
> +		reset-gpios = <&gpio1 17 GPIO_ACTIVE_LOW>; /* SD1_DAT1 */

Comments about SD1_* pad names are redundant.

> +		status = "disabled";

Why is it disabled here?

> +
> +		port {
> +			ov5642_to_ipu1_csi0_mux: endpoint {
> +				remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
> +				bus-width = <8>;
> +				hsync-active = <1>;
> +				vsync-active = <1>;
> +			};
> +		};
> +	};
>  };
>  
>  &i2c2 {
> @@ -322,6 +376,34 @@
>  			};
>  		};
>  	};
> +
> +	mipi_camera: ov5640 at 3c {

ov5640: camera at 3c

> +		compatible = "ovti,ov5640_mipi";
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&pinctrl_ov5640>;
> +		reg = <0x3c>;
> +		clocks = <&clks IMX6QDL_CLK_CKO>;
> +		clock-names = "xclk";
> +		xclk = <24000000>;
> +		DOVDD-supply = <&vgen4_reg>; /* 1.8v */
> +		AVDD-supply = <&vgen5_reg>;  /* 2.8v, rev C board is VGEN3
> +						rev B board is VGEN5 */
> +		DVDD-supply = <&vgen2_reg>;  /* 1.5v*/
> +		pwdn-gpios = <&gpio1 19 GPIO_ACTIVE_HIGH>; /* SD1_DAT2 */
> +		reset-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>; /* SD1_CLK */

Comments about SD1_* pad names are redundant.

> +
> +		port {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +
> +			ov5640_to_mipi_csi: endpoint at 1 {
> +				reg = <1>;
> +				remote-endpoint = <&mipi_csi_from_mipi_sensor>;
> +				data-lanes = <0 1>;
> +				clock-lanes = <2>;
> +			};
> +		};
> +	};
>  };
>  
>  &i2c3 {
> @@ -426,6 +508,36 @@
>  			>;
>  		};
>  
> +		pinctrl_ov5640: ov5640grp {
> +			fsl,pins = <
> +				MX6QDL_PAD_SD1_DAT2__GPIO1_IO19 0x80000000
> +				MX6QDL_PAD_SD1_CLK__GPIO1_IO20  0x80000000
> +			>;
> +		};
> +
> +		pinctrl_ov5642: ov5642grp {
> +			fsl,pins = <
> +				MX6QDL_PAD_SD1_DAT0__GPIO1_IO16 0x80000000
> +				MX6QDL_PAD_SD1_DAT1__GPIO1_IO17 0x80000000
> +			>;
> +		};
> +
> +		pinctrl_ipu1_csi0: ipu1grp-csi0 {

Please rename the node name to ipu1csi0grp.

Please add new pin control groups preserving the alphanimerical order.

> +			fsl,pins = <
> +				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_pcie: pciegrp {
>  			fsl,pins = <
>  				MX6QDL_PAD_GPIO_17__GPIO7_IO12	0x1b0b0
> 

--
With best wishes,
Vladimir

^ permalink raw reply

* [PATCH V9 0/3] irqchip: qcom: Add IRQ combiner driver
From: Agustin Vega-Frias @ 2017-01-04 12:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAJZ5v0h_v-iER5Fffdoczj4CAhetGYqOfJOhdO561_kNCnsEFg@mail.gmail.com>

On 2017-01-03 16:56, Rafael J. Wysocki wrote:
> On Tue, Jan 3, 2017 at 4:19 PM, Agustin Vega-Frias
> <agustinv@codeaurora.org> wrote:
>> Hi,
>> 
>> Is there any more feedback on this beyond Lorenzo's suggestion to drop
>> the conditional check on the first patch?
>> How can we move forward on this series?
> 
> Essentially, I need to convince myself that patches [1-2/3] are fine
> which hasn't happened yet.
> 

Thanks Rafael, I'll hold on for your feedback.

> Thanks,
> Rafael

-- 
Qualcomm Datacenter Technologies, Inc. on behalf of the Qualcomm 
Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a 
Linux Foundation Collaborative Project.

^ permalink raw reply

* [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: linux-arm-kernel
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 at 21 {

adv7180: camera at 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] rtc: armada38x: add __ro_after_init to armada38x_rtc_ops
From: Russell King - ARM Linux @ 2017-01-04 13:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <alpine.DEB.2.20.1701041320140.3073@hadrien>

On Wed, Jan 04, 2017 at 01:23:41PM +0100, Julia Lawall wrote:
> Basically, the strategy of the patch is that one may consider it
> preferable to duplicate the structure for the different alternatives,
> rather than use __ro_after_init.  Perhaps if the structure were larger,
> then __ro_after_init would be a better choice?

It depends on not just the size, but how many members need to be
modified, and obviously whether there are likely to be more than one
user of the structure as well.

So I'd say __ro_after_init rarely makes sense for an operations
structure - the only case I can see is:

- a large structure
- only a small number of elements need to be modified
- it is only single-use

which is probably quite rare - this one falls into two out of those
three.

There's another consideration (imho) too - we may wish, at a later
date, to make .text and .rodata both read-only from the start of the
kernel to harden the kernel against possibly init-time exploitation.
(Think about a buggy built-in driver with emulated hardware - much the
same problem that Kees is trying to address in one of his recent patch
sets but with hotplugged hardware while a screen-saver is active.)
Having function pointers in .rodata rather than the ro-after-init
section would provide better protection.

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply

* [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: linux-arm-kernel
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

* [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: linux-arm-kernel
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

* [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: linux-arm-kernel

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

* [RFC PATCH] usb: dwc3: add support for OTG driver compilation
From: Manish Narani @ 2017-01-04 13:22 UTC (permalink / raw)
  To: linux-arm-kernel
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] usb: dwc3: core: add OTG support function calls and modifications
From: Manish Narani @ 2017-01-04 13:22 UTC (permalink / raw)
  To: linux-arm-kernel
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: gadget: add support for OTG in gadget framework
From: Manish Narani @ 2017-01-04 13:22 UTC (permalink / raw)
  To: linux-arm-kernel
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: host: add support for OTG in DWC3 host driver
From: Manish Narani @ 2017-01-04 13:22 UTC (permalink / raw)
  To: linux-arm-kernel
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: otg: Adding OTG driver for DWC3 controller
From: Manish Narani @ 2017-01-04 13:23 UTC (permalink / raw)
  To: linux-arm-kernel
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: host: xhci: plat: add support for otg_set_host() call
From: Manish Narani @ 2017-01-04 13:23 UTC (permalink / raw)
  To: linux-arm-kernel
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

* arm64: virt_to_page() does not return right page for a kernel image address
From: Ard Biesheuvel @ 2017-01-04 13:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <e83640e2-61ed-4c29-b047-23965e670c08@redhat.com>

On 4 January 2017 at 12:23, Pratyush Anand <panand@redhat.com> wrote:
>
>
> On Wednesday 04 January 2017 05:36 PM, Mark Rutland wrote:
>>
>> So it seems we need to fix the crypto test.
>>
>> Looking at crypto/testmgr.c, I can't spot the kmap_atomic() or the
>> page_address()/virt_to_page(). I guess that's hidden behind helpers,
>> which might also be used elsewhere?
>>
>> Could you elaborate on where exactly the problem is in the crypto test?
>
>
> We have in test_acomp() -> crypto_acomp_decompress() -> tfm->decompress() ->
> scomp_acomp_decompress() -> scomp_acomp_comp_decomp() ->
> scatterwalk_map_and_copy() -> scatterwalk_copychunks()
>
>  41                 if (out != 2) {
>  42                         vaddr = scatterwalk_map(walk);
>  43                         memcpy_dir(buf, vaddr, len_this_page, out);
>  44                         scatterwalk_unmap(vaddr);
>  45                 }
>
>
> scatterwalk_map() gets vaddr from kmap_atomic().
>
> test_acomp() initializes sg:
> sg_init_one(&src, ctemplate[i].input, ilen);
>

This is the bug right here, and it is fixed already upstream. The
crypto test vectors are part of the kernel image, and should not be
used in scatterlists.

> ctemplate is a kernel address (like lzo_comp_tv_template), which was
> assigned to walk->sg latter and passed to kmap_atomic().
>
>
> ~Pratyush

^ permalink raw reply

* [PATCH] drm/meson: Fix plane atomic check when no crtc for the plane
From: Ville Syrjälä @ 2017-01-04 13:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483435254-27955-1-git-send-email-narmstrong@baylibre.com>

On Tue, Jan 03, 2017 at 10:20:54AM +0100, Neil Armstrong wrote:
> When no CRTC is associated with the plane, the meson_plane_atomic_check()
> call breaks the kernel with an Oops.
> 
> Fixes: bbbe775ec5b5 ("drm: Add support for Amlogic Meson Graphic Controller")
> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
> ---
>  drivers/gpu/drm/meson/meson_plane.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c
> index 4942ca0..7890e30 100644
> --- a/drivers/gpu/drm/meson/meson_plane.c
> +++ b/drivers/gpu/drm/meson/meson_plane.c
> @@ -51,6 +51,9 @@ static int meson_plane_atomic_check(struct drm_plane *plane,
>  	struct drm_crtc_state *crtc_state;
>  	struct drm_rect clip = { 0, };
>  
> +	if (!state->crtc)
> +		return 0;
> +

Since you're not going to call drm_plane_helper_check_state() you will
fail to update plane_state->visible.

What i915 does in this case is look for state->crtc first, and if that's
NULL it'll pick the crtc from the old state (plane->state->crtc). It
looks a bit ugly, but maybe we should hide it in a small helper function
that could be used by all drivers?

>  	crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
>  	if (IS_ERR(crtc_state))
>  		return PTR_ERR(crtc_state);
> -- 
> 1.9.1
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Ville Syrj?l?
Intel OTC

^ permalink raw reply

* [PATCH 2/2] arm64: mm: enable CONFIG_HOLES_IN_ZONE for NUMA
From: Will Deacon @ 2017-01-04 13:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1481706707-6211-3-git-send-email-ard.biesheuvel@linaro.org>

On Wed, Dec 14, 2016 at 09:11:47AM +0000, Ard Biesheuvel wrote:
> The NUMA code may get confused by the presence of NOMAP regions within
> zones, resulting in spurious BUG() checks where the node id deviates
> from the containing zone's node id.
> 
> Since the kernel has no business reasoning about node ids of pages it
> does not own in the first place, enable CONFIG_HOLES_IN_ZONE to ensure
> that such pages are disregarded.
> 
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  arch/arm64/Kconfig | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index 111742126897..0472afe64d55 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -614,6 +614,10 @@ config NEED_PER_CPU_EMBED_FIRST_CHUNK
>  	def_bool y
>  	depends on NUMA
>  
> +config HOLES_IN_ZONE
> +	def_bool y
> +	depends on NUMA
> +
>  source kernel/Kconfig.preempt
>  source kernel/Kconfig.hz

I'm happy to apply this, but I'll hold off until the first patch is queued
somewhere, since this doesn't help without the VM_BUG_ON being moved.

Alternatively, I can queue both if somebody from the mm camp acks the
first patch.

Will

^ permalink raw reply

* [PATCH 1/2] arm64: dma_mapping: allow PCI host driver to limit DMA mask
From: Arnd Bergmann @ 2017-01-04 13:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4bbc3494-bf95-9765-ccaf-cc3b3e1cd297@cogentembedded.com>

On Wednesday, January 4, 2017 9:24:09 AM CET Nikita Yushchenko wrote:
> > commit 9a57d58d116800a535510053136c6dd7a9c26e25
> > Author: Arnd Bergmann <arnd@arndb.de>
> > Date:   Tue Nov 17 14:06:55 2015 +0100
> > 
> >     [EXPERIMENTAL] ARM64: check implement dma_set_mask
> >     
> >     Needs work for coherent mask
> >     
> >     Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> 
> Unfortunately this is far incomplete
> 
> > @@ -957,6 +983,18 @@ void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
> >  	if (!dev->archdata.dma_ops)
> >  		dev->archdata.dma_ops = &swiotlb_dma_ops;
> >  
> > +	/*
> > +	 * we don't yet support buses that have a non-zero mapping.
> > +	 *  Let's hope we won't need it
> > +	 */
> > +	WARN_ON(dma_base != 0);
> > +
> > +	/*
> > +	 * Whatever the parent bus can set. A device must not set
> > +	 * a DMA mask larger than this.
> > +	 */
> > +	dev->archdata.parent_dma_mask = size;
> > +
> 
> ... because size/mask passed here for PCI devices are meaningless.
> 
> For OF platforms, this is called via of_dma_configure(), that checks
> dma-ranges of node that is *parent* for host bridge. Host bridge
> currently does not control this at all.

We need to think about this a bit. Is it actually the PCI host
bridge that limits the ranges here, or the bus that it is connected
to. In the latter case, the caller needs to be adapted to handle
both.

> In current device trees no dma-ranges is defined for nodes that are
> parents to pci host bridges. This will make of_dma_configure() to fall
> back to 32-bit size for all devices on all current platforms.  Thus
> applying this patch will immediately break 64-bit dma masks on all
> hardware that supports it.

No, it won't break it, it will just fall back to swiotlb for all the
ones that are lacking the dma-ranges property. I think this is correct
behavior.

> Also related: dma-ranges property used by several pci host bridges is
> *not* compatible with "legacy" dma-ranges parsed by of_get_dma_range() -
> former uses additional flags word at beginning.

Can you elaborate? Do we have PCI host bridges that use wrongly formatted
dma-ranges properties?

	Arnd

^ permalink raw reply

* [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: linux-arm-kernel
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

* [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: linux-arm-kernel
In-Reply-To: <1483536181-22356-1-git-send-email-mnarani@xilinx.com>


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
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 832 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170104/aae7b52a/attachment-0001.sig>

^ permalink raw reply

* [RFC PATCH] usb: dwc3: add support for OTG driver compilation
From: Felipe Balbi @ 2017-01-04 13:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483536181-22356-2-git-send-email-mnarani@xilinx.com>


Hi,

Manish Narani <manish.narani@xilinx.com> writes:
> 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

you just broke compilation

-- 
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 832 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170104/5e6aebb4/attachment.sig>

^ permalink raw reply


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