Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [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

* Add Allwinner Q8 tablets hardware manager
From: Hans de Goede @ 2016-10-14  7:53 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Rob, Mark, et al.,

Here is a first non RFC posting of my q8 tablet hardwaremanager work,
both the bindings as well as the in kernel implementation of it.

Mark, I know that we discussed this at ELCE and you clearly indicated
that according to you this does not belong in the kernel. I was a bit
surprised by this part of the discussion.

I had posted a RFC earlier and Rob had indicated that given that the q8
tablets are a special case, as my code uses actual probing rather then some
pre-arranged id mechanism with say an eeprom, that doing this in a
non-generic manner would be ok for my special case.

So at ELCE I did not have all the arguments I had to do this in the kernel
ready. I did actually make a concious choice for the kernel before starting
work on this, thinking about this more I remembered most of the arguments I
had when making my u-boot vs kernel decision. I still believe the kernel is
the right place for this. See the list of reasons below.

Regardless of where the implementation should go, please do review the
bindings (the first patch in the set), as we need those regardless of where
the actual code dealing with this is going to live.

So on to why I believe that the kernel is the best place to do this, at least
for my special use-case:

1. Configurability

Since the q8 tablets do not have any id mechanism I'm probing i2c busses to
generate i2c client dt nodes with the right address and compatible.
Some info in these nodes (e.g. touchscreen firmware-name) is not probable at
all and gets set by heuristics. This heuristics may get things wrong.
So my current implementation offers kernel cmdline options to override this.

Although having to specify kernel cmdline options is not the plug and play
experience I want to give end-users most distros do have documentation on
how to do this and doing this is relatively easy for end-users. Moving this
to the bootloader means moving to some bootloader specific config mechanism
which is going to be quite hard (and possibly dangerous) for users to use.

This alone to me is the reason to post the kernel implentation of my work
for upstream inclusion despite of Mark's objections.

2. Upgradability

Most users treat the bootloader like they treat an x86 machine BIOS/EFI,
they will never upgrade it unless they really have to. This means that it
will be very difficult to get users to actual benefit from bug-fixes /
improvements done to the probing code. Where as the kernel on boards running
e.g. Debian or Fedora gets regular updates together with the rest of the
system.

3. Infrastructure

The kernel simply has better infrastructure for doing these kind of things.
Yes we could improve the bootloader, but the kernel is also improving and
likely at a faster rate. Besides that the purpose of the bootloader is
mostly to be simple and small, load the kernel and get out of the way, not
to be a general purpose os kernel. So it will simply always have less
infrastructure and that is a good thing, otherwise we will be writing
another general purpose os which is a waste of effort.

4. This is not a new board file

Mark, at ELCE I got the feeling that your biggest worry / objection is
that this will bring back board files, but that is not the case, if you
look at the actual code it is nothing like a board-file at all. Where a
board file was instantiating device after device after device from c-code,
with maybe a few if-s in there to deal with board revisions. This code is
all about figuring out which accelerometer and touchscreen there are,
to then add nodes to the dt for this 2 devices, almost all the code is
probing, the actual dt modifying code is tiny and no direct device
instantiation is done at all.

5. This is an exception, not the rule

Yes this is introducing board (family of boards) specific c-code into the
kernel, so in a way it is reminiscent of board files. But sometimes this is
necessary, just look at all the vendor / platform specific code in the kernel
in drivers/platform/x86, or all the places where DMI strings are used to
uniquely identify a x86 board and adjust behavior.

But this really is the exception, not the rule. I've written/upstreamed a
number of drivers for q8 tablets hardware and if you look at e.g. the
silead touchscreen driver then in linux-next this is already used for 5
ARM dts files where no board specific C-code is involved at all.

So this again is very different from the board file era, where C-code
had to be used to fill device specific platform-data structs, here all
necessary info is contained in the dt-binding and there are many users
who do not need any board specific C-code in the kernel at all.

So dt is working as it should and is avoiding board specific C-code for
the majority of the cases. But sometimes hardware is not as we ideally
would like it to be; and for those *exceptions* we are sometimes going
to need C-code in the kernel, just like there is "board" specific C-code
in the x86 code.

Regards,

Hans

p.s.

Note that the patches actually implementing the q8 hardware manager depend
on Pantelis' "of: changesets" patches which are not upstream yet, so if we
can agree on doing this in the kernel then this cannot be merged until those
are merged. If we get a concensus on doing this in kernel being ok, then
I'll post a new version of those patches unless Pantelis' beats me to it.

^ permalink raw reply

* Low network throughput on i.MX28
From: Lothar Waßmann @ 2016-10-14  6:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <A57BCA39-0977-43C4-B22D-ED60F5E4B06D@embedded.rocks>

Hi,

On Thu, 13 Oct 2016 21:43:00 +0200 J?rg Krause wrote:
> Am 13. Oktober 2016 08:48:07 MESZ, schrieb "Lothar Wa?mann" <LW@KARO-electronics.de>:
> >On Thu, 13 Oct 2016 01:09:13 +0200 J?rg Krause wrote:
[...]
> >This is the iperf output on a TX28 with current mainline kernel
> >(4.8.0-rc5):
> >------------------------------------------------------------
> >Client connecting to 192.168.100.1, TCP port 5001
> >TCP window size: 43.8 KByte (default)
> >------------------------------------------------------------
> >[  3] local 192.168.100.56 port 60325 connected with 192.168.100.1 port
> >5001
> >[ ID] Interval       Transfer     Bandwidth
> >[  3]  0.0-10.0 sec  57.5 MBytes  48.2 Mbits/sec
> >
> >You might check your kernel DEBUG configs (especially
> >CONFIG_DEBUG_PAGEALLOC).
> 
> Thanks for sharing the iperf output. What LAN transceiver does the TX28 has assembled?
> 
The ethernet PHY is an SMSC LAN8710A.


Lothar Wa?mann

^ permalink raw reply

* [PATCH 2/3] arm64: hw_breakpoint: Handle inexact watchpoint addresses
From: Pratyush Anand @ 2016-10-14  3:15 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAJt8pk8OOTbuNhN635kRCot89eLjXPQqd2AG2qJ_t7GComtZCw@mail.gmail.com>



On Thursday 13 October 2016 10:33 PM, Pavel Labath wrote:
>> I think, its easier to go with your implementation. So, I have taken
>> > your patch and updated my perf/upstream_arm64_devel branch. May be you
>> > can give it a test for your test cases.
> I've checked out the new version of your branch, and it works great.
> I'll write a patch with additional test cases to go on top of your
> branch, as the tests there do not capture the bug I was fixing.

That would be great. We can send them all together as V2.

Thanks for your help.

~Pratyush

^ permalink raw reply

* [PATCH v8 8/8] ARM: dts: imx6q-evi: Fix onboard hub reset line
From: Peter Chen @ 2016-10-14  2:59 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476413995-20361-1-git-send-email-peter.chen@nxp.com>

From: Joshua Clayton <stillcompiling@gmail.com>

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

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

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

^ permalink raw reply related

* [PATCH v8 7/8] ARM: dts: imx6qdl-udoo.dtsi: fix onboard USB HUB property
From: Peter Chen @ 2016-10-14  2:59 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476413995-20361-1-git-send-email-peter.chen@nxp.com>

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

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

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

^ permalink raw reply related

* [PATCH v8 6/8] ARM: dts: imx6qdl: Enable usb node children with <reg>
From: Peter Chen @ 2016-10-14  2:59 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476413995-20361-1-git-send-email-peter.chen@nxp.com>

From: Joshua Clayton <stillcompiling@gmail.com>

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

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

diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index b13b0b2..8fc66e1 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -935,6 +935,8 @@
 
 			usbh1: usb at 02184200 {
 				compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
+				#address-cells = <1>;
+				#size-cells = <0>;
 				reg = <0x02184200 0x200>;
 				interrupts = <0 40 IRQ_TYPE_LEVEL_HIGH>;
 				clocks = <&clks IMX6QDL_CLK_USBOH3>;
@@ -949,6 +951,8 @@
 
 			usbh2: usb at 02184400 {
 				compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
+				#address-cells = <1>;
+				#size-cells = <0>;
 				reg = <0x02184400 0x200>;
 				interrupts = <0 41 IRQ_TYPE_LEVEL_HIGH>;
 				clocks = <&clks IMX6QDL_CLK_USBOH3>;
@@ -962,6 +966,8 @@
 
 			usbh3: usb at 02184600 {
 				compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
+				#address-cells = <1>;
+				#size-cells = <0>;
 				reg = <0x02184600 0x200>;
 				interrupts = <0 42 IRQ_TYPE_LEVEL_HIGH>;
 				clocks = <&clks IMX6QDL_CLK_USBOH3>;
-- 
2.7.4

^ permalink raw reply related

* [PATCH v8 5/8] usb: chipidea: let chipidea core device of_node equal's glue layer device of_node
From: Peter Chen @ 2016-10-14  2:59 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1476413995-20361-1-git-send-email-peter.chen@nxp.com>

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

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

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

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

^ permalink raw reply related


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