Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/3] ARM: dts: imx6qdl cleanups
From: Jagan Teki @ 2016-10-14  9:39 UTC (permalink / raw)
  To: linux-arm-kernel

Patchset for imx6qdl devicetree files cleanups.

Jagan Teki (3):
  arm: dts: imx6qdl: Fix "WARNING: please, no space before tabs"
  arm: dts: imx6qdl: Fix "ERROR: code indent should use tabs where
    possible"
  arm: dts: imx6qdl-wandboard-revb: Fix "ERROR: trailing whitespace"

 arch/arm/boot/dts/imx6qdl-apf6dev.dtsi         | 14 +++++------
 arch/arm/boot/dts/imx6qdl-gw52xx.dtsi          |  4 ++--
 arch/arm/boot/dts/imx6qdl-gw53xx.dtsi          |  4 ++--
 arch/arm/boot/dts/imx6qdl-gw54xx.dtsi          |  4 ++--
 arch/arm/boot/dts/imx6qdl-gw552x.dtsi          |  2 +-
 arch/arm/boot/dts/imx6qdl-phytec-pfla02.dtsi   |  6 ++---
 arch/arm/boot/dts/imx6qdl-sabreauto.dtsi       |  2 +-
 arch/arm/boot/dts/imx6qdl-sabresd.dtsi         |  4 ++--
 arch/arm/boot/dts/imx6qdl-tx6.dtsi             | 32 +++++++++++++-------------
 arch/arm/boot/dts/imx6qdl-wandboard-revb1.dtsi |  2 +-
 arch/arm/boot/dts/imx6qdl-wandboard.dtsi       |  4 ++--
 arch/arm/boot/dts/imx6qdl.dtsi                 |  6 ++---
 12 files changed, 42 insertions(+), 42 deletions(-)

-- 
2.7.4

^ permalink raw reply

* [PATCH V3 10/10] arm64: KVM: add guest SEA support
From: Punit Agrawal @ 2016-10-14  9:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <afa9a3c6-7516-cf5c-2366-f826f914d582@codeaurora.org>

"Baicar, Tyler" <tbaicar@codeaurora.org> writes:

> Hello Punit,
>
> On 10/13/2016 7:14 AM, Punit Agrawal wrote:
>> Hi Tyler,
>>
>> I know I've had my last comment already ;), but I thought I'd rather
>> raise the question than stay confused...
>>
>> Tyler Baicar <tbaicar@codeaurora.org> writes:
>>
>>> Currently external aborts are unsupported by the guest abort
>>> handling. Add handling for SEAs so that the host kernel reports
>>> SEAs which occur in the guest kernel.
>>>
>>> Signed-off-by: Tyler Baicar <tbaicar@codeaurora.org>
>>> ---
>>>   arch/arm/include/asm/kvm_arm.h       |  1 +
>>>   arch/arm/include/asm/system_misc.h   |  5 +++++
>>>   arch/arm/kvm/mmu.c                   | 15 +++++++++++++--
>>>   arch/arm64/include/asm/kvm_arm.h     |  1 +
>>>   arch/arm64/include/asm/system_misc.h |  2 ++
>>>   arch/arm64/mm/fault.c                | 13 +++++++++++++
>>>   6 files changed, 35 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h
>>> index e22089f..33a77509 100644
>>> --- a/arch/arm/include/asm/kvm_arm.h
>>> +++ b/arch/arm/include/asm/kvm_arm.h
>>> @@ -187,6 +187,7 @@
>>>   #define FSC_FAULT	(0x04)
>>>   #define FSC_ACCESS	(0x08)
>>>   #define FSC_PERM	(0x0c)
>>> +#define FSC_EXTABT	(0x10)
>>>     /* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */
>>>   #define HPFAR_MASK	(~0xf)
>>> diff --git a/arch/arm/include/asm/system_misc.h b/arch/arm/include/asm/system_misc.h
>>> index a3d61ad..86e1faa 100644
>>> --- a/arch/arm/include/asm/system_misc.h
>>> +++ b/arch/arm/include/asm/system_misc.h
>>> @@ -24,4 +24,9 @@ extern unsigned int user_debug;
>>>     #endif /* !__ASSEMBLY__ */
>>>   +inline int handle_guest_sea(unsigned long addr, unsigned int
>>> esr)
>>> +{
>>> +	return -1;
>>> +}
>>> +
>>>   #endif /* __ASM_ARM_SYSTEM_MISC_H */
>>> diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
>>> index e9a5c0e..24cde84 100644
>>> --- a/arch/arm/kvm/mmu.c
>>> +++ b/arch/arm/kvm/mmu.c
>>> @@ -29,6 +29,7 @@
>>>   #include <asm/kvm_asm.h>
>>>   #include <asm/kvm_emulate.h>
>>>   #include <asm/virt.h>
>>> +#include <asm/system_misc.h>
>>>     #include "trace.h"
>>>   @@ -1441,8 +1442,18 @@ int kvm_handle_guest_abort(struct kvm_vcpu
>>> *vcpu, struct kvm_run *run)
>>>     	/* Check the stage-2 fault is trans. fault or write fault */
>>>   	fault_status = kvm_vcpu_trap_get_fault_type(vcpu);
>>> -	if (fault_status != FSC_FAULT && fault_status != FSC_PERM &&
>>> -	    fault_status != FSC_ACCESS) {
>>> +
>>> +	if (fault_status == FSC_EXTABT) {
>>> +		if(handle_guest_sea((unsigned long)fault_ipa,
>>> +				    kvm_vcpu_get_hsr(vcpu))) {
>>> +			kvm_err("Failed to handle guest SEA, FSC: EC=%#x xFSC=%#lx ESR_EL2=%#lx\n",
>>> +				kvm_vcpu_trap_get_class(vcpu),
>>> +				(unsigned long)kvm_vcpu_trap_get_fault(vcpu),
>>> +				(unsigned long)kvm_vcpu_get_hsr(vcpu));
>>> +			return -EFAULT;
>>> +		}
>>> +	} else if (fault_status != FSC_FAULT && fault_status != FSC_PERM &&
>>> +		   fault_status != FSC_ACCESS) {
>>>   		kvm_err("Unsupported FSC: EC=%#x xFSC=%#lx ESR_EL2=%#lx\n",
>>>   			kvm_vcpu_trap_get_class(vcpu),
>>>   			(unsigned long)kvm_vcpu_trap_get_fault(vcpu),
>>> diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
>>> index 4b5c977..be0efb6 100644
>>> --- a/arch/arm64/include/asm/kvm_arm.h
>>> +++ b/arch/arm64/include/asm/kvm_arm.h
>>> @@ -201,6 +201,7 @@
>>>   #define FSC_FAULT	ESR_ELx_FSC_FAULT
>>>   #define FSC_ACCESS	ESR_ELx_FSC_ACCESS
>>>   #define FSC_PERM	ESR_ELx_FSC_PERM
>>> +#define FSC_EXTABT	ESR_ELx_FSC_EXTABT
>>>     /* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */
>>>   #define HPFAR_MASK	(~UL(0xf))
>>> diff --git a/arch/arm64/include/asm/system_misc.h b/arch/arm64/include/asm/system_misc.h
>>> index 90daf4a..8522fff 100644
>>> --- a/arch/arm64/include/asm/system_misc.h
>>> +++ b/arch/arm64/include/asm/system_misc.h
>>> @@ -77,4 +77,6 @@ extern void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
>>>   int sea_register_handler_chain(struct notifier_block *nb);
>>>   void sea_unregister_handler_chain(struct notifier_block *nb);
>>>   +int handle_guest_sea(unsigned long addr, unsigned int esr);
>>> +
>>>   #endif	/* __ASM_SYSTEM_MISC_H */
>>> diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
>>> index 81cb7ad..d714432 100644
>>> --- a/arch/arm64/mm/fault.c
>>> +++ b/arch/arm64/mm/fault.c
>>> @@ -597,6 +597,19 @@ static const char *fault_name(unsigned int esr)
>>>   }
>>>     /*
>>> + * Handle Synchronous External Aborts that occur in a guest kernel.
>>> + */
>>> +int handle_guest_sea(unsigned long addr, unsigned int esr)
>>> +{
>>> +	atomic_notifier_call_chain(&sea_handler_chain, 0, NULL);
>>> +
>>> +	pr_err("Synchronous External Abort: %s (0x%08x) at 0x%016lx\n",
>>> +		fault_name(esr), esr, addr);
>>> +
>>> +	return 0;
>>> +}
>> Don't we need to pass the abort to the guest?
> This requires some infrastructure to implement virtual "ACPI platform
> error interface" to expose the details of the abort to the guest. This
> patchset does not cover that and focuses on feature parity with other
> architectures that support APEI. There are discussions among Linaro
> partners about how this can be achieved in the long term, but that
> work is outside the scope of this patchset. This patch will ensure
> that if a guest encounters one of these errors then it will be
> reported before getting killed. Before this patch we would just get an
> unsupported FSC print out and then the guest would be killed.

OK.

I think we might be talking about different things though.

I am referring to the injection of the synchronous external abort into
the guest - similar to what's been done for prefetch abort in the
kvm_guest_handle_abort.

Maybe there is no benefit in passing the abort to the guest. In that
case, can you please add a comment where you handle external abort
(FSC_EXTABT) in kvm_guest_handle_abort.

>
> Thanks,
> Tyler
>>
>> Thanks,
>> Punit
>>
>>> +
>>> +/*
>>>    * Dispatch a data abort to the relevant handler.
>>>    */
>>>   asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr,

^ permalink raw reply

* [PATCH v7 5/5] ARM: dts: imx6qdl-icore: Add FEC support
From: Jagan Teki @ 2016-10-14  9:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476437243-13297-1-git-send-email-jteki@openedev.com>

From: Jagan Teki <jagan@amarulasolutions.com>

Add FEC support for Engicam i.CoreM6 dql modules.

Observed similar 'eth0: link is not ready' issue which was
discussed in [1] due rmii mode with external ref_clk, so added
clock node along with the properties mentioned by Shawn in [2]

FEC link log:
------------
$ ifconfig eth0 up
[   27.905187] SMSC LAN8710/LAN8720 2188000.ethernet:00: attached PHY driver
               [SMSC LAN8710/LAN8720] (mii_bus:phy_addr=2188000.ethernet:00, irq=-1)
[   27.918982] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready

[1] https://patchwork.kernel.org/patch/3491061/
[2] https://patchwork.kernel.org/patch/3490511/

Cc: Sascha Hauer <kernel@pengutronix.de>
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Matteo Lisi <matteo.lisi@engicam.com>
Cc: Michael Trimarchi <michael@amarulasolutions.com>
Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
---
Changes for v7:
        - none
Changes for v6:
        - none
Changes for v5:
        - new patch

 arch/arm/boot/dts/imx6qdl-icore.dtsi | 37 ++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/arch/arm/boot/dts/imx6qdl-icore.dtsi b/arch/arm/boot/dts/imx6qdl-icore.dtsi
index 4e79858..972f48f 100644
--- a/arch/arm/boot/dts/imx6qdl-icore.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-icore.dtsi
@@ -48,6 +48,18 @@
 		reg = <0x10000000 0x80000000>;
 	};
 
+	clocks {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		rmii_clk: clock at 0 {
+			compatible = "fixed-clock";
+			reg = <0>;
+			#clock-cells = <0>;
+			clock-frequency = <25000000>;  /* 25MHz for example */
+		};
+	};
+
 	reg_3p3v: regulator-3p3v {
 		compatible = "regulator-fixed";
 		regulator-name = "3P3V";
@@ -93,6 +105,15 @@
 	assigned-clock-parents = <&clks IMX6QDL_CLK_OSC>;
 };
 
+&fec {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_enet>;
+	phy-reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>;
+	clocks = <&clks 117>, <&clks 117>, <&rmii_clk>;
+	phy-mode = "rmii";
+	status = "okay";
+};
+
 &gpmi {
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_gpmi_nand>;
@@ -150,6 +171,22 @@
 };
 
 &iomuxc {
+	pinctrl_enet: enetgrp {
+		fsl,pins = <
+			MX6QDL_PAD_ENET_CRS_DV__ENET_RX_EN	0x1b0b0
+			MX6QDL_PAD_GPIO_16__ENET_REF_CLK	0x1b0b1
+			MX6QDL_PAD_ENET_TX_EN__ENET_TX_EN	0x1b0b0
+			MX6QDL_PAD_ENET_RXD1__ENET_RX_DATA1	0x1b0b0
+			MX6QDL_PAD_ENET_RXD0__ENET_RX_DATA0	0x1b0b0
+			MX6QDL_PAD_ENET_TXD1__ENET_TX_DATA1	0x1b0b0
+			MX6QDL_PAD_ENET_TXD0__ENET_TX_DATA0	0x1b0b0
+			MX6QDL_PAD_ENET_MDC__ENET_MDC		0x1b0b0
+			MX6QDL_PAD_ENET_MDIO__ENET_MDIO		0x1b0b0
+			MX6QDL_PAD_ENET_REF_CLK__GPIO1_IO23	0x1b0b0
+			MX6QDL_PAD_GPIO_17__GPIO7_IO12		0x1b0b0
+		>;
+	};
+
 	pinctrl_flexcan1: flexcan1grp {
 		fsl,pins = <
 			MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x1b020
-- 
2.7.4

^ permalink raw reply related

* [PATCH v7 4/5] ARM: dts: imx6qdl-icore: Add usbotg support
From: Jagan Teki @ 2016-10-14  9:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476437243-13297-1-git-send-email-jteki@openedev.com>

From: Jagan Teki <jagan@amarulasolutions.com>

Add usbotg support for Engicam i.CoreM6 dql modules.

Cc: Sascha Hauer <kernel@pengutronix.de>
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Matteo Lisi <matteo.lisi@engicam.com>
Cc: Michael Trimarchi <michael@amarulasolutions.com>
Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
---
Changes for v7:
        - none
Changes for v6:
        - none
Changes for v5:
        - none
Changes for v4:
        - new patch

 arch/arm/boot/dts/imx6qdl-icore.dtsi | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/arch/arm/boot/dts/imx6qdl-icore.dtsi b/arch/arm/boot/dts/imx6qdl-icore.dtsi
index ffec879..4e79858 100644
--- a/arch/arm/boot/dts/imx6qdl-icore.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-icore.dtsi
@@ -65,6 +65,15 @@
 		regulator-boot-on;
 		regulator-always-on;
 	};
+
+	reg_usb_otg_vbus: usb_otg_vbus {
+		compatible = "regulator-fixed";
+		regulator-name = "usb_otg_vbus";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-boot-on;
+		regulator-always-on;
+	};
 };
 
 &can1 {
@@ -124,6 +133,14 @@
 	status = "okay";
 };
 
+&usbotg {
+	vbus-supply = <&reg_usb_otg_vbus>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_usbotg>;
+	disable-over-current;
+	status = "okay";
+};
+
 &usdhc1 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_usdhc1>;
@@ -198,6 +215,12 @@
 		>;
 	};
 
+	pinctrl_usbotg: usbotggrp {
+		fsl,pins = <
+			MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059
+		>;
+	};
+
 	pinctrl_usdhc1: usdhc1grp {
 		fsl,pins = <
 			MX6QDL_PAD_SD1_CMD__SD1_CMD    0x17070
-- 
2.7.4

^ permalink raw reply related

* [PATCH v7 3/5] ARM: dts: imx6qdl-icore: Add usbhost support
From: Jagan Teki @ 2016-10-14  9:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476437243-13297-1-git-send-email-jteki@openedev.com>

From: Jagan Teki <jagan@amarulasolutions.com>

Add usbhost support for Engicam i.CoreM6 dql modules.

Cc: Sascha Hauer <kernel@pengutronix.de>
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Matteo Lisi <matteo.lisi@engicam.com>
Cc: Michael Trimarchi <michael@amarulasolutions.com>
Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
---
Changes for v7:
        - none
Changes for v6:
        - none
Changes for v5:
        - none
Changes for v4:
        - new patch

 arch/arm/boot/dts/imx6qdl-icore.dtsi | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/arch/arm/boot/dts/imx6qdl-icore.dtsi b/arch/arm/boot/dts/imx6qdl-icore.dtsi
index f424cd5..ffec879 100644
--- a/arch/arm/boot/dts/imx6qdl-icore.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-icore.dtsi
@@ -56,6 +56,15 @@
 		regulator-boot-on;
 		regulator-always-on;
 	};
+
+	reg_usb_h1_vbus: usb_h1_vbus {
+		compatible = "regulator-fixed";
+		regulator-name = "usb_h1_vbus";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-boot-on;
+		regulator-always-on;
+	};
 };
 
 &can1 {
@@ -109,6 +118,12 @@
 	status = "okay";
 };
 
+&usbh1 {
+	vbus-supply = <&reg_usb_h1_vbus>;
+	disable-over-current;
+	status = "okay";
+};
+
 &usdhc1 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_usdhc1>;
-- 
2.7.4

^ permalink raw reply related

* [PATCH v7 2/5] ARM: dts: imx6q: Add Engicam i.CoreM6 DualLite/Solo initial support
From: Jagan Teki @ 2016-10-14  9:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476437243-13297-1-git-send-email-jteki@openedev.com>

From: Jagan Teki <jagan@amarulasolutions.com>

i.CoreM6 DualLite/Solo modules are system on module solutions manufactured
by Engicam with following characteristics:
CPU           NXP i.MX6 DL, 800MHz
RAM           1GB, 32, 64 bit, DDR3-800/1066
NAND          SLC,512MB
Power supply  Single 5V
MAX LCD RES   FULLHD

and more info at
http://www.engicam.com/en/products/embedded/som/sodimm/i-core-m6s-dl-d-q

Cc: Sascha Hauer <kernel@pengutronix.de>
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Matteo Lisi <matteo.lisi@engicam.com>
Cc: Michael Trimarchi <michael@amarulasolutions.com>
Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
---
Changes for v7:
        - none
Changes for v6:
        - none
Changes for v5:
        - none
Changes for v4:
        - new patch
Changes for v3:
        - Use compatible as engicam,imx6-icore instead of fsl,imx6-icore
Changes for v2:
        - s/oaky/okay/g

 arch/arm/boot/dts/Makefile         |  1 +
 arch/arm/boot/dts/imx6dl-icore.dts | 59 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 60 insertions(+)
 create mode 100644 arch/arm/boot/dts/imx6dl-icore.dts

diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 89a3eea..8f8b14b 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -340,6 +340,7 @@ dtb-$(CONFIG_SOC_IMX6Q) += \
 	imx6dl-gw552x.dtb \
 	imx6dl-gw553x.dtb \
 	imx6dl-hummingboard.dtb \
+	imx6dl-icore.dtb \
 	imx6dl-nit6xlite.dtb \
 	imx6dl-nitrogen6x.dtb \
 	imx6dl-phytec-pbab01.dtb \
diff --git a/arch/arm/boot/dts/imx6dl-icore.dts b/arch/arm/boot/dts/imx6dl-icore.dts
new file mode 100644
index 0000000..aec332c
--- /dev/null
+++ b/arch/arm/boot/dts/imx6dl-icore.dts
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 Amarula Solutions B.V.
+ * Copyright (C) 2016 Engicam S.r.l.
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     version 2 as published by the Free Software Foundation.
+ *
+ *     This file 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.
+ *
+ * Or, alternatively
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+
+#include "imx6dl.dtsi"
+#include "imx6qdl-icore.dtsi"
+
+/ {
+	model = "Engicam i.CoreM6 DualLite/Solo Starter Kit";
+	compatible = "engicam,imx6-icore", "fsl,imx6dl";
+};
+
+&can1 {
+	status = "okay";
+};
+
+&can2 {
+	status = "okay";
+};
-- 
2.7.4

^ permalink raw reply related

* [PATCH v7 1/5] ARM: dts: imx6q: Add Engicam i.CoreM6 Quad/Dual initial support
From: Jagan Teki @ 2016-10-14  9:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476437243-13297-1-git-send-email-jteki@openedev.com>

From: Jagan Teki <jagan@amarulasolutions.com>

i.CoreM6 Quad/Dual modules are system on module solutions manufactured
by Engicam with following characteristics:
CPU           NXP i.MX6 DQ, 800MHz
RAM           1GB, 32, 64 bit, DDR3-800/1066
NAND          SLC,512MB
Power supply  Single 5V
MAX LCD RES   FULLHD

and more info at
http://www.engicam.com/en/products/embedded/som/sodimm/i-core-m6s-dl-d-q

Cc: Sascha Hauer <kernel@pengutronix.de>
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Matteo Lisi <matteo.lisi@engicam.com>
Cc: Michael Trimarchi <michael@amarulasolutions.com>
Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
---
Changes for v7:
        - none
Changes for v6:
        - none
Changes for v5:
        - removed fsl,legacy-bch-geometry property from gpmi node
Changes for v4:
        - new patch
Changes for v3:
        - Use compatible as engicam,imx6-icore instead of fsl,imx6-icore
        - Update IOMUX value for can1 and can2 nodes
        - Added reg_3p3v for can1 and can2 nodes
Changes for v2:
        - s/oaky/okay/g

 arch/arm/boot/dts/Makefile           |   1 +
 arch/arm/boot/dts/imx6q-icore.dts    |  59 +++++++++++
 arch/arm/boot/dts/imx6qdl-icore.dtsi | 196 +++++++++++++++++++++++++++++++++++
 3 files changed, 256 insertions(+)
 create mode 100644 arch/arm/boot/dts/imx6q-icore.dts
 create mode 100644 arch/arm/boot/dts/imx6qdl-icore.dtsi

diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index befcd26..89a3eea 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -381,6 +381,7 @@ dtb-$(CONFIG_SOC_IMX6Q) += \
 	imx6q-gw553x.dtb \
 	imx6q-h100.dtb \
 	imx6q-hummingboard.dtb \
+	imx6q-icore.dtb \
 	imx6q-icore-rqs.dtb \
 	imx6q-marsboard.dtb \
 	imx6q-nitrogen6x.dtb \
diff --git a/arch/arm/boot/dts/imx6q-icore.dts b/arch/arm/boot/dts/imx6q-icore.dts
new file mode 100644
index 0000000..025f543
--- /dev/null
+++ b/arch/arm/boot/dts/imx6q-icore.dts
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 Amarula Solutions B.V.
+ * Copyright (C) 2016 Engicam S.r.l.
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     version 2 as published by the Free Software Foundation.
+ *
+ *     This file 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.
+ *
+ * Or, alternatively
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+
+#include "imx6q.dtsi"
+#include "imx6qdl-icore.dtsi"
+
+/ {
+	model = "Engicam i.CoreM6 Quad/Dual Starter Kit";
+	compatible = "engicam,imx6-icore", "fsl,imx6q";
+};
+
+&can1 {
+	status = "okay";
+};
+
+&can2 {
+	status = "okay";
+};
diff --git a/arch/arm/boot/dts/imx6qdl-icore.dtsi b/arch/arm/boot/dts/imx6qdl-icore.dtsi
new file mode 100644
index 0000000..f424cd5
--- /dev/null
+++ b/arch/arm/boot/dts/imx6qdl-icore.dtsi
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2016 Amarula Solutions B.V.
+ * Copyright (C) 2016 Engicam S.r.l.
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     version 2 as published by the Free Software Foundation.
+ *
+ *     This file 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.
+ *
+ * Or, alternatively
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
+/ {
+	memory {
+		reg = <0x10000000 0x80000000>;
+	};
+
+	reg_3p3v: regulator-3p3v {
+		compatible = "regulator-fixed";
+		regulator-name = "3P3V";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+		regulator-always-on;
+	};
+};
+
+&can1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_flexcan1>;
+	xceiver-supply = <&reg_3p3v>;
+};
+
+&can2 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_flexcan2>;
+	xceiver-supply = <&reg_3p3v>;
+};
+
+&clks {
+	assigned-clocks = <&clks IMX6QDL_CLK_LVDS2_SEL>;
+	assigned-clock-parents = <&clks IMX6QDL_CLK_OSC>;
+};
+
+&gpmi {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_gpmi_nand>;
+	nand-on-flash-bbt;
+	status = "okay";
+};
+
+&i2c1 {
+	clock-frequency = <100000>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_i2c1>;
+	status = "okay";
+};
+
+&i2c2 {
+	clock-frequency = <100000>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_i2c2>;
+	status = "okay";
+};
+
+&i2c3 {
+	clock-frequency = <100000>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_i2c3>;
+	status = "okay";
+};
+
+&uart4 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_uart4>;
+	status = "okay";
+};
+
+&usdhc1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_usdhc1>;
+	cd-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>;
+	no-1-8-v;
+	status = "okay";
+};
+
+&iomuxc {
+	pinctrl_flexcan1: flexcan1grp {
+		fsl,pins = <
+			MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x1b020
+			MX6QDL_PAD_KEY_COL2__FLEXCAN1_TX 0x1b020
+		>;
+	};
+
+	pinctrl_flexcan2: flexcan2grp {
+		fsl,pins = <
+			MX6QDL_PAD_KEY_COL4__FLEXCAN2_TX 0x1b020
+			MX6QDL_PAD_KEY_ROW4__FLEXCAN2_RX 0x1b020
+		>;
+	};
+
+	pinctrl_gpmi_nand: gpmi-nand {
+		fsl,pins = <
+			MX6QDL_PAD_NANDF_CLE__NAND_CLE     0xb0b1
+			MX6QDL_PAD_NANDF_ALE__NAND_ALE     0xb0b1
+			MX6QDL_PAD_NANDF_WP_B__NAND_WP_B   0xb0b1
+			MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000
+			MX6QDL_PAD_NANDF_CS0__NAND_CE0_B   0xb0b1
+			MX6QDL_PAD_NANDF_CS1__NAND_CE1_B   0xb0b1
+			MX6QDL_PAD_SD4_CMD__NAND_RE_B      0xb0b1
+			MX6QDL_PAD_SD4_CLK__NAND_WE_B      0xb0b1
+			MX6QDL_PAD_NANDF_D0__NAND_DATA00   0xb0b1
+			MX6QDL_PAD_NANDF_D1__NAND_DATA01   0xb0b1
+			MX6QDL_PAD_NANDF_D2__NAND_DATA02   0xb0b1
+			MX6QDL_PAD_NANDF_D3__NAND_DATA03   0xb0b1
+			MX6QDL_PAD_NANDF_D4__NAND_DATA04   0xb0b1
+			MX6QDL_PAD_NANDF_D5__NAND_DATA05   0xb0b1
+			MX6QDL_PAD_NANDF_D6__NAND_DATA06   0xb0b1
+			MX6QDL_PAD_NANDF_D7__NAND_DATA07   0xb0b1
+			MX6QDL_PAD_SD4_DAT0__NAND_DQS      0x00b1
+		>;
+	};
+
+	pinctrl_i2c1: i2c1grp {
+		fsl,pins = <
+			MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1
+			MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1
+		>;
+	};
+
+	pinctrl_i2c2: i2c2grp {
+		fsl,pins = <
+			MX6QDL_PAD_EIM_EB2__I2C2_SCL  0x4001b8b1
+			MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
+		>;
+	};
+
+	pinctrl_i2c3: i2c3grp {
+		fsl,pins = <
+			MX6QDL_PAD_GPIO_5__I2C3_SCL  0x4001b8b1
+			MX6QDL_PAD_EIM_D18__I2C3_SDA 0x4001b8b1
+			MX6QDL_PAD_GPIO_0__CCM_CLKO1	0x130b0
+		>;
+	};
+
+	pinctrl_uart4: uart4grp {
+		fsl,pins = <
+			MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1
+			MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1
+		>;
+	};
+
+	pinctrl_usdhc1: usdhc1grp {
+		fsl,pins = <
+			MX6QDL_PAD_SD1_CMD__SD1_CMD    0x17070
+			MX6QDL_PAD_SD1_CLK__SD1_CLK    0x10070
+			MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17070
+			MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17070
+			MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17070
+			MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17070
+		>;
+	};
+};
-- 
2.7.4

^ permalink raw reply related

* [PATCH v7 0/5] ARM: dts: imx6q: Add Engicam i.CoreM6 dts
From: Jagan Teki @ 2016-10-14  9:27 UTC (permalink / raw)
  To: linux-arm-kernel

This is series add dts support for Engicam I.Core M6 qdl modules. just
rebased on top of linux-next of previous set[1].

[1] http://www.spinics.net/lists/kernel/msg2358233.html

Jagan Teki (5):
  ARM: dts: imx6q: Add Engicam i.CoreM6 Quad/Dual initial support
  ARM: dts: imx6q: Add Engicam i.CoreM6 DualLite/Solo initial support
  ARM: dts: imx6qdl-icore: Add usbhost support
  ARM: dts: imx6qdl-icore: Add usbotg support
  ARM: dts: imx6qdl-icore: Add FEC support

 arch/arm/boot/dts/Makefile           |   2 +
 arch/arm/boot/dts/imx6dl-icore.dts   |  59 ++++++++
 arch/arm/boot/dts/imx6q-icore.dts    |  59 ++++++++
 arch/arm/boot/dts/imx6qdl-icore.dtsi | 271 +++++++++++++++++++++++++++++++++++
 4 files changed, 391 insertions(+)
 create mode 100644 arch/arm/boot/dts/imx6dl-icore.dts
 create mode 100644 arch/arm/boot/dts/imx6q-icore.dts
 create mode 100644 arch/arm/boot/dts/imx6qdl-icore.dtsi

-- 
2.7.4

^ permalink raw reply

* [PATCH v2 6/6] ARM: dts: stm32f429: Add QSPI clock
From: gabriel.fernandez at st.com @ 2016-10-14  9:18 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476436699-21879-1-git-send-email-gabriel.fernandez@st.com>

From: Gabriel Fernandez <gabriel.fernandez@st.com>

This patch adds the QSPI clock for stm32f469 discovery board.

Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
---
 arch/arm/boot/dts/stm32f469-disco.dts | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm/boot/dts/stm32f469-disco.dts b/arch/arm/boot/dts/stm32f469-disco.dts
index e911af8..4052023 100644
--- a/arch/arm/boot/dts/stm32f469-disco.dts
+++ b/arch/arm/boot/dts/stm32f469-disco.dts
@@ -66,6 +66,10 @@
 	};
 };
 
+&rcc {
+	compatible = "st,stm32f469-rcc", "st,stm32-rcc";
+};
+
 &clk_hse {
 	clock-frequency = <8000000>;
 };
-- 
1.9.1

^ permalink raw reply related

* [PATCH v2 5/6] clk: stm32f469: Add QSPI clock
From: gabriel.fernandez at st.com @ 2016-10-14  9:18 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476436699-21879-1-git-send-email-gabriel.fernandez@st.com>

From: Gabriel Fernandez <gabriel.fernandez@st.com>

This patch adds the QSPI clock for stm32f469 discovery board.
The gate mapping is a little bit different from stm32f429 soc.

Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
---
 .../devicetree/bindings/clock/st,stm32-rcc.txt     |   4 +-
 drivers/clk/clk-stm32f4.c                          | 173 ++++++++++++++++++---
 2 files changed, 158 insertions(+), 19 deletions(-)

diff --git a/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt b/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt
index c209de6..0532d81 100644
--- a/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt
+++ b/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt
@@ -7,7 +7,9 @@ Please refer to clock-bindings.txt for common clock controller binding usage.
 Please also refer to reset.txt for common reset controller binding usage.
 
 Required properties:
-- compatible: Should be "st,stm32f42xx-rcc"
+- compatible: Should be:
+  "st,stm32f42xx-rcc"
+  "st,stm32f469-rcc"
 - reg: should be register base and length as documented in the
   datasheet
 - #reset-cells: 1, see below
diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c
index bf9448d..8544214 100644
--- a/drivers/clk/clk-stm32f4.c
+++ b/drivers/clk/clk-stm32f4.c
@@ -46,7 +46,7 @@ struct stm32f4_gate_data {
 	unsigned long flags;
 };
 
-static const struct stm32f4_gate_data stm32f4_gates[] __initconst = {
+static const struct stm32f4_gate_data stm32f429_gates[] __initconst = {
 	{ STM32F4_RCC_AHB1ENR,  0,	"gpioa",	"ahb_div" },
 	{ STM32F4_RCC_AHB1ENR,  1,	"gpiob",	"ahb_div" },
 	{ STM32F4_RCC_AHB1ENR,  2,	"gpioc",	"ahb_div" },
@@ -126,23 +126,109 @@ struct stm32f4_gate_data {
 	{ STM32F4_RCC_APB2ENR, 26,	"ltdc",		"apb2_div" },
 };
 
+static const struct stm32f4_gate_data stm32f469_gates[] __initconst = {
+	{ STM32F4_RCC_AHB1ENR,  0,	"gpioa",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR,  1,	"gpiob",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR,  2,	"gpioc",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR,  3,	"gpiod",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR,  4,	"gpioe",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR,  5,	"gpiof",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR,  6,	"gpiog",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR,  7,	"gpioh",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR,  8,	"gpioi",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR,  9,	"gpioj",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR, 10,	"gpiok",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR, 12,	"crc",		"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR, 18,	"bkpsra",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR, 20,	"ccmdatam",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR, 21,	"dma1",		"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR, 22,	"dma2",		"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR, 23,	"dma2d",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR, 25,	"ethmac",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR, 26,	"ethmactx",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR, 27,	"ethmacrx",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR, 28,	"ethmacptp",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR, 29,	"otghs",	"ahb_div" },
+	{ STM32F4_RCC_AHB1ENR, 30,	"otghsulpi",	"ahb_div" },
+
+	{ STM32F4_RCC_AHB2ENR,  0,	"dcmi",		"ahb_div" },
+	{ STM32F4_RCC_AHB2ENR,  4,	"cryp",		"ahb_div" },
+	{ STM32F4_RCC_AHB2ENR,  5,	"hash",		"ahb_div" },
+	{ STM32F4_RCC_AHB2ENR,  6,	"rng",		"pll48" },
+	{ STM32F4_RCC_AHB2ENR,  7,	"otgfs",	"pll48" },
+
+	{ STM32F4_RCC_AHB3ENR,  0,	"fmc",		"ahb_div",
+		CLK_IGNORE_UNUSED },
+	{ STM32F4_RCC_AHB3ENR,  1,	"qspi",		"ahb_div",
+		CLK_IGNORE_UNUSED },
+
+	{ STM32F4_RCC_APB1ENR,  0,	"tim2",		"apb1_mul" },
+	{ STM32F4_RCC_APB1ENR,  1,	"tim3",		"apb1_mul" },
+	{ STM32F4_RCC_APB1ENR,  2,	"tim4",		"apb1_mul" },
+	{ STM32F4_RCC_APB1ENR,  3,	"tim5",		"apb1_mul" },
+	{ STM32F4_RCC_APB1ENR,  4,	"tim6",		"apb1_mul" },
+	{ STM32F4_RCC_APB1ENR,  5,	"tim7",		"apb1_mul" },
+	{ STM32F4_RCC_APB1ENR,  6,	"tim12",	"apb1_mul" },
+	{ STM32F4_RCC_APB1ENR,  7,	"tim13",	"apb1_mul" },
+	{ STM32F4_RCC_APB1ENR,  8,	"tim14",	"apb1_mul" },
+	{ STM32F4_RCC_APB1ENR, 11,	"wwdg",		"apb1_div" },
+	{ STM32F4_RCC_APB1ENR, 14,	"spi2",		"apb1_div" },
+	{ STM32F4_RCC_APB1ENR, 15,	"spi3",		"apb1_div" },
+	{ STM32F4_RCC_APB1ENR, 17,	"uart2",	"apb1_div" },
+	{ STM32F4_RCC_APB1ENR, 18,	"uart3",	"apb1_div" },
+	{ STM32F4_RCC_APB1ENR, 19,	"uart4",	"apb1_div" },
+	{ STM32F4_RCC_APB1ENR, 20,	"uart5",	"apb1_div" },
+	{ STM32F4_RCC_APB1ENR, 21,	"i2c1",		"apb1_div" },
+	{ STM32F4_RCC_APB1ENR, 22,	"i2c2",		"apb1_div" },
+	{ STM32F4_RCC_APB1ENR, 23,	"i2c3",		"apb1_div" },
+	{ STM32F4_RCC_APB1ENR, 25,	"can1",		"apb1_div" },
+	{ STM32F4_RCC_APB1ENR, 26,	"can2",		"apb1_div" },
+	{ STM32F4_RCC_APB1ENR, 28,	"pwr",		"apb1_div" },
+	{ STM32F4_RCC_APB1ENR, 29,	"dac",		"apb1_div" },
+	{ STM32F4_RCC_APB1ENR, 30,	"uart7",	"apb1_div" },
+	{ STM32F4_RCC_APB1ENR, 31,	"uart8",	"apb1_div" },
+
+	{ STM32F4_RCC_APB2ENR,  0,	"tim1",		"apb2_mul" },
+	{ STM32F4_RCC_APB2ENR,  1,	"tim8",		"apb2_mul" },
+	{ STM32F4_RCC_APB2ENR,  4,	"usart1",	"apb2_div" },
+	{ STM32F4_RCC_APB2ENR,  5,	"usart6",	"apb2_div" },
+	{ STM32F4_RCC_APB2ENR,  8,	"adc1",		"apb2_div" },
+	{ STM32F4_RCC_APB2ENR,  9,	"adc2",		"apb2_div" },
+	{ STM32F4_RCC_APB2ENR, 10,	"adc3",		"apb2_div" },
+	{ STM32F4_RCC_APB2ENR, 11,	"sdio",		"pll48" },
+	{ STM32F4_RCC_APB2ENR, 12,	"spi1",		"apb2_div" },
+	{ STM32F4_RCC_APB2ENR, 13,	"spi4",		"apb2_div" },
+	{ STM32F4_RCC_APB2ENR, 14,	"syscfg",	"apb2_div" },
+	{ STM32F4_RCC_APB2ENR, 16,	"tim9",		"apb2_mul" },
+	{ STM32F4_RCC_APB2ENR, 17,	"tim10",	"apb2_mul" },
+	{ STM32F4_RCC_APB2ENR, 18,	"tim11",	"apb2_mul" },
+	{ STM32F4_RCC_APB2ENR, 20,	"spi5",		"apb2_div" },
+	{ STM32F4_RCC_APB2ENR, 21,	"spi6",		"apb2_div" },
+	{ STM32F4_RCC_APB2ENR, 22,	"sai1",		"apb2_div" },
+	{ STM32F4_RCC_APB2ENR, 26,	"ltdc",		"apb2_div" },
+};
+
 enum { SYSTICK, FCLK, CLK_LSI, CLK_LSE, CLK_HSE_RTC, CLK_RTC, END_PRIMARY_CLK };
-/*
- * MAX_CLKS is the maximum value in the enumeration below plus the combined
- * hweight of stm32f42xx_gate_map (plus one).
- */
-#define MAX_CLKS (71 + END_PRIMARY_CLK + 1)
 
 
 /*
  * This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx
  * have gate bits associated with them. Its combined hweight is 71.
  */
-static const u64 stm32f42xx_gate_map[] = { 0x000000f17ef417ffull,
-					   0x0000000000000001ull,
-					   0x04777f33f6fec9ffull };
+#define MAX_GATE_MAP 3
+
+static const u64 stm32f42xx_gate_map[MAX_GATE_MAP] = { 0x000000f17ef417ffull,
+						       0x0000000000000001ull,
+						       0x04777f33f6fec9ffull };
+
+static const u64 stm32f46xx_gate_map[MAX_GATE_MAP] = { 0x000000f17ef417ffull,
+						       0x0000000000000003ull,
+						       0x0c777f33f6fec9ffull };
+
+const u64 *stm32f4_gate_map;
+
+static struct clk_hw **clks;
 
-static struct clk_hw *clks[MAX_CLKS];
 static DEFINE_SPINLOCK(stm32f4_clk_lock);
 static void __iomem *base;
 
@@ -262,7 +348,7 @@ static void stm32f4_rcc_register_pll(const char *hse_clk, const char *hsi_clk)
  */
 static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary)
 {
-	u64 table[ARRAY_SIZE(stm32f42xx_gate_map)];
+	u64 table[MAX_GATE_MAP];
 
 	if (primary == 1) {
 		if (WARN_ON(secondary >= END_PRIMARY_CLK))
@@ -270,7 +356,7 @@ static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary)
 		return secondary;
 	}
 
-	memcpy(table, stm32f42xx_gate_map, sizeof(table));
+	memcpy(table, stm32f4_gate_map, sizeof(table));
 
 	/* only bits set in table can be used as indices */
 	if (WARN_ON(secondary >= BITS_PER_BYTE * sizeof(table) ||
@@ -532,10 +618,42 @@ static struct clk_hw *stm32_register_cclk(struct device *dev, const char *name,
 	{ 0 },
 };
 
+struct stm32f4_clk_data {
+	const struct stm32f4_gate_data *gates_data;
+	const u64 *gates_map;
+	int gates_num;
+};
+
+static const struct stm32f4_clk_data stm32f429_clk_data = {
+	.gates_data	= stm32f429_gates,
+	.gates_map	= stm32f42xx_gate_map,
+	.gates_num	= ARRAY_SIZE(stm32f429_gates),
+};
+
+static const struct stm32f4_clk_data stm32f469_clk_data = {
+	.gates_data	= stm32f469_gates,
+	.gates_map	= stm32f46xx_gate_map,
+	.gates_num	= ARRAY_SIZE(stm32f469_gates),
+};
+
+static const struct of_device_id stm32f4_of_match[] = {
+	{
+		.compatible = "st,stm32f42xx-rcc",
+		.data = &stm32f429_clk_data
+	},
+	{
+		.compatible = "st,stm32f469-rcc",
+		.data = &stm32f469_clk_data
+	},
+	{}
+};
+
 static void __init stm32f4_rcc_init(struct device_node *np)
 {
 	const char *hse_clk;
 	int n;
+	const struct of_device_id *match;
+	const struct stm32f4_clk_data *data;
 
 	base = of_iomap(np, 0);
 	if (!base) {
@@ -549,6 +667,19 @@ static void __init stm32f4_rcc_init(struct device_node *np)
 		goto fail;
 	}
 
+	match = of_match_node(stm32f4_of_match, np);
+	if (WARN_ON(!match))
+		return;
+
+	data = match->data;
+
+	clks = kmalloc_array(data->gates_num + END_PRIMARY_CLK,
+			sizeof(struct clk_hw *), GFP_KERNEL);
+	if (!clks)
+		goto fail;
+
+	stm32f4_gate_map = data->gates_map;
+
 	hse_clk = of_clk_get_parent_name(np, 0);
 
 	clk_register_fixed_rate_with_accuracy(NULL, "hsi", NULL, 0,
@@ -581,11 +712,15 @@ static void __init stm32f4_rcc_init(struct device_node *np)
 	clks[FCLK] = clk_hw_register_fixed_factor(NULL, "fclk", "ahb_div",
 					       0, 1, 1);
 
-	for (n = 0; n < ARRAY_SIZE(stm32f4_gates); n++) {
-		const struct stm32f4_gate_data *gd = &stm32f4_gates[n];
-		unsigned int secondary =
-		    8 * (gd->offset - STM32F4_RCC_AHB1ENR) + gd->bit_idx;
-		int idx = stm32f4_rcc_lookup_clk_idx(0, secondary);
+	for (n = 0; n < data->gates_num; n++) {
+		const struct stm32f4_gate_data *gd;
+		unsigned int secondary;
+		int idx;
+
+		gd = (struct stm32f4_gate_data *) &data->gates_data[n];
+		secondary = 8 * (gd->offset - STM32F4_RCC_AHB1ENR) +
+			gd->bit_idx;
+		idx = stm32f4_rcc_lookup_clk_idx(0, secondary);
 
 		if (idx < 0)
 			goto fail;
@@ -637,6 +772,8 @@ static void __init stm32f4_rcc_init(struct device_node *np)
 	of_clk_add_hw_provider(np, stm32f4_rcc_lookup_clk, NULL);
 	return;
 fail:
+	kfree(clks);
 	iounmap(base);
 }
-CLK_OF_DECLARE(stm32f4_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init);
+CLK_OF_DECLARE(stm32f42xx_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init);
+CLK_OF_DECLARE(stm32f46xx_rcc, "st,stm32f469-rcc", stm32f4_rcc_init);
-- 
1.9.1

^ permalink raw reply related

* [PATCH v2 4/6] clk: stm32f4: Add RTC clock
From: gabriel.fernandez at st.com @ 2016-10-14  9:18 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476436699-21879-1-git-send-email-gabriel.fernandez@st.com>

From: Gabriel Fernandez <gabriel.fernandez@st.com>

This patch introduces the support of the RTC clock.
RTC clock can have 3 sources: lsi, lse and hse_rtc.

Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
---
 drivers/clk/clk-stm32f4.c | 135 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 134 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c
index 789a8f77..bf9448d 100644
--- a/drivers/clk/clk-stm32f4.c
+++ b/drivers/clk/clk-stm32f4.c
@@ -126,7 +126,7 @@ struct stm32f4_gate_data {
 	{ STM32F4_RCC_APB2ENR, 26,	"ltdc",		"apb2_div" },
 };
 
-enum { SYSTICK, FCLK, CLK_LSI, CLK_LSE, END_PRIMARY_CLK };
+enum { SYSTICK, FCLK, CLK_LSI, CLK_LSE, CLK_HSE_RTC, CLK_RTC, END_PRIMARY_CLK };
 /*
  * MAX_CLKS is the maximum value in the enumeration below plus the combined
  * hweight of stm32f42xx_gate_map (plus one).
@@ -310,6 +310,15 @@ static inline void enable_power_domain_write_protection(void)
 	regmap_update_bits(pdrm, 0x00, (1 << 8), (0 << 8));
 }
 
+static inline void sofware_reset_backup_domain(void)
+{
+	unsigned long val;
+
+	val = readl(base + STM32F4_RCC_BDCR);
+	writel(val |= (1 << 16), base + STM32F4_RCC_BDCR);
+	writel(val & ~(1 << 16), base + STM32F4_RCC_BDCR);
+}
+
 struct stm32_rgate {
 	struct	clk_hw hw;
 	struct	clk_gate gate;
@@ -396,6 +405,113 @@ static struct clk_hw *clk_register_rgate(struct device *dev, const char *name,
 	return hw;
 }
 
+static int cclk_gate_enable(struct clk_hw *hw)
+{
+	int ret;
+
+	disable_power_domain_write_protection();
+
+	ret = clk_gate_ops.enable(hw);
+
+	enable_power_domain_write_protection();
+
+	return ret;
+}
+
+static void cclk_gate_disable(struct clk_hw *hw)
+{
+	disable_power_domain_write_protection();
+
+	clk_gate_ops.disable(hw);
+
+	enable_power_domain_write_protection();
+}
+
+static int cclk_gate_is_enabled(struct clk_hw *hw)
+{
+	return clk_gate_ops.is_enabled(hw);
+}
+
+static const struct clk_ops cclk_gate_ops = {
+	.enable		= cclk_gate_enable,
+	.disable	= cclk_gate_disable,
+	.is_enabled	= cclk_gate_is_enabled,
+};
+
+static u8 cclk_mux_get_parent(struct clk_hw *hw)
+{
+	return clk_mux_ops.get_parent(hw);
+}
+
+
+static int cclk_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+	int ret;
+
+	disable_power_domain_write_protection();
+
+	sofware_reset_backup_domain();
+
+	ret = clk_mux_ops.set_parent(hw, index);
+
+	enable_power_domain_write_protection();
+
+	return ret;
+}
+
+
+static const struct clk_ops cclk_mux_ops = {
+	.get_parent = cclk_mux_get_parent,
+	.set_parent = cclk_mux_set_parent,
+};
+
+static struct clk_hw *stm32_register_cclk(struct device *dev, const char *name,
+		const char * const *parent_names, int num_parents,
+		void __iomem *reg, u8 bit_idx, u8 shift, unsigned long flags,
+		spinlock_t *lock)
+{
+	struct clk_hw *hw;
+	struct clk_gate *gate;
+	struct clk_mux *mux;
+
+	gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
+	if (!gate) {
+		hw = ERR_PTR(-EINVAL);
+		goto fail;
+	}
+
+	mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
+	if (!mux) {
+		kfree(gate);
+		hw = ERR_PTR(-EINVAL);
+		goto fail;
+	}
+
+	gate->reg = reg;
+	gate->bit_idx = bit_idx;
+	gate->flags = 0;
+	gate->lock = lock;
+
+	mux->reg = reg;
+	mux->shift = shift;
+	mux->mask = 3;
+	mux->flags = 0;
+
+	hw = clk_hw_register_composite(dev, name, parent_names, num_parents,
+			&mux->hw, &cclk_mux_ops,
+			NULL, NULL,
+			&gate->hw, &cclk_gate_ops,
+			flags);
+
+	if (IS_ERR(hw)) {
+		kfree(gate);
+		kfree(mux);
+	}
+
+fail:
+	return hw;
+}
+
 static const char *sys_parents[] __initdata =   { "hsi", NULL, "pll" };
 
 const char *rtc_parents[4] = {
@@ -501,6 +617,23 @@ static void __init stm32f4_rcc_init(struct device_node *np)
 		goto fail;
 	}
 
+	clks[CLK_HSE_RTC] = clk_hw_register_divider(NULL, "hse-rtc", "clk-hse",
+			0, base + STM32F4_RCC_CFGR, 16, 5, 0,
+			&stm32f4_clk_lock);
+
+	if (IS_ERR(clks[CLK_HSE_RTC])) {
+		pr_err("Unable to register hse-rtc clock\n");
+		goto fail;
+	}
+
+	clks[CLK_RTC] = stm32_register_cclk(NULL, "rtc", rtc_parents, 4,
+			base + STM32F4_RCC_BDCR, 15, 8, 0, &stm32f4_clk_lock);
+
+	if (IS_ERR(clks[CLK_RTC])) {
+		pr_err("Unable to register rtc clock\n");
+		goto fail;
+	}
+
 	of_clk_add_hw_provider(np, stm32f4_rcc_lookup_clk, NULL);
 	return;
 fail:
-- 
1.9.1

^ permalink raw reply related

* [PATCH v2 3/6] arm: stmf32: Enable SYSCON
From: gabriel.fernandez at st.com @ 2016-10-14  9:18 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476436699-21879-1-git-send-email-gabriel.fernandez@st.com>

From: Gabriel Fernandez <gabriel.fernandez@st.com>

The clock drivers needs to disable the power domain write protection
using syscon/regmap to enable RTC clock.

Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
---
 arch/arm/configs/stm32_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/configs/stm32_defconfig b/arch/arm/configs/stm32_defconfig
index 1e5ec2a..ed1c0881 100644
--- a/arch/arm/configs/stm32_defconfig
+++ b/arch/arm/configs/stm32_defconfig
@@ -49,6 +49,7 @@ CONFIG_SERIAL_STM32=y
 CONFIG_SERIAL_STM32_CONSOLE=y
 # CONFIG_HW_RANDOM is not set
 # CONFIG_HWMON is not set
+CONFIG_MFD_SYSCON=y
 # CONFIG_USB_SUPPORT is not set
 CONFIG_NEW_LEDS=y
 CONFIG_LEDS_CLASS=y
-- 
1.9.1

^ permalink raw reply related

* [PATCH v2 2/6] ARM: dts: stm32f429: add LSI and LSE clocks
From: gabriel.fernandez at st.com @ 2016-10-14  9:18 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476436699-21879-1-git-send-email-gabriel.fernandez@st.com>

From: Gabriel Fernandez <gabriel.fernandez@st.com>

This patch adds lsi / lse oscillators. These clocks can be use by
RTC clocks.
The clock drivers needs to disable the power domain write protection using
syscon / regmap to enable these clocks.

Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
---
 arch/arm/boot/dts/stm32f429.dtsi | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index 336ee4f..2700449 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -56,6 +56,18 @@
 			compatible = "fixed-clock";
 			clock-frequency = <0>;
 		};
+
+		clk-lse {
+			#clock-cells = <0>;
+			compatible = "fixed-clock";
+			clock-frequency = <32768>;
+		};
+
+		clk-lsi {
+			#clock-cells = <0>;
+			compatible = "fixed-clock";
+			clock-frequency = <32000>;
+		};
 	};
 
 	soc {
@@ -185,6 +197,11 @@
 			interrupts = <1>, <2>, <3>, <6>, <7>, <8>, <9>, <10>, <23>, <40>, <41>, <42>, <62>, <76>;
 		};
 
+		pwrcfg: power-config at 40007000 {
+			compatible = "syscon";
+			reg = <0x40007000 0x400>;
+		};
+
 		pin-controller {
 			#address-cells = <1>;
 			#size-cells = <1>;
@@ -340,6 +357,7 @@
 			compatible = "st,stm32f42xx-rcc", "st,stm32-rcc";
 			reg = <0x40023800 0x400>;
 			clocks = <&clk_hse>;
+			st,syscfg = <&pwrcfg>;
 		};
 
 		dma1: dma-controller at 40026000 {
-- 
1.9.1

^ permalink raw reply related

* [PATCH v2 1/6] clk: stm32f4: Add LSI & LSE clocks
From: gabriel.fernandez at st.com @ 2016-10-14  9:18 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476436699-21879-1-git-send-email-gabriel.fernandez@st.com>

From: Gabriel Fernandez <gabriel.fernandez@st.com>

This patch introduces the support of the LSI & LSE clocks.
The clock drivers needs to disable the power domain write protection
using syscon/regmap to enable these clocks.

Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
---
 drivers/clk/clk-stm32f4.c | 138 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 134 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c
index 02d6810..789a8f77 100644
--- a/drivers/clk/clk-stm32f4.c
+++ b/drivers/clk/clk-stm32f4.c
@@ -19,10 +19,14 @@
 #include <linux/clk-provider.h>
 #include <linux/err.h>
 #include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/ioport.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
 
 #define STM32F4_RCC_PLLCFGR		0x04
 #define STM32F4_RCC_CFGR		0x08
@@ -31,6 +35,8 @@
 #define STM32F4_RCC_AHB3ENR		0x38
 #define STM32F4_RCC_APB1ENR		0x40
 #define STM32F4_RCC_APB2ENR		0x44
+#define STM32F4_RCC_BDCR		0x70
+#define STM32F4_RCC_CSR			0x74
 
 struct stm32f4_gate_data {
 	u8	offset;
@@ -120,13 +126,13 @@ struct stm32f4_gate_data {
 	{ STM32F4_RCC_APB2ENR, 26,	"ltdc",		"apb2_div" },
 };
 
+enum { SYSTICK, FCLK, CLK_LSI, CLK_LSE, END_PRIMARY_CLK };
 /*
  * MAX_CLKS is the maximum value in the enumeration below plus the combined
  * hweight of stm32f42xx_gate_map (plus one).
  */
-#define MAX_CLKS 74
+#define MAX_CLKS (71 + END_PRIMARY_CLK + 1)
 
-enum { SYSTICK, FCLK };
 
 /*
  * This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx
@@ -259,7 +265,7 @@ static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary)
 	u64 table[ARRAY_SIZE(stm32f42xx_gate_map)];
 
 	if (primary == 1) {
-		if (WARN_ON(secondary > FCLK))
+		if (WARN_ON(secondary >= END_PRIMARY_CLK))
 			return -EINVAL;
 		return secondary;
 	}
@@ -276,7 +282,7 @@ static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary)
 	table[BIT_ULL_WORD(secondary)] &=
 	    GENMASK_ULL(secondary % BITS_PER_LONG_LONG, 0);
 
-	return FCLK + hweight64(table[0]) +
+	return END_PRIMARY_CLK - 1 + hweight64(table[0]) +
 	       (BIT_ULL_WORD(secondary) >= 1 ? hweight64(table[1]) : 0) +
 	       (BIT_ULL_WORD(secondary) >= 2 ? hweight64(table[2]) : 0);
 }
@@ -292,8 +298,110 @@ static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary)
 	return clks[i];
 }
 
+static struct regmap *pdrm;
+
+static inline void disable_power_domain_write_protection(void)
+{
+	regmap_update_bits(pdrm, 0x00, (1 << 8), (1 << 8));
+}
+
+static inline void enable_power_domain_write_protection(void)
+{
+	regmap_update_bits(pdrm, 0x00, (1 << 8), (0 << 8));
+}
+
+struct stm32_rgate {
+	struct	clk_hw hw;
+	struct	clk_gate gate;
+	u8	bit_rdy_idx;
+};
+
+#define RTC_TIMEOUT 1000000
+
+#define to_rgclk(_hw) container_of(_hw, struct stm32_rgate, hw)
+
+static int rgclk_enable(struct clk_hw *hw)
+{
+	struct stm32_rgate *rgate = to_rgclk(hw);
+	struct clk_hw *gate_hw = &rgate->gate.hw;
+	struct clk_gate *gate = to_clk_gate(gate_hw);
+	u32 reg;
+	int ret;
+
+	__clk_hw_set_clk(gate_hw, hw);
+
+	disable_power_domain_write_protection();
+
+	clk_gate_ops.enable(gate_hw);
+
+	ret = readl_relaxed_poll_timeout_atomic(gate->reg, reg,
+			reg & rgate->bit_rdy_idx, 1000, RTC_TIMEOUT);
+
+	enable_power_domain_write_protection();
+
+	return ret;
+}
+
+static void rgclk_disable(struct clk_hw *hw)
+{
+	clk_gate_ops.disable(hw);
+}
+
+static int rgclk_is_enabled(struct clk_hw *hw)
+{
+	return clk_gate_ops.is_enabled(hw);
+}
+
+
+static const struct clk_ops rgclk_ops = {
+	.enable = rgclk_enable,
+	.disable = rgclk_disable,
+	.is_enabled = rgclk_is_enabled,
+};
+
+static struct clk_hw *clk_register_rgate(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		void __iomem *reg, u8 bit_idx, u8 bit_rdy_idx,
+		u8 clk_gate_flags, spinlock_t *lock)
+{
+	struct stm32_rgate *rgate;
+	struct clk_init_data init = { NULL };
+	struct clk_hw *hw;
+	int ret;
+
+	rgate = kzalloc(sizeof(*rgate), GFP_KERNEL);
+	if (!rgate)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &rgclk_ops;
+	init.flags = flags | CLK_IS_BASIC;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+
+	rgate->hw.init = &init;
+	rgate->bit_rdy_idx = bit_rdy_idx;
+
+	rgate->gate.lock = lock;
+	rgate->gate.reg = reg;
+	rgate->gate.bit_idx = bit_idx;
+
+	hw = &rgate->hw;
+	ret = clk_hw_register(dev, hw);
+	if (ret) {
+		kfree(rgate);
+		hw = ERR_PTR(ret);
+	}
+
+	return hw;
+}
+
 static const char *sys_parents[] __initdata =   { "hsi", NULL, "pll" };
 
+const char *rtc_parents[4] = {
+	"no-clock", "lse", "lsi", "hse-rtc"
+};
+
 static const struct clk_div_table ahb_div_table[] = {
 	{ 0x0,   1 }, { 0x1,   1 }, { 0x2,   1 }, { 0x3,   1 },
 	{ 0x4,   1 }, { 0x5,   1 }, { 0x6,   1 }, { 0x7,   1 },
@@ -319,6 +427,12 @@ static void __init stm32f4_rcc_init(struct device_node *np)
 		return;
 	}
 
+	pdrm = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+	if (IS_ERR(pdrm)) {
+		pr_err("%s: Unable to get syscfg\n", __func__);
+		goto fail;
+	}
+
 	hse_clk = of_clk_get_parent_name(np, 0);
 
 	clk_register_fixed_rate_with_accuracy(NULL, "hsi", NULL, 0,
@@ -371,6 +485,22 @@ static void __init stm32f4_rcc_init(struct device_node *np)
 		}
 	}
 
+	clks[CLK_LSI] = clk_register_rgate(NULL, "lsi", "clk-lsi", 0,
+			base + STM32F4_RCC_CSR, 0, 2, 0, &stm32f4_clk_lock);
+
+	if (IS_ERR(clks[CLK_LSI])) {
+		pr_err("Unable to register lsi clock\n");
+		goto fail;
+	}
+
+	clks[CLK_LSE] = clk_register_rgate(NULL, "lse", "clk-lse", 0,
+			base + STM32F4_RCC_BDCR, 0, 2, 0, &stm32f4_clk_lock);
+
+	if (IS_ERR(clks[CLK_LSE])) {
+		pr_err("Unable to register lse clock\n");
+		goto fail;
+	}
+
 	of_clk_add_hw_provider(np, stm32f4_rcc_lookup_clk, NULL);
 	return;
 fail:
-- 
1.9.1

^ permalink raw reply related

* [PATCH v2 0/6] STM32F4 Add RTC & QSPI clocks
From: gabriel.fernandez at st.com @ 2016-10-14  9:18 UTC (permalink / raw)
  To: linux-arm-kernel

From: Gabriel Fernandez <gabriel.fernandez@st.com>

v2:
 - rename compatible property "st,stm32f46xx-rcc" into "st,stm32f469-rcc"
 - cosmetic: remove bad copy/paste

This patch-set introduce RTC and QSPI clocks for STM32F4 socs
RTC clock has 3 parents clock oscillators (lsi/lse/hse_rtc)

example to use rtc clock:

		rtc: rtc at 40002800 {
			compatible = "st,stm32-rtc";
			reg = <0x40002800 0x400>;
			...
			clocks = <&rcc 1 CLK_RTC>;

			assigned-clocks =  <&rcc 1 CLK_RTC>;
			assigned-clock-parents = <&rcc 1 CLK_LSE>;
			...
		};

Gabriel Fernandez (6):
  clk: stm32f4: Add LSI & LSE clocks
  ARM: dts: stm32f429: add LSI and LSE clocks
  arm: stmf32: Enable SYSCON
  clk: stm32f4: Add RTC clock
  clk: stm32f469: Add QSPI clock
  ARM: dts: stm32f429: Add QSPI clock

 .../devicetree/bindings/clock/st,stm32-rcc.txt     |   4 +-
 arch/arm/boot/dts/stm32f429.dtsi                   |  18 +
 arch/arm/boot/dts/stm32f469-disco.dts              |   4 +
 arch/arm/configs/stm32_defconfig                   |   1 +
 drivers/clk/clk-stm32f4.c                          | 442 ++++++++++++++++++++-
 5 files changed, 447 insertions(+), 22 deletions(-)

-- 
1.9.1

^ permalink raw reply

* [PATCH v4 08/12] arm64/tracing: fix compat syscall handling
From: Marcin Nowakowski @ 2016-10-14  8:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476434115-25428-1-git-send-email-marcin.nowakowski@imgtec.com>

Add arch_syscall_addr for arm64 and define NR_compat_syscalls, as the
number of compat syscalls for arm64 exceeds the number defined by
NR_syscalls.
Convert the existing uses of __NR_compat_syscalls to the newly
introduced NR_compat_syscalls

Signed-off-by: Marcin Nowakowski <marcin.nowakowski@imgtec.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: linux-arm-kernel at lists.infradead.org
Acked-by: Will Deacon <will.deacon@arm.com>
---
 arch/arm64/include/asm/ftrace.h   | 12 +-----------
 arch/arm64/include/asm/unistd.h   |  2 +-
 arch/arm64/include/asm/unistd32.h |  2 +-
 arch/arm64/kernel/Makefile        |  1 +
 arch/arm64/kernel/entry.S         |  2 +-
 arch/arm64/kernel/ftrace.c        | 16 ++++++++++++++++
 arch/arm64/kernel/sys32.c         |  4 ++--
 7 files changed, 23 insertions(+), 16 deletions(-)

diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h
index caa955f..b57ff7c 100644
--- a/arch/arm64/include/asm/ftrace.h
+++ b/arch/arm64/include/asm/ftrace.h
@@ -41,17 +41,7 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr)
 
 #define ftrace_return_address(n) return_address(n)
 
-/*
- * Because AArch32 mode does not share the same syscall table with AArch64,
- * tracing compat syscalls may result in reporting bogus syscalls or even
- * hang-up, so just do not trace them.
- * See kernel/trace/trace_syscalls.c
- *
- * x86 code says:
- * If the user really wants these, then they should use the
- * raw syscall tracepoints with filtering.
- */
-#define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS
+#define ARCH_COMPAT_SYSCALL_NUMBERS_OVERLAP 1
 static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs)
 {
 	return is_compat_task();
diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h
index e78ac26..df56ffc 100644
--- a/arch/arm64/include/asm/unistd.h
+++ b/arch/arm64/include/asm/unistd.h
@@ -44,7 +44,7 @@
 #define __ARM_NR_compat_cacheflush	(__ARM_NR_COMPAT_BASE+2)
 #define __ARM_NR_compat_set_tls		(__ARM_NR_COMPAT_BASE+5)
 
-#define __NR_compat_syscalls		394
+#define NR_compat_syscalls		394
 #endif
 
 #define __ARCH_WANT_SYS_CLONE
diff --git a/arch/arm64/include/asm/unistd32.h b/arch/arm64/include/asm/unistd32.h
index b7e8ef1..0fd0909 100644
--- a/arch/arm64/include/asm/unistd32.h
+++ b/arch/arm64/include/asm/unistd32.h
@@ -812,5 +812,5 @@ __SYSCALL(__NR_pwritev2, compat_sys_pwritev2)
 
 /*
  * Please add new compat syscalls above this comment and update
- * __NR_compat_syscalls in asm/unistd.h.
+ * NR_compat_syscalls in asm/unistd.h.
  */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 7d66bba..7ea9cd3 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -30,6 +30,7 @@ $(obj)/%.stub.o: $(obj)/%.o FORCE
 arm64-obj-$(CONFIG_COMPAT)		+= sys32.o kuser32.o signal32.o 	\
 					   sys_compat.o entry32.o
 arm64-obj-$(CONFIG_FUNCTION_TRACER)	+= ftrace.o entry-ftrace.o
+arm64-obj-$(CONFIG_FTRACE_SYSCALLS)	+= ftrace.o
 arm64-obj-$(CONFIG_MODULES)		+= arm64ksyms.o module.o
 arm64-obj-$(CONFIG_ARM64_MODULE_PLTS)	+= module-plts.o
 arm64-obj-$(CONFIG_PERF_EVENTS)		+= perf_regs.o perf_callchain.o
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index 223d54a..9fb7ad1 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -518,7 +518,7 @@ el0_svc_compat:
 	 */
 	adrp	stbl, compat_sys_call_table	// load compat syscall table pointer
 	uxtw	scno, w7			// syscall number in w7 (r7)
-	mov     sc_nr, #__NR_compat_syscalls
+	mov     sc_nr, #NR_compat_syscalls
 	b	el0_svc_naked
 
 	.align	6
diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c
index 40ad08a..75d010f 100644
--- a/arch/arm64/kernel/ftrace.c
+++ b/arch/arm64/kernel/ftrace.c
@@ -176,4 +176,20 @@ int ftrace_disable_ftrace_graph_caller(void)
 	return ftrace_modify_graph_caller(false);
 }
 #endif /* CONFIG_DYNAMIC_FTRACE */
+
 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
+
+#if (defined CONFIG_FTRACE_SYSCALLS) && (defined CONFIG_COMPAT)
+
+extern const void *sys_call_table[];
+extern const void *compat_sys_call_table[];
+
+unsigned long __init arch_syscall_addr(int nr, bool compat)
+{
+	if (compat)
+		return (unsigned long)compat_sys_call_table[nr];
+
+	return (unsigned long)sys_call_table[nr];
+}
+
+#endif /* CONFIG_FTRACE_SYSCALLS && CONFIG_COMPAT */
diff --git a/arch/arm64/kernel/sys32.c b/arch/arm64/kernel/sys32.c
index a40b134..ae7a992 100644
--- a/arch/arm64/kernel/sys32.c
+++ b/arch/arm64/kernel/sys32.c
@@ -46,7 +46,7 @@ asmlinkage long compat_sys_mmap2_wrapper(void);
  * The sys_call_table array must be 4K aligned to be accessible from
  * kernel/entry.S.
  */
-void * const compat_sys_call_table[__NR_compat_syscalls] __aligned(4096) = {
-	[0 ... __NR_compat_syscalls - 1] = sys_ni_syscall,
+void * const compat_sys_call_table[NR_compat_syscalls] __aligned(4096) = {
+	[0 ... NR_compat_syscalls - 1] = sys_ni_syscall,
 #include <asm/unistd32.h>
 };
-- 
2.7.4

^ permalink raw reply related

* [PATCH 1/3] binding: irqchip: mtk-cirq: Add binding document
From: Youlin Pei @ 2016-10-14  8:23 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <58b24238-659c-c7da-d93c-65db63990930@gmail.com>

Hi Matthias,
Thanks for your review.

On Thu, 2016-10-13 at 15:00 +0200, Matthias Brugger wrote:
> 
> On 10/13/2016 07:06 AM, Youlin Pei wrote:
> > This commit adds the device tree binding document for
> > the mediatek cirq.
> >
> > Signed-off-by: Youlin Pei <youlin.pei@mediatek.com>
> >
> > ---
> > base on v4.8-rc1
> > ---
> >  .../interrupt-controller/mediatek,cirq.txt         |   30 ++++++++++++++++++++
> >  1 file changed, 30 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/mediatek,cirq.txt
> >
> > diff --git a/Documentation/devicetree/bindings/interrupt-controller/mediatek,cirq.txt b/Documentation/devicetree/bindings/interrupt-controller/mediatek,cirq.txt
> > new file mode 100644
> > index 0000000..ad16953
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/interrupt-controller/mediatek,cirq.txt
> > @@ -0,0 +1,30 @@
> > +* Mediatek 27xx cirq
> > +
> > +In Mediatek SOCs, the CIRQ is a low power interrupt controller designed to
> > +works outside MCUSYS which comprises with Cortex-Ax cores,CCI and GIC.
> > +The external interrupts (outside MCUSYS) will feed through CIRQ and connect
> > +to GIC in MCUSYS. When CIRQ is enabled, it will record the edge-sensitive
> > +interrupts and generated a pulse signal to parent interrupt controller when
> > +flush command is executed. With CIRQ, MCUSYS can be completely turned off
> > +to improve the system power consumption without losing interrupts.
> > +
> > +Required properties:
> > +- compatible: should be: "mediatek,mt2701-cirq".
> 
> The cirq is present in several SoCs. I suppose it is the same core in 
> all of them. So we can name it mediatek,mtk-cirq and add a 
> mediatek,mtXXXX-cirq for every SoC, just in case we will need it.

Okay, will change it in next version. Thanks.

> 
> Thanks,
> Matthias
Thanks.
Youlin

^ permalink raw reply

* [PATCH 2/3] irqchip: mtk-cirq: Add mediatek mtk-cirq implement
From: Youlin Pei @ 2016-10-14  8:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161013092808.3c4181dd@arm.com>

Hi Marc,
Thanks for your review.
On Thu, 2016-10-13 at 09:28 +0100, Marc Zyngier wrote:
> On Thu, 13 Oct 2016 13:06:33 +0800
> Youlin Pei <youlin.pei@mediatek.com> wrote:
> 
> > This commit add the mtk-cirq implement for mt2701.

> Can you please expand this a bit?

Okay, I will improve it as following:

In Mediatek SOCs, the CIRQ is a low power interrupt controller designed
to works outside MCUSYS which comprises with Cortex-Ax cores,CCI and
GIC.

The CIRQ controller is integrated in between MCUSYS(include Cortex-Ax,
CCI and GIC) and interrupt sources as the second level interrupt
controller. The external interrupts which outside MCUSYS will feed
through CIRQ then bypass to GIC. CIRQ can monitors all edge trigger
interupts. When an edge interrupt is triggered, CIRQ can record the
status and generate a pulse signal to GIC when flush command executed.

When system enters sleep mode, MCUSYS will be turned off to improve
power consumption, also GIC is power down. The edge trigger interrupts
will be lost in this scenario without CIRQ.

This patch provides the CIRQ irqchip implement.

> 
> > 
> > Signed-off-by: Youlin Pei <youlin.pei@mediatek.com>
> > ---
> >  drivers/irqchip/Makefile       |    2 +-
> >  drivers/irqchip/irq-mtk-cirq.c |  257 ++++++++++++++++++++++++++++++++++++++++
> >  2 files changed, 258 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/irqchip/irq-mtk-cirq.c
> > 
> > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> > index 4c203b6..eee95c6 100644
> > --- a/drivers/irqchip/Makefile
> > +++ b/drivers/irqchip/Makefile
> > @@ -59,7 +59,7 @@ obj-$(CONFIG_BCM7120_L2_IRQ)		+= irq-bcm7120-l2.o
> >  obj-$(CONFIG_BRCMSTB_L2_IRQ)		+= irq-brcmstb-l2.o
> >  obj-$(CONFIG_KEYSTONE_IRQ)		+= irq-keystone.o
> >  obj-$(CONFIG_MIPS_GIC)			+= irq-mips-gic.o
> > -obj-$(CONFIG_ARCH_MEDIATEK)		+= irq-mtk-sysirq.o
> > +obj-$(CONFIG_ARCH_MEDIATEK)		+= irq-mtk-sysirq.o irq-mtk-cirq.o
> >  obj-$(CONFIG_ARCH_DIGICOLOR)		+= irq-digicolor.o
> >  obj-$(CONFIG_RENESAS_H8300H_INTC)	+= irq-renesas-h8300h.o
> >  obj-$(CONFIG_RENESAS_H8S_INTC)		+= irq-renesas-h8s.o
> > diff --git a/drivers/irqchip/irq-mtk-cirq.c b/drivers/irqchip/irq-mtk-cirq.c
> > new file mode 100644
> > index 0000000..544767d
> > --- /dev/null
> > +++ b/drivers/irqchip/irq-mtk-cirq.c
> > @@ -0,0 +1,257 @@
> > +/*
> > + * Copyright (c) 2016 MediaTek Inc.
> > + * Author: Youlin.Pei <youlin.pei@mediatek.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 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <linux/irq.h>
> > +#include <linux/irqchip.h>
> > +#include <linux/irqdomain.h>
> > +#include <linux/of.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/of_address.h>
> > +#include <linux/io.h>
> > +#include <linux/slab.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/syscore_ops.h>
> > +
> > +#define CIRQ_MASK_SET	0xC0
> > +#define CIRQ_MASK_CLR	0x100
> > +#define CIRQ_SENS_SET	0x180
> > +#define CIRQ_SENS_CLR	0x1C0
> > +#define CIRQ_POL_SET	0x240
> > +#define CIRQ_POL_CLR	0x280
> > +#define CIRQ_CONTROL	0x300
> > +
> > +#define CIRQ_EN		0x1
> > +#define CIRQ_EDGE	0x2
> > +#define CIRQ_FLUSH	0x4
> > +
> > +#define CIRQ_IRQ_NUM    0x200
> > +
> > +struct mtk_cirq_chip_data {
> > +	void __iomem *base;
> > +	unsigned int ext_irq_start;
> > +};
> > +
> > +struct mtk_cirq_chip_data *cirq_data;
> 
> Any reason why this is not static?

My fault, will fix it in next version.

> 
> > +
> > +static void mtk_cirq_write_mask(struct irq_data *data, unsigned int offset)
> > +{
> > +	struct mtk_cirq_chip_data *chip_data = data->chip_data;
> > +	unsigned int cirq_num = data->hwirq - chip_data->ext_irq_start;
> 
> I wonder why you have to compute this offset. Ideally, data->hwirq
> should be the bit position (starting at zero). The fact that you have
> an offset between the pin number in this driver and the IRQ number in
> the GIC or sysirq should be resolved at alloc time.

Okay, will fix it in next version.

> 
> > +	u32 mask = 1 << (cirq_num % 32);
> > +
> > +	writel(mask, chip_data->base + offset + (cirq_num / 32) * 4);
> > +}
> > +
> > +static void mtk_cirq_mask(struct irq_data *data)
> > +{
> > +	mtk_cirq_write_mask(data, CIRQ_MASK_SET);
> > +	irq_chip_mask_parent(data);
> > +}
> > +
> > +static void mtk_cirq_unmask(struct irq_data *data)
> > +{
> > +	mtk_cirq_write_mask(data, CIRQ_MASK_CLR);
> > +	irq_chip_unmask_parent(data);
> > +}
> > +
> > +static int mtk_cirq_set_type(struct irq_data *data, unsigned int type)
> > +{
> > +	int ret;
> > +
> > +	switch (type & IRQ_TYPE_SENSE_MASK) {
> > +	case IRQ_TYPE_EDGE_FALLING:
> > +		mtk_cirq_write_mask(data, CIRQ_POL_CLR);
> > +		mtk_cirq_write_mask(data, CIRQ_SENS_CLR);
> > +		break;
> > +	case IRQ_TYPE_EDGE_RISING:
> > +		mtk_cirq_write_mask(data, CIRQ_POL_SET);
> > +		mtk_cirq_write_mask(data, CIRQ_SENS_CLR);
> > +		break;
> > +	case IRQ_TYPE_LEVEL_LOW:
> > +		mtk_cirq_write_mask(data, CIRQ_POL_CLR);
> > +		mtk_cirq_write_mask(data, CIRQ_SENS_SET);
> > +		break;
> > +	case IRQ_TYPE_LEVEL_HIGH:
> > +		mtk_cirq_write_mask(data, CIRQ_POL_SET);
> > +		mtk_cirq_write_mask(data, CIRQ_SENS_SET);
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +
> > +	data = data->parent_data;
> > +	ret = data->chip->irq_set_type(data, type);
> > +	return ret;
> > +}
> > +
> > +static struct irq_chip mtk_cirq_chip = {
> > +	.name			= "MT_CIRQ",
> > +	.irq_mask		= mtk_cirq_mask,
> > +	.irq_unmask		= mtk_cirq_unmask,
> > +	.irq_eoi		= irq_chip_eoi_parent,
> > +	.irq_set_type		= mtk_cirq_set_type,
> > +	.irq_retrigger		= irq_chip_retrigger_hierarchy,
> > +#ifdef CONFIG_SMP
> > +	.irq_set_affinity	= irq_chip_set_affinity_parent,
> > +#endif
> > +};
> 
> I'm surprised that you don't implement irq_set_wake. Do you wake-up on
> *any* interrupt?

The CIRQ controller don't have the wake up function, so irq_set_wake
is not implemented.

> 
> > +
> > +static int mtk_cirq_domain_translate(struct irq_domain *d,
> > +				     struct irq_fwspec *fwspec,
> > +				     unsigned long *hwirq,
> > +				     unsigned int *type)
> > +{
> > +	if (is_of_node(fwspec->fwnode)) {
> > +		if (fwspec->param_count != 3)
> > +			return -EINVAL;
> > +
> > +		/* No PPI should point to this domain */
> > +		if (fwspec->param[0] != 0)
> > +			return -EINVAL;
> > +
> > +		/* cirq support irq number check */
> > +		if (fwspec->param[1] < ;)
> > +			return -EINVAL;
> > +
> > +		*hwirq = fwspec->param[1];
> 
> So if you turn this into:
> 
> 		*hwirq = fwspec->param[1] - cirq_data->ext_irq_start;
> 
> and drop the above offset computing in your write function, you'd have
> something that'd make a bit more sense.

Okay, will fix it in next version. Thanks!

> 
> > +		*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
> > +		return 0;
> > +	}
> > +
> > +	return -EINVAL;
> > +}
> > +
> > +static int mtk_cirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> > +				 unsigned int nr_irqs, void *arg)
> > +{
> > +	int i;
> > +	irq_hw_number_t hwirq;
> > +	struct irq_fwspec *fwspec = arg;
> > +	struct irq_fwspec parent_fwspec = *fwspec;
> > +
> > +	if (fwspec->param_count != 3)
> > +		return -EINVAL;
> > +
> > +	/* cirq doesn't support PPI */
> > +	if (fwspec->param[0])
> > +		return -EINVAL;
> > +
> > +	if (fwspec->param[1] < cirq_data->ext_irq_start)
> > +		return -EINVAL;
> > +
> > +	hwirq = fwspec->param[1];
> > +	for (i = 0; i < nr_irqs; i++)
> > +		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
> > +					      &mtk_cirq_chip,
> > +					      domain->host_data);
> > +
> > +	parent_fwspec.fwnode = domain->parent->fwnode;
> > +	return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
> > +					    &parent_fwspec);
> > +}
> > +
> > +static const struct irq_domain_ops cirq_domain_ops = {
> > +	.translate	= mtk_cirq_domain_translate,
> > +	.alloc		= mtk_cirq_domain_alloc,
> > +	.free		= irq_domain_free_irqs_common,
> > +};
> > +
> > +#ifdef CONFIG_PM_SLEEP
> > +static int mtk_cirq_suspend(void)
> > +{
> > +	u32 value;
> > +
> > +	/* set edge_only mode, record edge-triggerd interrupts */
> > +	/* enable cirq */
> > +	value = readl_relaxed(cirq_data->base + CIRQ_CONTROL);
> > +	value |= (CIRQ_EDGE | CIRQ_EN);
> > +	writel(value, cirq_data->base + CIRQ_CONTROL);
> > +	return 0;
> > +}
> > +
> > +static void mtk_cirq_resume(void)
> > +{
> > +	u32 value;
> > +
> > +	/* flush recored interrupts, will send signals to parent controller */
> > +	value = readl_relaxed(cirq_data->base + CIRQ_CONTROL);
> > +	writel(value | CIRQ_FLUSH, cirq_data->base + CIRQ_CONTROL);
> > +
> > +	/* disable cirq */
> > +	value = readl_relaxed(cirq_data->base + CIRQ_CONTROL);
> > +	value &= ~(CIRQ_EDGE | CIRQ_EN);
> > +	writel(value, cirq_data->base + CIRQ_CONTROL);
> > +}
> > +
> > +static struct syscore_ops mtk_cirq_syscore_ops = {
> > +	.suspend	= mtk_cirq_suspend,
> > +	.resume		= mtk_cirq_resume,
> > +};
> > +
> > +static void mtk_cirq_syscore_init(void)
> > +{
> > +	register_syscore_ops(&mtk_cirq_syscore_ops);
> > +}
> > +#else
> > +static inline void mtk_cirq_syscore_init(void) {}
> > +#endif
> > +
> > +static int __init mtk_cirq_of_init(struct device_node *node,
> > +				   struct device_node *parent)
> > +{
> > +	struct irq_domain *domain, *domain_parent;
> > +	int ret;
> > +
> > +	domain_parent = irq_find_host(parent);
> > +	if (!domain_parent) {
> > +		pr_err("mtk_cirq: interrupt-parent not found\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	cirq_data = kzalloc(sizeof(*cirq_data), GFP_KERNEL);
> > +	if (!cirq_data)
> > +		return -ENOMEM;
> > +
> > +	cirq_data->base = of_iomap(node, 0);
> > +	if (!cirq_data->base) {
> > +		pr_err("mtk_cirq: unable to map cirq register\n");
> > +		ret = -ENXIO;
> > +		goto out_free;
> > +	}
> > +
> > +	if (of_property_read_u32(node, "mediatek,ext-irq-start",
> > +				 &cirq_data->ext_irq_start)) {
> > +		ret = -EINVAL;
> > +		goto out_free;
> > +	}
> > +
> > +	domain = irq_domain_add_hierarchy(domain_parent, 0, CIRQ_IRQ_NUM, node,
> > +					  &cirq_domain_ops, cirq_data);
> > +	if (!domain) {
> > +		ret = -ENOMEM;
> > +		goto out_unmap;
> > +	}
> > +
> > +	mtk_cirq_syscore_init();
> > +
> > +	return 0;
> > +
> > +out_unmap:
> > +	iounmap(cirq_data->base);
> > +out_free:
> > +	kfree(cirq_data);
> > +	return ret;
> > +}
> > +
> > +IRQCHIP_DECLARE(mtk_cirq, "mediatek,mt2701-cirq", mtk_cirq_of_init);
> 
> Thanks,
> 
> 	M.
Thanks,
Youlin 

^ permalink raw reply

* aarch64 ACPI boot regressed by commit 7ba5f605f3a0 ("arm64/numa: remove the limitation that cpu0 must bind to node0")
From: Andrew Jones @ 2016-10-14  8:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4a64cd93-5ead-aad6-1057-f42224d65b43@redhat.com>

On Fri, Oct 14, 2016 at 12:50:29AM +0200, Laszlo Ersek wrote:
> (4) Analysis (well, a lame attempt at that, because I have zero
> familiarity with this code). Let me quote the patch:
> 
> > commit 7ba5f605f3a0d9495aad539eeb8346d726dfc183
> > Author: Zhen Lei <thunder.leizhen@huawei.com>
> > Date:   Thu Sep 1 14:55:04 2016 +0800
> >
> >     arm64/numa: remove the limitation that cpu0 must bind to node0
> >
> >     1. Remove the old binding code.
> >     2. Read the nid of cpu0 from dts.
> >     3. Fallback the nid of cpu0 to 0 when numa=off is set in bootargs.
> >
> >     Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
> >     Signed-off-by: Will Deacon <will.deacon@arm.com>
> >
> > diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
> > index c3c08368a685..8b048e6ec34a 100644
> > --- a/arch/arm64/kernel/smp.c
> > +++ b/arch/arm64/kernel/smp.c
> > @@ -624,6 +624,7 @@ static void __init of_parse_and_init_cpus(void)
> >  			}
> >
> >  			bootcpu_valid = true;
> > +			early_map_cpu_to_node(0, of_node_to_nid(dn));
> >
> >  			/*
> >  			 * cpu_logical_map has already been
> > diff --git a/arch/arm64/mm/numa.c b/arch/arm64/mm/numa.c
> > index 0a15f010b64a..778a985c8a70 100644
> > --- a/arch/arm64/mm/numa.c
> > +++ b/arch/arm64/mm/numa.c
> > @@ -116,16 +116,24 @@ static void __init setup_node_to_cpumask_map(void)
> >   */
> >  void numa_store_cpu_info(unsigned int cpu)
> >  {
> > -	map_cpu_to_node(cpu, numa_off ? 0 : cpu_to_node_map[cpu]);
> > +	map_cpu_to_node(cpu, cpu_to_node_map[cpu]);
> >  }
> >
> >  void __init early_map_cpu_to_node(unsigned int cpu, int nid)
> >  {
> >  	/* fallback to node 0 */
> > -	if (nid < 0 || nid >= MAX_NUMNODES)
> > +	if (nid < 0 || nid >= MAX_NUMNODES || numa_off)
> >  		nid = 0;

The ACPI equivalent code must be missing (at least) the above, because,
even with DT, mach-virt won't have cpu to node mappings unless numa
is configured on the command line. Can you try adding something like

    -m 512 -smp 4 \
    -numa node,mem=256M,cpus=0-1,nodeid=0 \
    -numa node,mem=256M,cpus=2-3,nodeid=1

to your QEMU command line? Then when you boot with ACPI you'll get a
SRAT. If that works, then we're just missing the "no SRAT, nid = 0"
code (that should have been added with this patch)

Thanks,
drew

^ permalink raw reply

* [PATCH 6/6] misc: q8-hardwaremgr: Add quirk handling
From: Hans de Goede @ 2016-10-14  7:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161014075337.10452-1-hdegoede@redhat.com>

Some models require some special handling, e.g. one q8 tablet I've needs
esp,crystal-26M-en = <1> in the sdio_wifi dt node for the wifi to work
properly.

Add a q8_hardwaremgr_apply_quirks() helper function which identifies the
tablet based on the detected soc, touchscreen, accelerometer combo and
applies model specific quirks where necessary.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 drivers/misc/q8-hardwaremgr.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/drivers/misc/q8-hardwaremgr.c b/drivers/misc/q8-hardwaremgr.c
index 95cdaf2..cbf4f61 100644
--- a/drivers/misc/q8-hardwaremgr.c
+++ b/drivers/misc/q8-hardwaremgr.c
@@ -715,6 +715,28 @@ static void q8_hardwaremgr_apply_accelerometer(struct q8_hardwaremgr_data *data)
 	of_node_put(np);
 }
 
+static void q8_hardwaremgr_apply_quirks(struct q8_hardwaremgr_data *data)
+{
+	struct device_node *np;
+	struct of_changeset cset;
+
+	/* This A33 tzx-723q4 PCB tablet with esp8089 needs crystal_26M_en=1 */
+	if (data->soc == a33 && data->touchscreen.model == gsl1680_b482 &&
+	    data->accelerometer.model == dmard09 && !data->has_rda599x) {
+		dev_info(data->dev, "Applying crystal_26M_en=1 sdio_wifi quirk\n");
+		np = of_find_node_by_name(of_root, "sdio_wifi");
+		if (!np) {
+			dev_warn(data->dev, "Could not find sdio_wifi dt node\n");
+			return;
+		}
+		of_changeset_init(&cset);
+		of_changeset_add_property_u32(&cset, np,
+					      "esp,crystal-26M-en", 1);
+		of_changeset_apply(&cset);
+		of_node_put(np);
+	}
+}
+
 static int q8_hardwaremgr_do_probe(struct q8_hardwaremgr_data *data,
 				   struct q8_hardwaremgr_device *dev,
 				   const char *prefix, bus_probe_func func)
@@ -846,6 +868,7 @@ static int q8_hardwaremgr_probe(struct platform_device *pdev)
 
 	q8_hardwaremgr_apply_touchscreen(data);
 	q8_hardwaremgr_apply_accelerometer(data);
+	q8_hardwaremgr_apply_quirks(data);
 
 error:
 	kfree(data);
-- 
2.9.3

^ permalink raw reply related

* [PATCH 5/6] misc: q8-hardwaremgr: Better touchscreen defaults based on heuristics
From: Hans de Goede @ 2016-10-14  7:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161014075337.10452-1-hdegoede@redhat.com>

Some q8s with gsl1680 touchscreens need to use an alterternative
firmware file and/or need their x/y axis inverted.

This commit adds some heuristics to pick defaults based on other detected
tablet properties (mostly which accelerometer is detected) and sets
defaults based on this.

Although there is not necessarily any relationship between these
settings, this does give us the right defaults on all known model q8
tablets, making the touchscreen work out of the box without requiring
any kernel commandline options to be set.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 drivers/misc/q8-hardwaremgr.c | 81 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 74 insertions(+), 7 deletions(-)

diff --git a/drivers/misc/q8-hardwaremgr.c b/drivers/misc/q8-hardwaremgr.c
index 21754f5..95cdaf2 100644
--- a/drivers/misc/q8-hardwaremgr.c
+++ b/drivers/misc/q8-hardwaremgr.c
@@ -84,7 +84,12 @@ enum {
 };
 
 #define DA280_REG_CHIP_ID		0x01
+#define DA280_REG_ACC_Z_LSB		0x06
+#define DA280_REG_MODE_BW		0x11
 #define DA280_CHIP_ID			0x13
+#define DA280_MODE_ENABLE		0x1e
+#define DA280_MODE_DISABLE		0x9e
+
 #define DA311_REG_CHIP_ID		0x0f
 #define DA311_CHIP_ID			0x13
 
@@ -112,6 +117,7 @@ enum {
 
 enum {
 	accel_unknown,
+	da226,
 	da280,
 	da311,
 	dmard05,
@@ -313,8 +319,23 @@ static int q8_hardwaremgr_probe_touchscreen(struct q8_hardwaremgr_data *data,
 static void q8_hardwaremgr_apply_gsl1680_a082_variant(
 	struct q8_hardwaremgr_data *data)
 {
-	if (touchscreen_variant != -1)
+	if (touchscreen_variant != -1) {
 		data->touchscreen_variant = touchscreen_variant;
+	} else {
+		/*
+		 * These accelerometer based heuristics select the best
+		 * default based on known q8 tablets.
+		 */
+		switch (data->accelerometer.model) {
+		case mc3230:
+			data->touchscreen_invert_x = 1;
+			break;
+		case dmard10:
+		case mxc6225:
+			data->touchscreen_variant = 1;
+			break;
+		}
+	}
 
 	switch (data->touchscreen_variant) {
 	default:
@@ -338,8 +359,30 @@ static void q8_hardwaremgr_apply_gsl1680_a082_variant(
 static void q8_hardwaremgr_apply_gsl1680_b482_variant(
 	struct q8_hardwaremgr_data *data)
 {
-	if (touchscreen_variant != -1)
+	if (touchscreen_variant != -1) {
 		data->touchscreen_variant = touchscreen_variant;
+	} else {
+		/*
+		 * These accelerometer based heuristics select the best
+		 * default based on known q8 tablets.
+		 */
+		switch (data->accelerometer.model) {
+		case da280:
+			if (data->accelerometer.addr == 0x27)
+				; /* No-op */
+			else if (data->has_rda599x)
+				data->touchscreen_invert_x = 1;
+			else
+				data->touchscreen_invert_y = 1;
+			break;
+		case dmard09:
+			data->touchscreen_invert_x = 1;
+			break;
+		case mxc6225:
+			data->touchscreen_variant = 1;
+			break;
+		}
+	}
 
 	switch (data->touchscreen_variant) {
 	default:
@@ -587,16 +630,39 @@ static int q8_hardwaremgr_probe_dmard10(struct q8_hardwaremgr_data *data,
 static int q8_hardwaremgr_probe_da280(struct q8_hardwaremgr_data *data,
 				      struct i2c_client *client)
 {
-	int id;
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID);
+	if (ret != DA280_CHIP_ID)
+		return ret == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
 
-	id = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID);
-	if (id == DA280_CHIP_ID) {
+	/* da226 (2-axis) or da280 (3-axis) ? measure once to detect */
+	ret = i2c_smbus_write_byte_data(client, DA280_REG_MODE_BW,
+					DA280_MODE_ENABLE);
+	if (ret)
+		return ret == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+
+	msleep(10);
+
+	ret = i2c_smbus_read_word_data(client, DA280_REG_ACC_Z_LSB);
+	if (ret < 0)
+		return ret == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+
+	/* If not present Z reports max pos value (14 bits, 2 low bits 0) */
+	if (ret == 32764) {
+		data->accelerometer.compatible = "miramems,da226";
+		data->accelerometer.model = da226;
+	} else {
 		data->accelerometer.compatible = "miramems,da280";
 		data->accelerometer.model = da280;
-		return 0;
 	}
 
-	return id == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+	ret = i2c_smbus_write_byte_data(client, DA280_REG_MODE_BW,
+					DA280_MODE_DISABLE);
+	if (ret)
+		return ret == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+
+	return 0;
 }
 
 static int q8_hardwaremgr_probe_da311(struct q8_hardwaremgr_data *data,
@@ -625,6 +691,7 @@ static int q8_hardwaremgr_probe_accelerometer(struct q8_hardwaremgr_data *data,
 	PROBE_CLIENT(&data->accelerometer, 0x1d, q8_hardwaremgr_probe_dmard09);
 	PROBE_CLIENT(&data->accelerometer, 0x18, q8_hardwaremgr_probe_dmard10);
 	PROBE_CLIENT(&data->accelerometer, 0x26, q8_hardwaremgr_probe_da280);
+	PROBE_CLIENT(&data->accelerometer, 0x27, q8_hardwaremgr_probe_da280);
 	PROBE_CLIENT(&data->accelerometer, 0x27, q8_hardwaremgr_probe_da311);
 
 	return -ENODEV;
-- 
2.9.3

^ permalink raw reply related

* [PATCH 4/6] misc: q8-hardwaremgr: Add rda599x wift/bt/fm radio combo detection
From: Hans de Goede @ 2016-10-14  7:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161014075337.10452-1-hdegoede@redhat.com>

Some q8s have a rda599x sdio wifi/bt + i2c fm radio chip with its
i2c interface connected to the same i2c bus as the accelerometer.

There is no kernel driver for these devices yet, but it is useful to
know if one is present for the touchscreen variant selection heuristics.

And when in the future we do get a driver, this detection code will also
be useful to automatically register i2c client dt nodes for these devices.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 drivers/misc/q8-hardwaremgr.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/drivers/misc/q8-hardwaremgr.c b/drivers/misc/q8-hardwaremgr.c
index 8ed71a9..21754f5 100644
--- a/drivers/misc/q8-hardwaremgr.c
+++ b/drivers/misc/q8-hardwaremgr.c
@@ -144,6 +144,7 @@ struct q8_hardwaremgr_data {
 	int touchscreen_invert_y;
 	int touchscreen_swap_x_y;
 	const char *touchscreen_fw_name;
+	bool has_rda599x;
 };
 
 typedef int (*bus_probe_func)(struct q8_hardwaremgr_data *data,
@@ -454,6 +455,28 @@ static void q8_hardwaremgr_apply_touchscreen(struct q8_hardwaremgr_data *data)
 	of_node_put(np);
 }
 
+static int q8_hardwaremgr_probe_rda599x(struct q8_hardwaremgr_data *data,
+					struct i2c_client *client)
+{
+	int id;
+
+	/*
+	 * Check for the (integrated) rda580x / rda5820 fm receiver at 0x11
+	 *
+	 * Alternatively we could check for the wifi_rf i2c interface at
+	 * address 0x14, by selecting page/bank 1 through:
+	 * smbus_write_word_swapped(0x3f, 0x01)
+	 * and then doing a smbus_read_word_swapped(0x20) which will
+	 * return 0x5990 for a rda5990. We prefer the fm detect method since
+	 * we want to avoid doing any smbus_writes while probing.
+	 */
+	id = i2c_smbus_read_word_swapped(client, 0x0c);
+	if (id == 0x5802 || id == 0x5803 || id == 0x5805 || id == 0x5820)
+		data->has_rda599x = true;
+
+	return id == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+}
+
 static int q8_hardwaremgr_probe_mxc6225(struct q8_hardwaremgr_data *data,
 					struct i2c_client *client)
 {
@@ -594,6 +617,8 @@ static int q8_hardwaremgr_probe_da311(struct q8_hardwaremgr_data *data,
 static int q8_hardwaremgr_probe_accelerometer(struct q8_hardwaremgr_data *data,
 					      struct i2c_adapter *adap)
 {
+	/* The rda599x wifi/bt/fm shares the i2c bus with the accelerometer */
+	PROBE_CLIENT(NULL,		   0x11, q8_hardwaremgr_probe_rda599x);
 	PROBE_CLIENT(&data->accelerometer, 0x15, q8_hardwaremgr_probe_mxc6225);
 	PROBE_CLIENT(&data->accelerometer, 0x4c, q8_hardwaremgr_probe_mc3230);
 	PROBE_CLIENT(&data->accelerometer, 0x1c, q8_hardwaremgr_probe_dmard06);
@@ -749,6 +774,9 @@ static int q8_hardwaremgr_probe(struct platform_device *pdev)
 	if (ret)
 		goto error;
 
+	if (data->has_rda599x)
+		dev_info(data->dev, "Found a rda599x sdio/i2c wifi/bt/fm combo chip\n");
+
 	q8_hardwaremgr_apply_touchscreen(data);
 	q8_hardwaremgr_apply_accelerometer(data);
 
-- 
2.9.3

^ permalink raw reply related

* [PATCH 3/6] misc: q8-hardwaremgr: Add accelerometer detection
From: Hans de Goede @ 2016-10-14  7:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161014075337.10452-1-hdegoede@redhat.com>

Extend the q8-hardwaremgr with accelerometer detection.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 drivers/misc/Kconfig          |   2 +-
 drivers/misc/q8-hardwaremgr.c | 219 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 220 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index d6c1529..11b05b0 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -801,7 +801,7 @@ config Q8_HARDWAREMGR
 	default	n
 	help
 	  This option enables support for autodetecting the touchscreen
-	  on Allwinner Q8 tablets.
+	  and accelerometer on Allwinner Q8 tablets.
 
 	  If unsure, say N.
 
diff --git a/drivers/misc/q8-hardwaremgr.c b/drivers/misc/q8-hardwaremgr.c
index 346102d..8ed71a9 100644
--- a/drivers/misc/q8-hardwaremgr.c
+++ b/drivers/misc/q8-hardwaremgr.c
@@ -83,6 +83,48 @@ enum {
 	zet6251,
 };
 
+#define DA280_REG_CHIP_ID		0x01
+#define DA280_CHIP_ID			0x13
+#define DA311_REG_CHIP_ID		0x0f
+#define DA311_CHIP_ID			0x13
+
+#define DMARD06_CHIP_ID_REG		0x0f
+#define DMARD05_CHIP_ID			0x05
+#define DMARD06_CHIP_ID			0x06
+#define DMARD07_CHIP_ID			0x07
+#define DMARD09_REG_CHIPID		0x18
+#define DMARD09_CHIPID			0x95
+#define DMARD10_REG_STADR		0x12
+#define DMARD10_REG_STAINT		0x1c
+#define DMARD10_VALUE_STADR		0x55
+#define DMARD10_VALUE_STAINT		0xaa
+
+#define MC3230_REG_CHIP_ID		0x18
+#define MC3230_CHIP_ID			0x01
+#define MMA7660_CHIP_ID			0x00 /* Factory reserved on MMA7660 */
+#define MC3230_REG_PRODUCT_CODE		0x3b
+#define MMA7660_PRODUCT_CODE		0x00 /* Factory reserved on MMA7660 */
+#define MC3210_PRODUCT_CODE		0x90
+#define MC3230_PRODUCT_CODE		0x19
+
+#define MXC6225_REG_CHIP_ID		0x08
+#define MXC6225_CHIP_ID			0x05
+
+enum {
+	accel_unknown,
+	da280,
+	da311,
+	dmard05,
+	dmard06,
+	dmard07,
+	dmard09,
+	dmard10,
+	mc3210,
+	mc3230,
+	mma7660,
+	mxc6225,
+};
+
 struct q8_hardwaremgr_device {
 	int model;
 	int addr;
@@ -94,6 +136,7 @@ struct q8_hardwaremgr_data {
 	struct device *dev;
 	enum soc soc;
 	struct q8_hardwaremgr_device touchscreen;
+	struct q8_hardwaremgr_device accelerometer;
 	int touchscreen_variant;
 	int touchscreen_width;
 	int touchscreen_height;
@@ -411,6 +454,175 @@ static void q8_hardwaremgr_apply_touchscreen(struct q8_hardwaremgr_data *data)
 	of_node_put(np);
 }
 
+static int q8_hardwaremgr_probe_mxc6225(struct q8_hardwaremgr_data *data,
+					struct i2c_client *client)
+{
+	int id;
+
+	id = i2c_smbus_read_byte_data(client, MXC6225_REG_CHIP_ID);
+	/* Bits 7 - 5 of the chip-id register are undefined */
+	if (id >= 0 && (id & 0x1f) == MXC6225_CHIP_ID) {
+		data->accelerometer.compatible = "memsic,mxc6225";
+		data->accelerometer.model = mxc6225;
+		return 0;
+	}
+
+	return id == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+}
+
+static int q8_hardwaremgr_probe_mc3230(struct q8_hardwaremgr_data *data,
+				       struct i2c_client *client)
+{
+	int id, pc = 0;
+
+	/* First check chip-id, then product-id */
+	id = i2c_smbus_read_byte_data(client, MC3230_REG_CHIP_ID);
+	if (id == MC3230_CHIP_ID || id == MMA7660_CHIP_ID) {
+		pc = i2c_smbus_read_byte_data(client, MC3230_REG_PRODUCT_CODE);
+		switch (pc) {
+		case MMA7660_PRODUCT_CODE:
+			data->accelerometer.compatible = "fsl,mma7660";
+			data->accelerometer.model = mma7660;
+			return 0;
+		case MC3210_PRODUCT_CODE:
+			data->accelerometer.compatible = "mcube,mc3210";
+			data->accelerometer.model = mc3210;
+			return 0;
+		case MC3230_PRODUCT_CODE:
+			data->accelerometer.compatible = "mcube,mc3230";
+			data->accelerometer.model = mc3230;
+			return 0;
+		case -ETIMEDOUT:
+			return -ETIMEDOUT; /* Bus stuck bail immediately */
+		}
+	}
+
+	return id == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+}
+
+static int q8_hardwaremgr_probe_dmard06(struct q8_hardwaremgr_data *data,
+					struct i2c_client *client)
+{
+	int id;
+
+	id = i2c_smbus_read_byte_data(client, DMARD06_CHIP_ID_REG);
+	switch (id) {
+	case DMARD05_CHIP_ID:
+		data->accelerometer.compatible = "domintech,dmard05";
+		data->accelerometer.model = dmard05;
+		return 0;
+	case DMARD06_CHIP_ID:
+		data->accelerometer.compatible = "domintech,dmard06";
+		data->accelerometer.model = dmard06;
+		return 0;
+	case DMARD07_CHIP_ID:
+		data->accelerometer.compatible = "domintech,dmard07";
+		data->accelerometer.model = dmard07;
+		return 0;
+	}
+
+	return id == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+}
+
+static int q8_hardwaremgr_probe_dmard09(struct q8_hardwaremgr_data *data,
+					struct i2c_client *client)
+{
+	int id;
+
+	id = i2c_smbus_read_byte_data(client, DMARD09_REG_CHIPID);
+	if (id == DMARD09_CHIPID) {
+		data->accelerometer.compatible = "domintech,dmard09";
+		data->accelerometer.model = dmard09;
+		return 0;
+	}
+
+	return id == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+}
+
+static int q8_hardwaremgr_probe_dmard10(struct q8_hardwaremgr_data *data,
+					struct i2c_client *client)
+{
+	int stadr, staint = 0;
+
+	/* These 2 registers have special POR reset values used for id */
+	stadr = i2c_smbus_read_byte_data(client, DMARD10_REG_STADR);
+	if (stadr == DMARD10_VALUE_STADR) {
+		staint = i2c_smbus_read_byte_data(client, DMARD10_REG_STAINT);
+		switch (staint) {
+		case DMARD10_VALUE_STAINT:
+			data->accelerometer.compatible = "domintech,dmard10";
+			data->accelerometer.model = dmard10;
+			return 0;
+		case -ETIMEDOUT:
+			return -ETIMEDOUT; /* Bus stuck bail immediately */
+		}
+	}
+
+	return stadr == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+}
+
+static int q8_hardwaremgr_probe_da280(struct q8_hardwaremgr_data *data,
+				      struct i2c_client *client)
+{
+	int id;
+
+	id = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID);
+	if (id == DA280_CHIP_ID) {
+		data->accelerometer.compatible = "miramems,da280";
+		data->accelerometer.model = da280;
+		return 0;
+	}
+
+	return id == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+}
+
+static int q8_hardwaremgr_probe_da311(struct q8_hardwaremgr_data *data,
+				      struct i2c_client *client)
+{
+	int id;
+
+	id = i2c_smbus_read_byte_data(client, DA311_REG_CHIP_ID);
+	if (id == DA311_CHIP_ID) {
+		data->accelerometer.compatible = "miramems,da311";
+		data->accelerometer.model = da311;
+		return 0;
+	}
+
+	return id == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+}
+
+static int q8_hardwaremgr_probe_accelerometer(struct q8_hardwaremgr_data *data,
+					      struct i2c_adapter *adap)
+{
+	PROBE_CLIENT(&data->accelerometer, 0x15, q8_hardwaremgr_probe_mxc6225);
+	PROBE_CLIENT(&data->accelerometer, 0x4c, q8_hardwaremgr_probe_mc3230);
+	PROBE_CLIENT(&data->accelerometer, 0x1c, q8_hardwaremgr_probe_dmard06);
+	PROBE_CLIENT(&data->accelerometer, 0x1d, q8_hardwaremgr_probe_dmard09);
+	PROBE_CLIENT(&data->accelerometer, 0x18, q8_hardwaremgr_probe_dmard10);
+	PROBE_CLIENT(&data->accelerometer, 0x26, q8_hardwaremgr_probe_da280);
+	PROBE_CLIENT(&data->accelerometer, 0x27, q8_hardwaremgr_probe_da311);
+
+	return -ENODEV;
+}
+
+static void q8_hardwaremgr_apply_accelerometer(struct q8_hardwaremgr_data *data)
+{
+	struct of_changeset cset;
+	struct device_node *np;
+
+	if (data->accelerometer.model == accel_unknown)
+		return;
+
+	np = q8_hardware_mgr_apply_common(&data->accelerometer, &cset,
+					  "accelerometer");
+	if (!np)
+		return;
+
+	of_changeset_apply(&cset);
+
+	of_node_put(np);
+}
+
 static int q8_hardwaremgr_do_probe(struct q8_hardwaremgr_data *data,
 				   struct q8_hardwaremgr_device *dev,
 				   const char *prefix, bus_probe_func func)
@@ -531,7 +743,14 @@ static int q8_hardwaremgr_probe(struct platform_device *pdev)
 	if (ret)
 		goto error;
 
+	ret = q8_hardwaremgr_do_probe(data, &data->accelerometer,
+				      "accelerometer",
+				      q8_hardwaremgr_probe_accelerometer);
+	if (ret)
+		goto error;
+
 	q8_hardwaremgr_apply_touchscreen(data);
+	q8_hardwaremgr_apply_accelerometer(data);
 
 error:
 	kfree(data);
-- 
2.9.3

^ permalink raw reply related

* [PATCH 2/6] misc: Add Allwinner Q8 tablet hardware manager
From: Hans de Goede @ 2016-10-14  7:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161014075337.10452-1-hdegoede@redhat.com>

Allwinnner A13 / A23 / A33 based Q8 tablets are popular cheap 7" tablets
of which a new batch is produced every few weeks. Each batch uses a
different mix of touchscreen, accelerometer and wifi peripherals.

Given that each batch is different creating a devicetree for each variant
is not desirable. This commit adds a Q8 tablet hardware manager which
auto-detects the touchscreen and accelerometer so that a single generic
dts can be used for these tablets.

The wifi is connected to a discoverable bus (sdio or usb) and will be
autodetected by the mmc resp. usb subsystems.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 drivers/misc/Kconfig          |  11 +
 drivers/misc/Makefile         |   1 +
 drivers/misc/q8-hardwaremgr.c | 581 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 593 insertions(+)
 create mode 100644 drivers/misc/q8-hardwaremgr.c

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index d002528..d6c1529 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -794,6 +794,17 @@ config PANEL_BOOT_MESSAGE
 	  An empty message will only clear the display at driver init time. Any other
 	  printf()-formatted message is valid with newline and escape codes.
 
+config Q8_HARDWAREMGR
+	bool "Allwinner Q8 tablet hardware manager"
+	depends on I2C
+	depends on (ARCH_SUNXI && GPIOLIB && OF && OF_DYNAMIC) || COMPILE_TEST
+	default	n
+	help
+	  This option enables support for autodetecting the touchscreen
+	  on Allwinner Q8 tablets.
+
+	  If unsure, say N.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index fb32516..1a0ebb0 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_ECHO)		+= echo/
 obj-$(CONFIG_VEXPRESS_SYSCFG)	+= vexpress-syscfg.o
 obj-$(CONFIG_CXL_BASE)		+= cxl/
 obj-$(CONFIG_PANEL)             += panel.o
+obj-$(CONFIG_Q8_HARDWAREMGR)    += q8-hardwaremgr.o
 
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_core.o
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_bugs.o
diff --git a/drivers/misc/q8-hardwaremgr.c b/drivers/misc/q8-hardwaremgr.c
new file mode 100644
index 0000000..346102d
--- /dev/null
+++ b/drivers/misc/q8-hardwaremgr.c
@@ -0,0 +1,581 @@
+/*
+ * Allwinner q8 formfactor tablet hardware manager
+ *
+ * Copyright (C) 2016 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/unaligned.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+/*
+ * We can detect which touchscreen controller is used automatically,
+ * but some controllers can be wired up differently depending on the
+ * q8 PCB variant used, so they need different firmware files / settings.
+ *
+ * We allow the user to specify a firmware_variant to select a config
+ * from a list of known configs. We also allow overriding each setting
+ * individually.
+ */
+
+static int touchscreen_variant = -1;
+module_param(touchscreen_variant, int, 0444);
+MODULE_PARM_DESC(touchscreen_variant, "Touchscreen variant 0-x, -1 for auto");
+
+static int touchscreen_width = -1;
+module_param(touchscreen_width, int, 0444);
+MODULE_PARM_DESC(touchscreen_width, "Touchscreen width, -1 for auto");
+
+static int touchscreen_height = -1;
+module_param(touchscreen_height, int, 0444);
+MODULE_PARM_DESC(touchscreen_height, "Touchscreen height, -1 for auto");
+
+static int touchscreen_invert_x = -1;
+module_param(touchscreen_invert_x, int, 0444);
+MODULE_PARM_DESC(touchscreen_invert_x, "Touchscreen invert x, -1 for auto");
+
+static int touchscreen_invert_y = -1;
+module_param(touchscreen_invert_y, int, 0444);
+MODULE_PARM_DESC(touchscreen_invert_y, "Touchscreen invert y, -1 for auto");
+
+static int touchscreen_swap_x_y = -1;
+module_param(touchscreen_swap_x_y, int, 0444);
+MODULE_PARM_DESC(touchscreen_swap_x_y, "Touchscreen swap x y, -1 for auto");
+
+static char *touchscreen_fw_name;
+module_param(touchscreen_fw_name, charp, 0444);
+MODULE_PARM_DESC(touchscreen_fw_name, "Touchscreen firmware filename");
+
+enum soc {
+	a13,
+	a23,
+	a33,
+};
+
+#define TOUCHSCREEN_POWER_ON_DELAY	20
+#define SILEAD_REG_ID			0xFC
+#define EKTF2127_RESPONSE		0x52
+#define EKTF2127_REQUEST		0x53
+#define EKTF2127_WIDTH			0x63
+
+enum {
+	touchscreen_unknown,
+	gsl1680_a082,
+	gsl1680_b482,
+	ektf2127,
+	zet6251,
+};
+
+struct q8_hardwaremgr_device {
+	int model;
+	int addr;
+	const char *compatible;
+	bool delete_regulator;
+};
+
+struct q8_hardwaremgr_data {
+	struct device *dev;
+	enum soc soc;
+	struct q8_hardwaremgr_device touchscreen;
+	int touchscreen_variant;
+	int touchscreen_width;
+	int touchscreen_height;
+	int touchscreen_invert_x;
+	int touchscreen_invert_y;
+	int touchscreen_swap_x_y;
+	const char *touchscreen_fw_name;
+};
+
+typedef int (*bus_probe_func)(struct q8_hardwaremgr_data *data,
+			      struct i2c_adapter *adap);
+typedef int (*client_probe_func)(struct q8_hardwaremgr_data *data,
+				 struct i2c_client *client);
+
+static struct device_node *q8_hardware_mgr_apply_common(
+	struct q8_hardwaremgr_device *dev, struct of_changeset *cset,
+	const char *prefix)
+{
+	struct device_node *np;
+
+	np = of_find_node_by_name(of_root, prefix);
+	/* Never happens already checked in q8_hardwaremgr_do_probe() */
+	if (WARN_ON(!np))
+		return NULL;
+
+	of_changeset_init(cset);
+	of_changeset_add_property_u32(cset, np, "reg", dev->addr);
+	of_changeset_add_property_string(cset, np, "compatible",
+					 dev->compatible);
+	of_changeset_update_property_string(cset, np, "status", "okay");
+
+	if (dev->delete_regulator) {
+		struct property *p;
+
+		p = of_find_property(np, "vddio-supply", NULL);
+		/* Never happens already checked in q8_hardwaremgr_do_probe() */
+		if (WARN_ON(!p))
+			return np;
+
+		of_changeset_remove_property(cset, np, p);
+	}
+
+	return np; /* Allow the caller to make further changes */
+}
+
+static int q8_hardwaremgr_probe_client(struct q8_hardwaremgr_data *data,
+				       struct q8_hardwaremgr_device *dev,
+				       struct i2c_adapter *adap, u16 addr,
+				       client_probe_func client_probe)
+{
+	struct i2c_client *client;
+	int ret;
+
+	client = i2c_new_dummy(adap, addr);
+	if (!client)
+		return -ENOMEM;
+
+	/* ret will be one of 0: Success, -ETIMEDOUT: Bus stuck or -ENODEV */
+	ret = client_probe(data, client);
+	if (ret == 0)
+		dev->addr = addr;
+
+	i2c_unregister_device(client);
+
+	return ret;
+}
+
+#define PROBE_CLIENT(dev, addr, probe) \
+{ \
+	int ret = q8_hardwaremgr_probe_client(data, dev, adap, addr, probe); \
+	if (ret != -ENODEV) \
+		return ret; \
+}
+
+static int q8_hardwaremgr_probe_silead(struct q8_hardwaremgr_data *data,
+				       struct i2c_client *client)
+{
+	__le32 chip_id;
+	int ret;
+
+	ret = i2c_smbus_read_i2c_block_data(client, SILEAD_REG_ID,
+					    sizeof(chip_id), (u8 *)&chip_id);
+	if (ret != sizeof(chip_id))
+		return ret == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+
+	switch (le32_to_cpu(chip_id)) {
+	case 0xa0820000:
+		data->touchscreen.compatible = "silead,gsl1680";
+		data->touchscreen.model = gsl1680_a082;
+		dev_info(data->dev, "Silead touchscreen ID: 0xa0820000\n");
+		return 0;
+	case 0xb4820000:
+		data->touchscreen.compatible = "silead,gsl1680";
+		data->touchscreen.model = gsl1680_b482;
+		dev_info(data->dev, "Silead touchscreen ID: 0xb4820000\n");
+		return 0;
+	default:
+		dev_warn(data->dev, "Silead? touchscreen with unknown ID: 0x%08x\n",
+			 le32_to_cpu(chip_id));
+	}
+
+	return -ENODEV;
+}
+
+static int q8_hardwaremgr_probe_ektf2127(struct q8_hardwaremgr_data *data,
+					 struct i2c_client *client)
+{
+	unsigned char buff[4];
+	int ret;
+
+	/* Read hello, ignore data, depends on initial power state */
+	ret = i2c_master_recv(client, buff, 4);
+	if (ret != 4)
+		return ret == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+
+	/* Request width */
+	buff[0] = EKTF2127_REQUEST;
+	buff[1] = EKTF2127_WIDTH;
+	buff[2] = 0x00;
+	buff[3] = 0x00;
+	ret = i2c_master_send(client, buff, 4);
+	if (ret != 4)
+		return ret == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+
+	msleep(20);
+
+	/* Read response */
+	ret = i2c_master_recv(client, buff, 4);
+	if (ret != 4)
+		return ret == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+
+	if (buff[0] == EKTF2127_RESPONSE && buff[1] == EKTF2127_WIDTH) {
+		data->touchscreen.compatible = "elan,ektf2127";
+		data->touchscreen.model = ektf2127;
+		return 0;
+	}
+
+	return -ENODEV;
+}
+
+static int q8_hardwaremgr_probe_zet6251(struct q8_hardwaremgr_data *data,
+					struct i2c_client *client)
+{
+	unsigned char buff[4];
+	int ret;
+
+	/*
+	 * We only do a simple read finger data packet test, because some
+	 * versions require firmware to be loaded. If no firmware is loaded
+	 * the buffer will be filed with 0xff, so we ignore the contents.
+	 */
+	ret = i2c_master_recv(client, buff, 24);
+	if (ret != 24)
+		return ret == -ETIMEDOUT ? -ETIMEDOUT : -ENODEV;
+
+	data->touchscreen.compatible = "zeitec,zet6251";
+	data->touchscreen.model = zet6251;
+	return 0;
+}
+
+static int q8_hardwaremgr_probe_touchscreen(struct q8_hardwaremgr_data *data,
+					    struct i2c_adapter *adap)
+{
+	msleep(TOUCHSCREEN_POWER_ON_DELAY);
+
+	PROBE_CLIENT(&data->touchscreen, 0x40, q8_hardwaremgr_probe_silead);
+	PROBE_CLIENT(&data->touchscreen, 0x15, q8_hardwaremgr_probe_ektf2127);
+	PROBE_CLIENT(&data->touchscreen, 0x76, q8_hardwaremgr_probe_zet6251);
+
+	return -ENODEV;
+}
+
+static void q8_hardwaremgr_apply_gsl1680_a082_variant(
+	struct q8_hardwaremgr_data *data)
+{
+	if (touchscreen_variant != -1)
+		data->touchscreen_variant = touchscreen_variant;
+
+	switch (data->touchscreen_variant) {
+	default:
+		dev_warn(data->dev, "Error unknown touchscreen_variant %d using 0\n",
+			 touchscreen_variant);
+		/* Fall through */
+	case 0:
+		data->touchscreen_width = 1024;
+		data->touchscreen_height = 600;
+		data->touchscreen_fw_name = "gsl1680-a082-q8-700.fw";
+		break;
+	case 1:
+		data->touchscreen_width = 480;
+		data->touchscreen_height = 800;
+		data->touchscreen_swap_x_y = 1;
+		data->touchscreen_fw_name = "gsl1680-a082-q8-a70.fw";
+		break;
+	}
+}
+
+static void q8_hardwaremgr_apply_gsl1680_b482_variant(
+	struct q8_hardwaremgr_data *data)
+{
+	if (touchscreen_variant != -1)
+		data->touchscreen_variant = touchscreen_variant;
+
+	switch (data->touchscreen_variant) {
+	default:
+		dev_warn(data->dev, "Error unknown touchscreen_variant %d using 0\n",
+			 touchscreen_variant);
+		/* Fall through */
+	case 0:
+		data->touchscreen_width = 960;
+		data->touchscreen_height = 640;
+		data->touchscreen_fw_name = "gsl1680-b482-q8-d702.fw";
+		break;
+	case 1:
+		data->touchscreen_width = 960;
+		data->touchscreen_height = 640;
+		data->touchscreen_fw_name = "gsl1680-b482-q8-a70.fw";
+		break;
+	}
+}
+
+static void q8_hardwaremgr_issue_gsl1680_warning(
+	struct q8_hardwaremgr_data *data)
+{
+	dev_warn(data->dev, "gsl1680 touchscreen may require kernel cmdline parameters to function properly\n");
+	dev_warn(data->dev, "Try q8_hardwaremgr.touchscreen_invert_x=%d if x coordinates are inverted\n",
+		 !data->touchscreen_invert_x);
+	dev_warn(data->dev, "Try q8_hardwaremgr.touchscreen_variant=%d if coordinates are all over the place\n",
+		 !data->touchscreen_variant);
+
+#define	show(x) \
+	dev_info(data->dev, #x " %d (%s)\n", data->x, \
+		 (x == -1) ? "auto" : "user supplied")
+
+	show(touchscreen_variant);
+	show(touchscreen_width);
+	show(touchscreen_height);
+	show(touchscreen_invert_x);
+	show(touchscreen_invert_y);
+	show(touchscreen_swap_x_y);
+	dev_info(data->dev, "touchscreen_fw_name %s (%s)\n",
+		 data->touchscreen_fw_name,
+		 (touchscreen_fw_name == NULL) ? "auto" : "user supplied");
+#undef show
+}
+
+static void q8_hardwaremgr_apply_touchscreen(struct q8_hardwaremgr_data *data)
+{
+	struct of_changeset cset;
+	struct device_node *np;
+
+	switch (data->touchscreen.model) {
+	case touchscreen_unknown:
+		return;
+	case gsl1680_a082:
+		q8_hardwaremgr_apply_gsl1680_a082_variant(data);
+		break;
+	case gsl1680_b482:
+		q8_hardwaremgr_apply_gsl1680_b482_variant(data);
+		break;
+	case ektf2127:
+	case zet6251:
+		/* These have only 1 variant */
+		break;
+	}
+
+	if (touchscreen_width != -1)
+		data->touchscreen_width = touchscreen_width;
+
+	if (touchscreen_height != -1)
+		data->touchscreen_height = touchscreen_height;
+
+	if (touchscreen_invert_x != -1)
+		data->touchscreen_invert_x = touchscreen_invert_x;
+
+	if (touchscreen_invert_y != -1)
+		data->touchscreen_invert_y = touchscreen_invert_y;
+
+	if (touchscreen_swap_x_y != -1)
+		data->touchscreen_swap_x_y = touchscreen_swap_x_y;
+
+	if (touchscreen_fw_name)
+		data->touchscreen_fw_name = touchscreen_fw_name;
+
+	if (data->touchscreen.model == gsl1680_a082 ||
+	    data->touchscreen.model == gsl1680_b482)
+		q8_hardwaremgr_issue_gsl1680_warning(data);
+
+	np = q8_hardware_mgr_apply_common(&data->touchscreen, &cset,
+					  "touchscreen");
+	if (!np)
+		return;
+
+	if (data->touchscreen_width)
+		of_changeset_add_property_u32(&cset, np, "touchscreen-size-x",
+					      data->touchscreen_width);
+	if (data->touchscreen_height)
+		of_changeset_add_property_u32(&cset, np, "touchscreen-size-y",
+					      data->touchscreen_height);
+	if (data->touchscreen_invert_x)
+		of_changeset_add_property_bool(&cset, np,
+					       "touchscreen-inverted-x");
+	if (data->touchscreen_invert_y)
+		of_changeset_add_property_bool(&cset, np,
+					       "touchscreen-inverted-y");
+	if (data->touchscreen_swap_x_y)
+		of_changeset_add_property_bool(&cset, np,
+					       "touchscreen-swapped-x-y");
+	if (data->touchscreen_fw_name)
+		of_changeset_add_property_string(&cset, np, "firmware-name",
+						 data->touchscreen_fw_name);
+
+	of_changeset_apply(&cset);
+
+	of_node_put(np);
+}
+
+static int q8_hardwaremgr_do_probe(struct q8_hardwaremgr_data *data,
+				   struct q8_hardwaremgr_device *dev,
+				   const char *prefix, bus_probe_func func)
+{
+	struct device_node *np;
+	struct i2c_adapter *adap;
+	struct regulator *reg;
+	struct gpio_desc *gpio;
+	int ret = 0;
+
+	np = of_find_node_by_name(of_root, prefix);
+	if (!np) {
+		dev_err(data->dev, "Error %s node is missing\n", prefix);
+		return -EINVAL;
+	}
+
+	/*
+	 * Patch the dt_node into our device since there is no device for
+	 * the probed hw yet (status = disabled) .
+	 */
+	data->dev->of_node = np;
+
+	ret = pinctrl_bind_pins(data->dev);
+	if (ret)
+		goto put_node;
+
+	adap = of_get_i2c_adapter_by_node(np->parent);
+	if (!adap) {
+		ret = -EPROBE_DEFER;
+		goto put_pins;
+	}
+
+	reg = regulator_get_optional(data->dev, "vddio");
+	if (IS_ERR(reg)) {
+		ret = PTR_ERR(reg);
+		if (ret == -EPROBE_DEFER)
+			goto put_adapter;
+		reg = NULL;
+	}
+
+	gpio = fwnode_get_named_gpiod(&np->fwnode, "power-gpios");
+	if (IS_ERR(gpio)) {
+		ret = PTR_ERR(gpio);
+		if (ret == -EPROBE_DEFER)
+			goto put_reg;
+		gpio = NULL;
+	}
+
+	/* First try with only the power gpio driven high */
+	if (gpio) {
+		ret = gpiod_direction_output(gpio, 1);
+		if (ret)
+			goto put_gpio;
+	}
+
+	dev_info(data->dev, "Probing %s without a regulator\n", prefix);
+	ret = func(data, adap);
+	if (ret != 0 && reg) {
+		/* Second try, also enable the regulator */
+		ret = regulator_enable(reg);
+		if (ret)
+			goto restore_gpio;
+
+		dev_info(data->dev, "Probing %s with a regulator\n", prefix);
+		ret = func(data, adap);
+
+		regulator_disable(reg);
+	} else if (reg)
+		dev->delete_regulator = true; /* Regulator not needed */
+
+	if (ret == 0)
+		dev_info(data->dev, "Found %s at 0x%02x\n",
+			 dev->compatible, dev->addr);
+	else
+		ret = 0; /* Not finding a device is not an error */
+
+restore_gpio:
+	if (gpio)
+		gpiod_direction_output(gpio, 0);
+put_gpio:
+	if (gpio)
+		gpiod_put(gpio);
+put_reg:
+	if (reg)
+		regulator_put(reg);
+put_adapter:
+	i2c_put_adapter(adap);
+
+put_pins:
+	/* Undo our manual pinctrl_bind_pins() */
+	if (data->dev->pins) {
+		devm_pinctrl_put(data->dev->pins->p);
+		devm_kfree(data->dev, data->dev->pins);
+		data->dev->pins = NULL;
+	}
+
+put_node:
+	data->dev->of_node = NULL;
+	of_node_put(np);
+
+	return ret;
+}
+
+static int q8_hardwaremgr_probe(struct platform_device *pdev)
+{
+	struct q8_hardwaremgr_data *data;
+	int ret = 0;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->dev = &pdev->dev;
+	data->soc = (long)pdev->dev.platform_data;
+
+	ret = q8_hardwaremgr_do_probe(data, &data->touchscreen, "touchscreen",
+				      q8_hardwaremgr_probe_touchscreen);
+	if (ret)
+		goto error;
+
+	q8_hardwaremgr_apply_touchscreen(data);
+
+error:
+	kfree(data);
+
+	return ret;
+}
+
+static struct platform_driver q8_hardwaremgr_driver = {
+	.driver = {
+		.name	= "q8-hwmgr",
+	},
+	.probe	= q8_hardwaremgr_probe,
+};
+
+static int __init q8_hardwaremgr_init(void)
+{
+	struct platform_device *pdev;
+	enum soc soc;
+	int ret;
+
+	if (of_machine_is_compatible("allwinner,q8-a13"))
+		soc = a13;
+	else if (of_machine_is_compatible("allwinner,q8-a23"))
+		soc = a23;
+	else if (of_machine_is_compatible("allwinner,q8-a33"))
+		soc = a33;
+	else
+		return 0;
+
+	pdev = platform_device_alloc("q8-hwmgr", 0);
+	if (!pdev)
+		return -ENOMEM;
+
+	pdev->dev.platform_data = (void *)(long)soc;
+
+	ret = platform_device_add(pdev);
+	if (ret)
+		return ret;
+
+	return platform_driver_register(&q8_hardwaremgr_driver);
+}
+
+device_initcall(q8_hardwaremgr_init);
+
+MODULE_DESCRIPTION("Allwinner q8 formfactor tablet hardware manager");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
-- 
2.9.3

^ permalink raw reply related

* [PATCH 1/6] dt: bindings: Add Allwinner Q8 tablets hardware manager bindings
From: Hans de Goede @ 2016-10-14  7:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161014075337.10452-1-hdegoede@redhat.com>

Allwinnner A13 / A23 / A33 based Q8 tablets are popular cheap 7" tablets
of which a new batch is produced every few weeks. Each batch uses a
different mix of touchscreen, accelerometer and wifi peripherals.

Given that each batch is different creating a devicetree for each variant
is not desirable, work is being done on a Q8 tablet hardware manager which
auto-detects the touchscreen and accelerometer and will update the dt with
what it has found, so that a single generic dt can be used for these tablets.

This commit adds dt-bindings for this hardware manager.

Note the wifi is connected to a discoverable bus (sdio or usb) and will be
autodetected by the mmc resp. usb subsystems.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 .../misc/allwinner,sunxi-q8-hardwaremgr.txt        | 71 ++++++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/misc/allwinner,sunxi-q8-hardwaremgr.txt

diff --git a/Documentation/devicetree/bindings/misc/allwinner,sunxi-q8-hardwaremgr.txt b/Documentation/devicetree/bindings/misc/allwinner,sunxi-q8-hardwaremgr.txt
new file mode 100644
index 0000000..a81b258
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/allwinner,sunxi-q8-hardwaremgr.txt
@@ -0,0 +1,71 @@
+Q8 tablet hardware manager
+--------------------------
+
+Allwinnner A13 / A23 / A33 based Q8 tablets are popular cheap 7" tablets of
+which a new batch is produced every few weeks. Each batch uses a different
+mix of touchscreen, accelerometer and wifi peripherals.
+
+Given that each batch is different creating a devicetree for each variant is
+not desirable. The Q8 tablet hardware manager bindings are bindings for an os
+module which auto-detects the touchscreen and accelerometer so that a single
+generic dts can be used for these tablets.
+
+The wifi is connected to a discoverable bus and will be autodetected by the os.
+
+Required properties:
+ - toplevel / machine compatible, one of:
+	"allwinner,q8-a13"
+	"allwinner,q8-a23"
+	"allwinner,q8-a33"
+ - touchscreen node   : There must be a template touchscreen node named
+			"touchscreen", this must be a child node of the
+			touchscreen i2c bus
+ - accelerometer node : There must be a template accelerometer node named
+			"accelerometer", this must be a child node of the
+			accelerometer i2c bus
+
+touchscreen node required properties:
+ - interrupt-parent   : phandle pointing to the interrupt controller
+			serving the touchscreen interrupt
+ - interrupts         : interrupt specification for the touchscreen interrupt
+ - power-gpios        : Specification for the pin connected to the touchscreen's
+			enable / wake pin. This needs to be driven high to
+			enable the touchscreen controller
+
+touchscreen node optional properties:
+ - vddio-supply       : regulator phandle for the touchscreen vddio supply
+
+accelerometer node optional properties:
+ - interrupt-parent   : phandle pointing to the interrupt controller
+			serving the accelerometer interrupt
+ - interrupts         : interrupt specification for the accelerometer interrupt
+
+Example:
+
+/ {
+	compatible = "allwinner,q8-a23", "allwinner,sun8i-a23";
+};
+
+&i2c0 {
+	touchscreen: touchscreen at 0 {
+		interrupt-parent = <&pio>;
+		interrupts = <1 5 IRQ_TYPE_EDGE_FALLING>; /* PB5 */
+		power-gpios = <&pio 7 1 GPIO_ACTIVE_HIGH>; /* PH1 */
+		vddio-supply = <&reg_ldo_io1>;
+		/*
+		 * Enabled by sunxi-q8-hardwaremgr if it detects a
+		 * known model touchscreen.
+		 */
+		status = "disabled";
+	};
+};
+
+&i2c1 {
+	accelerometer: accelerometer at 0 {
+		/*
+		 * Enabled by sunxi-q8-hardwaremgr if it detects a
+		 * known model accelerometer.
+		 */
+		status = "disabled";
+	};
+};
-- 
2.9.3

^ permalink raw reply related


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