The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH v2 1/1] usb: xhci: clear EINT bit in status correctly
From: Lu Baolu @ 2017-01-03  6:45 UTC (permalink / raw)
  To: Mathias Nyman; +Cc: linux-usb, linux-kernel, Lu Baolu, stable, Felipe Balbi

EINT(Event Interrupt) is a write-1-to-clear type of bit in xhci
status register. It should be cleared by writing a 1. Writing 0
to this bit has no effect.

Xhci driver tries to clear this bit by writing 0 to it. This is
not the right way to go. This patch corrects this by reading the
register first, then clearing all RO/RW1C/RsvZ bits and setting
the clearing bit, and writing back the new value at last.

Xhci spec requires that software that uses EINT shall clear it
prior to clearing any IP flags in section 5.4.2. This is the
reason why this patch is CC'ed stable as well.

Cc: <stable@vger.kernel.org> # v3.14+
Cc: Felipe Balbi <felipe.balbi@linux.intel.com>
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
Change log:
v1->v2:
 - Fixed a warning reported by 0-day kbuild robot.

 drivers/usb/host/xhci.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 1cd5641..bb08c6b 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -721,7 +721,7 @@ void xhci_stop(struct usb_hcd *hcd)
 	xhci_dbg_trace(xhci, trace_xhci_dbg_init,
 			"// Disabling event ring interrupts");
 	temp = readl(&xhci->op_regs->status);
-	writel(temp & ~STS_EINT, &xhci->op_regs->status);
+	writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status);
 	temp = readl(&xhci->ir_set->irq_pending);
 	writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending);
 	xhci_print_ir_set(xhci, 0);
@@ -1054,7 +1054,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
 
 		xhci_dbg(xhci, "// Disabling event ring interrupts\n");
 		temp = readl(&xhci->op_regs->status);
-		writel(temp & ~STS_EINT, &xhci->op_regs->status);
+		writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status);
 		temp = readl(&xhci->ir_set->irq_pending);
 		writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending);
 		xhci_print_ir_set(xhci, 0);
-- 
2.1.4

^ permalink raw reply related

* Re: [PATCH v6 8/9] dt-bindings: mux-adg792a: document devicetree bindings for ADG792A/G mux
From: Peter Rosin @ 2017-01-03  6:44 UTC (permalink / raw)
  To: Jonathan Cameron, Jonathan Cameron, linux-kernel
  Cc: Wolfram Sang, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jonathan Corbet,
	Arnd Bergmann, Greg Kroah-Hartman, linux-i2c, devicetree,
	linux-iio, linux-doc
In-Reply-To: <90A8A4B3-670B-4409-B7AB-47BF4B2ABDDA@jic23.retrosnub.co.uk>

On 2017-01-02 22:13, Jonathan Cameron wrote:
> 
> 
> On 2 January 2017 20:47:58 GMT+00:00, Peter Rosin <peda@axentia.se> wrote:
>> On 2017-01-02 19:05, Jonathan Cameron wrote:
>>> On 02/01/17 16:01, Peter Rosin wrote:
>>>> On 2017-01-01 12:00, Jonathan Cameron wrote:
>>>>> On 30/11/16 08:17, Peter Rosin wrote:
>>>>>> Analog Devices ADG792A/G is a triple 4:1 mux.
>>>>>>
>>>>>> Signed-off-by: Peter Rosin <peda@axentia.se>
>>>>> Few comments inline.  Worth adding anything about the gpio (output pins) to
>>>>> the binding at this stage as well?  Would certainly be nice to support
>>>>> them.
>>>>
>>>> I'll add optional properties "gpio-controller;" and "#gpio-cells = <2>;"
>>>> with the usual interpretation in v7 (but no implementation...) Is that
>>>> enough?
>>>>
>>>>> Jonathan
>>>>>> ---
>>>>>>  .../devicetree/bindings/misc/mux-adg792a.txt       | 64 ++++++++++++++++++++++
>>>>>>  1 file changed, 64 insertions(+)
>>>>>>  create mode 100644 Documentation/devicetree/bindings/misc/mux-adg792a.txt
>>>>>>
>>>>>> diff --git a/Documentation/devicetree/bindings/misc/mux-adg792a.txt b/Documentation/devicetree/bindings/misc/mux-adg792a.txt
>>>>>> new file mode 100644
>>>>>> index 000000000000..4677f9ab1c55
>>>>>> --- /dev/null
>>>>>> +++ b/Documentation/devicetree/bindings/misc/mux-adg792a.txt
>>>>>> @@ -0,0 +1,64 @@
>>>>>> +Bindings for Analog Devices ADG792A/G Triple 4:1 Multiplexers
>>>>>> +
>>>>>> +Required properties:
>>>>>> +- compatible : "adi,adg792a" or "adi,adg792g"
>>>>>> +- #mux-control-cells : <0> if parallel, or <1> if not.
>>>>>> +* Standard mux-controller bindings as decribed in mux-controller.txt
>>>>>> +
>>>>>> +Optional properties:
>>>>>> +- adi,parallel : if present, the three muxes are bound together with a single
>>>>>> +  mux controller, controlling all three muxes in parallel.
>>>>>> +- adi,idle-state : if present, array of states the three mux controllers will
>>>>>> +  have when idle (or, if parallel, a single idle-state).
>>>>> Hmm. These are actually a policy decision.  As only one policy will make
>>>>> sense for a given set of hardware probably fine to have it in here I guess.
>>>>> Might be worth adding a note to say this though.
>>>>
>>>> I don't really know what you want me to add, do you have a suggestion for the
>>>> wording?
>>>>
>>>>>> +
>>>>>> +Mux controller states 0 through 3 correspond to signals A through D in the
>>>>>> +datasheet. Mux controller states 4 and 5 are only available as possible idle
>>>>>> +states. State 4 represents that nothing is connected, and state 5 represents
>>>>>> +that the mux controller keeps the mux in its previously selected state during
>>>>>> +the idle period. State 5 is the default idle state.
>>>>> I'm never a great fan of magic numbers.  Can we represent this more cleanly by
>>>>> breaking it into multiple properties?
>>>>> Optional:
>>>>> adi,idle-switch-to-channel : switch to this channel when idle.
>>>>> adi,idle-high-impedance : <boolean> the nothing connected state?
>>>>>
>>>>> If neither present leaves it in previous state?
>>>>
>>>> It's not that easy. adi,idle-state is an array when there are three single
>>>> pole quadruple throw muxes, so there really needs to be a number for each
>>>> desired idle-behavior. Unless you have a better idea for how to describe
>>>> that?
>>> The above with arrays for each of the two parameters?
>>> Though then you need a priority documented - I'd say high impedance overrides
>>> the channel selection if both are present.
>>
>> How would you specify that the first mux should idle in "state 5", the second
>> should idle in "state 4" and the third in "state 0"? (original state numbering)
>>
>> You'd still need a magic number for the default idle state (state 5) so that
>> you can skip entries in the arrays. Or am I missing something?
> Ah I had missed state 5. Hmm would need explicit control for that as well. Not nice...
> 
> Perhaps 3 state control (magic number but with channel nums separate)
> 
> Idle-state array of <switchtostate, currentstate, highimpedance>
> 
> Idle-state array of states to switch to if so set?
> 
> Slight nicer than a mess of the two things perhaps?

Perhaps making adi,idle-state an array of tuples <mux-number state> and
add adi,idle-high-impedance as an array of mux-numbers, so that my example
above would come out as:

	adi,idle-high-impedance = <1>; /* mux 1 idles with high imp */
	adi,idle-state = <2 0>;  /* mux 2 idles in state 0 (signal A) */

mux 0 is not mentioned and idles in its previously selected state.


If you want mux 0 to idle with high impedance:

	adi,idle-high-impedance = <0 1>;
	adi,idle-state = <2 0>;

If you want mux 0 to idle with signal C:

	adi,idle-high-impedance = <1>;
	adi,idle-state = <0 3>, <2 0>;

>>>>>> +
>>>>>> +Example:
>>>>>> +
>>>>>> +	/* three independent mux controllers (of which one is used) */
>>>>>> +	&i2c0 {
>>>>>> +		mux: adg792a@50 {
>>>>>> +			compatible = "adi,adg792a";
>>>>>> +			reg = <0x50>;
>>>>>> +			#mux-control-cells = <1>;
>>>>>> +		};
>>>>>> +	};
>>>>>> +
>>>>>> +	adc-mux {
>>>>>> +		compatible = "iio-mux";
>>>>>> +		io-channels = <&adc 0>;
>>>>>> +		io-channel-names = "parent";
>>>>>> +
>>>>>> +		mux-controls = <&mux 1>;
>>>>>> +
>>>>>> +		channels = "sync-1", "", "out";
>>>>>> +	};
>>>>>> +
>>>>>> +
>>>>>> +	/*
>>>>>> +	 * Three parallel muxes with one mux controller, useful e.g. if
>>>>>> +	 * the adc is differential, thus needing two signals to be muxed
>>>>>> +	 * simultaneously for correct operation.
>>>>>> +	 */
>>>>>> +	&i2c0 {
>>>>>> +		pmux: adg792a@50 {
>>>>>> +			compatible = "adi,adg792a";
>>>>>> +			reg = <0x50>;
>>>>>> +			#mux-control-cells = <0>;
>>>>>> +			adi,parallel;
>>>>>> +		};
>>>>>> +	};
>>>>>> +
>>>>>> +	diff-adc-mux {
>>>>>> +		compatible = "iio-mux";
>>>>>> +		io-channels = <&adc 0>;
>>>>>> +		io-channel-names = "parent";
>>>>>> +
>>>>>> +		mux-controls = <&pmux>;
>>>>>> +
>>>>>> +		channels = "sync-1", "", "out";
>>>>>> +	};
>>>>>>
>>>>>
>>>>
>>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply

* RE: [PATCH 1/2] arm64:dt:ls1046a: Add TMU device tree support for LS1046A
From: Troy Jia @ 2017-01-03  6:44 UTC (permalink / raw)
  To: Shawn Guo
  Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	Scott Wood, edubezval@gmail.com, Y.T. Tang, robh+dt@kernel.org,
	rui.zhang@intel.com, linux-arm-kernel@lists.infradead.org
In-Reply-To: <20170103062633.GR20956@dragon>

> 
> On Tue, Jan 03, 2017 at 03:49:33AM +0000, Troy Jia wrote:
> > > > @@ -279,6 +282,82 @@
> > > >  			clocks = <&sysclk>;
> > > >  		};
> > > >
> > > > +		tmu: tmu@1f00000 {
> > > > +			compatible = "fsl,qoriq-tmu";
> > > > +			reg = <0x0 0x1f00000 0x0 0x10000>;
> > > > +			interrupts = <0 33 0x4>;
> > > > +			fsl,tmu-range = <0xb0000 0x9002a 0x6004c 0x30062>;
> > > > +			fsl,tmu-calibration = <0x00000000 0x00000026
> > > > +					       0x00000001 0x0000002d
> > > > +					       0x00000002 0x00000032
> > > > +					       0x00000003 0x00000039
> > > > +					       0x00000004 0x0000003f
> > > > +					       0x00000005 0x00000046
> > > > +					       0x00000006 0x0000004d
> > > > +					       0x00000007 0x00000054
> > > > +					       0x00000008 0x0000005a
> > > > +					       0x00000009 0x00000061
> > > > +					       0x0000000a 0x0000006a
> > > > +					       0x0000000b 0x00000071
> > > > +
> > >
> > > Instead of a newline, can we have a single line comment here to tell
> > > how these calibration data is grouped?
> >
> > Each group represent one temperature range. It's a good idea to add comment
> here.
> > Could I just add one comment like below to clarify all four groups?
> > /* Each calibration data group represent one temperature range. There
> > are four ranges in total */
> 
> Probably the following form?
> 
> 	/* Calibration data group 1 */
> 	...
> 	/* Calibration data group 2 */
> 	...
> 	/* Calibration data group 3 */
> 	...
> 	/* Calibration data group 4 */
> 	...
> 

It's good for me.

> 
> >
> > >
> > > > +					       0x00010000 0x00000025
> > > > +					       0x00010001 0x0000002c
> > > > +					       0x00010002 0x00000035
> > > > +					       0x00010003 0x0000003d
> > > > +					       0x00010004 0x00000045
> > > > +					       0x00010005 0x0000004e
> > > > +					       0x00010006 0x00000057
> > > > +					       0x00010007 0x00000061
> > > > +					       0x00010008 0x0000006b
> > > > +					       0x00010009 0x00000076
> > > > +
> > > > +					       0x00020000 0x00000029
> > > > +					       0x00020001 0x00000033
> > > > +					       0x00020002 0x0000003d
> > > > +					       0x00020003 0x00000049
> > > > +					       0x00020004 0x00000056
> > > > +					       0x00020005 0x00000061
> > > > +					       0x00020006 0x0000006d
> > > > +
> > > > +					       0x00030000 0x00000021
> > > > +					       0x00030001 0x0000002a
> > > > +					       0x00030002 0x0000003c
> > > > +					       0x00030003 0x0000004e>;
> > > > +			big-endian;
> > > > +			#thermal-sensor-cells = <1>;
> > > > +		};
> > > > +
> > > > +		thermal-zones {
> > > > +			cpu_thermal: cpu-thermal {
> > > > +				polling-delay-passive = <1000>;
> > > > +				polling-delay = <5000>;
> > > > +
> > >
> > > We usually do not have newline between properties but nodes, or
> > > between property list and child node.
> >
> > I just follow the style of thermal binding of
> > Documentation/devicetree/bindings/thermal/thermal.txt.
> 
> Different subsystem or binding examples use different style, but when we put
> things together in the platform dts, we would like to have them in a unified style.

Good point here.
I will fix it and send the new version.

Thanks.
-Hongtao.

^ permalink raw reply

* Re: [RFC PATCH] ACPI/PCI: Fix bus range comparation in pci_mcfg_lookup
From: Tomasz Nowicki @ 2017-01-03  6:39 UTC (permalink / raw)
  To: Zhou Wang, Rafael J. Wysocki, Len Brown, Jayachandran C,
	Lorenzo Pieralisi, jorn Helgaas
  Cc: liudongdong3, gabriele.paoloni, linux-acpi, linux-pci,
	linux-kernel
In-Reply-To: <1482397663-98715-1-git-send-email-wangzhou1@hisilicon.com>

On 22.12.2016 10:07, Zhou Wang wrote:
> Multiple PCIe host bridges may exists in one PCIe segment. So bus range for each
> host bridge should be in the coverage of bus range of related PCIe segment.
>
> This patch will support this kind of scenario:
>
> MCFG:
> 	bus range: 0x00~0xff.
> 	segment: 0.
> DSDT:
> 	host bridge 1:
> 		bus range: 0x00~0x1f.
> 		segment: 0.
> 	host bridge 2:
> 		bus range: 0x20~0x4f.
> 		segment: 0.
>
> Signed-off-by: Zhou Wang <wangzhou1@hisilicon.com>
> ---
>  drivers/acpi/pci_mcfg.c | 5 ++---
>  1 file changed, 2 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
> index b5b376e..46a3e32 100644
> --- a/drivers/acpi/pci_mcfg.c
> +++ b/drivers/acpi/pci_mcfg.c
> @@ -40,11 +40,10 @@ phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res)
>  	struct mcfg_entry *e;
>
>  	/*
> -	 * We expect exact match, unless MCFG entry end bus covers more than
> -	 * specified by caller.
> +	 * We expect the range in bus_res in the coverage of MCFG bus range.
>  	 */
>  	list_for_each_entry(e, &pci_mcfg_list, list) {
> -		if (e->segment == seg && e->bus_start == bus_res->start &&
> +		if (e->segment == seg && e->bus_start <= bus_res->start &&
>  		    e->bus_end >= bus_res->end)
>  			return e->addr;
>  	}
>

Looks good to me.

Reviewed-by: Tomasz Nowicki <tn@semihalf.com>

Thanks,
Tomasz

^ permalink raw reply

* [PATCH 8/8] ARM: dts: imx6q-evi: Fix onboard hub reset line
From: Peter Chen @ 2017-01-03  6:33 UTC (permalink / raw)
  To: gregkh, stern, ulf.hansson, broonie, sre, robh+dt, shawnguo, rjw,
	dbaryshkov
  Cc: heiko, linux-arm-kernel, p.zabel, devicetree, pawel.moll,
	mark.rutland, linux-usb, arnd, s.hauer, mail, troy.kisky,
	festevam, oscar, stephen.boyd, linux-pm, stillcompiling,
	linux-kernel, mka, vaibhav.hiremath, gary.bisson, hverkuil, krzk,
	Peter Chen
In-Reply-To: <1483425211-14473-1-git-send-email-peter.chen@nxp.com>

From: Joshua Clayton <stillcompiling@gmail.com>

Previously the onboard hub was made to work by treating its
reset gpio as a regulator enable.
Get rid of that kludge now that pwseq has added reset gpio support
Move pin muxing the hub reset pin into the usbh1 group

Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
Signed-off-by: Peter Chen <peter.chen@nxp.com>
---
 arch/arm/boot/dts/imx6q-evi.dts | 25 +++++++------------------
 1 file changed, 7 insertions(+), 18 deletions(-)

diff --git a/arch/arm/boot/dts/imx6q-evi.dts b/arch/arm/boot/dts/imx6q-evi.dts
index 7c7c1a8..79a0bd5 100644
--- a/arch/arm/boot/dts/imx6q-evi.dts
+++ b/arch/arm/boot/dts/imx6q-evi.dts
@@ -54,18 +54,6 @@
 		reg = <0x10000000 0x40000000>;
 	};
 
-	reg_usbh1_vbus: regulator-usbhubreset {
-		compatible = "regulator-fixed";
-		regulator-name = "usbh1_vbus";
-		regulator-min-microvolt = <5000000>;
-		regulator-max-microvolt = <5000000>;
-		enable-active-high;
-		startup-delay-us = <2>;
-		pinctrl-names = "default";
-		pinctrl-0 = <&pinctrl_usbh1_hubreset>;
-		gpio = <&gpio7 12 GPIO_ACTIVE_HIGH>;
-	};
-
 	reg_usb_otg_vbus: regulator-usbotgvbus {
 		compatible = "regulator-fixed";
 		regulator-name = "usb_otg_vbus";
@@ -207,12 +195,18 @@
 };
 
 &usbh1 {
-	vbus-supply = <&reg_usbh1_vbus>;
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_usbh1>;
 	dr_mode = "host";
 	disable-over-current;
 	status = "okay";
+
+	usb2415host: hub@1 {
+		compatible = "usb424,2513";
+		reg = <1>;
+		reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>;
+		reset-duration-us = <3000>;
+	};
 };
 
 &usbotg {
@@ -468,11 +462,6 @@
 			MX6QDL_PAD_GPIO_3__USB_H1_OC 0x1b0b0
 			/* usbh1_b OC */
 			MX6QDL_PAD_GPIO_0__GPIO1_IO00 0x1b0b0
-		>;
-	};
-
-	pinctrl_usbh1_hubreset: usbh1hubresetgrp {
-		fsl,pins = <
 			MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x1b0b0
 		>;
 	};
-- 
2.7.4

^ permalink raw reply related

* [PATCH 3/6] PCI/ASPM: Read and setup L1 substate capabilities
From: Rajat Jain @ 2017-01-03  6:34 UTC (permalink / raw)
  To: Bjorn Helgaas, Keith Busch, Andreas Ziegler, Jonathan Yong,
	Shawn Lin, David Daney, Julia Lawall, Ram Amrani, Doug Ledford,
	Wang Sheng-Hui, linux-pci, linux-kernel
  Cc: Rajat Jain, Rajat Jain, Brian Norris
In-Reply-To: <1483425255-101923-1-git-send-email-rajatja@google.com>

The PCIe spec says that only function 0 of a multi-function
downstream component would implement the capability structure.

This patch adds code to read the L1 substate capability structures
of upstream and downstream components of the link, and sets it up
in the device structure.

Signed-off-by: Rajat Jain <rajatja@google.com>
---
 drivers/pci/pcie/aspm.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 58 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 402c229..7a3ad85 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -49,6 +49,7 @@ struct aspm_latency {
 
 struct pcie_link_state {
 	struct pci_dev *pdev;		/* Upstream component of the Link */
+	struct pci_dev *downstream;	/* Downstream component, function 0 */
 	struct pcie_link_state *root;	/* pointer to the root port link */
 	struct pcie_link_state *parent;	/* pointer to the parent Link state */
 	struct list_head sibling;	/* node in link_list */
@@ -300,6 +301,12 @@ struct aspm_register_info {
 	u32 enabled:2;
 	u32 latency_encoding_l0s;
 	u32 latency_encoding_l1;
+
+	/* L1 substates */
+	u32 l1ss_cap_ptr;
+	u32 l1ss_cap;
+	u32 l1ss_ctl1;
+	u32 l1ss_ctl2;
 };
 
 static void pcie_get_aspm_reg(struct pci_dev *pdev,
@@ -314,6 +321,22 @@ static void pcie_get_aspm_reg(struct pci_dev *pdev,
 	info->latency_encoding_l1  = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15;
 	pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &reg16);
 	info->enabled = reg16 & PCI_EXP_LNKCTL_ASPMC;
+
+	/* Read L1 PM substate capabilities */
+	info->l1ss_cap = info->l1ss_ctl1 = info->l1ss_ctl2 = 0;
+	info->l1ss_cap_ptr = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS);
+	if (!info->l1ss_cap_ptr)
+		return;
+	pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CAP,
+			      &info->l1ss_cap);
+	if (!(info->l1ss_cap & PCI_L1SS_CAP_L1_PM_SS)) {
+		info->l1ss_cap = 0;
+		return;
+	}
+	pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CTL1,
+			      &info->l1ss_ctl1);
+	pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CTL2,
+			      &info->l1ss_ctl2);
 }
 
 static void pcie_aspm_check_latency(struct pci_dev *endpoint)
@@ -355,6 +378,20 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint)
 	}
 }
 
+/*
+ * The L1 PM substate capability is only implemented in function 0 in a
+ * multi function device.
+ */
+static inline struct pci_dev *pci_function_0(struct pci_bus *linkbus)
+{
+	struct pci_dev *child;
+
+	list_for_each_entry(child, &linkbus->devices, bus_list)
+		if (PCI_FUNC(child->devfn) == 0)
+			return child;
+	return NULL;
+}
+
 static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
 {
 	struct pci_dev *child, *parent = link->pdev;
@@ -370,8 +407,9 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
 
 	/* Get upstream/downstream components' register state */
 	pcie_get_aspm_reg(parent, &upreg);
-	child = list_entry(linkbus->devices.next, struct pci_dev, bus_list);
+	child = pci_function_0(linkbus);
 	pcie_get_aspm_reg(child, &dwreg);
+	link->downstream = child;
 
 	/*
 	 * If ASPM not supported, don't mess with the clocks and link,
@@ -414,6 +452,25 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
 	link->latency_up.l1 = calc_l1_latency(upreg.latency_encoding_l1);
 	link->latency_dw.l1 = calc_l1_latency(dwreg.latency_encoding_l1);
 
+	/* Setup L1 substate */
+	if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_ASPM_L1_1)
+		link->aspm_support |= ASPM_STATE_L1_1;
+	if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_ASPM_L1_2)
+		link->aspm_support |= ASPM_STATE_L1_2;
+	if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_PCIPM_L1_1)
+		link->aspm_support |= ASPM_STATE_L1_1_PCIPM;
+	if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_PCIPM_L1_2)
+		link->aspm_support |= ASPM_STATE_L1_2_PCIPM;
+
+	if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_ASPM_L1_1)
+		link->aspm_enabled |= ASPM_STATE_L1_1;
+	if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_ASPM_L1_2)
+		link->aspm_enabled |= ASPM_STATE_L1_2;
+	if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_1)
+		link->aspm_enabled |= ASPM_STATE_L1_1_PCIPM;
+	if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_2)
+		link->aspm_enabled |= ASPM_STATE_L1_2_PCIPM;
+
 	/* Save default state */
 	link->aspm_default = link->aspm_enabled;
 
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related

* [PATCH 4/6] PCI/ASPM: Calculate and save the L1.2 timing parameters
From: Rajat Jain @ 2017-01-03  6:34 UTC (permalink / raw)
  To: Bjorn Helgaas, Keith Busch, Andreas Ziegler, Jonathan Yong,
	Shawn Lin, David Daney, Julia Lawall, Ram Amrani, Doug Ledford,
	Wang Sheng-Hui, linux-pci, linux-kernel
  Cc: Rajat Jain, Rajat Jain, Brian Norris
In-Reply-To: <1483425255-101923-1-git-send-email-rajatja@google.com>

Calculate and save the timing parameters that need to be programmed
if we need to enable L1.2 substates later.

We use the same logic (and a constant value for 1 of the
parameters) as used by Intel's coreboot:

https://www.coreboot.org/pipermail/coreboot-gerrit/2015-March/021134.html
https://review.coreboot.org/#/c/8832/

Signed-off-by: Rajat Jain <rajatja@google.com>
---
 drivers/pci/pcie/aspm.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 79 insertions(+)

diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 7a3ad85..a70afdf 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -42,6 +42,18 @@
 #define ASPM_STATE_ALL		(ASPM_STATE_L0S | ASPM_STATE_L1 |	\
 				 ASPM_STATE_L1SS)
 
+/*
+ * When L1 substates are enabled, the LTR L1.2 threshold is a timing parameter
+ * that decides whether L1.1 or L1.2 is entered (Refer PCIe spec for details).
+ * Not sure is there is a way to "calculate" this on the fly, but may be we
+ * could turn it into a parameter in future. This value has been taken from
+ * the following files from Intel's coreboot (which is the only code I found
+ * to have used this):
+ * https://www.coreboot.org/pipermail/coreboot-gerrit/2015-March/021134.html
+ * https://review.coreboot.org/#/c/8832/
+ */
+#define LTR_L1_2_THRESHOLD_BITS	((1 << 21) | (1 << 23) | (1 << 30))
+
 struct aspm_latency {
 	u32 l0s;			/* L0s latency (nsec) */
 	u32 l1;				/* L1 latency (nsec) */
@@ -76,6 +88,14 @@ struct pcie_link_state {
 	 * has one slot under it, so at most there are 8 functions.
 	 */
 	struct aspm_latency acceptable[8];
+
+	/* L1 PM Substate info */
+	struct {
+		u32 up_cap_ptr;		/* L1SS cap ptr in upstream dev */
+		u32 dw_cap_ptr;		/* L1SS cap ptr in downstream dev */
+		u32 ctl1;		/* value to be programmed in ctl1 */
+		u32 ctl2;		/* value to be programmed in ctl2 */
+	} l1ss;
 };
 
 static int aspm_disabled, aspm_force;
@@ -296,6 +316,22 @@ static u32 calc_l1_acceptable(u32 encoding)
 	return (1000 << encoding);
 }
 
+/* Convert L1SS T_pwr encoding to usec */
+static u32 calc_l1ss_pwron(struct pci_dev *pdev, u32 scale, u32 val)
+{
+	switch (scale) {
+	case 0:
+		return val * 2;
+	case 1:
+		return val * 10;
+	case 2:
+		return val * 100;
+	}
+	dev_err(&pdev->dev, "%s: Invalid T_PwrOn scale: %u\n",
+		__func__, scale);
+	return 0;
+}
+
 struct aspm_register_info {
 	u32 support:2;
 	u32 enabled:2;
@@ -392,6 +428,46 @@ static inline struct pci_dev *pci_function_0(struct pci_bus *linkbus)
 	return NULL;
 }
 
+/* Calculate L1.2 PM substate timing parameters */
+static void aspm_calc_l1ss_info(struct pcie_link_state *link,
+				struct aspm_register_info *upreg,
+				struct aspm_register_info *dwreg)
+{
+	u32 val1, val2, scale1, scale2;
+
+	link->l1ss.up_cap_ptr = upreg->l1ss_cap_ptr;
+	link->l1ss.dw_cap_ptr = dwreg->l1ss_cap_ptr;
+	link->l1ss.ctl1 = link->l1ss.ctl2 = 0;
+
+	if (!(link->aspm_support & ASPM_STATE_L1_2_MASK))
+		return;
+
+	/* Choose the greater of the two T_cmn_mode_rstr_time */
+	val1 = (upreg->l1ss_cap >> 8) & 0xFF;
+	val2 = (upreg->l1ss_cap >> 8) & 0xFF;
+	if (val1 > val2)
+		link->l1ss.ctl1 |= val1 << 8;
+	else
+		link->l1ss.ctl1 |= val2 << 8;
+	/*
+	 * We currently use LTR L1.2 threshold to be fixed constant picked from
+	 * Intel's coreboot.
+	 */
+	link->l1ss.ctl1 |= LTR_L1_2_THRESHOLD_BITS;
+
+	/* Choose the greater of the two T_pwr_on */
+	val1 = (upreg->l1ss_cap >> 19) & 0x1F;
+	scale1 = (upreg->l1ss_cap >> 16) & 0x03;
+	val2 = (dwreg->l1ss_cap >> 19) & 0x1F;
+	scale2 = (dwreg->l1ss_cap >> 16) & 0x03;
+
+	if (calc_l1ss_pwron(link->pdev, scale1, val1) >
+	    calc_l1ss_pwron(link->downstream, scale2, val2))
+		link->l1ss.ctl2 |= scale1 | (val1 << 3);
+	else
+		link->l1ss.ctl2 |= scale2 | (val2 << 3);
+}
+
 static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
 {
 	struct pci_dev *child, *parent = link->pdev;
@@ -471,6 +547,9 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
 	if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_2)
 		link->aspm_enabled |= ASPM_STATE_L1_2_PCIPM;
 
+	if (link->aspm_support & ASPM_STATE_L1SS)
+		aspm_calc_l1ss_info(link, &upreg, &dwreg);
+
 	/* Save default state */
 	link->aspm_default = link->aspm_enabled;
 
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related

* [PATCH 6/6] PCI/ASPM: Add comment about L1 substate latency
From: Rajat Jain @ 2017-01-03  6:34 UTC (permalink / raw)
  To: Bjorn Helgaas, Keith Busch, Andreas Ziegler, Jonathan Yong,
	Shawn Lin, David Daney, Julia Lawall, Ram Amrani, Doug Ledford,
	Wang Sheng-Hui, linux-pci, linux-kernel
  Cc: Rajat Jain, Rajat Jain, Brian Norris
In-Reply-To: <1483425255-101923-1-git-send-email-rajatja@google.com>

Since the exit latencies for L1 substates are not advertised by
a device, it is not clear in spec how to do a L1 substate exit
latency check. We assume that the L1 exit latencies advertised
by a device include L1 substate latencies (and hence do not do any
check). If that is not true, we should do some sort of check here.

(I'm not clear about what that check should like currenlty. I'd be
glad to take up any suggestions).

Signed-off-by: Rajat Jain <rajatja@google.com>
---
 drivers/pci/pcie/aspm.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 6735f38..cb5602c 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -403,6 +403,14 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint)
 		 * Check L1 latency.
 		 * Every switch on the path to root complex need 1
 		 * more microsecond for L1. Spec doesn't mention L0s.
+		 *
+		 * The exit latencies for L1 substates are not advertised
+		 * by a device. Since the spec also doesn't mention a way
+		 * to determine max latencies introduced by enabling L1
+		 * substates on the components,  it is not clear how to do
+		 * a L1 substate exit latency check. We assume that the
+		 * L1 exit latencies advertised by a device include L1
+		 * substate latencies (and hence do not do any check)
 		 */
 		latency = max_t(u32, link->latency_up.l1, link->latency_dw.l1);
 		if ((link->aspm_capable & ASPM_STATE_L1) &&
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related

* [PATCH 5/6] PCI/ASPM: Actually configure the L1 substate settings to the device
From: Rajat Jain @ 2017-01-03  6:34 UTC (permalink / raw)
  To: Bjorn Helgaas, Keith Busch, Andreas Ziegler, Jonathan Yong,
	Shawn Lin, David Daney, Julia Lawall, Ram Amrani, Doug Ledford,
	Wang Sheng-Hui, linux-pci, linux-kernel
  Cc: Rajat Jain, Rajat Jain, Brian Norris
In-Reply-To: <1483425255-101923-1-git-send-email-rajatja@google.com>

Add code to actually configure the L1 substate settigns on the
upstream and downstream device, while taking care of the rules
dictated by the PCIe spec.

Signed-off-by: Rajat Jain <rajatja@google.com>
---
 drivers/pci/pcie/aspm.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 104 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index a70afdf..6735f38 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -588,6 +588,92 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
 	}
 }
 
+static inline void pci_clear_and_set_dword(struct pci_dev *pdev, int pos,
+					   u32 clear, u32 set)
+{
+	u32 val;
+
+	pci_read_config_dword(pdev, pos, &val);
+	val &= ~clear;
+	val |= set;
+	pci_write_config_dword(pdev, pos, val);
+}
+
+/* Configure the ASPM L1 substates */
+static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state)
+{
+	u32 val, enable_req;
+	struct pci_dev *child = link->downstream, *parent = link->pdev;
+	u32 up_cap_ptr = link->l1ss.up_cap_ptr;
+	u32 dw_cap_ptr = link->l1ss.dw_cap_ptr;
+
+	enable_req = (link->aspm_enabled ^ state) & state;
+
+	/*
+	 * Here are the rules specified in the PCIe spec for enabling L1SS:
+	 * - When enabling L1.x, enable bit at parent first, then at child
+	 * - When disabling L1.x, disable bit at child first, then at parent
+	 * - When enabling ASPM L1.x, need to disable L1
+	 *   (at child followed by parent).
+	 * - The ASPM/PCIPM L1.2 must be disabled while programming timin
+	 *   parameters
+	 *
+	 * To keep it simple, disable all L1SS bits first, and later enable
+	 * what is needed.
+	 */
+
+	/* Disable all L1 substates */
+	pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1,
+				PCI_L1SS_CTL1_L1SS_MASK, 0);
+	pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1,
+				PCI_L1SS_CTL1_L1SS_MASK, 0);
+	/*
+	 * If needed, disable L1, and it gets enabled later
+	 * in pcie_config_aspm_link().
+	 */
+	if (enable_req & (ASPM_STATE_L1_1 | ASPM_STATE_L1_2)) {
+		pcie_capability_clear_and_set_word(child, PCI_EXP_LNKCTL,
+						   PCI_EXP_LNKCTL_ASPM_L1, 0);
+		pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL,
+						   PCI_EXP_LNKCTL_ASPM_L1, 0);
+	}
+
+	if (enable_req & ASPM_STATE_L1_2_MASK) {
+
+		/* Program T_pwr_on in both ports */
+		pci_write_config_dword(parent, up_cap_ptr + PCI_L1SS_CTL2,
+				       link->l1ss.ctl2);
+		pci_write_config_dword(child, dw_cap_ptr + PCI_L1SS_CTL2,
+				       link->l1ss.ctl2);
+
+		/* Program T_cmn_mode in parent */
+		pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1,
+					0xFF00, link->l1ss.ctl1);
+
+		/* Program LTR L1.2 threshold in both ports */
+		pci_clear_and_set_dword(parent,	dw_cap_ptr + PCI_L1SS_CTL1,
+					0xE3FF0000, link->l1ss.ctl1);
+		pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1,
+					0xE3FF0000, link->l1ss.ctl1);
+	}
+
+	val = 0;
+	if (state & ASPM_STATE_L1_1)
+		val |= PCI_L1SS_CTL1_ASPM_L1_1;
+	if (state & ASPM_STATE_L1_2)
+		val |= PCI_L1SS_CTL1_ASPM_L1_2;
+	if (state & ASPM_STATE_L1_1_PCIPM)
+		val |= PCI_L1SS_CTL1_PCIPM_L1_1;
+	if (state & ASPM_STATE_L1_2_PCIPM)
+		val |= PCI_L1SS_CTL1_PCIPM_L1_2;
+
+	/* Enable what we need to enable */
+	pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1,
+				PCI_L1SS_CAP_L1_PM_SS, val);
+	pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1,
+				PCI_L1SS_CAP_L1_PM_SS, val);
+}
+
 static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
 {
 	pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL,
@@ -597,11 +683,23 @@ static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
 static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
 {
 	u32 upstream = 0, dwstream = 0;
-	struct pci_dev *child, *parent = link->pdev;
+	struct pci_dev *child = link->downstream, *parent = link->pdev;
 	struct pci_bus *linkbus = parent->subordinate;
 
-	/* Nothing to do if the link is already in the requested state */
+	/* Enable only the states that were not explicitly disabled */
 	state &= (link->aspm_capable & ~link->aspm_disable);
+
+	/* Can't enable any substates if L1 is not enabled */
+	if (!(state & ASPM_STATE_L1))
+		state &= ~ASPM_STATE_L1SS;
+
+	/* Spec says both ports must be in D0 before enabling PCI PM substates*/
+	if (parent->current_state != PCI_D0 || child->current_state != PCI_D0) {
+		state &= ~ASPM_STATE_L1_SS_PCIPM;
+		state |= (link->aspm_enabled & ASPM_STATE_L1_SS_PCIPM);
+	}
+
+	/* Nothing to do if the link is already in the requested state */
 	if (link->aspm_enabled == state)
 		return;
 	/* Convert ASPM state to upstream/downstream ASPM register state */
@@ -613,6 +711,10 @@ static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
 		upstream |= PCI_EXP_LNKCTL_ASPM_L1;
 		dwstream |= PCI_EXP_LNKCTL_ASPM_L1;
 	}
+
+	if (link->aspm_capable & ASPM_STATE_L1SS)
+		pcie_config_aspm_l1ss(link, state);
+
 	/*
 	 * Spec 2.0 suggests all functions should be configured the
 	 * same setting for ASPM. Enabling ASPM L1 should be done in
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related

* [PATCH 2/6] PCI/ASPM: Introduce L1 substates and a Kconfig for it
From: Rajat Jain @ 2017-01-03  6:34 UTC (permalink / raw)
  To: Bjorn Helgaas, Keith Busch, Andreas Ziegler, Jonathan Yong,
	Shawn Lin, David Daney, Julia Lawall, Ram Amrani, Doug Ledford,
	Wang Sheng-Hui, linux-pci, linux-kernel
  Cc: Rajat Jain, Rajat Jain, Brian Norris
In-Reply-To: <1483425255-101923-1-git-send-email-rajatja@google.com>

Introduce the L1 sub states. (For details about L1 substates,
please refer to:
https://pcisig.com/sites/default/files/specification_documents/ECN_L1_PM_Substates_with_CLKREQ_31_May_2013_Rev10a.pdf)

This patch adds macros for the 4 new L1 substates, and adds
a new ASPM "POWER_SUPERSAVE" policy that can be used to enable
L1 substates on a system if desired. The new policy is in a sense,
a super set of the existing POWERSAVE policy. The 4 policies are now:

DEFAULT: Reads and uses whatever ASPM states BIOS enabled
PERFORMANCE: Everything except L0 disabled.
POWERSAVE: L0s and L1 enabled (but not L1 substates)
POWER_SUPERSAVE: L0s + L1 + L1 substates also enabled.

Signed-off-by: Rajat Jain <rajatja@google.com>
---
 drivers/pci/pcie/Kconfig |  8 ++++++++
 drivers/pci/pcie/aspm.c  | 39 +++++++++++++++++++++++++++++----------
 2 files changed, 37 insertions(+), 10 deletions(-)

diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
index 7ce7763..ac53edb 100644
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -71,6 +71,14 @@ config PCIEASPM_POWERSAVE
 	  Enable PCI Express ASPM L0s and L1 where possible, even if the
 	  BIOS did not.
 
+config PCIEASPM_POWER_SUPERSAVE
+	bool "Power Supersave"
+	depends on PCIEASPM
+	help
+	  Same as PCIEASPM_POWERSAVE, except it also enables L1 substates where
+	  possible. This would result in higher power savings while staying in L1
+	  where the components support it.
+
 config PCIEASPM_PERFORMANCE
 	bool "Performance"
 	depends on PCIEASPM
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 17ac1dc..402c229 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -30,8 +30,17 @@
 #define ASPM_STATE_L0S_UP	(1)	/* Upstream direction L0s state */
 #define ASPM_STATE_L0S_DW	(2)	/* Downstream direction L0s state */
 #define ASPM_STATE_L1		(4)	/* L1 state */
+#define ASPM_STATE_L1_1		(8)	/* ASPM L1.1 state */
+#define ASPM_STATE_L1_2		(0x10)	/* ASPM L1.2 state */
+#define ASPM_STATE_L1_1_PCIPM	(0x20)	/* PCI PM L1.1 state */
+#define ASPM_STATE_L1_2_PCIPM	(0x40)	/* PCI PM L1.2 state */
+#define ASPM_STATE_L1_SS_PCIPM	(ASPM_STATE_L1_1_PCIPM | ASPM_STATE_L1_2_PCIPM)
+#define ASPM_STATE_L1_2_MASK	(ASPM_STATE_L1_2 | ASPM_STATE_L1_2_PCIPM)
+#define ASPM_STATE_L1SS		(ASPM_STATE_L1_1 | ASPM_STATE_L1_1_PCIPM |\
+				 ASPM_STATE_L1_2_MASK)
 #define ASPM_STATE_L0S		(ASPM_STATE_L0S_UP | ASPM_STATE_L0S_DW)
-#define ASPM_STATE_ALL		(ASPM_STATE_L0S | ASPM_STATE_L1)
+#define ASPM_STATE_ALL		(ASPM_STATE_L0S | ASPM_STATE_L1 |	\
+				 ASPM_STATE_L1SS)
 
 struct aspm_latency {
 	u32 l0s;			/* L0s latency (nsec) */
@@ -47,11 +56,11 @@ struct pcie_link_state {
 	struct list_head link;		/* node in parent's children list */
 
 	/* ASPM state */
-	u32 aspm_support:3;		/* Supported ASPM state */
-	u32 aspm_enabled:3;		/* Enabled ASPM state */
-	u32 aspm_capable:3;		/* Capable ASPM state with latency */
-	u32 aspm_default:3;		/* Default ASPM state by BIOS */
-	u32 aspm_disable:3;		/* Disabled ASPM state */
+	u32 aspm_support:7;		/* Supported ASPM state */
+	u32 aspm_enabled:7;		/* Enabled ASPM state */
+	u32 aspm_capable:7;		/* Capable ASPM state with latency */
+	u32 aspm_default:7;		/* Default ASPM state by BIOS */
+	u32 aspm_disable:7;		/* Disabled ASPM state */
 
 	/* Clock PM state */
 	u32 clkpm_capable:1;		/* Clock PM capable? */
@@ -76,11 +85,14 @@ static LIST_HEAD(link_list);
 #define POLICY_DEFAULT 0	/* BIOS default setting */
 #define POLICY_PERFORMANCE 1	/* high performance */
 #define POLICY_POWERSAVE 2	/* high power saving */
+#define POLICY_POWER_SUPERSAVE 3 /* Possibly Even higher power saving */
 
 #ifdef CONFIG_PCIEASPM_PERFORMANCE
 static int aspm_policy = POLICY_PERFORMANCE;
 #elif defined CONFIG_PCIEASPM_POWERSAVE
 static int aspm_policy = POLICY_POWERSAVE;
+#elif defined CONFIG_PCIEASPM_POWER_SUPERSAVE
+static int aspm_policy = POLICY_POWER_SUPERSAVE;
 #else
 static int aspm_policy;
 #endif
@@ -88,7 +100,8 @@ static int aspm_policy;
 static const char *policy_str[] = {
 	[POLICY_DEFAULT] = "default",
 	[POLICY_PERFORMANCE] = "performance",
-	[POLICY_POWERSAVE] = "powersave"
+	[POLICY_POWERSAVE] = "powersave",
+	[POLICY_POWER_SUPERSAVE] = "powersupersave"
 };
 
 #define LINK_RETRAIN_TIMEOUT HZ
@@ -101,6 +114,9 @@ static int policy_to_aspm_state(struct pcie_link_state *link)
 		return 0;
 	case POLICY_POWERSAVE:
 		/* Enable ASPM L0s/L1 */
+		return (ASPM_STATE_L0S | ASPM_STATE_L1);
+	case POLICY_POWER_SUPERSAVE:
+		/* Enable Everything */
 		return ASPM_STATE_ALL;
 	case POLICY_DEFAULT:
 		return link->aspm_default;
@@ -115,7 +131,8 @@ static int policy_to_clkpm_state(struct pcie_link_state *link)
 		/* Disable ASPM and Clock PM */
 		return 0;
 	case POLICY_POWERSAVE:
-		/* Disable Clock PM */
+	case POLICY_POWER_SUPERSAVE:
+		/* Enable Clock PM */
 		return 1;
 	case POLICY_DEFAULT:
 		return link->clkpm_default;
@@ -612,7 +629,8 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
 	 * the BIOS's expectation, we'll do so once pci_enable_device() is
 	 * called.
 	 */
-	if (aspm_policy != POLICY_POWERSAVE) {
+	if (aspm_policy != POLICY_POWERSAVE &&
+	    aspm_policy != POLICY_POWER_SUPERSAVE) {
 		pcie_config_aspm_path(link);
 		pcie_set_clkpm(link, policy_to_clkpm_state(link));
 	}
@@ -712,7 +730,8 @@ void pcie_aspm_powersave_config_link(struct pci_dev *pdev)
 	if (aspm_disabled || !link)
 		return;
 
-	if (aspm_policy != POLICY_POWERSAVE)
+	if (aspm_policy != POLICY_POWERSAVE &&
+	    aspm_policy != POLICY_POWER_SUPERSAVE)
 		return;
 
 	down_read(&pci_bus_sem);
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related

* [PATCH 1/6] PCI: Add L1 substate capability structure register definitions
From: Rajat Jain @ 2017-01-03  6:34 UTC (permalink / raw)
  To: Bjorn Helgaas, Keith Busch, Andreas Ziegler, Jonathan Yong,
	Shawn Lin, David Daney, Julia Lawall, Ram Amrani, Doug Ledford,
	Wang Sheng-Hui, linux-pci, linux-kernel
  Cc: Rajat Jain, Rajat Jain, Brian Norris
In-Reply-To: <1483425255-101923-1-git-send-email-rajatja@google.com>

These definitions will be used in subsequent patches.

Signed-off-by: Rajat Jain <rajatja@google.com>
---
 include/uapi/linux/pci_regs.h | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index 174d114..f48d06e 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -682,6 +682,7 @@
 #define PCI_EXT_CAP_ID_PMUX	0x1A	/* Protocol Multiplexing */
 #define PCI_EXT_CAP_ID_PASID	0x1B	/* Process Address Space ID */
 #define PCI_EXT_CAP_ID_DPC	0x1D	/* Downstream Port Containment */
+#define PCI_EXT_CAP_ID_L1SS	0x1E	/* L1 PM Substates */
 #define PCI_EXT_CAP_ID_PTM	0x1F	/* Precision Time Measurement */
 #define PCI_EXT_CAP_ID_MAX	PCI_EXT_CAP_ID_PTM
 
@@ -985,4 +986,19 @@
 #define  PCI_PTM_CTRL_ENABLE		0x00000001  /* PTM enable */
 #define  PCI_PTM_CTRL_ROOT		0x00000002  /* Root select */
 
+/* L1 PM Substates */
+#define PCI_L1SS_CAP		    4	/* capability register */
+#define  PCI_L1SS_CAP_PCIPM_L1_2	 1	/* PCI PM L1.2 Support */
+#define  PCI_L1SS_CAP_PCIPM_L1_1	 2	/* PCI PM L1.1 Support */
+#define  PCI_L1SS_CAP_ASPM_L1_2		 4	/* ASPM L1.2 Support */
+#define  PCI_L1SS_CAP_ASPM_L1_1		 8	/* ASPM L1.1 Support */
+#define  PCI_L1SS_CAP_L1_PM_SS		16	/* L1 PM Substates Support */
+#define PCI_L1SS_CTL1		    8	/* Control Register 1 */
+#define  PCI_L1SS_CTL1_PCIPM_L1_2	1	/* PCI PM L1.2 Enable */
+#define  PCI_L1SS_CTL1_PCIPM_L1_1	2	/* PCI PM L1.1 Support */
+#define  PCI_L1SS_CTL1_ASPM_L1_2	4	/* ASPM L1.2 Support */
+#define  PCI_L1SS_CTL1_ASPM_L1_1	8	/* ASPM L1.1 Support */
+#define  PCI_L1SS_CTL1_L1SS_MASK	0x0000000F
+#define PCI_L1SS_CTL2		    0xC	/* Control Register 2 */
+
 #endif /* LINUX_PCI_REGS_H */
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related

* [PATCH 0/6] PCI/ASPM: Add PCIe L1 PM substate support
From: Rajat Jain @ 2017-01-03  6:34 UTC (permalink / raw)
  To: Bjorn Helgaas, Keith Busch, Andreas Ziegler, Jonathan Yong,
	Shawn Lin, David Daney, Julia Lawall, Ram Amrani, Doug Ledford,
	Wang Sheng-Hui, linux-pci, linux-kernel
  Cc: Rajat Jain, Rajat Jain, Brian Norris

This patchset adds the PCIe L1 PM substate support to the kernel.
The feature is described at:
https://pcisig.com/sites/default/files/specification_documents/ECN_L1_PM_Substates_with_CLKREQ_31_May_2013_Rev10a.pdf

Its all logically one patch (and may be some of them should be
squashed later) , but I've broken down into smaller patches for
ease of review. 

This is currently rebased on top of Bjorn's master branch.

Rajat Jain (6):
  PCI: Add L1 substate capability structure register definitions
  PCI/ASPM: Introduce L1 substates and a Kconfig for it
  PCI/ASPM: Read and setup L1 substate capabilities
  PCI/ASPM: Calculate and save the L1.2 timing parameters
  PCI/ASPM: Actually configure the L1 substate settings to the device
  PCI/ASPM: Add comment about L1 substate latency

 drivers/pci/pcie/Kconfig      |   8 ++
 drivers/pci/pcie/aspm.c       | 291 ++++++++++++++++++++++++++++++++++++++++--
 include/uapi/linux/pci_regs.h |  16 +++
 3 files changed, 302 insertions(+), 13 deletions(-)

-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply

* [PATCH 6/8] ARM: dts: imx6qdl: Enable usb node children with <reg>
From: Peter Chen @ 2017-01-03  6:33 UTC (permalink / raw)
  To: gregkh, stern, ulf.hansson, broonie, sre, robh+dt, shawnguo, rjw,
	dbaryshkov
  Cc: heiko, linux-arm-kernel, p.zabel, devicetree, pawel.moll,
	mark.rutland, linux-usb, arnd, s.hauer, mail, troy.kisky,
	festevam, oscar, stephen.boyd, linux-pm, stillcompiling,
	linux-kernel, mka, vaibhav.hiremath, gary.bisson, hverkuil, krzk,
	Peter Chen
In-Reply-To: <1483425211-14473-1-git-send-email-peter.chen@nxp.com>

From: Joshua Clayton <stillcompiling@gmail.com>

Give usb nodes #address and #size attributes, so that a child node
representing a permanently connected device such as an onboard hub may
be addressed with a <reg> attribute

Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
Signed-off-by: Peter Chen <peter.chen@nxp.com>
---
 arch/arm/boot/dts/imx6qdl.dtsi | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index 53e6e63..e6725f1 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -936,6 +936,8 @@
 
 			usbh1: usb@02184200 {
 				compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
+				#address-cells = <1>;
+				#size-cells = <0>;
 				reg = <0x02184200 0x200>;
 				interrupts = <0 40 IRQ_TYPE_LEVEL_HIGH>;
 				clocks = <&clks IMX6QDL_CLK_USBOH3>;
@@ -950,6 +952,8 @@
 
 			usbh2: usb@02184400 {
 				compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
+				#address-cells = <1>;
+				#size-cells = <0>;
 				reg = <0x02184400 0x200>;
 				interrupts = <0 41 IRQ_TYPE_LEVEL_HIGH>;
 				clocks = <&clks IMX6QDL_CLK_USBOH3>;
@@ -963,6 +967,8 @@
 
 			usbh3: usb@02184600 {
 				compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
+				#address-cells = <1>;
+				#size-cells = <0>;
 				reg = <0x02184600 0x200>;
 				interrupts = <0 42 IRQ_TYPE_LEVEL_HIGH>;
 				clocks = <&clks IMX6QDL_CLK_USBOH3>;
-- 
2.7.4

^ permalink raw reply related

* [PATCH 7/8] ARM: dts: imx6qdl-udoo.dtsi: fix onboard USB HUB property
From: Peter Chen @ 2017-01-03  6:33 UTC (permalink / raw)
  To: gregkh, stern, ulf.hansson, broonie, sre, robh+dt, shawnguo, rjw,
	dbaryshkov
  Cc: heiko, linux-arm-kernel, p.zabel, devicetree, pawel.moll,
	mark.rutland, linux-usb, arnd, s.hauer, mail, troy.kisky,
	festevam, oscar, stephen.boyd, linux-pm, stillcompiling,
	linux-kernel, mka, vaibhav.hiremath, gary.bisson, hverkuil, krzk,
	Peter Chen
In-Reply-To: <1483425211-14473-1-git-send-email-peter.chen@nxp.com>

The current dts describes USB HUB's property at USB controller's
entry, it is improper. The USB HUB should be the child node
under USB controller, and power sequence properties are under
it. Besides, using gpio pinctrl setting for USB2415's reset pin.

Signed-off-by: Peter Chen <peter.chen@nxp.com>
Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
Tested-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
---
 arch/arm/boot/dts/imx6qdl-udoo.dtsi | 26 ++++++++++++--------------
 1 file changed, 12 insertions(+), 14 deletions(-)

diff --git a/arch/arm/boot/dts/imx6qdl-udoo.dtsi b/arch/arm/boot/dts/imx6qdl-udoo.dtsi
index c96c91d..a173de2 100644
--- a/arch/arm/boot/dts/imx6qdl-udoo.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-udoo.dtsi
@@ -9,6 +9,8 @@
  *
  */
 
+#include <dt-bindings/gpio/gpio.h>
+
 / {
 	aliases {
 		backlight = &backlight;
@@ -58,17 +60,6 @@
 		#address-cells = <1>;
 		#size-cells = <0>;
 
-		reg_usb_h1_vbus: regulator@0 {
-			compatible = "regulator-fixed";
-			reg = <0>;
-			regulator-name = "usb_h1_vbus";
-			regulator-min-microvolt = <5000000>;
-			regulator-max-microvolt = <5000000>;
-			enable-active-high;
-			startup-delay-us = <2>; /* USB2415 requires a POR of 1 us minimum */
-			gpio = <&gpio7 12 0>;
-		};
-
 		reg_panel: regulator@1 {
 			compatible = "regulator-fixed";
 			reg = <1>;
@@ -188,7 +179,7 @@
 
 		pinctrl_usbh: usbhgrp {
 			fsl,pins = <
-				MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000
+				MX6QDL_PAD_GPIO_17__GPIO7_IO12	0x1b0b0
 				MX6QDL_PAD_NANDF_CS2__CCM_CLKO2 0x130b0
 			>;
 		};
@@ -259,9 +250,16 @@
 &usbh1 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_usbh>;
-	vbus-supply = <&reg_usb_h1_vbus>;
-	clocks = <&clks IMX6QDL_CLK_CKO>;
 	status = "okay";
+
+	usb2415: hub@1 {
+		compatible = "usb424,2514";
+		reg = <1>;
+
+		clocks = <&clks IMX6QDL_CLK_CKO>;
+		reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>;
+		reset-duration-us = <3000>;
+	};
 };
 
 &usdhc3 {
-- 
2.7.4

^ permalink raw reply related

* [PATCH 4/8] usb: core: add power sequence handling for USB devices
From: Peter Chen @ 2017-01-03  6:33 UTC (permalink / raw)
  To: gregkh, stern, ulf.hansson, broonie, sre, robh+dt, shawnguo, rjw,
	dbaryshkov
  Cc: heiko, linux-arm-kernel, p.zabel, devicetree, pawel.moll,
	mark.rutland, linux-usb, arnd, s.hauer, mail, troy.kisky,
	festevam, oscar, stephen.boyd, linux-pm, stillcompiling,
	linux-kernel, mka, vaibhav.hiremath, gary.bisson, hverkuil, krzk,
	Peter Chen
In-Reply-To: <1483425211-14473-1-git-send-email-peter.chen@nxp.com>

Some hard-wired USB devices need to do power sequence to let the
device work normally, the typical power sequence like: enable USB
PHY clock, toggle reset pin, etc. But current Linux USB driver
lacks of such code to do it, it may cause some hard-wired USB devices
works abnormal or can't be recognized by controller at all.

In this patch, it calls power sequence library APIs to finish
the power sequence events. It will do power on sequence at hub's
probe for all devices under this hub (includes root hub).
At hub_disconnect, it will do power off sequence which is at powered
on list.

Signed-off-by: Peter Chen <peter.chen@nxp.com>
Tested-by Joshua Clayton <stillcompiling@gmail.com>
Tested-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
Reviewed-by: Vaibhav Hiremath <hvaibhav.linux@gmail.com>
---
 drivers/usb/Kconfig    |  1 +
 drivers/usb/core/hub.c | 48 ++++++++++++++++++++++++++++++++++++++++++++----
 drivers/usb/core/hub.h |  1 +
 3 files changed, 46 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index fbe493d..706f261 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -40,6 +40,7 @@ config USB
 	tristate "Support for Host-side USB"
 	depends on USB_ARCH_HAS_HCD
 	select USB_COMMON
+	select POWER_SEQUENCE
 	select NLS  # for UTF-8 strings
 	---help---
 	  Universal Serial Bus (USB) is a specification for a serial bus
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 1fa5c0f..cec2fad 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -28,6 +28,7 @@
 #include <linux/mutex.h>
 #include <linux/random.h>
 #include <linux/pm_qos.h>
+#include <linux/power/pwrseq.h>
 
 #include <linux/uaccess.h>
 #include <asm/byteorder.h>
@@ -1645,6 +1646,7 @@ static void hub_disconnect(struct usb_interface *intf)
 	hub->error = 0;
 	hub_quiesce(hub, HUB_DISCONNECT);
 
+	of_pwrseq_off_list(&hub->pwrseq_on_list);
 	mutex_lock(&usb_port_peer_mutex);
 
 	/* Avoid races with recursively_mark_NOTATTACHED() */
@@ -1672,12 +1674,41 @@ static void hub_disconnect(struct usb_interface *intf)
 	kref_put(&hub->kref, hub_release);
 }
 
+#ifdef CONFIG_OF
+static int hub_of_pwrseq_on(struct usb_hub *hub)
+{
+	struct device *parent;
+	struct usb_device *hdev = hub->hdev;
+	struct device_node *np;
+	int ret;
+
+	if (hdev->parent)
+		parent = &hdev->dev;
+	else
+		parent = bus_to_hcd(hdev->bus)->self.controller;
+
+	for_each_child_of_node(parent->of_node, np) {
+		ret = of_pwrseq_on_list(np, &hub->pwrseq_on_list);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+#else
+static int hub_of_pwrseq_on(struct usb_hub *hub)
+{
+	return 0;
+}
+#endif
+
 static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
 	struct usb_host_interface *desc;
 	struct usb_endpoint_descriptor *endpoint;
 	struct usb_device *hdev;
 	struct usb_hub *hub;
+	int ret = -ENODEV;
 
 	desc = intf->cur_altsetting;
 	hdev = interface_to_usbdev(intf);
@@ -1782,6 +1813,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
 	INIT_DELAYED_WORK(&hub->leds, led_work);
 	INIT_DELAYED_WORK(&hub->init_work, NULL);
 	INIT_WORK(&hub->events, hub_event);
+	INIT_LIST_HEAD(&hub->pwrseq_on_list);
 	usb_get_intf(intf);
 	usb_get_dev(hdev);
 
@@ -1795,11 +1827,14 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
 	if (id->driver_info & HUB_QUIRK_CHECK_PORT_AUTOSUSPEND)
 		hub->quirk_check_port_auto_suspend = 1;
 
-	if (hub_configure(hub, endpoint) >= 0)
-		return 0;
+	if (hub_configure(hub, endpoint) >= 0) {
+		ret = hub_of_pwrseq_on(hub);
+		if (!ret)
+			return 0;
+	}
 
 	hub_disconnect(intf);
-	return -ENODEV;
+	return ret;
 }
 
 static int
@@ -3613,14 +3648,19 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
 
 	/* stop hub_wq and related activity */
 	hub_quiesce(hub, HUB_SUSPEND);
-	return 0;
+	return pwrseq_suspend_list(&hub->pwrseq_on_list);
 }
 
 static int hub_resume(struct usb_interface *intf)
 {
 	struct usb_hub *hub = usb_get_intfdata(intf);
+	int ret;
 
 	dev_dbg(&intf->dev, "%s\n", __func__);
+	ret = pwrseq_resume_list(&hub->pwrseq_on_list);
+	if (ret)
+		return ret;
+
 	hub_activate(hub, HUB_RESUME);
 	return 0;
 }
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 34c1a7e..cd86f91 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -78,6 +78,7 @@ struct usb_hub {
 	struct delayed_work	init_work;
 	struct work_struct      events;
 	struct usb_port		**ports;
+	struct list_head	pwrseq_on_list; /* powered pwrseq node list */
 };
 
 /**
-- 
2.7.4

^ permalink raw reply related

* [PATCH 5/8] usb: chipidea: let chipidea core device of_node equal's glue layer device of_node
From: Peter Chen @ 2017-01-03  6:33 UTC (permalink / raw)
  To: gregkh, stern, ulf.hansson, broonie, sre, robh+dt, shawnguo, rjw,
	dbaryshkov
  Cc: heiko, linux-arm-kernel, p.zabel, devicetree, pawel.moll,
	mark.rutland, linux-usb, arnd, s.hauer, mail, troy.kisky,
	festevam, oscar, stephen.boyd, linux-pm, stillcompiling,
	linux-kernel, mka, vaibhav.hiremath, gary.bisson, hverkuil, krzk,
	Peter Chen
In-Reply-To: <1483425211-14473-1-git-send-email-peter.chen@nxp.com>

From: Peter Chen <peter.chen@freescale.com>

At device tree, we have no device node for chipidea core,
the glue layer's node is the parent node for host and udc
device. But in related driver, the parent device is chipidea
core. So, in order to let the common driver get parent's node,
we let the core's device node equals glue layer device node.

Signed-off-by: Peter Chen <peter.chen@freescale.com>
Tested-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
Tested-by Joshua Clayton <stillcompiling@gmail.com>
---
 drivers/usb/chipidea/core.c | 27 ++++++++++++++++++++++-----
 1 file changed, 22 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 3dbb4a2..fdffc67 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -928,6 +928,16 @@ static int ci_hdrc_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
+	/*
+	 * At device tree, we have no device node for chipidea core,
+	 * the glue layer's node is the parent node for host and udc
+	 * device. But in related driver, the parent device is chipidea
+	 * core. So, in order to let the common driver get parent's node,
+	 * we let the core's device node equals glue layer's node.
+	 */
+	if (dev->parent && dev->parent->of_node)
+		dev->of_node = dev->parent->of_node;
+
 	if (ci->platdata->phy) {
 		ci->phy = ci->platdata->phy;
 	} else if (ci->platdata->usb_phy) {
@@ -938,11 +948,15 @@ static int ci_hdrc_probe(struct platform_device *pdev)
 
 		/* if both generic PHY and USB PHY layers aren't enabled */
 		if (PTR_ERR(ci->phy) == -ENOSYS &&
-				PTR_ERR(ci->usb_phy) == -ENXIO)
-			return -ENXIO;
+				PTR_ERR(ci->usb_phy) == -ENXIO) {
+			ret = -ENXIO;
+			goto clear_of_node;
+		}
 
-		if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy))
-			return -EPROBE_DEFER;
+		if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) {
+			ret = -EPROBE_DEFER;
+			goto clear_of_node;
+		}
 
 		if (IS_ERR(ci->phy))
 			ci->phy = NULL;
@@ -953,7 +967,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
 	ret = ci_usb_phy_init(ci);
 	if (ret) {
 		dev_err(dev, "unable to init phy: %d\n", ret);
-		return ret;
+		goto clear_of_node;
 	}
 
 	ci->hw_bank.phys = res->start;
@@ -1059,6 +1073,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
 	ci_role_destroy(ci);
 deinit_phy:
 	ci_usb_phy_exit(ci);
+clear_of_node:
+	dev->of_node = NULL;
 
 	return ret;
 }
@@ -1077,6 +1093,7 @@ static int ci_hdrc_remove(struct platform_device *pdev)
 	ci_extcon_unregister(ci);
 	ci_role_destroy(ci);
 	ci_hdrc_enter_lpm(ci, true);
+	ci->dev->of_node = NULL;
 	ci_usb_phy_exit(ci);
 
 	return 0;
-- 
2.7.4

^ permalink raw reply related

* [PATCH 3/8] binding-doc: usb: usb-device: add optional properties for power sequence
From: Peter Chen @ 2017-01-03  6:33 UTC (permalink / raw)
  To: gregkh, stern, ulf.hansson, broonie, sre, robh+dt, shawnguo, rjw,
	dbaryshkov
  Cc: heiko, linux-arm-kernel, p.zabel, devicetree, pawel.moll,
	mark.rutland, linux-usb, arnd, s.hauer, mail, troy.kisky,
	festevam, oscar, stephen.boyd, linux-pm, stillcompiling,
	linux-kernel, mka, vaibhav.hiremath, gary.bisson, hverkuil, krzk,
	Peter Chen
In-Reply-To: <1483425211-14473-1-git-send-email-peter.chen@nxp.com>

Add optional properties for power sequence.

Signed-off-by: Peter Chen <peter.chen@nxp.com>
Acked-by: Rob Herring <robh@kernel.org>
---
 Documentation/devicetree/bindings/usb/usb-device.txt | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/usb/usb-device.txt b/Documentation/devicetree/bindings/usb/usb-device.txt
index 1c35e7b..3661dd2 100644
--- a/Documentation/devicetree/bindings/usb/usb-device.txt
+++ b/Documentation/devicetree/bindings/usb/usb-device.txt
@@ -13,6 +13,10 @@ Required properties:
 - reg: the port number which this device is connecting to, the range
   is 1-31.
 
+Optional properties:
+power sequence properties, see
+Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt for detail
+
 Example:
 
 &usb1 {
@@ -21,8 +25,12 @@ Example:
 	#address-cells = <1>;
 	#size-cells = <0>;
 
-	hub: genesys@1 {
+	genesys: hub@1 {
 		compatible = "usb5e3,608";
 		reg = <1>;
+
+		clocks = <&clks IMX6SX_CLK_CKO>;
+		reset-gpios = <&gpio4 5 GPIO_ACTIVE_LOW>; /* hub reset pin */
+		reset-duration-us = <10>;
 	};
 }
-- 
2.7.4

^ permalink raw reply related

* [PATCH 2/8] power: add power sequence library
From: Peter Chen @ 2017-01-03  6:33 UTC (permalink / raw)
  To: gregkh, stern, ulf.hansson, broonie, sre, robh+dt, shawnguo, rjw,
	dbaryshkov
  Cc: heiko, linux-arm-kernel, p.zabel, devicetree, pawel.moll,
	mark.rutland, linux-usb, arnd, s.hauer, mail, troy.kisky,
	festevam, oscar, stephen.boyd, linux-pm, stillcompiling,
	linux-kernel, mka, vaibhav.hiremath, gary.bisson, hverkuil, krzk,
	Peter Chen
In-Reply-To: <1483425211-14473-1-git-send-email-peter.chen@nxp.com>

We have an well-known problem that the device needs to do some power
sequence before it can be recognized by related host, the typical
example like hard-wired mmc devices and usb devices.

This power sequence is hard to be described at device tree and handled by
related host driver, so we have created a common power sequence
library to cover this requirement. The core code has supplied
some common helpers for host driver, and individual power sequence
libraries handle kinds of power sequence for devices. The pwrseq
librares always need to allocate extra instance for compatible
string match.

pwrseq_generic is intended for general purpose of power sequence, which
handles gpios and clocks currently, and can cover other controls in
future. The host driver just needs to call of_pwrseq_on/of_pwrseq_off
if only one power sequence is needed, else call of_pwrseq_on_list
/of_pwrseq_off_list instead (eg, USB hub driver).

For new power sequence library, it can add its compatible string
to pwrseq_of_match_table, then the pwrseq core will match it with
DT's, and choose this library at runtime.

Signed-off-by: Peter Chen <peter.chen@nxp.com>
Tested-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
Tested-by Joshua Clayton <stillcompiling@gmail.com>
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Tested-by: Matthias Kaehlcke <mka@chromium.org>
---
 MAINTAINERS                           |   9 +
 drivers/power/Kconfig                 |   1 +
 drivers/power/Makefile                |   1 +
 drivers/power/pwrseq/Kconfig          |  20 ++
 drivers/power/pwrseq/Makefile         |   2 +
 drivers/power/pwrseq/core.c           | 335 ++++++++++++++++++++++++++++++++++
 drivers/power/pwrseq/pwrseq_generic.c | 224 +++++++++++++++++++++++
 include/linux/power/pwrseq.h          |  81 ++++++++
 8 files changed, 673 insertions(+)
 create mode 100644 drivers/power/pwrseq/Kconfig
 create mode 100644 drivers/power/pwrseq/Makefile
 create mode 100644 drivers/power/pwrseq/core.c
 create mode 100644 drivers/power/pwrseq/pwrseq_generic.c
 create mode 100644 include/linux/power/pwrseq.h

diff --git a/MAINTAINERS b/MAINTAINERS
index cfff2c9..ae2aa25 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9828,6 +9828,15 @@ F:	include/linux/pm_*
 F:	include/linux/powercap.h
 F:	drivers/powercap/
 
+POWER SEQUENCE LIBRARY
+M:	Peter Chen <Peter.Chen@nxp.com>
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
+L:	linux-pm@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/power/pwrseq/
+F:	drivers/power/pwrseq/
+F:	include/linux/power/pwrseq.h
+
 POWER SUPPLY CLASS/SUBSYSTEM and DRIVERS
 M:	Sebastian Reichel <sre@kernel.org>
 L:	linux-pm@vger.kernel.org
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 63454b5..c1bb046 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -1,3 +1,4 @@
 source "drivers/power/avs/Kconfig"
 source "drivers/power/reset/Kconfig"
 source "drivers/power/supply/Kconfig"
+source "drivers/power/pwrseq/Kconfig"
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index ff35c71..7db8035 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_POWER_AVS)		+= avs/
 obj-$(CONFIG_POWER_RESET)	+= reset/
 obj-$(CONFIG_POWER_SUPPLY)	+= supply/
+obj-$(CONFIG_POWER_SEQUENCE)	+= pwrseq/
diff --git a/drivers/power/pwrseq/Kconfig b/drivers/power/pwrseq/Kconfig
new file mode 100644
index 0000000..c6b3569
--- /dev/null
+++ b/drivers/power/pwrseq/Kconfig
@@ -0,0 +1,20 @@
+#
+# Power Sequence library
+#
+
+menuconfig POWER_SEQUENCE
+	bool "Power sequence control"
+	help
+	   It is used for drivers which needs to do power sequence
+	   (eg, turn on clock, toggle reset gpio) before the related
+	   devices can be found by hardware, eg, USB bus.
+
+if POWER_SEQUENCE
+
+config PWRSEQ_GENERIC
+	bool "Generic power sequence control"
+	depends on OF
+	help
+	   This is the generic power sequence control library, and is
+	   supposed to support common power sequence usage.
+endif
diff --git a/drivers/power/pwrseq/Makefile b/drivers/power/pwrseq/Makefile
new file mode 100644
index 0000000..ad82389
--- /dev/null
+++ b/drivers/power/pwrseq/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_POWER_SEQUENCE) += core.o
+obj-$(CONFIG_PWRSEQ_GENERIC) += pwrseq_generic.o
diff --git a/drivers/power/pwrseq/core.c b/drivers/power/pwrseq/core.c
new file mode 100644
index 0000000..3d19e62
--- /dev/null
+++ b/drivers/power/pwrseq/core.c
@@ -0,0 +1,335 @@
+/*
+ * core.c	power sequence core file
+ *
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Author: Peter Chen <peter.chen@nxp.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.
+ */
+
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/power/pwrseq.h>
+
+static DEFINE_MUTEX(pwrseq_list_mutex);
+static LIST_HEAD(pwrseq_list);
+
+static int pwrseq_get(struct device_node *np, struct pwrseq *p)
+{
+	if (p && p->get)
+		return p->get(np, p);
+
+	return -ENOTSUPP;
+}
+
+static int pwrseq_on(struct pwrseq *p)
+{
+	if (p && p->on)
+		return p->on(p);
+
+	return -ENOTSUPP;
+}
+
+static void pwrseq_off(struct pwrseq *p)
+{
+	if (p && p->off)
+		p->off(p);
+}
+
+static void pwrseq_put(struct pwrseq *p)
+{
+	if (p && p->put)
+		p->put(p);
+}
+
+/**
+ * pwrseq_register - Add pwrseq instance to global pwrseq list
+ *
+ * @pwrseq: the pwrseq instance
+ */
+void pwrseq_register(struct pwrseq *pwrseq)
+{
+	mutex_lock(&pwrseq_list_mutex);
+	list_add(&pwrseq->node, &pwrseq_list);
+	mutex_unlock(&pwrseq_list_mutex);
+}
+EXPORT_SYMBOL_GPL(pwrseq_register);
+
+/**
+ * pwrseq_unregister - Remove pwrseq instance from global pwrseq list
+ *
+ * @pwrseq: the pwrseq instance
+ */
+void pwrseq_unregister(struct pwrseq *pwrseq)
+{
+	mutex_lock(&pwrseq_list_mutex);
+	list_del(&pwrseq->node);
+	mutex_unlock(&pwrseq_list_mutex);
+}
+EXPORT_SYMBOL_GPL(pwrseq_unregister);
+
+static struct pwrseq *pwrseq_find_available_instance(struct device_node *np)
+{
+	struct pwrseq *pwrseq;
+
+	mutex_lock(&pwrseq_list_mutex);
+	list_for_each_entry(pwrseq, &pwrseq_list, node) {
+		if (pwrseq->used)
+			continue;
+
+		/* compare compatible string for pwrseq node */
+		if (of_match_node(pwrseq->pwrseq_of_match_table, np)) {
+			pwrseq->used = true;
+			mutex_unlock(&pwrseq_list_mutex);
+			return pwrseq;
+		}
+
+		/* return generic pwrseq instance */
+		if (!strcmp(pwrseq->pwrseq_of_match_table->compatible,
+				"generic")) {
+			pr_debug("using generic pwrseq instance for %s\n",
+				np->full_name);
+			pwrseq->used = true;
+			mutex_unlock(&pwrseq_list_mutex);
+			return pwrseq;
+		}
+	}
+	mutex_unlock(&pwrseq_list_mutex);
+	pr_debug("Can't find any pwrseq instances for %s\n", np->full_name);
+
+	return NULL;
+}
+
+/**
+ * of_pwrseq_on - Carry out power sequence on for device node
+ *
+ * @np: the device node would like to power on
+ *
+ * Carry out a single device power on.  If multiple devices
+ * need to be handled, use of_pwrseq_on_list() instead.
+ *
+ * Return a pointer to the power sequence instance on success,
+ * or an error code otherwise.
+ */
+struct pwrseq *of_pwrseq_on(struct device_node *np)
+{
+	struct pwrseq *pwrseq;
+	int ret;
+
+	pwrseq = pwrseq_find_available_instance(np);
+	if (!pwrseq)
+		return ERR_PTR(-ENOENT);
+
+	ret = pwrseq_get(np, pwrseq);
+	if (ret) {
+		/* Mark current pwrseq as unused */
+		pwrseq->used = false;
+		return ERR_PTR(ret);
+	}
+
+	ret = pwrseq_on(pwrseq);
+	if (ret)
+		goto pwr_put;
+
+	return pwrseq;
+
+pwr_put:
+	pwrseq_put(pwrseq);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(of_pwrseq_on);
+
+/**
+ * of_pwrseq_off - Carry out power sequence off for this pwrseq instance
+ *
+ * @pwrseq: the pwrseq instance which related device would like to be off
+ *
+ * This API is used to power off single device, it is the opposite
+ * operation for of_pwrseq_on.
+ */
+void of_pwrseq_off(struct pwrseq *pwrseq)
+{
+	pwrseq_off(pwrseq);
+	pwrseq_put(pwrseq);
+}
+EXPORT_SYMBOL_GPL(of_pwrseq_off);
+
+/**
+ * of_pwrseq_on_list - Carry out power sequence on for list
+ *
+ * @np: the device node would like to power on
+ * @head: the list head for pwrseq list on this bus
+ *
+ * This API is used to power on multiple devices at single bus.
+ * If there are several devices on bus (eg, USB bus), uses this
+ * this API. Otherwise, use of_pwrseq_on instead. After the device
+ * is powered on successfully, it will be added to pwrseq list for
+ * this bus. The caller needs to use mutex_lock for concurrent.
+ *
+ * Return 0 on success, or an error value otherwise.
+ */
+int of_pwrseq_on_list(struct device_node *np, struct list_head *head)
+{
+	struct pwrseq *pwrseq;
+	struct pwrseq_list_per_dev *pwrseq_list_node;
+
+	pwrseq_list_node = kzalloc(sizeof(*pwrseq_list_node), GFP_KERNEL);
+	if (!pwrseq_list_node)
+		return -ENOMEM;
+
+	pwrseq = of_pwrseq_on(np);
+	if (IS_ERR(pwrseq)) {
+		kfree(pwrseq_list_node);
+		return PTR_ERR(pwrseq);
+	}
+
+	pwrseq_list_node->pwrseq = pwrseq;
+	list_add(&pwrseq_list_node->list, head);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_pwrseq_on_list);
+
+/**
+ * of_pwrseq_off_list - Carry out power sequence off for the list
+ *
+ * @head: the list head for pwrseq instance list on this bus
+ *
+ * This API is used to power off all devices on this bus, it is
+ * the opposite operation for of_pwrseq_on_list.
+ * The caller needs to use mutex_lock for concurrent.
+ */
+void of_pwrseq_off_list(struct list_head *head)
+{
+	struct pwrseq *pwrseq;
+	struct pwrseq_list_per_dev *pwrseq_list_node, *tmp_node;
+
+	list_for_each_entry_safe(pwrseq_list_node, tmp_node, head, list) {
+		pwrseq = pwrseq_list_node->pwrseq;
+		of_pwrseq_off(pwrseq);
+		list_del(&pwrseq_list_node->list);
+		kfree(pwrseq_list_node);
+	}
+}
+EXPORT_SYMBOL_GPL(of_pwrseq_off_list);
+
+/**
+ * pwrseq_suspend - Carry out power sequence suspend for this pwrseq instance
+ *
+ * @pwrseq: the pwrseq instance
+ *
+ * This API is used to do suspend operation on pwrseq instance.
+ *
+ * Return 0 on success, or an error value otherwise.
+ */
+int pwrseq_suspend(struct pwrseq *p)
+{
+	int ret = 0;
+
+	if (p && p->suspend)
+		ret = p->suspend(p);
+	else
+		return ret;
+
+	if (!ret)
+		p->suspended = true;
+	else
+		pr_err("%s failed\n", __func__);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pwrseq_suspend);
+
+/**
+ * pwrseq_resume - Carry out power sequence resume for this pwrseq instance
+ *
+ * @pwrseq: the pwrseq instance
+ *
+ * This API is used to do resume operation on pwrseq instance.
+ *
+ * Return 0 on success, or an error value otherwise.
+ */
+int pwrseq_resume(struct pwrseq *p)
+{
+	int ret = 0;
+
+	if (p && p->resume)
+		ret = p->resume(p);
+	else
+		return ret;
+
+	if (!ret)
+		p->suspended = false;
+	else
+		pr_err("%s failed\n", __func__);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pwrseq_resume);
+
+/**
+ * pwrseq_suspend_list - Carry out power sequence suspend for list
+ *
+ * @head: the list head for pwrseq instance list on this bus
+ *
+ * This API is used to do suspend on all power sequence instances on this bus.
+ * The caller needs to use mutex_lock for concurrent.
+ */
+int pwrseq_suspend_list(struct list_head *head)
+{
+	struct pwrseq *pwrseq;
+	struct pwrseq_list_per_dev *pwrseq_list_node;
+	int ret = 0;
+
+	list_for_each_entry(pwrseq_list_node, head, list) {
+		ret = pwrseq_suspend(pwrseq_list_node->pwrseq);
+		if (ret)
+			break;
+	}
+
+	if (ret) {
+		list_for_each_entry(pwrseq_list_node, head, list) {
+			pwrseq = pwrseq_list_node->pwrseq;
+			if (pwrseq->suspended)
+				pwrseq_resume(pwrseq);
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pwrseq_suspend_list);
+
+/**
+ * pwrseq_resume_list - Carry out power sequence resume for the list
+ *
+ * @head: the list head for pwrseq instance list on this bus
+ *
+ * This API is used to do resume on all power sequence instances on this bus.
+ * The caller needs to use mutex_lock for concurrent.
+ */
+int pwrseq_resume_list(struct list_head *head)
+{
+	struct pwrseq_list_per_dev *pwrseq_list_node;
+	int ret = 0;
+
+	list_for_each_entry(pwrseq_list_node, head, list) {
+		ret = pwrseq_resume(pwrseq_list_node->pwrseq);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pwrseq_resume_list);
diff --git a/drivers/power/pwrseq/pwrseq_generic.c b/drivers/power/pwrseq/pwrseq_generic.c
new file mode 100644
index 0000000..0e70a38
--- /dev/null
+++ b/drivers/power/pwrseq/pwrseq_generic.c
@@ -0,0 +1,224 @@
+/*
+ * pwrseq_generic.c	Generic power sequence handling
+ *
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Author: Peter Chen <peter.chen@nxp.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+
+#include <linux/power/pwrseq.h>
+
+struct pwrseq_generic {
+	struct pwrseq pwrseq;
+	struct gpio_desc *gpiod_reset;
+	struct clk *clks[PWRSEQ_MAX_CLKS];
+	u32 duration_us;
+	bool suspended;
+};
+
+#define to_generic_pwrseq(p) container_of(p, struct pwrseq_generic, pwrseq)
+
+static int pwrseq_generic_alloc_instance(void);
+static const struct of_device_id generic_id_table[] = {
+	{ .compatible = "generic",},
+	{ /* sentinel */ }
+};
+
+static int pwrseq_generic_suspend(struct pwrseq *pwrseq)
+{
+	struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
+	int clk;
+
+	for (clk = PWRSEQ_MAX_CLKS - 1; clk >= 0; clk--)
+		clk_disable_unprepare(pwrseq_gen->clks[clk]);
+
+	pwrseq_gen->suspended = true;
+	return 0;
+}
+
+static int pwrseq_generic_resume(struct pwrseq *pwrseq)
+{
+	struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
+	int clk, ret = 0;
+
+	for (clk = 0; clk < PWRSEQ_MAX_CLKS && pwrseq_gen->clks[clk]; clk++) {
+		ret = clk_prepare_enable(pwrseq_gen->clks[clk]);
+		if (ret) {
+			pr_err("Can't enable clock, ret=%d\n", ret);
+			goto err_disable_clks;
+		}
+	}
+
+	pwrseq_gen->suspended = false;
+	return ret;
+
+err_disable_clks:
+	while (--clk >= 0)
+		clk_disable_unprepare(pwrseq_gen->clks[clk]);
+
+	return ret;
+}
+
+static void pwrseq_generic_put(struct pwrseq *pwrseq)
+{
+	struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
+	int clk;
+
+	if (pwrseq_gen->gpiod_reset)
+		gpiod_put(pwrseq_gen->gpiod_reset);
+
+	for (clk = 0; clk < PWRSEQ_MAX_CLKS; clk++)
+		clk_put(pwrseq_gen->clks[clk]);
+
+	pwrseq_unregister(&pwrseq_gen->pwrseq);
+	kfree(pwrseq_gen);
+}
+
+static void pwrseq_generic_off(struct pwrseq *pwrseq)
+{
+	struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
+	int clk;
+
+	if (pwrseq_gen->suspended)
+		return;
+
+	for (clk = PWRSEQ_MAX_CLKS - 1; clk >= 0; clk--)
+		clk_disable_unprepare(pwrseq_gen->clks[clk]);
+}
+
+static int pwrseq_generic_on(struct pwrseq *pwrseq)
+{
+	struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
+	int clk, ret = 0;
+	struct gpio_desc *gpiod_reset = pwrseq_gen->gpiod_reset;
+
+	for (clk = 0; clk < PWRSEQ_MAX_CLKS && pwrseq_gen->clks[clk]; clk++) {
+		ret = clk_prepare_enable(pwrseq_gen->clks[clk]);
+		if (ret) {
+			pr_err("Can't enable clock, ret=%d\n", ret);
+			goto err_disable_clks;
+		}
+	}
+
+	if (gpiod_reset) {
+		u32 duration_us = pwrseq_gen->duration_us;
+
+		if (duration_us <= 10)
+			udelay(10);
+		else
+			usleep_range(duration_us, duration_us + 100);
+		gpiod_set_value(gpiod_reset, 0);
+	}
+
+	return ret;
+
+err_disable_clks:
+	while (--clk >= 0)
+		clk_disable_unprepare(pwrseq_gen->clks[clk]);
+
+	return ret;
+}
+
+static int pwrseq_generic_get(struct device_node *np, struct pwrseq *pwrseq)
+{
+	struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq);
+	enum of_gpio_flags flags;
+	int reset_gpio, clk, ret = 0;
+
+	for (clk = 0; clk < PWRSEQ_MAX_CLKS; clk++) {
+		pwrseq_gen->clks[clk] = of_clk_get(np, clk);
+		if (IS_ERR(pwrseq_gen->clks[clk])) {
+			ret = PTR_ERR(pwrseq_gen->clks[clk]);
+			if (ret != -ENOENT)
+				goto err_put_clks;
+			pwrseq_gen->clks[clk] = NULL;
+			break;
+		}
+	}
+
+	reset_gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, &flags);
+	if (gpio_is_valid(reset_gpio)) {
+		unsigned long gpio_flags;
+
+		if (flags & OF_GPIO_ACTIVE_LOW)
+			gpio_flags = GPIOF_ACTIVE_LOW | GPIOF_OUT_INIT_LOW;
+		else
+			gpio_flags = GPIOF_OUT_INIT_HIGH;
+
+		ret = gpio_request_one(reset_gpio, gpio_flags,
+				"pwrseq-reset-gpios");
+		if (ret)
+			goto err_put_clks;
+
+		pwrseq_gen->gpiod_reset = gpio_to_desc(reset_gpio);
+		of_property_read_u32(np, "reset-duration-us",
+				&pwrseq_gen->duration_us);
+	} else if (reset_gpio == -ENOENT) {
+		; /* no such gpio */
+	} else {
+		ret = reset_gpio;
+		pr_err("Failed to get reset gpio on %s, err = %d\n",
+				np->full_name, reset_gpio);
+		goto err_put_clks;
+	}
+
+	/* allocate new one for later pwrseq instance request */
+	ret = pwrseq_generic_alloc_instance();
+	if (ret)
+		goto err_put_gpio;
+
+	return 0;
+
+err_put_gpio:
+	if (pwrseq_gen->gpiod_reset)
+		gpiod_put(pwrseq_gen->gpiod_reset);
+err_put_clks:
+	while (--clk >= 0)
+		clk_put(pwrseq_gen->clks[clk]);
+	return ret;
+}
+
+static int pwrseq_generic_alloc_instance(void)
+{
+	struct pwrseq_generic *pwrseq_gen;
+
+	pwrseq_gen = kzalloc(sizeof(*pwrseq_gen), GFP_KERNEL);
+	if (!pwrseq_gen)
+		return -ENOMEM;
+
+	pwrseq_gen->pwrseq.pwrseq_of_match_table = generic_id_table;
+	pwrseq_gen->pwrseq.get = pwrseq_generic_get;
+	pwrseq_gen->pwrseq.on = pwrseq_generic_on;
+	pwrseq_gen->pwrseq.off = pwrseq_generic_off;
+	pwrseq_gen->pwrseq.put = pwrseq_generic_put;
+	pwrseq_gen->pwrseq.suspend = pwrseq_generic_suspend;
+	pwrseq_gen->pwrseq.resume = pwrseq_generic_resume;
+
+	pwrseq_register(&pwrseq_gen->pwrseq);
+	return 0;
+}
+
+static int __init pwrseq_generic_register(void)
+{
+	return pwrseq_generic_alloc_instance();
+}
+postcore_initcall(pwrseq_generic_register)
diff --git a/include/linux/power/pwrseq.h b/include/linux/power/pwrseq.h
new file mode 100644
index 0000000..cbc344c
--- /dev/null
+++ b/include/linux/power/pwrseq.h
@@ -0,0 +1,81 @@
+#ifndef __LINUX_PWRSEQ_H
+#define __LINUX_PWRSEQ_H
+
+#include <linux/of.h>
+
+#define PWRSEQ_MAX_CLKS		3
+
+/**
+ * struct pwrseq - the power sequence structure
+ * @pwrseq_of_match_table: the OF device id table this pwrseq library supports
+ * @node: the list pointer to be added to pwrseq list
+ * @get: the API is used to get pwrseq instance from the device node
+ * @on: do power on for this pwrseq instance
+ * @off: do power off for this pwrseq instance
+ * @put: release the resources on this pwrseq instance
+ * @suspend: do suspend operation on this pwrseq instance
+ * @resume: do resume operation on this pwrseq instance
+ * @used: this pwrseq instance is used by device
+ */
+struct pwrseq {
+	const struct of_device_id *pwrseq_of_match_table;
+	struct list_head node;
+	int (*get)(struct device_node *np, struct pwrseq *p);
+	int (*on)(struct pwrseq *p);
+	void (*off)(struct pwrseq *p);
+	void (*put)(struct pwrseq *p);
+	int (*suspend)(struct pwrseq *p);
+	int (*resume)(struct pwrseq *p);
+	bool used;
+	bool suspended;
+};
+
+/* used for power sequence instance list in one driver */
+struct pwrseq_list_per_dev {
+	struct pwrseq *pwrseq;
+	struct list_head list;
+};
+
+#if IS_ENABLED(CONFIG_POWER_SEQUENCE)
+void pwrseq_register(struct pwrseq *pwrseq);
+void pwrseq_unregister(struct pwrseq *pwrseq);
+struct pwrseq *of_pwrseq_on(struct device_node *np);
+void of_pwrseq_off(struct pwrseq *pwrseq);
+int of_pwrseq_on_list(struct device_node *np, struct list_head *head);
+void of_pwrseq_off_list(struct list_head *head);
+int pwrseq_suspend(struct pwrseq *p);
+int pwrseq_resume(struct pwrseq *p);
+int pwrseq_suspend_list(struct list_head *head);
+int pwrseq_resume_list(struct list_head *head);
+#else
+static inline void pwrseq_register(struct pwrseq *pwrseq) {}
+static inline void pwrseq_unregister(struct pwrseq *pwrseq) {}
+static inline struct pwrseq *of_pwrseq_on(struct device_node *np)
+{
+	return NULL;
+}
+static void of_pwrseq_off(struct pwrseq *pwrseq) {}
+static int of_pwrseq_on_list(struct device_node *np, struct list_head *head)
+{
+	return 0;
+}
+static void of_pwrseq_off_list(struct list_head *head) {}
+static int pwrseq_suspend(struct pwrseq *p)
+{
+	return 0;
+}
+static int pwrseq_resume(struct pwrseq *p)
+{
+	return 0;
+}
+static int pwrseq_suspend_list(struct list_head *head)
+{
+	return 0;
+}
+static int pwrseq_resume_list(struct list_head *head)
+{
+	return 0;
+}
+#endif /* CONFIG_POWER_SEQUENCE */
+
+#endif  /* __LINUX_PWRSEQ_H */
-- 
2.7.4

^ permalink raw reply related

* [PATCH 1/8] binding-doc: power: pwrseq-generic: add binding doc for generic power sequence library
From: Peter Chen @ 2017-01-03  6:33 UTC (permalink / raw)
  To: gregkh, stern, ulf.hansson, broonie, sre, robh+dt, shawnguo, rjw,
	dbaryshkov
  Cc: heiko, linux-arm-kernel, p.zabel, devicetree, pawel.moll,
	mark.rutland, linux-usb, arnd, s.hauer, mail, troy.kisky,
	festevam, oscar, stephen.boyd, linux-pm, stillcompiling,
	linux-kernel, mka, vaibhav.hiremath, gary.bisson, hverkuil, krzk,
	Peter Chen
In-Reply-To: <1483425211-14473-1-git-send-email-peter.chen@nxp.com>

Add binding doc for generic power sequence library.

Signed-off-by: Peter Chen <peter.chen@nxp.com>
Acked-by: Philipp Zabel <p.zabel@pengutronix.de>
Acked-by: Rob Herring <robh@kernel.org>
---
 .../bindings/power/pwrseq/pwrseq-generic.txt       | 48 ++++++++++++++++++++++
 1 file changed, 48 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt

diff --git a/Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt
new file mode 100644
index 0000000..ebf0d47
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt
@@ -0,0 +1,48 @@
+The generic power sequence library
+
+Some hard-wired devices (eg USB/MMC) need to do power sequence before
+the device can be enumerated on the bus, the typical power sequence
+like: enable USB PHY clock, toggle reset pin, etc. But current
+Linux device driver lacks of such code to do it, it may cause some
+hard-wired devices works abnormal or can't be recognized by
+controller at all. The power sequence will be done before this device
+can be found at the bus.
+
+The power sequence properties is under the device node.
+
+Optional properties:
+- clocks: the input clocks for device.
+- reset-gpios: Should specify the GPIO for reset.
+- reset-duration-us: the duration in microsecond for assert reset signal.
+
+Below is the example of USB power sequence properties on USB device
+nodes which have two level USB hubs.
+
+&usbotg1 {
+	vbus-supply = <&reg_usb_otg1_vbus>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_usb_otg1_id>;
+	status = "okay";
+
+	#address-cells = <1>;
+	#size-cells = <0>;
+	genesys: hub@1 {
+		compatible = "usb5e3,608";
+		reg = <1>;
+
+		clocks = <&clks IMX6SX_CLK_CKO>;
+		reset-gpios = <&gpio4 5 GPIO_ACTIVE_LOW>; /* hub reset pin */
+		reset-duration-us = <10>;
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+		asix: ethernet@1 {
+			compatible = "usbb95,1708";
+			reg = <1>;
+
+			clocks = <&clks IMX6SX_CLK_IPG>;
+			reset-gpios = <&gpio4 6 GPIO_ACTIVE_LOW>; /* ethernet_rst */
+			reset-duration-us = <15>;
+		};
+	};
+};
-- 
2.7.4

^ permalink raw reply related

* [PATCH 0/8] power: add power sequence library
From: Peter Chen @ 2017-01-03  6:33 UTC (permalink / raw)
  To: gregkh, stern, ulf.hansson, broonie, sre, robh+dt, shawnguo, rjw,
	dbaryshkov
  Cc: heiko, linux-arm-kernel, p.zabel, devicetree, pawel.moll,
	mark.rutland, linux-usb, arnd, s.hauer, mail, troy.kisky,
	festevam, oscar, stephen.boyd, linux-pm, stillcompiling,
	linux-kernel, mka, vaibhav.hiremath, gary.bisson, hverkuil, krzk,
	Peter Chen

Hi all,

This is a follow-up for my last power sequence framework patch set [1].
According to Rob Herring and Ulf Hansson's comments[2]. The kinds of
power sequence instances will be added at postcore_initcall, the match
criteria is compatible string first, if the compatible string is not
matched between dts and library, it will try to use generic power sequence.
	 
The host driver just needs to call of_pwrseq_on/of_pwrseq_off
if only one power sequence instance is needed, for more power sequences
are used, using of_pwrseq_on_list/of_pwrseq_off_list instead (eg, USB hub driver).

In future, if there are special power sequence requirements, the special
power sequence library can be created.

This patch set is tested on i.mx6 sabresx evk using a dts change, I use
two hot-plug devices to simulate this use case, the related binding
change is updated at patch [1/6], The udoo board changes were tested
using my last power sequence patch set.[3]

Except for hard-wired MMC and USB devices, I find the USB ULPI PHY also
need to power on itself before it can be found by ULPI bus.

[1] http://www.spinics.net/lists/linux-usb/msg142755.html
[2] http://www.spinics.net/lists/linux-usb/msg143106.html
[3] http://www.spinics.net/lists/linux-usb/msg142815.html

Changes for v11:
- Fix warning: (USB) selects POWER_SEQUENCE which has unmet direct dependencies (OF)
- Delete redundant copyright statement.
- Change pr_warn to pr_debug at wrseq_find_available_instance
- Refine kerneldoc
- %s/ENONET/ENOENT 
- Allocate pwrseq list node before than carry out power sequence on 
- Add mutex_lock/mutex_lock for pwrseq node browse at pwrseq_find_available_instance
- Add pwrseq_suspend/resume for API both single instance and list 
- Add .pwrseq_suspend/resume for pwrseq_generic.c
- Add pwrseq_suspend_list and pwrseq_resume_list for USB hub suspend
  and resume routine

Changes for v10:
- Improve the kernel-doc for power sequence core, including exported APIs and
  main structure. [Patch 2/8]
- Change Kconfig, and let the user choose power sequence. [Patch 2/8]
- Delete EXPORT_SYMBOL and change related APIs as local, these APIs do not
  be intended to export currently. [Patch 2/8]
- Selete POWER_SEQUENCE at USB core's Kconfig. [Patch 4/8]

Changes for v9:
- Add Vaibhav Hiremath's reviewed-by [Patch 4/8]
- Rebase to v4.9-rc1

Changes for v8:
- Allocate one extra pwrseq instance if pwrseq_get has succeed, it can avoid
  preallocate instances problem which the number of instance is decided at
  compile time, thanks for Heiko Stuebner's suggestion [Patch 2/8]
- Delete pwrseq_compatible_sample.c which is the demo purpose to show compatible
  match method. [Patch 2/8]
- Add Maciej S. Szmigiero's tested-by. [Patch 7/8]

Changes for v7:
- Create kinds of power sequence instance at postcore_initcall, and match
  the instance with node using compatible string, the beneit of this is
  the host driver doesn't need to consider which pwrseq instance needs
  to be used, and pwrseq core will match it, however, it eats some memories
  if less power sequence instances are used. [Patch 2/8]
- Add pwrseq_compatible_sample.c to test match pwrseq using device_id. [Patch 2/8]
- Fix the comments Vaibhav Hiremath adds for error path for clock and do not
  use device_node for parameters at pwrseq_on. [Patch 2/8]
- Simplify the caller to use power sequence, follows Alan's commnets [Patch 4/8]
- Tested three pwrseq instances together using both specific compatible string and
  generic libraries.

Changes for v6:
- Add Matthias Kaehlcke's Reviewed-by and Tested-by. (patch [2/6])
- Change chipidea core of_node assignment for coming user. (patch [5/6])
- Applies Joshua Clayton's three dts changes for two boards,
  the USB device's reg has only #address-cells, but without #size-cells.

Changes for v5:
- Delete pwrseq_register/pwrseq_unregister, which is useless currently
- Fix the linker error when the pwrseq user is compiled as module

Changes for v4:
- Create the patch on next-20160722 
- Fix the of_node is not NULL after chipidea driver is unbinded [Patch 5/6]
- Using more friendly wait method for reset gpio [Patch 2/6]
- Support multiple input clocks [Patch 2/6]
- Add Rob Herring's ack for DT changes
- Add Joshua Clayton's Tested-by

Changes for v3:
- Delete "power-sequence" property at binding-doc, and change related code
  at both library and user code.
- Change binding-doc example node name with Rob's comments
- of_get_named_gpio_flags only gets the gpio, but without setting gpio flags,
  add additional code request gpio with proper gpio flags
- Add Philipp Zabel's Ack and MAINTAINER's entry

Changes for v2:
- Delete "pwrseq" prefix and clock-names for properties at dt binding
- Should use structure not but its pointer for kzalloc
- Since chipidea core has no of_node, let core's of_node equals glue
  layer's at core's probe

Joshua Clayton (2):
  ARM: dts: imx6qdl: Enable usb node children with <reg>
  ARM: dts: imx6q-evi: Fix onboard hub reset line

Peter Chen (6):
  binding-doc: power: pwrseq-generic: add binding doc for generic power
    sequence library
  power: add power sequence library
  binding-doc: usb: usb-device: add optional properties for power
    sequence
  usb: core: add power sequence handling for USB devices
  usb: chipidea: let chipidea core device of_node equal's glue layer
    device of_node
  ARM: dts: imx6qdl-udoo.dtsi: fix onboard USB HUB property

 .../bindings/power/pwrseq/pwrseq-generic.txt       |  48 +++
 .../devicetree/bindings/usb/usb-device.txt         |  10 +-
 MAINTAINERS                                        |   9 +
 arch/arm/boot/dts/imx6q-evi.dts                    |  25 +-
 arch/arm/boot/dts/imx6qdl-udoo.dtsi                |  26 +-
 arch/arm/boot/dts/imx6qdl.dtsi                     |   6 +
 drivers/power/Kconfig                              |   1 +
 drivers/power/Makefile                             |   1 +
 drivers/power/pwrseq/Kconfig                       |  20 ++
 drivers/power/pwrseq/Makefile                      |   2 +
 drivers/power/pwrseq/core.c                        | 335 +++++++++++++++++++++
 drivers/power/pwrseq/pwrseq_generic.c              | 224 ++++++++++++++
 drivers/usb/Kconfig                                |   1 +
 drivers/usb/chipidea/core.c                        |  27 +-
 drivers/usb/core/hub.c                             |  48 ++-
 drivers/usb/core/hub.h                             |   1 +
 include/linux/power/pwrseq.h                       |  81 +++++
 17 files changed, 823 insertions(+), 42 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt
 create mode 100644 drivers/power/pwrseq/Kconfig
 create mode 100644 drivers/power/pwrseq/Makefile
 create mode 100644 drivers/power/pwrseq/core.c
 create mode 100644 drivers/power/pwrseq/pwrseq_generic.c
 create mode 100644 include/linux/power/pwrseq.h

-- 
2.7.4

^ permalink raw reply

* RE:FW : Aluminum gravity die casting
From: Bob @ 2017-01-03  6:36 UTC (permalink / raw)
  To: linux-kernel

Hello, 

We are the specialized manufacturer of aluminum casting in China with advantages as follows: 
1. All kinds of casting processes: die casting, sand casting, gravity premanent mold casting to meet your different demand. 
2. 17 years experience only dedicated in aluminum castings with precision machining, More profession, more competitive. 
3. All molds made by ourself to reduce your cost. Advanced machining equipments and testing equipments. 

40% of our shipment are for your market, proved our competitive in pricing and quality. Shall you have any interest, pls contact for possible deal.
 
b.rgds 
Bob Hu 
Ningbo Yinzhou Xusheng Machinery Factory
Cell: 0086-1395832 7774 
Skype: bobhu1 
Tel: 0086-574-88128603 
bob@xs-aluminumcasting.com 
www.xs-aluminumcasting.com

^ permalink raw reply

* Re: [PATCH v2] drm/mediatek: Support UYVY and YUYV format for overlay
From: Daniel Kurtz @ 2017-01-03  6:27 UTC (permalink / raw)
  To: Bibby Hsieh
  Cc: David Airlie, Matthias Brugger, Daniel Vetter, dri-devel,
	moderated list:ARM/Mediatek SoC support, Yingjoe Chen, Cawa Cheng,
	Philipp Zabel, YT Shen, Thierry Reding, CK Hu, Mao Huang,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, Sascha Hauer
In-Reply-To: <1483079183-38637-1-git-send-email-bibby.hsieh@mediatek.com>

On Fri, Dec 30, 2016 at 2:26 PM, Bibby Hsieh <bibby.hsieh@mediatek.com> wrote:
>
> MT8173 overlay can support UYVY and YUYV format,
> we add the format in DRM driver.
>
> Signed-off-by: Bibby Hsieh <bibby.hsieh@mediatek.com>
> Reviewed-by: Daniel Kurtz <djkurtz@chromium.org>
> ---
>  drivers/gpu/drm/mediatek/mtk_disp_ovl.c  | 21 +++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_drm_plane.c |  2 ++
>  2 files changed, 23 insertions(+)
>
> diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
> index c703102..de05845 100644
> --- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
> +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
> @@ -40,10 +40,13 @@
>  #define        OVL_RDMA_MEM_GMC        0x40402020
>
>  #define OVL_CON_BYTE_SWAP      BIT(24)
> +#define OVL_CON_MTX_YUV_TO_RGB (6 << 16)
>  #define OVL_CON_CLRFMT_RGB565  (0 << 12)
>  #define OVL_CON_CLRFMT_RGB888  (1 << 12)
>  #define OVL_CON_CLRFMT_RGBA8888        (2 << 12)
>  #define OVL_CON_CLRFMT_ARGB8888        (3 << 12)
> +#define OVL_CON_CLRFMT_UYVY    (4 << 12)
> +#define OVL_CON_CLRFMT_YUYV    (5 << 12)

Why not just add " | OVL_CON_MTX_YUV_TO_RGB" here in the definition of
these two constants, instead of adding a helper function?

>  #define        OVL_CON_AEN             BIT(8)
>  #define        OVL_CON_ALPHA           0xff
>
> @@ -162,6 +165,21 @@ static unsigned int ovl_fmt_convert(unsigned int fmt)
>         case DRM_FORMAT_XBGR8888:
>         case DRM_FORMAT_ABGR8888:
>                 return OVL_CON_CLRFMT_RGBA8888 | OVL_CON_BYTE_SWAP;
> +       case DRM_FORMAT_UYVY:
> +               return OVL_CON_CLRFMT_UYVY;
> +       case DRM_FORMAT_YUYV:
> +               return OVL_CON_CLRFMT_YUYV;
> +       }
> +}
> +
> +static bool ovl_yuv_space(unsigned int fmt)
> +{
> +       switch (fmt) {
> +       case DRM_FORMAT_UYVY:
> +       case DRM_FORMAT_YUYV:
> +               return true;
> +       default:
> +               return false;
>         }
>  }
>
> @@ -183,6 +201,9 @@ static void mtk_ovl_layer_config(struct mtk_ddp_comp *comp, unsigned int idx,
>         if (idx != 0)
>                 con |= OVL_CON_AEN | OVL_CON_ALPHA;
>
> +       if (ovl_yuv_space(fmt))
> +               con |= OVL_CON_MTX_YUV_TO_RGB;
> +
>         writel_relaxed(con, comp->regs + DISP_REG_OVL_CON(idx));
>         writel_relaxed(pitch, comp->regs + DISP_REG_OVL_PITCH(idx));
>         writel_relaxed(src_size, comp->regs + DISP_REG_OVL_SRC_SIZE(idx));
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.c b/drivers/gpu/drm/mediatek/mtk_drm_plane.c
> index c461a23..8c02d1d 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_plane.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.c
> @@ -28,6 +28,8 @@
>         DRM_FORMAT_XRGB8888,
>         DRM_FORMAT_ARGB8888,
>         DRM_FORMAT_RGB565,
> +       DRM_FORMAT_UYVY,
> +       DRM_FORMAT_YUYV,
>  };
>
>  static void mtk_plane_reset(struct drm_plane *plane)
> --
> 1.9.1
>

^ permalink raw reply

* Re: [PATCH 1/2] arm64:dt:ls1046a: Add TMU device tree support for LS1046A
From: Shawn Guo @ 2017-01-03  6:26 UTC (permalink / raw)
  To: Troy Jia
  Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	Scott Wood, edubezval@gmail.com, Y.T. Tang, robh+dt@kernel.org,
	rui.zhang@intel.com, linux-arm-kernel@lists.infradead.org
In-Reply-To: <AM4PR0401MB1843C415E2CF7D58BD5A1064EB6E0@AM4PR0401MB1843.eurprd04.prod.outlook.com>

On Tue, Jan 03, 2017 at 03:49:33AM +0000, Troy Jia wrote:
> > > @@ -279,6 +282,82 @@
> > >  			clocks = <&sysclk>;
> > >  		};
> > >
> > > +		tmu: tmu@1f00000 {
> > > +			compatible = "fsl,qoriq-tmu";
> > > +			reg = <0x0 0x1f00000 0x0 0x10000>;
> > > +			interrupts = <0 33 0x4>;
> > > +			fsl,tmu-range = <0xb0000 0x9002a 0x6004c 0x30062>;
> > > +			fsl,tmu-calibration = <0x00000000 0x00000026
> > > +					       0x00000001 0x0000002d
> > > +					       0x00000002 0x00000032
> > > +					       0x00000003 0x00000039
> > > +					       0x00000004 0x0000003f
> > > +					       0x00000005 0x00000046
> > > +					       0x00000006 0x0000004d
> > > +					       0x00000007 0x00000054
> > > +					       0x00000008 0x0000005a
> > > +					       0x00000009 0x00000061
> > > +					       0x0000000a 0x0000006a
> > > +					       0x0000000b 0x00000071
> > > +
> > 
> > Instead of a newline, can we have a single line comment here to tell how these
> > calibration data is grouped?
> 
> Each group represent one temperature range. It's a good idea to add comment here.
> Could I just add one comment like below to clarify all four groups?
> /* Each calibration data group represent one temperature range. There are four ranges in total */

Probably the following form?

	/* Calibration data group 1 */
	...
	/* Calibration data group 2 */
	...
	/* Calibration data group 3 */
	...
	/* Calibration data group 4 */
	...


> 
> > 
> > > +					       0x00010000 0x00000025
> > > +					       0x00010001 0x0000002c
> > > +					       0x00010002 0x00000035
> > > +					       0x00010003 0x0000003d
> > > +					       0x00010004 0x00000045
> > > +					       0x00010005 0x0000004e
> > > +					       0x00010006 0x00000057
> > > +					       0x00010007 0x00000061
> > > +					       0x00010008 0x0000006b
> > > +					       0x00010009 0x00000076
> > > +
> > > +					       0x00020000 0x00000029
> > > +					       0x00020001 0x00000033
> > > +					       0x00020002 0x0000003d
> > > +					       0x00020003 0x00000049
> > > +					       0x00020004 0x00000056
> > > +					       0x00020005 0x00000061
> > > +					       0x00020006 0x0000006d
> > > +
> > > +					       0x00030000 0x00000021
> > > +					       0x00030001 0x0000002a
> > > +					       0x00030002 0x0000003c
> > > +					       0x00030003 0x0000004e>;
> > > +			big-endian;
> > > +			#thermal-sensor-cells = <1>;
> > > +		};
> > > +
> > > +		thermal-zones {
> > > +			cpu_thermal: cpu-thermal {
> > > +				polling-delay-passive = <1000>;
> > > +				polling-delay = <5000>;
> > > +
> > 
> > We usually do not have newline between properties but nodes, or between
> > property list and child node.
> 
> I just follow the style of thermal binding of
> Documentation/devicetree/bindings/thermal/thermal.txt.

Different subsystem or binding examples use different style, but when we
put things together in the platform dts, we would like to have them in a
unified style.

Shawn

^ permalink raw reply

* Re: [PATCH 2/2] usb: host: xhci: Handle the right timeout command
From: Baolin Wang @ 2017-01-03  6:20 UTC (permalink / raw)
  To: Mathias Nyman
  Cc: Lu Baolu, Mathias Nyman, Greg KH, USB, LKML, Mark Brown,
	Lu, Baolu
In-Reply-To: <586A6A61.8040902@linux.intel.com>

On 2 January 2017 at 22:57, Mathias Nyman <mathias.nyman@linux.intel.com> wrote:
> On 27.12.2016 05:07, Baolin Wang wrote:
>>
>> Hi,
>>
>> On 21 December 2016 at 21:00, Mathias Nyman
>> <mathias.nyman@linux.intel.com> wrote:
>>>
>>> On 21.12.2016 04:22, Baolin Wang wrote:
>>>>
>>>>
>>>> Hi Mathias,
>>>>
>>>> On 20 December 2016 at 23:13, Mathias Nyman
>>>> <mathias.nyman@linux.intel.com> wrote:
>>>>>
>>>>>
>>>>> On 20.12.2016 09:30, Baolin Wang wrote:
>>>>> ...
>>>>>
>>>>> Alright, I gathered all current work related to xhci races and timeouts
>>>>> and put them into a branch:
>>>>>
>>>>> git://git.kernel.org/pub/scm/linux/kernel/git/mnyman/xhci.git
>>>>> timeout_race_fixes
>>>>>
>>>>> Its based on 4.9
>>>>> It includes a few other patches just to avoid conflicts and  make my
>>>>> life
>>>>> easier
>>>>>
>>>>> Interesting patches are:
>>>>>
>>>>> ee4eb91 xhci: remove unnecessary check for pending timer
>>>>> 0cba67d xhci: detect stop endpoint race using pending timer instead of
>>>>> counter.
>>>>> 4f2535f xhci: Handle command completion and timeout race
>>>>> b9d00d7 usb: host: xhci: Fix possible wild pointer when handling abort
>>>>> command
>>>>> 529a5a0 usb: xhci: fix possible wild pointer
>>>>> 4766555 xhci: Fix race related to abort operation
>>>>> de834a3 xhci: Use delayed_work instead of timer for command timeout
>>>>> 69973b8 Linux 4.9
>>>>>
>>>>> The fixes for command queue races will go to usb-linus and stable, the
>>>>> reworks for stop ep watchdog timer will go to usb-next.
>>>>
>>>>
>>>>
>>>> How about applying below patch in your 'timeout_race_fixes' branch?
>>>> [PATCH] usb: host: xhci: Clean up commands when stop endpoint command is
>>>> timeout
>>>> https://lkml.org/lkml/2016/12/13/94
>>>>
>>>
>>> usb_hc_died() should eventyally end up calling xhci_mem_cleanup()
>>> which will cleanup the command queue. But I need to look into that
>>
>>
>> usb_hc_died() did not call xhci_mem_cleanup() to clean up command
>> queue, it just disconnects all children devices attached on the dying
>> hub. I did not find where it will clean up the command queue when
>> issuing usb_hc_died(). Also before issuing usb_hc_died() in
>> xhci_handle_command_timeout(), we will call
>> xhci_cleanup_command_queue(). I think it should same as in
>> xhci_stop_endpoint_command_watchdog().
>>
>
> You're right, it doesn't call xhci_mem_cleanup.
> Need to look at this after getting first series of fixes to usb-linus

Thanks.

-- 
Baolin.wang
Best Regards

^ permalink raw reply

* Re: [PATCH net 9/9] virtio-net: XDP support for small buffers
From: Jason Wang @ 2017-01-03  6:16 UTC (permalink / raw)
  To: John Fastabend, mst, virtualization, netdev, linux-kernel
  Cc: john.r.fastabend
In-Reply-To: <586AD79B.2070203@gmail.com>



On 2017年01月03日 06:43, John Fastabend wrote:
> On 16-12-23 06:37 AM, Jason Wang wrote:
>> Commit f600b6905015 ("virtio_net: Add XDP support") leaves the case of
>> small receive buffer untouched. This will confuse the user who want to
>> set XDP but use small buffers. Other than forbid XDP in small buffer
>> mode, let's make it work. XDP then can only work at skb->data since
>> virtio-net create skbs during refill, this is sub optimal which could
>> be optimized in the future.
>>
>> Cc: John Fastabend <john.r.fastabend@intel.com>
>> Signed-off-by: Jason Wang <jasowang@redhat.com>
>> ---
>>   drivers/net/virtio_net.c | 112 ++++++++++++++++++++++++++++++++++++-----------
>>   1 file changed, 87 insertions(+), 25 deletions(-)
>>
> Hi Jason,
>
> I was doing some more testing on this what do you think about doing this
> so that free_unused_bufs() handles the buffer free with dev_kfree_skb()
> instead of put_page in small receive mode. Seems more correct to me.
>
>
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index 783e842..27ff76c 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -1898,6 +1898,10 @@ static void free_receive_page_frags(struct virtnet_info *vi)
>
>   static bool is_xdp_queue(struct virtnet_info *vi, int q)
>   {
> +       /* For small receive mode always use kfree_skb variants */
> +       if (!vi->mergeable_rx_bufs)
> +               return false;
> +
>          if (q < (vi->curr_queue_pairs - vi->xdp_queue_pairs))
>                  return false;
>          else if (q < vi->curr_queue_pairs)
>
>
> patch is untested just spotted doing code review.
>
> Thanks,
> John

We probably need a better name for this function.

Acked-by: Jason Wang <jasowang@redhat.com>

^ 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