Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 06/10] ARM64: meson: enable MESON_IRQ_GPIO in Kconfig
From: Jerome Brunet @ 2016-10-04 15:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475593708-10526-1-git-send-email-jbrunet@baylibre.com>

Add select MESON_IRQ_GPIO in Kconfig for Amlogic's meson SoC family

Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 arch/arm64/Kconfig.platforms | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index cfbdf02ef566..846479d4492d 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -95,6 +95,7 @@ config ARCH_MESON
 	select PINCTRL_MESON
 	select COMMON_CLK_AMLOGIC
 	select COMMON_CLK_GXBB
+	select MESON_GPIO_IRQ
 	help
 	  This enables support for the Amlogic S905 SoCs.
 
-- 
2.7.4

^ permalink raw reply related

* [RFC 05/10] dt-bindings: pinctrl: meson: update gpio dt-bindings
From: Jerome Brunet @ 2016-10-04 15:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475593708-10526-1-git-send-email-jbrunet@baylibre.com>

Add description for the interrupt-parent property of the gpio sub-node
If provided here, this property must be a phandle to an interrupt
controller suitable for meson pinctrl, like the meson gpio interrupt
controller.

Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 Documentation/devicetree/bindings/pinctrl/meson,pinctrl.txt | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/Documentation/devicetree/bindings/pinctrl/meson,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/meson,pinctrl.txt
index fe7fe0b03cfb..39932c4dfb32 100644
--- a/Documentation/devicetree/bindings/pinctrl/meson,pinctrl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/meson,pinctrl.txt
@@ -23,6 +23,10 @@ Required properties for sub-nodes are:
  - gpio-controller: identifies the node as a gpio controller
  - #gpio-cells: must be 2
 
+Optional property for sub-nodes is:
+ - interrupt-parent: must be a phandle to the meson gpio interrupt controller.
+   if this property is provided, enables gpio ability to generate interrupts
+
 === Other sub-nodes ===
 
 Child nodes without the "gpio-controller" represent some desired
-- 
2.7.4

^ permalink raw reply related

* [RFC 04/10] pinctrl: meson: allow gpio to request irq
From: Jerome Brunet @ 2016-10-04 15:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475593708-10526-1-git-send-email-jbrunet@baylibre.com>

Add the ability for gpio to request irq from the gpio interrupt controller
if present. We have to specificaly that the parent interrupt controller is
the gpio interrupt controller because gpio on meson SoCs can't generate
interrupt directly on the GIC.

Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/pinctrl/Kconfig               |  2 +
 drivers/pinctrl/meson/pinctrl-meson.c | 83 ++++++++++++++++++++++++++++++++++-
 drivers/pinctrl/meson/pinctrl-meson.h |  1 +
 3 files changed, 85 insertions(+), 1 deletion(-)

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 0e75d94972ba..d5bfbfcddab0 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -126,7 +126,9 @@ config PINCTRL_MESON
 	select PINCONF
 	select GENERIC_PINCONF
 	select GPIOLIB
+	select IRQ_DOMAIN
 	select OF_GPIO
+	select OF_IRQ
 	select REGMAP_MMIO
 
 config PINCTRL_OXNAS
diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c
index 57122eda155a..f9be3ff5e11d 100644
--- a/drivers/pinctrl/meson/pinctrl-meson.c
+++ b/drivers/pinctrl/meson/pinctrl-meson.c
@@ -50,6 +50,7 @@
 #include <linux/io.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_irq.h>
 #include <linux/pinctrl/pinconf-generic.h>
 #include <linux/pinctrl/pinconf.h>
 #include <linux/pinctrl/pinctrl.h>
@@ -481,6 +482,58 @@ static void meson_gpio_set(struct gpio_chip *chip, unsigned gpio, int value)
 			   value ? BIT(bit) : 0);
 }
 
+static int meson_gpio_to_hwirq(struct meson_bank *bank, unsigned int offset)
+{
+	unsigned int hwirq;
+
+	if (bank->irq_first < 0)
+		/* this bank cannot generate irqs */
+		return -1;
+
+	hwirq = offset - bank->first + bank->irq_first;
+
+	if (hwirq > bank->irq_last)
+		/* this pin cannot generate irqs */
+		return -1;
+
+	return hwirq;
+}
+
+static int meson_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
+{
+	struct meson_pinctrl *pc = gpiochip_get_data(chip);
+	struct meson_bank *bank;
+	struct irq_fwspec fwspec;
+	unsigned int hwirq;
+	int ret;
+
+	ret = meson_get_bank(pc, offset, &bank);
+	if (ret)
+		return ret;
+
+	/*
+	 * The interrupt controller might be missing, in such case we can't
+	 * provide an interrupt for a pin
+	 */
+	if (is_fwnode_irqchip(pc->fwnode)) {
+		dev_info(pc->dev, "interrupt controller not found\n");
+		return 0;
+	}
+
+	hwirq = meson_gpio_to_hwirq(bank, offset);
+	if (hwirq < 0) {
+		dev_dbg(pc->dev, "no interrupt for pin %u\n", offset);
+		return 0;
+	}
+
+	fwspec.fwnode = pc->fwnode;
+	fwspec.param_count = 2;
+	fwspec.param[0] = hwirq;
+	fwspec.param[1] = IRQ_TYPE_NONE;
+
+	return irq_create_fwspec_mapping(&fwspec);
+}
+
 static int meson_gpio_get(struct gpio_chip *chip, unsigned gpio)
 {
 	struct meson_pinctrl *pc = gpiochip_get_data(chip);
@@ -539,6 +592,7 @@ static int meson_gpiolib_register(struct meson_pinctrl *pc)
 	pc->chip.direction_output = meson_gpio_direction_output;
 	pc->chip.get = meson_gpio_get;
 	pc->chip.set = meson_gpio_set;
+	pc->chip.to_irq = meson_gpio_to_irq;
 	pc->chip.base = pc->data->pin_base;
 	pc->chip.ngpio = pc->data->num_pins;
 	pc->chip.can_sleep = false;
@@ -598,6 +652,33 @@ static struct regmap *meson_map_resource(struct meson_pinctrl *pc,
 	return devm_regmap_init_mmio(pc->dev, base, &meson_regmap_config);
 }
 
+static int meson_pinctrl_get_irq_gpio_intc(struct meson_pinctrl *pc,
+					   struct device_node *node)
+{
+	struct device_node *np;
+
+	/*
+	 * Make sure gpio don't request IRQ directly to the GIC
+	 * TODO: Find a better way to make sure we have a compatible
+	 *       Interrupt controller here
+	 */
+	if (!of_get_property(node, "interrupt-parent", NULL)) {
+		dev_dbg(pc->dev, "interrupt controller not found\n");
+		pc->fwnode = NULL;
+		return 0;
+	}
+
+	np = of_irq_find_parent(node);
+	if (!np) {
+		dev_err(pc->dev, "irq parent not found\n");
+		return -EINVAL;
+	}
+
+	pc->fwnode = of_node_to_fwnode(np);
+
+	return 0;
+}
+
 static int meson_pinctrl_parse_dt(struct meson_pinctrl *pc,
 				  struct device_node *node)
 {
@@ -643,7 +724,7 @@ static int meson_pinctrl_parse_dt(struct meson_pinctrl *pc,
 		return PTR_ERR(pc->reg_gpio);
 	}
 
-	return 0;
+	return meson_pinctrl_get_irq_gpio_intc(pc, gpio_np);
 }
 
 static int meson_pinctrl_probe(struct platform_device *pdev)
diff --git a/drivers/pinctrl/meson/pinctrl-meson.h b/drivers/pinctrl/meson/pinctrl-meson.h
index 785705996e60..4bcc4900b3eb 100644
--- a/drivers/pinctrl/meson/pinctrl-meson.h
+++ b/drivers/pinctrl/meson/pinctrl-meson.h
@@ -122,6 +122,7 @@ struct meson_pinctrl {
 	struct regmap *reg_gpio;
 	struct gpio_chip chip;
 	struct device_node *of_node;
+	struct fwnode_handle *fwnode;
 };
 
 #define PIN(x, b)	(b + x)
-- 
2.7.4

^ permalink raw reply related

* [RFC 03/10] pinctrl: meson: update pinctrl data with gpio irq base number
From: Jerome Brunet @ 2016-10-04 15:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475593708-10526-1-git-send-email-jbrunet@baylibre.com>

This patch extends meson's pinctrl SoC specific data by adding the gpio
irq base number. This will allow gpios to request an interrupt on the
gpio interrupt controller using this base irq number the pin offset in
bank

Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/pinctrl/meson/pinctrl-meson-gxbb.c | 22 ++++++++++----------
 drivers/pinctrl/meson/pinctrl-meson.h      | 15 +++++++++-----
 drivers/pinctrl/meson/pinctrl-meson8.c     | 20 +++++++++----------
 drivers/pinctrl/meson/pinctrl-meson8b.c    | 32 ++++++++++++++++++++----------
 4 files changed, 53 insertions(+), 36 deletions(-)

diff --git a/drivers/pinctrl/meson/pinctrl-meson-gxbb.c b/drivers/pinctrl/meson/pinctrl-meson-gxbb.c
index c3928aa3fefa..29b66684b287 100644
--- a/drivers/pinctrl/meson/pinctrl-meson-gxbb.c
+++ b/drivers/pinctrl/meson/pinctrl-meson-gxbb.c
@@ -715,20 +715,20 @@ static struct meson_pmx_func meson_gxbb_aobus_functions[] = {
 };
 
 static struct meson_bank meson_gxbb_periphs_banks[] = {
-	/*   name    first                      last                    pullen  pull    dir     out     in  */
-	BANK("X",    PIN(GPIOX_0, EE_OFF),	PIN(GPIOX_22, EE_OFF),  4,  0,  4,  0,  12, 0,  13, 0,  14, 0),
-	BANK("Y",    PIN(GPIOY_0, EE_OFF),	PIN(GPIOY_16, EE_OFF),  1,  0,  1,  0,  3,  0,  4,  0,  5,  0),
-	BANK("DV",   PIN(GPIODV_0, EE_OFF),	PIN(GPIODV_29, EE_OFF), 0,  0,  0,  0,  0,  0,  1,  0,  2,  0),
-	BANK("H",    PIN(GPIOH_0, EE_OFF),	PIN(GPIOH_3, EE_OFF),   1, 20,  1, 20,  3, 20,  4, 20,  5, 20),
-	BANK("Z",    PIN(GPIOZ_0, EE_OFF),	PIN(GPIOZ_15, EE_OFF),  3,  0,  3,  0,  9,  0,  10, 0, 11,  0),
-	BANK("CARD", PIN(CARD_0, EE_OFF),	PIN(CARD_6, EE_OFF),    2, 20,  2, 20,  6, 20,  7, 20,  8, 20),
-	BANK("BOOT", PIN(BOOT_0, EE_OFF),	PIN(BOOT_17, EE_OFF),   2,  0,  2,  0,  6,  0,  7,  0,  8,  0),
-	BANK("CLK",  PIN(GPIOCLK_0, EE_OFF),	PIN(GPIOCLK_3, EE_OFF), 3, 28,  3, 28,  9, 28, 10, 28, 11, 28),
+	/*   name    first                      last                    irq       pullen  pull    dir     out     in  */
+	BANK("X",    PIN(GPIOX_0, EE_OFF),	PIN(GPIOX_22, EE_OFF),  106, 128, 4,  0,  4,  0,  12, 0,  13, 0,  14, 0),
+	BANK("Y",    PIN(GPIOY_0, EE_OFF),	PIN(GPIOY_16, EE_OFF),   89, 105, 1,  0,  1,  0,  3,  0,  4,  0,  5,  0),
+	BANK("DV",   PIN(GPIODV_0, EE_OFF),	PIN(GPIODV_29, EE_OFF),  59,  88, 0,  0,  0,  0,  0,  0,  1,  0,  2,  0),
+	BANK("H",    PIN(GPIOH_0, EE_OFF),	PIN(GPIOH_3, EE_OFF),    30,  33, 1, 20,  1, 20,  3, 20,  4, 20,  5, 20),
+	BANK("Z",    PIN(GPIOZ_0, EE_OFF),	PIN(GPIOZ_15, EE_OFF),   14,  29, 3,  0,  3,  0,  9,  0,  10, 0, 11,  0),
+	BANK("CARD", PIN(CARD_0, EE_OFF),	PIN(CARD_6, EE_OFF),     52,  58, 2, 20,  2, 20,  6, 20,  7, 20,  8, 20),
+	BANK("BOOT", PIN(BOOT_0, EE_OFF),	PIN(BOOT_17, EE_OFF),    34,  51, 2,  0,  2,  0,  6,  0,  7,  0,  8,  0),
+	BANK("CLK",  PIN(GPIOCLK_0, EE_OFF),	PIN(GPIOCLK_3, EE_OFF), 129, 132, 3, 28,  3, 28,  9, 28, 10, 28, 11, 28),
 };
 
 static struct meson_bank meson_gxbb_aobus_banks[] = {
-	/*   name    first              last               pullen  pull    dir     out     in  */
-	BANK("AO",   PIN(GPIOAO_0, 0),  PIN(GPIOAO_13, 0), 0,  0,  0, 16,  0,  0,  0, 16,  1,  0),
+	/*   name    first              last               irq    pullen  pull    dir     out     in  */
+	BANK("AO",   PIN(GPIOAO_0, 0),  PIN(GPIOAO_13, 0), 0, 13, 0,  0,  0, 16,  0,  0,  0, 16,  1,  0),
 };
 
 struct meson_pinctrl_data meson_gxbb_periphs_pinctrl_data = {
diff --git a/drivers/pinctrl/meson/pinctrl-meson.h b/drivers/pinctrl/meson/pinctrl-meson.h
index 98b5080650c1..785705996e60 100644
--- a/drivers/pinctrl/meson/pinctrl-meson.h
+++ b/drivers/pinctrl/meson/pinctrl-meson.h
@@ -81,6 +81,7 @@ enum meson_reg_type {
  * @name:	bank name
  * @first:	first pin of the bank
  * @last:	last pin of the bank
+ * @irq:	hwirq base number of the bank
  * @regs:	array of register descriptors
  *
  * A bank represents a set of pins controlled by a contiguous set of
@@ -92,6 +93,8 @@ struct meson_bank {
 	const char *name;
 	unsigned int first;
 	unsigned int last;
+	int irq_first;
+	int irq_last;
 	struct meson_reg_desc regs[NUM_REG];
 };
 
@@ -147,12 +150,14 @@ struct meson_pinctrl {
 		.num_groups = ARRAY_SIZE(fn ## _groups),		\
 	}
 
-#define BANK(n, f, l, per, peb, pr, pb, dr, db, or, ob, ir, ib)		\
+#define BANK(n, f, l, fi, li, per, peb, pr, pb, dr, db, or, ob, ir, ib)	\
 	{								\
-		.name	= n,						\
-		.first	= f,						\
-		.last	= l,						\
-		.regs	= {						\
+		.name		= n,					\
+		.first		= f,					\
+		.last		= l,					\
+		.irq_first	= fi,					\
+		.irq_last	= li,					\
+		.regs = {						\
 			[REG_PULLEN]	= { per, peb },			\
 			[REG_PULL]	= { pr, pb },			\
 			[REG_DIR]	= { dr, db },			\
diff --git a/drivers/pinctrl/meson/pinctrl-meson8.c b/drivers/pinctrl/meson/pinctrl-meson8.c
index 07f1cb21c1b8..32449820f455 100644
--- a/drivers/pinctrl/meson/pinctrl-meson8.c
+++ b/drivers/pinctrl/meson/pinctrl-meson8.c
@@ -916,19 +916,19 @@ static struct meson_pmx_func meson8_aobus_functions[] = {
 };
 
 static struct meson_bank meson8_cbus_banks[] = {
-	/*   name    first             last                 pullen  pull    dir     out     in  */
-	BANK("X",    PIN(GPIOX_0, 0),  PIN(GPIOX_21, 0),    4,  0,  4,  0,  0,  0,  1,  0,  2,  0),
-	BANK("Y",    PIN(GPIOY_0, 0),  PIN(GPIOY_16, 0),    3,  0,  3,  0,  3,  0,  4,  0,  5,  0),
-	BANK("DV",   PIN(GPIODV_0, 0), PIN(GPIODV_29, 0),   0,  0,  0,  0,  7,  0,  8,  0,  9,  0),
-	BANK("H",    PIN(GPIOH_0, 0),  PIN(GPIOH_9, 0),     1, 16,  1, 16,  9, 19, 10, 19, 11, 19),
-	BANK("Z",    PIN(GPIOZ_0, 0),  PIN(GPIOZ_14, 0),    1,  0,  1,  0,  3, 17,  4, 17,  5, 17),
-	BANK("CARD", PIN(CARD_0, 0),   PIN(CARD_6, 0),      2, 20,  2, 20,  0, 22,  1, 22,  2, 22),
-	BANK("BOOT", PIN(BOOT_0, 0),   PIN(BOOT_18, 0),     2,  0,  2,  0,  9,  0, 10,  0, 11,  0),
+	/*   name    first             last                 irq       pullen  pull    dir     out     in  */
+	BANK("X",    PIN(GPIOX_0, 0),  PIN(GPIOX_21, 0),    112, 133, 4,  0,  4,  0,  0,  0,  1,  0,  2,  0),
+	BANK("Y",    PIN(GPIOY_0, 0),  PIN(GPIOY_16, 0),    95,  111, 3,  0,  3,  0,  3,  0,  4,  0,  5,  0),
+	BANK("DV",   PIN(GPIODV_0, 0), PIN(GPIODV_29, 0),   65,   94, 0,  0,  0,  0,  7,  0,  8,  0,  9,  0),
+	BANK("H",    PIN(GPIOH_0, 0),  PIN(GPIOH_9, 0),     29,   38, 1, 16,  1, 16,  9, 19, 10, 19, 11, 19),
+	BANK("Z",    PIN(GPIOZ_0, 0),  PIN(GPIOZ_14, 0),    14,   28, 1,  0,  1,  0,  3, 17,  4, 17,  5, 17),
+	BANK("CARD", PIN(CARD_0, 0),   PIN(CARD_6, 0),      58,   64, 2, 20,  2, 20,  0, 22,  1, 22,  2, 22),
+	BANK("BOOT", PIN(BOOT_0, 0),   PIN(BOOT_18, 0),     39,   57, 2,  0,  2,  0,  9,  0, 10,  0, 11,  0),
 };
 
 static struct meson_bank meson8_aobus_banks[] = {
-	/*   name    first                  last                      pullen  pull    dir     out     in  */
-	BANK("AO",   PIN(GPIOAO_0, AO_OFF), PIN(GPIO_TEST_N, AO_OFF), 0,  0,  0, 16,  0,  0,  0, 16,  1,  0),
+	/*   name    first                  last                      irq    pullen  pull    dir     out     in  */
+	BANK("AO",   PIN(GPIOAO_0, AO_OFF), PIN(GPIO_TEST_N, AO_OFF), 0, 13, 0,  0,  0, 16,  0,  0,  0, 16,  1,  0),
 };
 
 struct meson_pinctrl_data meson8_cbus_pinctrl_data = {
diff --git a/drivers/pinctrl/meson/pinctrl-meson8b.c b/drivers/pinctrl/meson/pinctrl-meson8b.c
index 76f077f18193..9242888fea5f 100644
--- a/drivers/pinctrl/meson/pinctrl-meson8b.c
+++ b/drivers/pinctrl/meson/pinctrl-meson8b.c
@@ -124,6 +124,12 @@ static const struct pinctrl_pin_desc meson8b_aobus_pins[] = {
 	MESON_PIN(GPIOAO_11, AO_OFF),
 	MESON_PIN(GPIOAO_12, AO_OFF),
 	MESON_PIN(GPIOAO_13, AO_OFF),
+
+	/*
+	 * The following 2 pins are not mentionned in the public datasheet
+	 * According to this datasheet, they can't be used with the gpio
+	 * interrupt controller
+	 */
 	MESON_PIN(GPIO_BSD_EN, AO_OFF),
 	MESON_PIN(GPIO_TEST_N, AO_OFF),
 };
@@ -881,19 +887,25 @@ static struct meson_pmx_func meson8b_aobus_functions[] = {
 };
 
 static struct meson_bank meson8b_cbus_banks[] = {
-	/*   name    first                      last                   pullen  pull    dir     out     in  */
-	BANK("X",    PIN(GPIOX_0, 0),		PIN(GPIOX_21, 0),      4,  0,  4,  0,  0,  0,  1,  0,  2,  0),
-	BANK("Y",    PIN(GPIOY_0, 0),		PIN(GPIOY_14, 0),      3,  0,  3,  0,  3,  0,  4,  0,  5,  0),
-	BANK("DV",   PIN(GPIODV_9, 0),		PIN(GPIODV_29, 0),     0,  0,  0,  0,  7,  0,  8,  0,  9,  0),
-	BANK("H",    PIN(GPIOH_0, 0),		PIN(GPIOH_9, 0),       1, 16,  1, 16,  9, 19, 10, 19, 11, 19),
-	BANK("CARD", PIN(CARD_0, 0),		PIN(CARD_6, 0),        2, 20,  2, 20,  0, 22,  1, 22,  2, 22),
-	BANK("BOOT", PIN(BOOT_0, 0),		PIN(BOOT_18, 0),       2,  0,  2,  0,  9,  0, 10,  0, 11,  0),
-	BANK("DIF",  PIN(DIF_0_P, 0),		PIN(DIF_4_N, 0),       5,  8,  5,  8, 12, 12, 13, 12, 14, 12),
+	/*   name    first                      last                irq      pullen  pull    dir     out     in  */
+	BANK("X",    PIN(GPIOX_0, 0),		PIN(GPIOX_21, 0),   97, 118, 4,  0,  4,  0,  0,  0,  1,  0,  2,  0),
+	BANK("Y",    PIN(GPIOY_0, 0),		PIN(GPIOY_14, 0),   80,  96, 3,  0,  3,  0,  3,  0,  4,  0,  5,  0),
+	BANK("DV",   PIN(GPIODV_9, 0),		PIN(GPIODV_29, 0),  59,  79, 0,  0,  0,  0,  7,  0,  8,  0,  9,  0),
+	BANK("H",    PIN(GPIOH_0, 0),		PIN(GPIOH_9, 0),    14,  23, 1, 16,  1, 16,  9, 19, 10, 19, 11, 19),
+	BANK("CARD", PIN(CARD_0, 0),		PIN(CARD_6, 0),     43,  49, 2, 20,  2, 20,  0, 22,  1, 22,  2, 22),
+	BANK("BOOT", PIN(BOOT_0, 0),		PIN(BOOT_18, 0),    24,  42, 2,  0,  2,  0,  9,  0, 10,  0, 11,  0),
+
+	/*
+	 * The following bank is not mentionned in the public datasheet
+	 * There is no information whether it can be used with the gpio
+	 * interrupt controller
+	 */
+	BANK("DIF",  PIN(DIF_0_P, 0),		PIN(DIF_4_N, 0),    -1,  -1, 5,  8,  5,  8, 12, 12, 13, 12, 14, 12),
 };
 
 static struct meson_bank meson8b_aobus_banks[] = {
-	/*   name    first                  last                      pullen  pull    dir     out     in  */
-	BANK("AO",   PIN(GPIOAO_0, AO_OFF), PIN(GPIO_TEST_N, AO_OFF), 0,  0,  0, 16,  0,  0,  0, 16,  1,  0),
+	/*   name    first                  last                      irq    pullen  pull    dir     out     in  */
+	BANK("AO",   PIN(GPIOAO_0, AO_OFF), PIN(GPIO_TEST_N, AO_OFF), 0, 13, 0,  0,  0, 16,  0,  0,  0, 16,  1,  0),
 };
 
 struct meson_pinctrl_data meson8b_cbus_pinctrl_data = {
-- 
2.7.4

^ permalink raw reply related

* [RFC 02/10] dt-bindings: interrupt-controller: add DT binding for meson GPIO interrupt controller
From: Jerome Brunet @ 2016-10-04 15:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475593708-10526-1-git-send-email-jbrunet@baylibre.com>

This commit adds the device tree bindings description for Amlogic's GPIO
interrupt controller available on the meson8, meson8b and gxbb SoC families

Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 .../amlogic,meson-gpio-intc.txt                    | 39 ++++++++++++++++++++++
 1 file changed, 39 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt

diff --git a/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt
new file mode 100644
index 000000000000..bd4cceefcda1
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt
@@ -0,0 +1,39 @@
+Amlogic meson GPIO interrupt controller
+
+Meson SoCs contains an interrupt controller which is able watch the SoC pads
+and generate an interrupt on edges or level. The controller is essentially a
+256 pads to 8 GIC interrupt multiplexer, with a filter block to select edge
+or level and polarity. We don?t expose all 256 mux inputs because the
+documentation shows that upper part is not mapped to any pad. The actual number
+of interrupt exposed depends on the SoC.
+
+Required properties:
+
+- compatible : should be: "amlogic,meson8-gpio-intc? or
+  ?amlogic,meson8b-gpio-intc? or ?amlogic,gxbb-gpio-intc?
+- interrupts : List of the GIC?s interrupts used as parent interrupts.
+  There should 8 of these interrupts.
+- interrupt-parent : a phandle to the GIC the interrupts are routed to.
+  Usually this is provided at the root level of the device tree as it is
+  common to most of the SoC
+- reg : Specifies base physical address and size of the registers.
+- interrupt-controller : Identifies the node as an interrupt controller.
+- #interrupt-cells : Specifies the number of cells needed to encode an
+  interrupt source. The value must be 2.
+
+Exemple:
+
+gpio_interrupt: interrupt-controller at 9880 {
+	compatible = "amlogic,gxbb-gpio-intc";
+	reg = <0x0 0x9880 0x0 0x10>;
+	interrupts = <GIC_SPI 64 IRQ_TYPE_NONE>,
+		   <GIC_SPI 65 IRQ_TYPE_NONE>,
+		   <GIC_SPI 66 IRQ_TYPE_NONE>,
+		   <GIC_SPI 67 IRQ_TYPE_NONE>,
+		   <GIC_SPI 68 IRQ_TYPE_NONE>,
+		   <GIC_SPI 69 IRQ_TYPE_NONE>,
+		   <GIC_SPI 70 IRQ_TYPE_NONE>,
+		   <GIC_SPI 71 IRQ_TYPE_NONE>;
+	interrupt-controller;
+	#interrupt-cells = <2>;
+};
-- 
2.7.4

^ permalink raw reply related

* [RFC 01/10] irqchip: meson: add support for gpio interrupt controller
From: Jerome Brunet @ 2016-10-04 15:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475593708-10526-1-git-send-email-jbrunet@baylibre.com>

Add support for the intterrupt gpio controller found on Amlogic's meson
SoC family.

Unlike what the IP name suggest, it is not directly linked to the gpio
subsystem. It is actually an indepedent IP that is able to spy on the
SoC pad. For that purpose, it can mux and filter (edge or level and
polarity) any single SoC pad to one of the 8 GIC's interrupts it owns.

Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/irqchip/Kconfig          |   9 +
 drivers/irqchip/Makefile         |   1 +
 drivers/irqchip/irq-meson-gpio.c | 398 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 408 insertions(+)
 create mode 100644 drivers/irqchip/irq-meson-gpio.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 82b0b5daf3f5..168837263e80 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -279,3 +279,12 @@ config EZNPS_GIC
 config STM32_EXTI
 	bool
 	select IRQ_DOMAIN
+
+config MESON_GPIO_IRQ
+       bool "Meson GPIO Interrupt Multiplexer"
+       depends on ARCH_MESON || COMPILE_TEST
+       select IRQ_DOMAIN
+       select IRQ_DOMAIN_HIERARCHY
+       help
+         Support Meson SoC Family GPIO Interrupt Multiplexer
+
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index e4dbfc85abdb..33f913d037d0 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -74,3 +74,4 @@ obj-$(CONFIG_LS_SCFG_MSI)		+= irq-ls-scfg-msi.o
 obj-$(CONFIG_EZNPS_GIC)			+= irq-eznps.o
 obj-$(CONFIG_ARCH_ASPEED)		+= irq-aspeed-vic.o
 obj-$(CONFIG_STM32_EXTI) 		+= irq-stm32-exti.o
+obj-$(CONFIG_MESON_GPIO_IRQ)		+= irq-meson-gpio.o
diff --git a/drivers/irqchip/irq-meson-gpio.c b/drivers/irqchip/irq-meson-gpio.c
new file mode 100644
index 000000000000..184025a9cdaf
--- /dev/null
+++ b/drivers/irqchip/irq-meson-gpio.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2015 Endless Mobile, Inc.
+ * Author: Carlo Caione <carlo@endlessm.com>
+ * Copyright (c) 2016 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+
+#define IRQ_FREE (-1)
+
+#define REG_EDGE_POL	0x00
+#define REG_PIN_03_SEL	0x04
+#define REG_PIN_47_SEL	0x08
+#define REG_FILTER_SEL	0x0c
+
+#define REG_EDGE_POL_MASK(x)	(BIT(x) | BIT(16 + (x)))
+#define REG_EDGE_POL_EDGE(x)	BIT(x)
+#define REG_EDGE_POL_LOW(x)	BIT(16 + (x))
+#define REG_PIN_SEL_SHIFT(x)	(((x) % 4) * 8)
+#define REG_FILTER_SEL_SHIFT(x)	((x) * 4)
+
+struct meson_gpio_irq_params {
+	unsigned int nr_hwirqs;
+};
+
+struct meson_gpio_irq_domain {
+	struct irq_fwspec *parent_irqs;
+	void __iomem *base;
+	int nr_parent_irqs;
+	int *irq_map;
+};
+
+struct meson_gpio_irq_chip {
+	void __iomem *base;
+	int index;
+};
+
+static const struct meson_gpio_irq_params meson8_params = {
+	.nr_hwirqs = 134,
+};
+
+static const struct meson_gpio_irq_params meson8b_params = {
+	.nr_hwirqs = 119,
+};
+
+static const struct meson_gpio_irq_params gxbb_params = {
+	.nr_hwirqs = 133,
+};
+
+static const struct of_device_id meson_irq_gpio_matches[] = {
+	{ .compatible = "amlogic,meson8-gpio-intc", .data = &meson8_params },
+	{ .compatible = "amlogic,meson8b-gpio-intc", .data = &meson8b_params },
+	{ .compatible = "amlogic,gxbb-gpio-intc", .data = &gxbb_params },
+	{ }
+};
+
+static void meson_gpio_irq_update_bits(void __iomem *base, unsigned int reg,
+				       u32 mask, u32 val)
+{
+	u32 tmp;
+
+	tmp = readl(base + reg);
+	tmp &= ~mask;
+	tmp |= val;
+
+	writel(tmp, base + reg);
+}
+
+static int meson_gpio_irq_get_index(struct meson_gpio_irq_domain *domain_data,
+				    int hwirq)
+{
+	int i;
+
+	for (i = 0; i < domain_data->nr_parent_irqs; i++) {
+		if (domain_data->irq_map[i] == hwirq)
+			return i;
+	}
+
+	return -1;
+}
+
+static int meson_gpio_irq_map_pirq(struct meson_gpio_irq_domain *domain_data,
+				   irq_hw_number_t hwirq)
+{
+	int index;
+	unsigned int reg;
+
+	index = meson_gpio_irq_get_index(domain_data, IRQ_FREE);
+	if (index < 0) {
+		pr_err("No irq available\n");
+		return -ENOSPC;
+	}
+
+	domain_data->irq_map[index] = hwirq;
+
+	reg = (index < 4) ? REG_PIN_03_SEL : REG_PIN_47_SEL;
+	meson_gpio_irq_update_bits(domain_data->base, reg,
+				   0xff << REG_PIN_SEL_SHIFT(index),
+				   hwirq << REG_PIN_SEL_SHIFT(index));
+
+	pr_debug("hwirq %d assigned to channel %d\n", (int)hwirq, index);
+
+	return index;
+}
+
+static int meson_gpio_irq_set_type(struct irq_data *data, unsigned int type)
+{
+	struct meson_gpio_irq_chip *cd = irq_data_get_irq_chip_data(data);
+	u32 val = 0;
+
+	pr_debug("set type of hwirq %lu to %u\n", data->hwirq, type);
+
+	if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
+		return -EINVAL;
+
+	if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))
+		val |= REG_EDGE_POL_EDGE(cd->index);
+
+	if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING)) {
+		val |= REG_EDGE_POL_LOW(cd->index);
+
+		/*
+		 * According to the datasheet, the polarity block invert the
+		 * signal on the path to parent interrupt
+		 */
+		if (type & IRQ_TYPE_LEVEL_LOW)
+			type = (type & ~IRQ_TYPE_LEVEL_LOW)
+				| IRQ_TYPE_LEVEL_HIGH;
+		else
+			type = (type & ~IRQ_TYPE_EDGE_FALLING)
+				| IRQ_TYPE_EDGE_RISING;
+	}
+
+	meson_gpio_irq_update_bits(cd->base, REG_EDGE_POL,
+				   REG_EDGE_POL_MASK(cd->index), val);
+
+	return irq_chip_set_type_parent(data, type);
+}
+
+static struct irq_chip meson_gpio_irq_chip = {
+	.name			= "meson-gpio-irqchip",
+	.irq_mask		= irq_chip_mask_parent,
+	.irq_unmask		= irq_chip_unmask_parent,
+	.irq_eoi		= irq_chip_eoi_parent,
+	.irq_set_type		= meson_gpio_irq_set_type,
+	.irq_retrigger		= irq_chip_retrigger_hierarchy,
+#ifdef CONFIG_SMP
+	.irq_set_affinity	= irq_chip_set_affinity_parent,
+#endif
+};
+
+static int meson_gpio_irq_domain_translate(struct irq_domain *domain,
+					   struct irq_fwspec *fwspec,
+					   unsigned long *hwirq,
+					   unsigned int *type)
+{
+	if (is_of_node(fwspec->fwnode)) {
+		if (fwspec->param_count != 2)
+			return -EINVAL;
+
+		*hwirq = fwspec->param[0];
+		*type = fwspec->param[1];
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int meson_gpio_irq_domain_alloc(struct irq_domain *domain,
+				       unsigned int virq,
+				       unsigned int nr_irqs,
+				       void *data)
+{
+	struct irq_fwspec *fwspec_parent, *fwspec = data;
+	struct meson_gpio_irq_domain *domain_data = domain->host_data;
+	struct meson_gpio_irq_chip *cd;
+	unsigned long hwirq;
+	unsigned int type;
+	int i, index, ret;
+
+	ret = meson_gpio_irq_domain_translate(domain, fwspec, &hwirq, &type);
+	if (ret)
+		return ret;
+
+	pr_debug("irq %d, nr_irqs %d, hwirqs %lu\n", virq, nr_irqs, hwirq);
+
+	for (i = 0; i < nr_irqs; i++) {
+		index = meson_gpio_irq_map_pirq(domain_data, hwirq + i);
+		if (index < 0)
+			return index;
+
+		cd = kzalloc(sizeof(*cd), GFP_KERNEL);
+		if (!cd)
+			return -ENOMEM;
+
+		cd->base = domain_data->base;
+		cd->index = index;
+		fwspec_parent = &domain_data->parent_irqs[index];
+
+		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+					      &meson_gpio_irq_chip,
+					      cd);
+
+		ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1,
+						   fwspec_parent);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static void meson_gpio_irq_domain_free(struct irq_domain *domain,
+				       unsigned int virq,
+				       unsigned int nr_irqs)
+{
+	struct meson_gpio_irq_domain *domain_data = domain->host_data;
+	struct meson_gpio_irq_chip *cd;
+	struct irq_data *irq_data;
+	int i;
+
+	for (i = 0; i < nr_irqs; i++) {
+		irq_data = irq_domain_get_irq_data(domain, virq + i);
+		cd = irq_data_get_irq_chip_data(irq_data);
+
+		domain_data->irq_map[cd->index] = IRQ_FREE;
+		kfree(cd);
+	}
+
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+
+static const struct irq_domain_ops meson_gpio_irq_domain_ops = {
+	.alloc		= meson_gpio_irq_domain_alloc,
+	.free		= meson_gpio_irq_domain_free,
+	.translate	= meson_gpio_irq_domain_translate,
+};
+
+
+static int __init meson_gpio_irq_get_one_pirq(struct device_node *node,
+					      int index,
+					      struct irq_fwspec *pirq)
+{
+	int ret, i;
+	struct of_phandle_args oirq;
+
+	ret = of_irq_parse_one(node, index, &oirq);
+	if (ret < 0)
+		return ret;
+
+	pirq->fwnode = of_node_to_fwnode(oirq.np);
+
+	if (!pirq->fwnode) {
+		pr_err("can't get interrupt fwnode\n");
+		return -ENODEV;
+	}
+
+	pirq->param_count = oirq.args_count;
+	for (i = 0; i < oirq.args_count; i++)
+		pirq->param[i] = oirq.args[i];
+
+	return 0;
+}
+
+
+static int __init
+meson_gpio_irq_init_domain(struct device_node *node,
+			   struct meson_gpio_irq_domain *domain_data)
+{
+	int ret, i, n_pirqs;
+	int *map;
+	struct irq_fwspec *pirqs;
+
+	n_pirqs = of_irq_count(node);
+	if (n_pirqs == 0) {
+		pr_err("missing parent interrupts\n");
+		return -ENODEV;
+	}
+
+	pirqs = kcalloc(n_pirqs, sizeof(*pirqs), GFP_KERNEL);
+	if (!pirqs)
+		return -ENOMEM;
+
+	map = kcalloc(n_pirqs, sizeof(*map), GFP_KERNEL);
+	if (!map)
+		return -ENOMEM;
+
+	for (i = 0; i < n_pirqs; i++) {
+		map[i] = IRQ_FREE;
+		ret = meson_gpio_irq_get_one_pirq(node, i, &pirqs[i]);
+		if (ret < 0)
+			return ret;
+	}
+
+	domain_data->nr_parent_irqs = n_pirqs;
+	domain_data->parent_irqs = pirqs;
+	domain_data->irq_map = map;
+
+	return 0;
+}
+
+static int __init meson_gpio_irq_of_init(struct device_node *node,
+					 struct device_node *parent)
+{
+	struct irq_domain *domain, *parent_domain;
+	const struct of_device_id *match;
+	const struct meson_gpio_irq_params *params;
+	struct meson_gpio_irq_domain *domain_data;
+	int ret;
+
+	match = of_match_node(meson_irq_gpio_matches, node);
+	if (!match)
+		return -ENODEV;
+	params = match->data;
+
+	if (!parent) {
+		pr_err("missing parent interrupt node\n");
+		return -ENODEV;
+	}
+
+	parent_domain = irq_find_host(parent);
+	if (!parent_domain) {
+		pr_err("unable to obtain parent domain\n");
+		return -ENXIO;
+	}
+
+	domain_data = kzalloc(sizeof(*domain_data), GFP_KERNEL);
+	if (!domain_data)
+		return -ENOMEM;
+
+	domain_data->base = of_iomap(node, 0);
+	if (!domain_data->base) {
+		ret = -ENOMEM;
+		goto out_free_dev;
+	}
+
+	ret = meson_gpio_irq_init_domain(node, domain_data);
+	if (ret < 0)
+		goto out_free_dev_content;
+
+	domain = irq_domain_add_hierarchy(parent_domain, 0, params->nr_hwirqs,
+					  node, &meson_gpio_irq_domain_ops,
+					  domain_data);
+
+	if (!domain) {
+		pr_err("failed to allocated domain\n");
+		ret = -ENOMEM;
+		goto out_free_dev_content;
+	}
+
+	pr_info("%d to %d gpio interrupt mux initialized\n",
+		params->nr_hwirqs, domain_data->nr_parent_irqs);
+
+	return 0;
+
+out_free_dev_content:
+	kfree(domain_data->parent_irqs);
+	kfree(domain_data->irq_map);
+	iounmap(domain_data->base);
+
+out_free_dev:
+	kfree(domain_data);
+
+	return ret;
+}
+IRQCHIP_DECLARE(meson8_gpio_intc, "amlogic,meson8-gpio-intc",
+		meson_gpio_irq_of_init);
+IRQCHIP_DECLARE(meson8b_gpio_intc, "amlogic,meson8b-gpio-intc",
+		meson_gpio_irq_of_init);
+IRQCHIP_DECLARE(gxbb_gpio_intc, "amlogic,gxbb-gpio-intc",
+		meson_gpio_irq_of_init);
-- 
2.7.4

^ permalink raw reply related

* [RFC 00/10] irqchip: meson: add support for the gpio interrupt controller
From: Jerome Brunet @ 2016-10-04 15:08 UTC (permalink / raw)
  To: linux-arm-kernel

This patch series adds support for the GPIO interrupt controller found
on Amlogic's meson SoC families.

Unlike what the name suggests, this controller is not part of the SoC GPIO
subsystem. It's an indepedent controller which can watch almost all pad of
the SoC and generate and interrupt from it. Some pins, which are not part
of the public datasheet, don't seem to have this capability though.

Hardware wise, the controller is a 256 to 8 multiplexer. It can take up
to 256 input pads and route them to any of 8 GIC's interrupts. There is also
a filter block in the middle to select the appropriate edge or level.

The number of interrupt declared by the irqchip is lowered from 256 to the
actual number of signal routed to the controller on each SoC family. As we
have access to only 8 GIC?s interrupts, these are allocated when an
interrupt is requested from the controller, on a first come, first served
basis.

This series has been tested on Amlogic S905-P200 board with the front
panel power button. Directly passing an IRQ or using gpio_to_irq both work
with this driver.

This work is derived from the previous work of Carlo Caione [1].

http://lkml.kernel.org/r/1448987062-31225-1-git-send-email-carlo at caione.org

Note:
As the comment will explain, patch 10 is not strictly required but would
be an appreciated bonus.

Jerome Brunet (10):
  irqchip: meson: add support for gpio interrupt controller
  dt-bindings: interrupt-controller: add DT binding for meson GPIO
    interrupt controller
  pinctrl: meson: update pinctrl data with gpio irq base number
  pinctrl: meson: allow gpio to request irq
  dt-bindings: pinctrl: meson: update gpio dt-bindings
  ARM64: meson: enable MESON_IRQ_GPIO in Kconfig
  ARM: meson: enable MESON_IRQ_GPIO in Kconfig for meson8
  ARM64: dts: amlogic: enable gpio interrupt controller on gxbb
  ARM: dts: amlogic: enable gpio interrupt controller on meson8
  irqchip: meson: Add support for IRQ_TYPE_EDGE_BOTH

 .../amlogic,meson-gpio-intc.txt                    |  39 ++
 .../devicetree/bindings/pinctrl/meson,pinctrl.txt  |   4 +
 arch/arm/boot/dts/meson8.dtsi                      |  19 +
 arch/arm/boot/dts/meson8b.dtsi                     |  19 +
 arch/arm/mach-meson/Kconfig                        |   2 +
 arch/arm64/Kconfig.platforms                       |   1 +
 arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi        |  17 +
 drivers/irqchip/Kconfig                            |   9 +
 drivers/irqchip/Makefile                           |   1 +
 drivers/irqchip/irq-meson-gpio.c                   | 437 +++++++++++++++++++++
 drivers/pinctrl/Kconfig                            |   2 +
 drivers/pinctrl/meson/pinctrl-meson-gxbb.c         |  22 +-
 drivers/pinctrl/meson/pinctrl-meson.c              |  83 +++-
 drivers/pinctrl/meson/pinctrl-meson.h              |  16 +-
 drivers/pinctrl/meson/pinctrl-meson8.c             |  20 +-
 drivers/pinctrl/meson/pinctrl-meson8b.c            |  32 +-
 16 files changed, 686 insertions(+), 37 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt
 create mode 100644 drivers/irqchip/irq-meson-gpio.c

-- 
2.7.4

^ permalink raw reply

* [PATCH 2/2] mfd: ab8500-debugfs: remove unused function
From: Lee Jones @ 2016-10-04 14:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474695413-30460-1-git-send-email-baoyou.xie@linaro.org>

On Sat, 24 Sep 2016, Baoyou Xie wrote:

> We get 1 warning when building kernel with W=1:
> drivers/mfd/ab8500-debugfs.c:1395:6: warning: no previous prototype for 'ab8500_dump_all_banks_to_mem' [-Wmissing-prototypes]
> 
> In fact, this function is called by no one and not exported,
> so this patch removes it.
> 
> Signed-off-by: Baoyou Xie <baoyou.xie@linaro.org>
> ---
>  drivers/mfd/ab8500-debugfs.c | 54 --------------------------------------------
>  1 file changed, 54 deletions(-)

This is already in -next:

  e310960300e78764f59a4b044205b02a5c59381a

> diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
> index 0aecd7b..9f04a25 100644
> --- a/drivers/mfd/ab8500-debugfs.c
> +++ b/drivers/mfd/ab8500-debugfs.c
> @@ -1382,60 +1382,6 @@ void ab8500_dump_all_banks(struct device *dev)
>  	}
>  }
>  
> -/* Space for 500 registers. */
> -#define DUMP_MAX_REGS 700
> -static struct ab8500_register_dump
> -{
> -	u8 bank;
> -	u8 reg;
> -	u8 value;
> -} ab8500_complete_register_dump[DUMP_MAX_REGS];
> -
> -/* This shall only be called upon kernel panic! */
> -void ab8500_dump_all_banks_to_mem(void)
> -{
> -	int i, r = 0;
> -	u8 bank;
> -	int err = 0;
> -
> -	pr_info("Saving all ABB registers for crash analysis.\n");
> -
> -	for (bank = 0; bank < AB8500_NUM_BANKS; bank++) {
> -		for (i = 0; i < debug_ranges[bank].num_ranges; i++) {
> -			u8 reg;
> -
> -			for (reg = debug_ranges[bank].range[i].first;
> -			     reg <= debug_ranges[bank].range[i].last;
> -			     reg++) {
> -				u8 value;
> -
> -				err = prcmu_abb_read(bank, reg, &value, 1);
> -
> -				if (err < 0)
> -					goto out;
> -
> -				ab8500_complete_register_dump[r].bank = bank;
> -				ab8500_complete_register_dump[r].reg = reg;
> -				ab8500_complete_register_dump[r].value = value;
> -
> -				r++;
> -
> -				if (r >= DUMP_MAX_REGS) {
> -					pr_err("%s: too many register to dump!\n",
> -						__func__);
> -					err = -EINVAL;
> -					goto out;
> -				}
> -			}
> -		}
> -	}
> -out:
> -	if (err >= 0)
> -		pr_info("Saved all ABB registers.\n");
> -	else
> -		pr_info("Failed to save all ABB registers.\n");
> -}
> -
>  static int ab8500_all_banks_open(struct inode *inode, struct file *file)
>  {
>  	struct seq_file *s;

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply

* [PATCH v6] soc: qcom: add l2 cache perf events driver
From: Neil Leeder @ 2016-10-04 14:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474492374-12140-1-git-send-email-nleeder@codeaurora.org>


On 9/21/2016 05:12 PM, Neil Leeder wrote:
> Adds perf events support for L2 cache PMU.
> 
> The L2 cache PMU driver is named 'l2cache_0' and can be used
> with perf events to profile L2 events such as cache hits
> and misses.
> 
> Signed-off-by: Neil Leeder <nleeder@codeaurora.org>
> ---
> v6: restore accidentally dropped Kconfig dependencies
> 
> v5:
> Fold the header and l2-accessors into .c file
> Use multi-instance framework for hotplug
> Change terminology from slice to cluster for clarity
> Remove unnecessary rmw sequence for enable registers
> Use prev_count in hwc rather than in slice
> Enforce all events in same group on same CPU
> Add comments, rename variables for clarity
> 
> v4:
> Replace notifier with hotplug statemachine
> Allocate PMU struct dynamically
> 
> v3:
> Remove exports from l2-accessors
> Change l2-accessors Kconfig to make it not user-selectable
> Reorder and remove unnecessary includes
> 
> v2:
> Add the l2-accessors patch to this patchset, previously posted separately.
> Remove sampling and per-task functionality for this uncore PMU.
> Use cpumask to replace code which filtered events to one cpu per slice.
> Replace manual event filtering with filter_match callback.
> Use a separate used_mask for event groups.
> Add hotplug notifier for CPU and irq migration.
> Remove extraneous synchronisation instructions.
> Other miscellaneous cleanup.
> 
>  drivers/soc/qcom/Kconfig         |   9 +
>  drivers/soc/qcom/Makefile        |   1 +
>  drivers/soc/qcom/perf_event_l2.c | 948 +++++++++++++++++++++++++++++++++++++++
>  include/linux/cpuhotplug.h       |   1 +
>  4 files changed, 959 insertions(+)
>  create mode 100644 drivers/soc/qcom/perf_event_l2.c
> 
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index 461b387..3fa27a8 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -10,6 +10,15 @@ config QCOM_GSBI
>            functions for connecting the underlying serial UART, SPI, and I2C
>            devices to the output pins.
>  
> +config QCOM_PERF_EVENTS_L2
> +	bool "Qualcomm Technologies L2-cache perf events"
> +	depends on ARCH_QCOM && ARM64 && HW_PERF_EVENTS && ACPI
> +	  help
> +	  Provides support for the L2 cache performance monitor unit (PMU)
> +	  in Qualcomm Technologies processors.
> +	  Adds the L2 cache PMU into the perf events subsystem for
> +	  monitoring L2 cache events.
> +
>  config QCOM_PM
>  	bool "Qualcomm Power Management"
>  	depends on ARCH_QCOM && !ARM64
> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
> index fdd664e..4c9df3b 100644
> --- a/drivers/soc/qcom/Makefile
> +++ b/drivers/soc/qcom/Makefile
> @@ -1,4 +1,5 @@
>  obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
> +obj-$(CONFIG_QCOM_PERF_EVENTS_L2)	+= perf_event_l2.o
>  obj-$(CONFIG_QCOM_PM)	+=	spm.o
>  obj-$(CONFIG_QCOM_SMD) +=	smd.o
>  obj-$(CONFIG_QCOM_SMD_RPM)	+= smd-rpm.o
> diff --git a/drivers/soc/qcom/perf_event_l2.c b/drivers/soc/qcom/perf_event_l2.c
> new file mode 100644
> index 0000000..bbf47c9
> --- /dev/null
> +++ b/drivers/soc/qcom/perf_event_l2.c
> @@ -0,0 +1,948 @@
> +/* Copyright (c) 2015,2016 The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only 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/acpi.h>
> +#include <linux/interrupt.h>
> +#include <linux/perf_event.h>
> +#include <linux/platform_device.h>
> +
> +#define MAX_L2_CTRS             9
> +
> +#define L2PMCR_NUM_EV_SHIFT     11
> +#define L2PMCR_NUM_EV_MASK      0x1F
> +
> +#define L2PMCR                  0x400
> +#define L2PMCNTENCLR            0x403
> +#define L2PMCNTENSET            0x404
> +#define L2PMINTENCLR            0x405
> +#define L2PMINTENSET            0x406
> +#define L2PMOVSCLR              0x407
> +#define L2PMOVSSET              0x408
> +#define L2PMCCNTCR              0x409
> +#define L2PMCCNTR               0x40A
> +#define L2PMCCNTSR              0x40C
> +#define L2PMRESR                0x410
> +#define IA_L2PMXEVCNTCR_BASE    0x420
> +#define IA_L2PMXEVCNTR_BASE     0x421
> +#define IA_L2PMXEVFILTER_BASE   0x423
> +#define IA_L2PMXEVTYPER_BASE    0x424
> +
> +#define IA_L2_REG_OFFSET        0x10
> +
> +#define L2PMXEVFILTER_SUFILTER_ALL      0x000E0000
> +#define L2PMXEVFILTER_ORGFILTER_IDINDEP 0x00000004
> +#define L2PMXEVFILTER_ORGFILTER_ALL     0x00000003
> +
> +#define L2PM_CC_ENABLE          0x80000000
> +
> +#define L2EVTYPER_REG_SHIFT     3
> +
> +#define L2PMRESR_GROUP_BITS     8
> +#define L2PMRESR_GROUP_MASK     GENMASK(7, 0)
> +
> +#define L2CYCLE_CTR_BIT         31
> +#define L2CYCLE_CTR_RAW_CODE    0xFE
> +
> +#define L2PMCR_RESET_ALL        0x6
> +#define L2PMCR_COUNTERS_ENABLE  0x1
> +#define L2PMCR_COUNTERS_DISABLE 0x0
> +
> +#define L2PMRESR_EN             ((u64)1 << 63)
> +
> +#define L2_EVT_MASK             0x00000FFF
> +#define L2_EVT_CODE_MASK        0x00000FF0
> +#define L2_EVT_GRP_MASK         0x0000000F
> +#define L2_EVT_CODE_SHIFT       4
> +#define L2_EVT_GRP_SHIFT        0
> +
> +#define L2_EVT_CODE(event)   (((event) & L2_EVT_CODE_MASK) >> L2_EVT_CODE_SHIFT)
> +#define L2_EVT_GROUP(event)  (((event) & L2_EVT_GRP_MASK) >> L2_EVT_GRP_SHIFT)
> +
> +#define L2_EVT_GROUP_MAX        7
> +
> +#define L2_MAX_PERIOD           U32_MAX
> +#define L2_CNT_PERIOD           (U32_MAX - GENMASK(26, 0))
> +
> +#define L2CPUSRSELR_EL1         S3_3_c15_c0_6
> +#define L2CPUSRDR_EL1           S3_3_c15_c0_7
> +
> +static DEFINE_RAW_SPINLOCK(l2_access_lock);
> +
> +/**
> + * set_l2_indirect_reg: write value to an L2 register
> + * @reg: Address of L2 register.
> + * @value: Value to be written to register.
> + *
> + * Use architecturally required barriers for ordering between system register
> + * accesses
> + */
> +static void set_l2_indirect_reg(u64 reg, u64 val)
> +{
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&l2_access_lock, flags);
> +	write_sysreg(reg, L2CPUSRSELR_EL1);
> +	isb();
> +	write_sysreg(val, L2CPUSRDR_EL1);
> +	isb();
> +	raw_spin_unlock_irqrestore(&l2_access_lock, flags);
> +}
> +
> +/**
> + * get_l2_indirect_reg: read an L2 register value
> + * @reg: Address of L2 register.
> + *
> + * Use architecturally required barriers for ordering between system register
> + * accesses
> + */
> +static u64 get_l2_indirect_reg(u64 reg)
> +{
> +	u64 val;
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&l2_access_lock, flags);
> +	write_sysreg(reg, L2CPUSRSELR_EL1);
> +	isb();
> +	val = read_sysreg(L2CPUSRDR_EL1);
> +	raw_spin_unlock_irqrestore(&l2_access_lock, flags);
> +
> +	return val;
> +}
> +
> +/*
> + * Aggregate PMU. Implements the core pmu functions and manages
> + * the hardware PMUs.
> + */
> +struct l2cache_pmu {
> +	struct hlist_node node;
> +	u32 num_pmus;
> +	struct pmu pmu;
> +	int num_counters;
> +	cpumask_t cpumask;
> +	struct platform_device *pdev;
> +};
> +
> +/*
> + * The cache is made up of one or more clusters, each cluster has its own PMU.
> + * Each cluster is associated with one or more CPUs.
> + * This structure represents one of the hardware PMUs.
> + *
> + * Events can be envisioned as a 2-dimensional array. Each column represents
> + * a group of events. There are 8 groups. Only one entry from each
> + * group can be in use at a time. When an event is assigned a counter
> + * by *_event_add(), the counter index is assigned to group_to_counter[group].
> + * This allows *filter_match() to detect and reject conflicting events in
> + * the same group.
> + * Events are specified as 0xCCG, where CC is 2 hex digits specifying
> + * the code (array row) and G specifies the group (column).
> + *
> + * In addition there is a cycle counter event specified by L2CYCLE_CTR_RAW_CODE
> + * which is outside the above scheme.
> + */
> +struct hml2_pmu {
> +	struct perf_event *events[MAX_L2_CTRS];
> +	struct l2cache_pmu *l2cache_pmu;
> +	DECLARE_BITMAP(used_counters, MAX_L2_CTRS);
> +	DECLARE_BITMAP(used_groups, L2_EVT_GROUP_MAX + 1);
> +	int group_to_counter[L2_EVT_GROUP_MAX + 1];
> +	int irq;
> +	/* The CPU that is used for collecting events on this cluster */
> +	int on_cpu;
> +	/* All the CPUs associated with this cluster */
> +	cpumask_t cluster_cpus;
> +	spinlock_t pmu_lock;
> +};
> +
> +#define to_l2cache_pmu(p) (container_of(p, struct l2cache_pmu, pmu))
> +
> +static DEFINE_PER_CPU(struct hml2_pmu *, pmu_cluster);
> +static u32 l2_cycle_ctr_idx;
> +static u32 l2_counter_present_mask;
> +
> +static inline u32 idx_to_reg_bit(u32 idx)
> +{
> +	if (idx == l2_cycle_ctr_idx)
> +		return BIT(L2CYCLE_CTR_BIT);
> +
> +	return BIT(idx);
> +}
> +
> +static inline struct hml2_pmu *get_hml2_pmu(int cpu)
> +{
> +	return per_cpu(pmu_cluster, cpu);
> +}
> +
> +static void hml2_pmu__reset_on_cluster(void *x)
> +{
> +	/* Reset all ctrs */
> +	set_l2_indirect_reg(L2PMCR, L2PMCR_RESET_ALL);
> +	set_l2_indirect_reg(L2PMCNTENCLR, l2_counter_present_mask);
> +	set_l2_indirect_reg(L2PMINTENCLR, l2_counter_present_mask);
> +	set_l2_indirect_reg(L2PMOVSCLR, l2_counter_present_mask);
> +}
> +
> +static inline void hml2_pmu__reset(struct hml2_pmu *cluster)
> +{
> +	cpumask_t *mask = &cluster->cluster_cpus;
> +
> +	if (smp_call_function_any(mask, hml2_pmu__reset_on_cluster, NULL, 1))
> +		dev_err(&cluster->l2cache_pmu->pdev->dev,
> +			"Failed to reset on cluster with cpu %d\n",
> +			cpumask_first(&cluster->cluster_cpus));
> +}
> +
> +static inline void hml2_pmu__enable(void)
> +{
> +	set_l2_indirect_reg(L2PMCR, L2PMCR_COUNTERS_ENABLE);
> +}
> +
> +static inline void hml2_pmu__disable(void)
> +{
> +	set_l2_indirect_reg(L2PMCR, L2PMCR_COUNTERS_DISABLE);
> +}
> +
> +static inline void hml2_pmu__counter_set_value(u32 idx, u64 value)
> +{
> +	u32 counter_reg;
> +
> +	if (idx == l2_cycle_ctr_idx) {
> +		set_l2_indirect_reg(L2PMCCNTR, value);
> +	} else {
> +		counter_reg = (idx * IA_L2_REG_OFFSET) + IA_L2PMXEVCNTR_BASE;
> +		set_l2_indirect_reg(counter_reg, value & GENMASK(31, 0));
> +	}
> +}
> +
> +static inline u64 hml2_pmu__counter_get_value(u32 idx)
> +{
> +	u64 value;
> +	u32 counter_reg;
> +
> +	if (idx == l2_cycle_ctr_idx) {
> +		value = get_l2_indirect_reg(L2PMCCNTR);
> +	} else {
> +		counter_reg = (idx * IA_L2_REG_OFFSET) + IA_L2PMXEVCNTR_BASE;
> +		value = get_l2_indirect_reg(counter_reg);
> +	}
> +
> +	return value;
> +}
> +
> +static inline void hml2_pmu__counter_enable(u32 idx)
> +{
> +	set_l2_indirect_reg(L2PMCNTENSET, idx_to_reg_bit(idx));
> +}
> +
> +static inline void hml2_pmu__counter_disable(u32 idx)
> +{
> +	set_l2_indirect_reg(L2PMCNTENCLR, idx_to_reg_bit(idx));
> +}
> +
> +static inline void hml2_pmu__counter_enable_interrupt(u32 idx)
> +{
> +	set_l2_indirect_reg(L2PMINTENSET, idx_to_reg_bit(idx));
> +}
> +
> +static inline void hml2_pmu__counter_disable_interrupt(u32 idx)
> +{
> +	set_l2_indirect_reg(L2PMINTENCLR, idx_to_reg_bit(idx));
> +}
> +
> +static inline void hml2_pmu__set_evccntcr(u32 val)
> +{
> +	set_l2_indirect_reg(L2PMCCNTCR, val);
> +}
> +
> +static inline void hml2_pmu__set_evcntcr(u32 ctr, u32 val)
> +{
> +	u32 evtcr_reg = (ctr * IA_L2_REG_OFFSET) + IA_L2PMXEVCNTCR_BASE;
> +
> +	set_l2_indirect_reg(evtcr_reg, val);
> +}
> +
> +static inline void hml2_pmu__set_evtyper(u32 ctr, u32 val)
> +{
> +	u32 evtype_reg = (ctr * IA_L2_REG_OFFSET) + IA_L2PMXEVTYPER_BASE;
> +
> +	set_l2_indirect_reg(evtype_reg, val);
> +}
> +
> +static void hml2_pmu__set_resr(struct hml2_pmu *cluster,
> +			       u32 event_group, u32 event_cc)
> +{
> +	u64 field;
> +	u64 resr_val;
> +	u32 shift;
> +	unsigned long flags;
> +
> +	shift = L2PMRESR_GROUP_BITS * event_group;
> +	field = ((u64)(event_cc & L2PMRESR_GROUP_MASK) << shift) | L2PMRESR_EN;
> +
> +	spin_lock_irqsave(&cluster->pmu_lock, flags);
> +
> +	resr_val = get_l2_indirect_reg(L2PMRESR);
> +	resr_val &= ~(L2PMRESR_GROUP_MASK << shift);
> +	resr_val |= field;
> +	set_l2_indirect_reg(L2PMRESR, resr_val);
> +
> +	spin_unlock_irqrestore(&cluster->pmu_lock, flags);
> +}
> +
> +/*
> + * Hardware allows filtering of events based on the originating
> + * CPU. Turn this off by setting filter bits to allow events from
> + * all CPUS, subunits and ID independent events in this cluster.
> + */
> +static inline void hml2_pmu__set_evfilter_sys_mode(u32 ctr)
> +{
> +	u32 reg = (ctr * IA_L2_REG_OFFSET) + IA_L2PMXEVFILTER_BASE;
> +	u32 val =  L2PMXEVFILTER_SUFILTER_ALL |
> +		   L2PMXEVFILTER_ORGFILTER_IDINDEP |
> +		   L2PMXEVFILTER_ORGFILTER_ALL;
> +
> +	set_l2_indirect_reg(reg, val);
> +}
> +
> +static inline u32 hml2_pmu__getreset_ovsr(void)
> +{
> +	u32 result = get_l2_indirect_reg(L2PMOVSSET);
> +
> +	set_l2_indirect_reg(L2PMOVSCLR, result);
> +	return result;
> +}
> +
> +static inline bool hml2_pmu__has_overflowed(u32 ovsr)
> +{
> +	return !!(ovsr & l2_counter_present_mask);
> +}
> +
> +static inline bool hml2_pmu__counter_has_overflowed(u32 ovsr, u32 idx)
> +{
> +	return !!(ovsr & idx_to_reg_bit(idx));
> +}
> +
> +static void l2_cache__event_update_from_cluster(struct perf_event *event,
> +						struct hml2_pmu *cluster)
> +{
> +	struct hw_perf_event *hwc = &event->hw;
> +	u64 delta64, prev, now;
> +	u32 delta;
> +	u32 idx = hwc->idx;
> +
> +	do {
> +		prev = local64_read(&hwc->prev_count);
> +		now = hml2_pmu__counter_get_value(idx);
> +	} while (local64_cmpxchg(&hwc->prev_count, prev, now) != prev);
> +
> +	if (idx == l2_cycle_ctr_idx) {
> +		/*
> +		 * The cycle counter is 64-bit so needs separate handling
> +		 * of 64-bit delta.
> +		 */
> +		delta64 = now - prev;
> +		local64_add(delta64, &event->count);
> +	} else {
> +		/*
> +		 * 32-bit counters need the unsigned 32-bit math to handle
> +		 * overflow and now < prev
> +		 */
> +		delta = now - prev;
> +		local64_add(delta, &event->count);
> +	}
> +}
> +
> +static void l2_cache__cluster_set_period(struct hml2_pmu *cluster,
> +				       struct hw_perf_event *hwc)
> +{
> +	u64 base = L2_MAX_PERIOD - (L2_CNT_PERIOD - 1);
> +	u32 idx = hwc->idx;
> +	u64 prev = local64_read(&hwc->prev_count);
> +	u64 value;
> +
> +	/*
> +	 * Limit the maximum period to prevent the counter value
> +	 * from overtaking the one we are about to program.
> +	 * Use a starting value which is high enough that after
> +	 * an overflow, interrupt latency will not cause the count
> +	 * to reach the base value. If the previous value
> +	 * is below the base, increase it to be above the base
> +	 * and update prev_count accordingly. Otherwise if
> +	 * the previous value is already above the base
> +	 * nothing needs to be done to prev_count.
> +	 */
> +	if (prev < base) {
> +		value = base + prev;
> +		local64_set(&hwc->prev_count, value);
> +	} else {
> +		value = prev;
> +	}
> +
> +	hml2_pmu__counter_set_value(idx, value);
> +}
> +
> +static int l2_cache__get_event_idx(struct hml2_pmu *cluster,
> +				   struct perf_event *event)
> +{
> +	struct hw_perf_event *hwc = &event->hw;
> +	int idx;
> +
> +	if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) {
> +		if (test_and_set_bit(l2_cycle_ctr_idx, cluster->used_counters))
> +			return -EAGAIN;
> +
> +		return l2_cycle_ctr_idx;
> +	}
> +
> +	for (idx = 0; idx < cluster->l2cache_pmu->num_counters - 1; idx++) {
> +		if (!test_and_set_bit(idx, cluster->used_counters)) {
> +			set_bit(L2_EVT_GROUP(hwc->config_base),
> +				cluster->used_groups);
> +			return idx;
> +		}
> +	}
> +
> +	/* The counters are all in use. */
> +	return -EAGAIN;
> +}
> +
> +static void l2_cache__clear_event_idx(struct hml2_pmu *cluster,
> +				      struct perf_event *event)
> +{
> +	struct hw_perf_event *hwc = &event->hw;
> +	int idx = hwc->idx;
> +
> +	clear_bit(idx, cluster->used_counters);
> +	if (hwc->config_base != L2CYCLE_CTR_RAW_CODE)
> +		clear_bit(L2_EVT_GROUP(hwc->config_base), cluster->used_groups);
> +}
> +
> +static irqreturn_t l2_cache__handle_irq(int irq_num, void *data)
> +{
> +	struct hml2_pmu *cluster = data;
> +	int num_counters = cluster->l2cache_pmu->num_counters;
> +	u32 ovsr;
> +	int idx;
> +
> +	ovsr = hml2_pmu__getreset_ovsr();
> +	if (!hml2_pmu__has_overflowed(ovsr))
> +		return IRQ_NONE;
> +
> +	for_each_set_bit(idx, cluster->used_counters, num_counters) {
> +		struct perf_event *event = cluster->events[idx];
> +		struct hw_perf_event *hwc;
> +
> +		if (!hml2_pmu__counter_has_overflowed(ovsr, idx))
> +			continue;
> +
> +		l2_cache__event_update_from_cluster(event, cluster);
> +		hwc = &event->hw;
> +
> +		l2_cache__cluster_set_period(cluster, hwc);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * Implementation of abstract pmu functionality required by
> + * the core perf events code.
> + */
> +
> +static void l2_cache__pmu_enable(struct pmu *pmu)
> +{
> +	/*
> +	 * Although there is only one PMU (per socket) controlling multiple
> +	 * physical PMUs (per cluster), because we do not support per-task mode
> +	 * each event is associated with a CPU. Each event has pmu_enable
> +	 * called on its CPU, so here it is only necessary to enable the
> +	 * counters for the current CPU.
> +	 */
> +
> +	hml2_pmu__enable();
> +}
> +
> +static void l2_cache__pmu_disable(struct pmu *pmu)
> +{
> +	hml2_pmu__disable();
> +}
> +
> +static int l2_cache__event_init(struct perf_event *event)
> +{
> +	struct hw_perf_event *hwc = &event->hw;
> +	struct hml2_pmu *cluster;
> +	struct perf_event *sibling;
> +	struct l2cache_pmu *l2cache_pmu;
> +
> +	if (event->attr.type != event->pmu->type)
> +		return -ENOENT;
> +
> +	l2cache_pmu = to_l2cache_pmu(event->pmu);
> +
> +	if (hwc->sample_period) {
> +		dev_warn(&l2cache_pmu->pdev->dev, "Sampling not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +
> +	if (event->cpu < 0) {
> +		dev_warn(&l2cache_pmu->pdev->dev, "Per-task mode not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +
> +	/* We cannot filter accurately so we just don't allow it. */
> +	if (event->attr.exclude_user || event->attr.exclude_kernel ||
> +	    event->attr.exclude_hv || event->attr.exclude_idle) {
> +		dev_warn(&l2cache_pmu->pdev->dev, "Can't exclude execution levels\n");
> +		return -EOPNOTSUPP;
> +	}
> +
> +	if (((L2_EVT_GROUP(event->attr.config) > L2_EVT_GROUP_MAX) ||
> +	     ((event->attr.config & ~L2_EVT_MASK) != 0)) &&
> +	    (event->attr.config != L2CYCLE_CTR_RAW_CODE)) {
> +		dev_warn(&l2cache_pmu->pdev->dev, "Invalid config %llx\n",
> +			 event->attr.config);
> +		return -EINVAL;
> +	}
> +
> +	/* Don't allow groups with mixed PMUs, except for s/w events */
> +	if (event->group_leader->pmu != event->pmu &&
> +	    !is_software_event(event->group_leader)) {
> +		dev_warn(&l2cache_pmu->pdev->dev,
> +			 "Can't create mixed PMU group\n");
> +		return -EINVAL;
> +	}
> +
> +	list_for_each_entry(sibling, &event->group_leader->sibling_list,
> +			    group_entry)
> +		if (sibling->pmu != event->pmu &&
> +		    !is_software_event(sibling)) {
> +			dev_warn(&l2cache_pmu->pdev->dev,
> +				 "Can't create mixed PMU group\n");
> +			return -EINVAL;
> +		}
> +
> +	/* Ensure all events in a group are on the same cpu */
> +	cluster = get_hml2_pmu(event->cpu);
> +	if ((event->group_leader != event) &&
> +	    (cluster->on_cpu != event->group_leader->cpu)) {
> +		dev_warn(&l2cache_pmu->pdev->dev,
> +			 "Can't create group on CPUs %d and %d",
> +			 event->cpu, event->group_leader->cpu);
> +		return -EINVAL;
> +	}
> +
> +	hwc->idx = -1;
> +	hwc->config_base = event->attr.config;
> +
> +	/*
> +	 * Ensure all events are on the same cpu so all events are in the
> +	 * same cpu context, to avoid races on pmu_enable etc.
> +	 */
> +	event->cpu = cluster->on_cpu;
> +
> +	return 0;
> +}
> +
> +static void l2_cache__event_start(struct perf_event *event, int flags)
> +{
> +	struct hml2_pmu *cluster;
> +	struct hw_perf_event *hwc = &event->hw;
> +	int idx = hwc->idx;
> +	u32 config;
> +	u32 event_cc, event_group;
> +
> +	hwc->state = 0;
> +
> +	cluster = get_hml2_pmu(event->cpu);
> +	l2_cache__cluster_set_period(cluster, hwc);
> +
> +	if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) {
> +		hml2_pmu__set_evccntcr(0x0);
> +	} else {
> +		config = hwc->config_base;
> +		event_cc    = L2_EVT_CODE(config);
> +		event_group = L2_EVT_GROUP(config);
> +
> +		hml2_pmu__set_evcntcr(idx, 0x0);
> +		hml2_pmu__set_evtyper(idx, event_group);
> +		hml2_pmu__set_resr(cluster, event_group, event_cc);
> +		hml2_pmu__set_evfilter_sys_mode(idx);
> +	}
> +
> +	hml2_pmu__counter_enable_interrupt(idx);
> +	hml2_pmu__counter_enable(idx);
> +}
> +
> +static void l2_cache__event_stop(struct perf_event *event, int flags)
> +{
> +	struct hml2_pmu *cluster;
> +	struct hw_perf_event *hwc = &event->hw;
> +	int idx = hwc->idx;
> +
> +	if (!(hwc->state & PERF_HES_STOPPED)) {
> +		cluster = get_hml2_pmu(event->cpu);
> +		hml2_pmu__counter_disable_interrupt(idx);
> +		hml2_pmu__counter_disable(idx);
> +
> +		if (flags & PERF_EF_UPDATE)
> +			l2_cache__event_update_from_cluster(event, cluster);
> +		hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
> +	}
> +}
> +
> +static int l2_cache__event_add(struct perf_event *event, int flags)
> +{
> +	struct hw_perf_event *hwc = &event->hw;
> +	int idx;
> +	int err = 0;
> +	struct hml2_pmu *cluster;
> +
> +	cluster = get_hml2_pmu(event->cpu);
> +
> +	idx = l2_cache__get_event_idx(cluster, event);
> +	if (idx < 0) {
> +		err = idx;
> +		return err;
> +	}
> +
> +	hwc->idx = idx;
> +	hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
> +	cluster->events[idx] = event;
> +	cluster->group_to_counter[L2_EVT_GROUP(hwc->config_base)] = idx;
> +	local64_set(&hwc->prev_count, 0ULL);
> +
> +	if (flags & PERF_EF_START)
> +		l2_cache__event_start(event, flags);
> +
> +	/* Propagate changes to the userspace mapping. */
> +	perf_event_update_userpage(event);
> +
> +	return err;
> +}
> +
> +static void l2_cache__event_del(struct perf_event *event, int flags)
> +{
> +	struct hw_perf_event *hwc = &event->hw;
> +	struct hml2_pmu *cluster;
> +	int idx = hwc->idx;
> +
> +	cluster = get_hml2_pmu(event->cpu);
> +	l2_cache__event_stop(event, flags | PERF_EF_UPDATE);
> +	cluster->events[idx] = NULL;
> +	l2_cache__clear_event_idx(cluster, event);
> +
> +	perf_event_update_userpage(event);
> +}
> +
> +static void l2_cache__event_read(struct perf_event *event)
> +{
> +	l2_cache__event_update_from_cluster(event, get_hml2_pmu(event->cpu));
> +}
> +
> +static int l2_cache_filter_match(struct perf_event *event)
> +{
> +	struct hw_perf_event *hwc = &event->hw;
> +	struct hml2_pmu *cluster = get_hml2_pmu(event->cpu);
> +	unsigned int group = L2_EVT_GROUP(hwc->config_base);
> +
> +	/* check for column exclusion: group already in use by another event */
> +	if (test_bit(group, cluster->used_groups) &&
> +	    cluster->events[cluster->group_to_counter[group]] != event)
> +		return 0;
> +
> +	return 1;
> +}
> +
> +static ssize_t l2_cache_pmu_cpumask_show(struct device *dev,
> +					 struct device_attribute *attr,
> +					 char *buf)
> +{
> +	struct l2cache_pmu *l2cache_pmu = to_l2cache_pmu(dev_get_drvdata(dev));
> +
> +	return cpumap_print_to_pagebuf(true, buf, &l2cache_pmu->cpumask);
> +}
> +
> +static struct device_attribute l2_cache_pmu_cpumask_attr =
> +		__ATTR(cpumask, S_IRUGO, l2_cache_pmu_cpumask_show, NULL);
> +
> +static struct attribute *l2_cache_pmu_cpumask_attrs[] = {
> +	&l2_cache_pmu_cpumask_attr.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group l2_cache_pmu_cpumask_group = {
> +	.attrs = l2_cache_pmu_cpumask_attrs,
> +};
> +
> +/* CCG format for perf RAW codes. */
> +PMU_FORMAT_ATTR(l2_code,   "config:4-11");
> +PMU_FORMAT_ATTR(l2_group,  "config:0-3");
> +static struct attribute *l2_cache_pmu_formats[] = {
> +	&format_attr_l2_code.attr,
> +	&format_attr_l2_group.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group l2_cache_pmu_format_group = {
> +	.name = "format",
> +	.attrs = l2_cache_pmu_formats,
> +};
> +
> +static const struct attribute_group *l2_cache_pmu_attr_grps[] = {
> +	&l2_cache_pmu_format_group,
> +	&l2_cache_pmu_cpumask_group,
> +	NULL,
> +};
> +
> +/*
> + * Generic device handlers
> + */
> +
> +static const struct acpi_device_id l2_cache_pmu_acpi_match[] = {
> +	{ "QCOM8130", },
> +	{ }
> +};
> +
> +static int get_num_counters(void)
> +{
> +	int val;
> +
> +	val = get_l2_indirect_reg(L2PMCR);
> +
> +	/*
> +	 * Read number of counters from L2PMCR and add 1
> +	 * for the cycle counter.
> +	 */
> +	return ((val >> L2PMCR_NUM_EV_SHIFT) & L2PMCR_NUM_EV_MASK) + 1;
> +}
> +
> +static int l2cache_pmu_online_cpu(unsigned int cpu, struct hlist_node *node)
> +{
> +	struct hml2_pmu *cluster;
> +	cpumask_t cluster_online_cpus;
> +	struct l2cache_pmu *l2cache_pmu;
> +
> +	l2cache_pmu = hlist_entry_safe(node, struct l2cache_pmu, node);
> +	cluster = get_hml2_pmu(cpu);
> +	cpumask_and(&cluster_online_cpus, &cluster->cluster_cpus,
> +		    cpu_online_mask);
> +
> +	if (cpumask_weight(&cluster_online_cpus) == 1) {
> +		/* all CPUs on this cluster were down, use this one */
> +		cluster->on_cpu = cpu;
> +		cpumask_set_cpu(cpu, &l2cache_pmu->cpumask);
> +		WARN_ON(irq_set_affinity(cluster->irq, cpumask_of(cpu)));
> +	}
> +
> +	return 0;
> +}
> +
> +static int l2cache_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node)
> +{
> +	struct hml2_pmu *cluster;
> +	struct l2cache_pmu *l2cache_pmu;
> +	cpumask_t cluster_online_cpus;
> +	unsigned int target;
> +
> +	l2cache_pmu = hlist_entry_safe(node, struct l2cache_pmu, node);
> +
> +	if (!cpumask_test_and_clear_cpu(cpu, &l2cache_pmu->cpumask))
> +		return 0;
> +	cluster = get_hml2_pmu(cpu);
> +	cpumask_and(&cluster_online_cpus, &cluster->cluster_cpus,
> +		    cpu_online_mask);
> +
> +	/* Any other CPU for this cluster which is still online */
> +	target = cpumask_any_but(&cluster_online_cpus, cpu);
> +	if (target >= nr_cpu_ids)
> +		return 0;
> +
> +	perf_pmu_migrate_context(&l2cache_pmu->pmu, cpu, target);
> +	cluster->on_cpu = target;
> +	cpumask_set_cpu(target, &l2cache_pmu->cpumask);
> +	WARN_ON(irq_set_affinity(cluster->irq, cpumask_of(target)));
> +
> +	return 0;
> +}
> +
> +static int l2_cache_pmu_probe_cluster(struct device *dev, void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev->parent);
> +	struct platform_device *sdev = to_platform_device(dev);
> +	struct l2cache_pmu *l2cache_pmu = data;
> +	struct hml2_pmu *cluster;
> +	struct acpi_device *device;
> +	unsigned long fw_cluster_id;
> +	int cpu;
> +	int err;
> +	int irq;
> +
> +	if (acpi_bus_get_device(ACPI_HANDLE(dev), &device))
> +		return -ENODEV;
> +
> +	if (kstrtol(device->pnp.unique_id, 10, &fw_cluster_id) < 0) {
> +		dev_err(&pdev->dev, "unable to read ACPI uid\n");
> +		return -ENODEV;
> +	}
> +
> +	irq = platform_get_irq(sdev, 0);
> +	if (irq < 0) {
> +		dev_err(&pdev->dev,
> +			"Failed to get valid irq for cluster %ld\n",
> +			fw_cluster_id);
> +		return irq;
> +	}
> +
> +	cluster = devm_kzalloc(&pdev->dev, sizeof(*cluster), GFP_KERNEL);
> +	if (!cluster)
> +		return -ENOMEM;
> +
> +	cluster->l2cache_pmu = l2cache_pmu;
> +	for_each_present_cpu(cpu) {
> +		if (topology_physical_package_id(cpu) == fw_cluster_id) {
> +			cpumask_set_cpu(cpu, &cluster->cluster_cpus);
> +			per_cpu(pmu_cluster, cpu) = cluster;
> +		}
> +	}
> +	cluster->irq = irq;
> +
> +	if (cpumask_empty(&cluster->cluster_cpus)) {
> +		dev_err(&pdev->dev, "No CPUs found for L2 cache instance %ld\n",
> +			fw_cluster_id);
> +		return -ENODEV;
> +	}
> +
> +	/* Pick one CPU to be the preferred one to use in the cluster */
> +	cluster->on_cpu = cpumask_first(&cluster->cluster_cpus);
> +
> +	if (irq_set_affinity(irq, cpumask_of(cluster->on_cpu))) {
> +		dev_err(&pdev->dev,
> +			"Unable to set irq affinity (irq=%d, cpu=%d)\n",
> +			irq, cluster->on_cpu);
> +		return -ENODEV;
> +	}
> +
> +	err = devm_request_irq(&pdev->dev, irq, l2_cache__handle_irq,
> +			       IRQF_NOBALANCING, "l2-cache-pmu", cluster);
> +	if (err) {
> +		dev_err(&pdev->dev,
> +			"Unable to request IRQ%d for L2 PMU counters\n", irq);
> +		return err;
> +	}
> +
> +	dev_info(&pdev->dev,
> +		 "Registered L2 cache PMU instance %ld with %d CPUs\n",
> +		 fw_cluster_id, cpumask_weight(&cluster->cluster_cpus));
> +
> +	cluster->pmu_lock = __SPIN_LOCK_UNLOCKED(cluster->pmu_lock);
> +	cpumask_set_cpu(cluster->on_cpu, &l2cache_pmu->cpumask);
> +
> +	hml2_pmu__reset(cluster);
> +	l2cache_pmu->num_pmus++;
> +
> +	return 0;
> +}
> +
> +static int l2_cache_pmu_probe(struct platform_device *pdev)
> +{
> +	int err;
> +	struct l2cache_pmu *l2cache_pmu;
> +
> +	l2cache_pmu =
> +		devm_kzalloc(&pdev->dev, sizeof(*l2cache_pmu), GFP_KERNEL);
> +	if (!l2cache_pmu)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, l2cache_pmu);
> +	l2cache_pmu->pmu = (struct pmu) {
> +		/* suffix is instance id for future use with multiple sockets */
> +		.name		= "l2cache_0",
> +		.task_ctx_nr    = perf_invalid_context,
> +		.pmu_enable	= l2_cache__pmu_enable,
> +		.pmu_disable	= l2_cache__pmu_disable,
> +		.event_init	= l2_cache__event_init,
> +		.add		= l2_cache__event_add,
> +		.del		= l2_cache__event_del,
> +		.start		= l2_cache__event_start,
> +		.stop		= l2_cache__event_stop,
> +		.read		= l2_cache__event_read,
> +		.attr_groups	= l2_cache_pmu_attr_grps,
> +		.filter_match   = l2_cache_filter_match,
> +	};
> +
> +	l2cache_pmu->num_counters = get_num_counters();
> +	l2cache_pmu->pdev = pdev;
> +	l2_cycle_ctr_idx = l2cache_pmu->num_counters - 1;
> +	l2_counter_present_mask = GENMASK(l2cache_pmu->num_counters - 2, 0) |
> +		L2PM_CC_ENABLE;
> +
> +	cpumask_clear(&l2cache_pmu->cpumask);
> +
> +	/* Read cluster info and initialize each cluster */
> +	err = device_for_each_child(&pdev->dev, l2cache_pmu,
> +				    l2_cache_pmu_probe_cluster);
> +	if (err < 0)
> +		return err;
> +
> +	if (l2cache_pmu->num_pmus == 0) {
> +		dev_err(&pdev->dev, "No hardware L2 cache PMUs found\n");
> +		return -ENODEV;
> +	}
> +
> +	err = perf_pmu_register(&l2cache_pmu->pmu, l2cache_pmu->pmu.name, -1);
> +	if (err < 0) {
> +		dev_err(&pdev->dev, "Error %d registering L2 cache PMU\n", err);
> +		return err;
> +	}
> +
> +	dev_info(&pdev->dev, "Registered L2 cache PMU using %d HW PMUs\n",
> +		 l2cache_pmu->num_pmus);
> +
> +	err = cpuhp_state_add_instance_nocalls(CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
> +					       &l2cache_pmu->node);
> +
> +	return err;
> +}
> +
> +static int l2_cache_pmu_remove(struct platform_device *pdev)
> +{
> +	struct l2cache_pmu *l2cache_pmu =
> +		to_l2cache_pmu(platform_get_drvdata(pdev));
> +
> +	cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
> +					       &l2cache_pmu->node);
> +	perf_pmu_unregister(&l2cache_pmu->pmu);
> +	return 0;
> +}
> +
> +static struct platform_driver l2_cache_pmu_driver = {
> +	.driver = {
> +		.name = "qcom-l2cache-pmu",
> +		.owner = THIS_MODULE,
> +		.acpi_match_table = ACPI_PTR(l2_cache_pmu_acpi_match),
> +	},
> +	.probe = l2_cache_pmu_probe,
> +	.remove = l2_cache_pmu_remove,
> +};
> +
> +static int __init register_l2_cache_pmu_driver(void)
> +{
> +	int err;
> +
> +	err = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
> +				      "AP_PERF_ARM_QCOM_L2_ONLINE",
> +				      l2cache_pmu_online_cpu,
> +				      l2cache_pmu_offline_cpu);
> +	if (err)
> +		return err;
> +
> +	return platform_driver_register(&l2_cache_pmu_driver);
> +}
> +device_initcall(register_l2_cache_pmu_driver);
> diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
> index 45a4287..f342842 100644
> --- a/include/linux/cpuhotplug.h
> +++ b/include/linux/cpuhotplug.h
> @@ -113,6 +113,7 @@ enum cpuhp_state {
>  	CPUHP_AP_PERF_ARM_CCI_ONLINE,
>  	CPUHP_AP_PERF_ARM_CCN_ONLINE,
>  	CPUHP_AP_PERF_ARM_L2X0_ONLINE,
> +	CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
>  	CPUHP_AP_WORKQUEUE_ONLINE,
>  	CPUHP_AP_RCUTREE_ONLINE,
>  	CPUHP_AP_NOTIFY_ONLINE,
> 

I believe this addresses all the issues raised previously - are there any other comments? Thanks. 

Neil

-- 
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.

^ permalink raw reply

* Coresight ETF trace dump failed in Juno r1 with 4.8-rc8
From: Suzuki K Poulose @ 2016-10-04 14:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAGhh56H7a9oEYxzEA4gGvBey=COMqwgisd4tzewU8DLGcGrsww@mail.gmail.com>

On 04/10/16 06:37, Venkatesh Vivekanandan wrote:
> On Mon, Oct 3, 2016 at 6:44 PM, Sudeep Holla <sudeep.holla@arm.com> wrote:
>> Hi Venkatesh,
>>
>> On 03/10/16 12:36, Venkatesh Vivekanandan wrote:
>>>
>>> Hi All,
>>>
>>> I am trying to collect ETF trace from Juno R1 and could see "cpu
>>> stall" while dumping the trace. Attached is the log of sequence
>>> followed. Was trying to collect the trace data from hardware and see
>>> if it is any valid data. Am I missing anything here?.
>>>
>>
>> There are few fixes from me and Suzuki queued for v4.9.
>> Can you check if this issue persists even on linux-next ?
>
> Issue is the same in linux-next as well. Please find the attached log.
>
> linaro-test [rc=0]# dd if=/dev/20010000.etf of=/cstrace.bin bs=1
> [  120.009698] INFO: rcu_preempt detected stalls on CPUs/tasks:
> [  120.015307]  2-...: (1 GPs behind) idle=f11/140000000000000/0
> softirq=224/224 fqs=1903
> [  120.023226]  (detected by 1, t=5255 jiffies, g=-1, c=-2, q=19)
> [  120.029001] Task dump for CPU 2:
> [  120.032190] dd              R  running task        0  1270   1267 0x00000002
> [  120.039172] Call trace:
> [  120.041594] [<ffff000008085534>] __switch_to+0xc8/0xd4
> [  120.046675] [<0000000000020000>] 0x20000
>
> Steps followed,
> # git clone git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
> linux-next
> # cd linux-next
> # make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
> # make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig  <---
> enable coresight
> # make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j8 Image
> # make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- dtbs
> # arch/arm64/boot/Image <--- copied this kernel
> # arch/arm64/boot/dts/arm/juno-r1.dtb <--- copied this dtb
>
> Top commit in linux-next is,
>
> commit c7d3b912180a9bb0733e5cfab84e5a7493dd3599
> Author: Stephen Rothwell <sfr@canb.auug.org.au>
> Date:   Tue Oct 4 14:52:03 2016 +1100
>
>     Add linux-next specific files for 20161004

Can't reproduce it here either.

root at localhost:/sys/bus/coresight/devices# echo 1 > 20010000.etf/enable_sink
root at localhost:/sys/bus/coresight/devices# echo 1 > 22140000.etm/enable_source
root at localhost:/sys/bus/coresight/devices# dd if=/dev/20010000.etf bs=1 of=/root/etr.bin
65536+0 records in
65536+0 records out
65536 bytes (66 kB) copied, 0.227546 s, 288 kB/s
root at localhost:/sys/bus/coresight/devices# dd if=/dev/20010000.etf bs=1 of=/root/etr.bin
65536+0 records in
65536+0 records out
65536 bytes (66 kB) copied, 0.233527 s, 281 kB/s
root at localhost:/sys/bus/coresight/devices# echo 0 > 20010000.etf/enable_sink
root at localhost:/sys/bus/coresight/devices# dd if=/dev/20010000.etf bs=1 of=/root/etr.bin
65536+0 records in
65536+0 records out
65536 bytes (66 kB) copied, 0.474943 s, 138 kB/s

FWIW, here is my firmware version :

NOTICE:  Booting Trusted Firmware
NOTICE:  BL1: v1.1(release):e04723e21362
NOTICE:  BL1: Built : 15:39:56, Sep  1 2015
NOTICE:  BL1: Booting BL2
NOTICE:  BL2: v1.1(release):e04723e21362
NOTICE:  BL2: Built : 15:42:30, Sep  1 2015
NOTICE:  BL1: Booting BL3-1
NOTICE:  BL3-1: v1.1(release):604d5da6f2aa
NOTICE:  BL3-1: Built : 14:50:36, Sep 10 2015
UEFI firmware (version ea31f8e built at 16:35:17 on Aug  5 2015)


Cheers
Suzuki

^ permalink raw reply

* [PATCH 3/3] Revert "ACPI,PCI,IRQ: remove SCI penalize function"
From: Thomas Gleixner @ 2016-10-04 14:23 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <babdb433-2be5-2c04-cbdd-c2af443447cb@codeaurora.org>

On Tue, 4 Oct 2016, Sinan Kaya wrote:

> On 10/4/2016 3:23 AM, Thomas Gleixner wrote:
> > On Sat, 1 Oct 2016, Sinan Kaya wrote:
> > 
> >> This reverts commit 9e5ed6d1fb87 ("ACPI,PCI,IRQ: remove SCI penalize
> >> function"). SCI penalty API was replaced by the runtime penalty calculation
> >> based on the value of acpi_gbl_FADT.sci_interrupt.
> > 
> > This does more than only reverting said commit ....
> 
> The SCI function was removed in two steps (first refactor and then remove). 
> I was trying to do the revert at one step. I can divide into two if it makes
> it better

No one step is fine. But this wants to be documented in the changelog.

> >> acpi_gbl_FADT.sci_interrupt type does not get updated at the right time
> >> for some platforms and results in incorrect penalty assignment for PCI
> >> IRQs as irq_get_trigger_type returns the wrong type.
> > 
> > And the obvious question is: Why does irq_get_trigger_type() return the
> > wrong type?
> 
> Here is some history:
> 
> I now remember that Bjorn indicated the race condition possibility in this thread
> here.
> 
> https://lkml.org/lkml/2016/3/8/640

> My understanding is that register_gsi function delivers the IRQ found in
> the ACPI table to the interrupt controller driver.  Penalties are
> calculated before a link object is enabled to find out which interrupt
> has the least number of users. By the time penalties are calculated, the
> IRQ is not registered yet and it returns the wrong type.

Ok.

> > 
> > What's the root cause of this problem? Your changelog does not tell
> > anything.
> 
> If you are OK with the above description, I can add this to the commit message.

Yes please.
 
Thanks,

	tglx

^ permalink raw reply

* [PATCH v3 2/2] crypto: marvell - Don't break chain for computable last ahash requests
From: Boris Brezillon @ 2016-10-04 14:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161004125720.3347-3-romain.perier@free-electrons.com>

On Tue,  4 Oct 2016 14:57:20 +0200
Romain Perier <romain.perier@free-electrons.com> wrote:

> Currently, the driver breaks chain for all kind of hash requests in order to
> don't override intermediate states of partial ahash updates. However, some final
> ahash requests can be directly processed by the engine, and so without
> intermediate state. This is typically the case for most for the HMAC requests
> processed via IPSec.
> 
> This commits adds a TDMA descriptor to copy context for these of requests
> into the "op" dma pool, then it allow to chain these requests at the DMA level.
> The 'complete' operation is also updated to retrieve the MAC digest from the
> right location.
> 
> Signed-off-by: Romain Perier <romain.perier@free-electrons.com>
> ---
> 
> Changes in v3:
>  - Copy the whole context back to RAM and not just the digest. Also
>    fixed a rebase issue ^^ (whoops)
> 
> Changes in v2:
>  - Replaced BUG_ON by an error
>  - Add a variable "break_chain", with "type" to break the chain
> 
>    with ahash requests. It improves code readability.
>  drivers/crypto/marvell/hash.c | 79 +++++++++++++++++++++++++++++++++++--------
>  1 file changed, 64 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/crypto/marvell/hash.c b/drivers/crypto/marvell/hash.c
> index 9f28468..b36f196 100644
> --- a/drivers/crypto/marvell/hash.c
> +++ b/drivers/crypto/marvell/hash.c
> @@ -312,24 +312,53 @@ static void mv_cesa_ahash_complete(struct crypto_async_request *req)
>  	int i;
>  
>  	digsize = crypto_ahash_digestsize(crypto_ahash_reqtfm(ahashreq));
> -	for (i = 0; i < digsize / 4; i++)
> -		creq->state[i] = readl_relaxed(engine->regs + CESA_IVDIG(i));
>  
> -	if (creq->last_req) {
> +	if (mv_cesa_req_get_type(&creq->base) == CESA_DMA_REQ &&
> +	    !(creq->base.chain.last->flags & CESA_TDMA_BREAK_CHAIN)) {
> +		struct mv_cesa_tdma_desc *tdma = NULL;
> +		__le32 *data = NULL;
> +
> +		for (tdma = creq->base.chain.first; tdma; tdma = tdma->next) {
> +			u32 type = tdma->flags & CESA_TDMA_TYPE_MSK;
> +			if (type ==  CESA_TDMA_RESULT)
> +				break;
> +		}

You should be able to drop the DUMMY desc at the end of the chain and
replace it by the RESULT desc. This way, you won't have to iterate over
the chain to find the TDMA_RESULT element: it should always be the last
desc in the chain.

> +
> +		if (!tdma) {
> +			dev_err(cesa_dev->dev, "Failed to retrieve tdma "
> +					       "descriptor for outer data\n");
> +			return;
> +		}
> +
>  		/*
> -		 * Hardware's MD5 digest is in little endian format, but
> -		 * SHA in big endian format
> +		 * Result is already in the correct endianess when the SA is
> +		 * used
>  		 */
> -		if (creq->algo_le) {
> -			__le32 *result = (void *)ahashreq->result;
> +		data = tdma->op->ctx.hash.hash;
> +		for (i = 0; i < digsize / 4; i++)
> +			creq->state[i] = cpu_to_le32(data[i]);
>  
> -			for (i = 0; i < digsize / 4; i++)
> -				result[i] = cpu_to_le32(creq->state[i]);
> -		} else {
> -			__be32 *result = (void *)ahashreq->result;
> +		memcpy(ahashreq->result, data, digsize);
> +	} else {
> +		for (i = 0; i < digsize / 4; i++)
> +			creq->state[i] = readl_relaxed(engine->regs +
> +						       CESA_IVDIG(i));
> +		if (creq->last_req) {
> +			/*
> +			* Hardware's MD5 digest is in little endian format, but
> +			* SHA in big endian format
> +			*/
> +			if (creq->algo_le) {
> +				__le32 *result = (void *)ahashreq->result;
> +
> +				for (i = 0; i < digsize / 4; i++)
> +					result[i] = cpu_to_le32(creq->state[i]);
> +			} else {
> +				__be32 *result = (void *)ahashreq->result;
>  
> -			for (i = 0; i < digsize / 4; i++)
> -				result[i] = cpu_to_be32(creq->state[i]);
> +				for (i = 0; i < digsize / 4; i++)
> +					result[i] = cpu_to_be32(creq->state[i]);
> +			}
>  		}
>  	}
>  
> @@ -504,6 +533,12 @@ mv_cesa_ahash_dma_last_req(struct mv_cesa_tdma_chain *chain,
>  						CESA_SA_DESC_CFG_LAST_FRAG,
>  				      CESA_SA_DESC_CFG_FRAG_MSK);
>  
> +		ret = mv_cesa_dma_add_result_op(chain,
> +						CESA_SA_CFG_SRAM_OFFSET,
> +						CESA_SA_DATA_SRAM_OFFSET,
> +						CESA_TDMA_SRC_IN_SRAM, flags);
> +		if (ret)
> +			return ERR_PTR(-ENOMEM);
>  		return op;
>  	}
>  
> @@ -564,6 +599,8 @@ static int mv_cesa_ahash_dma_req_init(struct ahash_request *req)
>  	struct mv_cesa_op_ctx *op = NULL;
>  	unsigned int frag_len;
>  	int ret;
> +	u32 type;
> +	bool break_chain = true;
>  
>  	basereq->chain.first = NULL;
>  	basereq->chain.last = NULL;
> @@ -635,6 +672,16 @@ static int mv_cesa_ahash_dma_req_init(struct ahash_request *req)
>  		goto err_free_tdma;
>  	}
>  
> +	/*
> +	 * If results are copied via DMA, this means that this
> +	 * request can be directly processed by the engine,
> +	 * without partial updates. So we can chain it at the
> +	 * DMA level with other requests.
> +	 */
> +	type = basereq->chain.last->flags & CESA_TDMA_TYPE_MSK;
> +	if (type == CESA_TDMA_RESULT)
> +		break_chain = false;
> +
>  	if (op) {
>  		/* Add dummy desc to wait for crypto operation end */
>  		ret = mv_cesa_dma_add_dummy_end(&basereq->chain, flags);
> @@ -648,8 +695,10 @@ static int mv_cesa_ahash_dma_req_init(struct ahash_request *req)
>  	else
>  		creq->cache_ptr = 0;
>  
> -	basereq->chain.last->flags |= (CESA_TDMA_END_OF_REQ |
> -				       CESA_TDMA_BREAK_CHAIN);
> +	basereq->chain.last->flags |= CESA_TDMA_END_OF_REQ;
> +
> +	if (break_chain)
> +		basereq->chain.last->flags |= CESA_TDMA_BREAK_CHAIN;

Not sure this break_chain variable is really needed. you can directly
test the type of the last element in the TDMA chain here and if it's
!= CESA_TDMA_RESULT, pass the CESA_TDMA_BREAK_CHAIN flag.

>  
>  	return 0;
>  

^ permalink raw reply

* [PATCH 3/3] Revert "ACPI,PCI,IRQ: remove SCI penalize function"
From: Sinan Kaya @ 2016-10-04 14:10 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <alpine.DEB.2.20.1610040919110.5278@nanos>

On 10/4/2016 3:23 AM, Thomas Gleixner wrote:
> On Sat, 1 Oct 2016, Sinan Kaya wrote:
> 
>> This reverts commit 9e5ed6d1fb87 ("ACPI,PCI,IRQ: remove SCI penalize
>> function"). SCI penalty API was replaced by the runtime penalty calculation
>> based on the value of acpi_gbl_FADT.sci_interrupt.
> 
> This does more than only reverting said commit ....

The SCI function was removed in two steps (first refactor and then remove). 
I was trying to do the revert at one step. I can divide into two if it makes
it better.

>  
>> acpi_gbl_FADT.sci_interrupt type does not get updated at the right time
>> for some platforms and results in incorrect penalty assignment for PCI
>> IRQs as irq_get_trigger_type returns the wrong type.
> 
> And the obvious question is: Why does irq_get_trigger_type() return the
> wrong type?

Here is some history:

I now remember that Bjorn indicated the race condition possibility in this thread
here.

https://lkml.org/lkml/2016/3/8/640

My understanding is that register_gsi function delivers the IRQ found in the ACPI table
to the interrupt controller driver.

Penalties are calculated before a link object is enabled to find out which interrupt
has the least number of users. By the time penalties are calculated, the IRQ is not
registered yet and it returns the wrong type.

> 
> What's the root cause of this problem? Your changelog does not tell
> anything.

If you are OK with the above description, I can add this to the commit message.


> 
> Thanks,
> 
> 	tglx
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


-- 
Sinan Kaya
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.

^ permalink raw reply

* [PATCH] arm: Added support for getcpu() vDSO using TPIDRURW
From: Fredrik Markstrom @ 2016-10-04 13:49 UTC (permalink / raw)
  To: linux-arm-kernel

This makes getcpu() ~1000 times faster, this is very useful when
implementing per-cpu buffers in userspace (to avoid cache line
bouncing). As an example lttng ust becomes ~30% faster.

The patch will break applications using TPIDRURW (which is context switched
since commit 4780adeefd042482f624f5e0d577bf9cdcbb760 ("ARM: 7735/2:
Preserve the user r/w register TPIDRURW on context switch and fork")) and
is therefore made configurable.

Signed-off-by: Fredrik Markstrom <fredrik.markstrom@gmail.com>
---
 arch/arm/include/asm/tls.h   |  8 +++++++-
 arch/arm/kernel/entry-armv.S |  1 -
 arch/arm/mm/Kconfig          | 10 ++++++++++
 arch/arm/vdso/Makefile       |  3 +++
 arch/arm/vdso/vdso.lds.S     |  3 +++
 5 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/arch/arm/include/asm/tls.h b/arch/arm/include/asm/tls.h
index 5f833f7..170fd76 100644
--- a/arch/arm/include/asm/tls.h
+++ b/arch/arm/include/asm/tls.h
@@ -10,10 +10,15 @@
 	.endm
 
 	.macro switch_tls_v6k, base, tp, tpuser, tmp1, tmp2
+#ifdef CONFIG_VDSO_GETCPU
+	ldr	\tpuser, [r2, #TI_CPU]
+#else
 	mrc	p15, 0, \tmp2, c13, c0, 2	@ get the user r/w register
+	ldr	\tpuser, [r2, #TI_TP_VALUE + 4]
+	str	\tmp2, [\base, #TI_TP_VALUE + 4] @ save it
+#endif
 	mcr	p15, 0, \tp, c13, c0, 3		@ set TLS register
 	mcr	p15, 0, \tpuser, c13, c0, 2	@ and the user r/w register
-	str	\tmp2, [\base, #TI_TP_VALUE + 4] @ save it
 	.endm
 
 	.macro switch_tls_v6, base, tp, tpuser, tmp1, tmp2
@@ -22,6 +27,7 @@
 	mov	\tmp2, #0xffff0fff
 	tst	\tmp1, #HWCAP_TLS		@ hardware TLS available?
 	streq	\tp, [\tmp2, #-15]		@ set TLS value at 0xffff0ff0
+	ldrne	\tpuser, [r2, #TI_TP_VALUE + 4] @ load the saved user r/w reg
 	mrcne	p15, 0, \tmp2, c13, c0, 2	@ get the user r/w register
 	mcrne	p15, 0, \tp, c13, c0, 3		@ yes, set TLS register
 	mcrne	p15, 0, \tpuser, c13, c0, 2	@ set user r/w register
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 9f157e7..4e1369a 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -787,7 +787,6 @@ ENTRY(__switch_to)
  THUMB(	str	sp, [ip], #4		   )
  THUMB(	str	lr, [ip], #4		   )
 	ldr	r4, [r2, #TI_TP_VALUE]
-	ldr	r5, [r2, #TI_TP_VALUE + 4]
 #ifdef CONFIG_CPU_USE_DOMAINS
 	mrc	p15, 0, r6, c3, c0, 0		@ Get domain register
 	str	r6, [r1, #TI_CPU_DOMAIN]	@ Save old domain register
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index c1799dd..f18334a 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -854,6 +854,16 @@ config VDSO
 	  You must have glibc 2.22 or later for programs to seamlessly
 	  take advantage of this.
 
+config VDSO_GETCPU
+	bool "Enable VDSO for getcpu"
+	depends on VDSO && (CPU_V6K || CPU_V7 || CPU_V7M)
+	help
+	  Say Y to make getcpu a VDSO (fast) call. This is useful if you
+	  want to implement per cpu buffers to avoid cache line bouncing
+	  in user mode.
+	  This mechanism uses the TPIDRURW register so enabling it will break
+	  applications using this register for it's own purpose.
+
 config DMA_CACHE_RWFO
 	bool "Enable read/write for ownership DMA cache maintenance"
 	depends on CPU_V6K && SMP
diff --git a/arch/arm/vdso/Makefile b/arch/arm/vdso/Makefile
index 59a8fa7..9f1ec51 100644
--- a/arch/arm/vdso/Makefile
+++ b/arch/arm/vdso/Makefile
@@ -1,6 +1,9 @@
 hostprogs-y := vdsomunge
 
 obj-vdso := vgettimeofday.o datapage.o
+#ifeq ($(CONFIG_VDSO_GETCPU),y)
+obj-vdso += vgetcpu.o
+#endif
 
 # Build rules
 targets := $(obj-vdso) vdso.so vdso.so.dbg vdso.so.raw vdso.lds
diff --git a/arch/arm/vdso/vdso.lds.S b/arch/arm/vdso/vdso.lds.S
index 89ca89f..1af39fb 100644
--- a/arch/arm/vdso/vdso.lds.S
+++ b/arch/arm/vdso/vdso.lds.S
@@ -82,6 +82,9 @@ VERSION
 	global:
 		__vdso_clock_gettime;
 		__vdso_gettimeofday;
+#ifdef CONFIG_VDSO_GETCPU
+		__vdso_getcpu;
+#endif
 	local: *;
 	};
 }
-- 
2.7.2

^ permalink raw reply related

* [PATCH 01/14] dma: sun6i-dma: Add burst case of 4
From: Jean-Francois Moine @ 2016-10-04 13:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161004141221.55327f1b@free-electrons.com>

On Tue, 4 Oct 2016 14:12:21 +0200
Thomas Petazzoni <thomas.petazzoni@free-electrons.com> wrote:

> > > Add the case of a burst of 4 which is handled by the SoC.
> > > 
> > > Signed-off-by: Myl?ne Josserand <mylene.josserand@free-electrons.com>
> > > ---
> > >  drivers/dma/sun6i-dma.c | 2 ++
> > >  1 file changed, 2 insertions(+)
> > > 
> > > diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
> > > index 8346199..0485204 100644
> > > --- a/drivers/dma/sun6i-dma.c
> > > +++ b/drivers/dma/sun6i-dma.c
> > > @@ -240,6 +240,8 @@ static inline s8 convert_burst(u32 maxburst)
> > >  	switch (maxburst) {
> > >  	case 1:
> > >  		return 0;
> > > +	case 4:
> > > +		return 1;
> > >  	case 8:
> > >  		return 2;
> > >  	default:
> > > -- 
> > > 2.9.3  
> > 
> > This patch has already been rejected by Maxime in the threads
> > 	http://www.spinics.net/lists/dmaengine/msg08610.html
> > and
> > 	http://www.spinics.net/lists/dmaengine/msg08719.html
> > 
> > I hope you will find the way he wants for this maxburst to be added.
> 
> I was about to reply to Mylene's e-mail, suggesting that she should add
> a comment in the code (and maybe in the commit log) to explain why this
> addition is needed, and also that even though the schematics say that
> value "1" (max burst size of 4 bytes) is reserved, it is in fact
> incorrect. The Allwinner BSP code is really using this value, and it's
> the value that makes audio work, so we believe the datasheet is simply
> incorrect.
> 
> We already discussed it with Maxime, so I believe he should agree this
> time. But I would suggest to have such details explained in the commit
> log and in a comment in the code.

Strange. Looking at the datasheets of the A23, A31, A33, A83T and H3
(these are the SoCs using the DMA sun6i), only the H3 can have 4 as the
burst size (the doc is unclear for the A31).

Well, I was submitting for the H3, Myl?ne is submitting for the A33.
So, what about the A23, A31 and A83T?

-- 
Ken ar c'henta?	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

^ permalink raw reply

* [PATCH v2 5/7] arm64: dts: exynos: Add dts files for Samsung Exynos5433 64bit SoC
From: Krzysztof Kozlowski @ 2016-10-04 13:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAMuHMdV3WdS75QXsJgEXirqV-d=RJa-LTq4OdvS6fgQWS+DO-A@mail.gmail.com>

On Tue, Oct 04, 2016 at 03:37:35PM +0200, Geert Uytterhoeven wrote:
> On Wed, Aug 24, 2016 at 3:49 PM, Chanwoo Choi <cw00.choi@samsung.com> wrote:
> > --- /dev/null
> > +++ b/arch/arm64/boot/dts/exynos/exynos5433.dtsi
> 
> > +       cluster_a53_opp_table: opp_table0 {
> > +               compatible = "operating-points-v2";
> > +               opp-shared;
> > +
> > +               opp at 400000000 {
> > +                       opp-hz = /bits/ 64 <400000000>;
> > +                       opp-microvolt = <900000>;
> > +               };
> 
> With W=1:
> 
> Warning (unit_address_vs_reg): Node /opp_table0/opp at 400000000 has a
> unit name, but no reg property

AFAIR, there should be an exception for this... [1] But maybe it was not
added after all?

Best regards,
Krzysztof


[1] http://lists.infradead.org/pipermail/linux-arm-kernel/2016-April/419738.html

^ permalink raw reply

* [PATCH 3/3] dt-bindings: oxnas: Update Pinctrl and GPIO for OX820 Support
From: Neil Armstrong @ 2016-10-04 13:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161004134148.23028-1-narmstrong@baylibre.com>

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 Documentation/devicetree/bindings/gpio/gpio_oxnas.txt       | 2 +-
 Documentation/devicetree/bindings/pinctrl/oxnas,pinctrl.txt | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/gpio/gpio_oxnas.txt b/Documentation/devicetree/bindings/gpio/gpio_oxnas.txt
index 928ed4f..9665147 100644
--- a/Documentation/devicetree/bindings/gpio/gpio_oxnas.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio_oxnas.txt
@@ -3,7 +3,7 @@
 Please refer to gpio.txt for generic information regarding GPIO bindings.
 
 Required properties:
- - compatible: "oxsemi,ox810se-gpio"
+ - compatible: "oxsemi,ox810se-gpio" or "oxsemi,ox820-gpio"
  - reg: Base address and length for the device.
  - interrupts: The port interrupt shared by all pins.
  - gpio-controller: Marks the port as GPIO controller.
diff --git a/Documentation/devicetree/bindings/pinctrl/oxnas,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/oxnas,pinctrl.txt
index d607432..09e81a9 100644
--- a/Documentation/devicetree/bindings/pinctrl/oxnas,pinctrl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/oxnas,pinctrl.txt
@@ -9,7 +9,7 @@ used for a specific device or function. This node represents configurations of
 pins, optional function, and optional mux related configuration.
 
 Required properties for pin controller node:
- - compatible: "oxsemi,ox810se-pinctrl"
+ - compatible: "oxsemi,ox810se-pinctrl" or "oxsemi,ox820-pinctrl"
  - oxsemi,sys-ctrl: a phandle to the system controller syscon node
 
 Required properties for pin configuration sub-nodes:
-- 
2.7.0

^ permalink raw reply related

* [PATCH 2/3] pinctrl: oxnas: Add support for OX820
From: Neil Armstrong @ 2016-10-04 13:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161004134148.23028-1-narmstrong@baylibre.com>

Add support for the Oxford Semiconductor OX820 which is similar as OX810 but
has 50 pins and two registers banks to setup alternate functions.
Add specific pins, groups and functions structures.
Add DT match data to select corresponding support.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 drivers/pinctrl/pinctrl-oxnas.c | 433 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 433 insertions(+)

diff --git a/drivers/pinctrl/pinctrl-oxnas.c b/drivers/pinctrl/pinctrl-oxnas.c
index 218ad48..494ec9a 100644
--- a/drivers/pinctrl/pinctrl-oxnas.c
+++ b/drivers/pinctrl/pinctrl-oxnas.c
@@ -47,6 +47,15 @@
 #define PINMUX_810_PULLUP_CTRL0		0xac
 #define PINMUX_810_PULLUP_CTRL1		0xb0
 
+/* OX820 Regmap Offsets */
+#define PINMUX_820_BANK_OFFSET		0x100000
+#define PINMUX_820_SECONDARY_SEL	0x14
+#define PINMUX_820_TERTIARY_SEL		0x8c
+#define PINMUX_820_QUATERNARY_SEL	0x94
+#define PINMUX_820_DEBUG_SEL		0x9c
+#define PINMUX_820_ALTERNATIVE_SEL	0xa4
+#define PINMUX_820_PULLUP_CTRL		0xac
+
 /* GPIO Registers */
 #define INPUT_VALUE	0x00
 #define OUTPUT_EN	0x04
@@ -138,6 +147,59 @@ static const struct pinctrl_pin_desc oxnas_ox810se_pins[] = {
 	PINCTRL_PIN(34, "gpio34"),
 };
 
+static const struct pinctrl_pin_desc oxnas_ox820_pins[] = {
+	PINCTRL_PIN(0, "gpio0"),
+	PINCTRL_PIN(1, "gpio1"),
+	PINCTRL_PIN(2, "gpio2"),
+	PINCTRL_PIN(3, "gpio3"),
+	PINCTRL_PIN(4, "gpio4"),
+	PINCTRL_PIN(5, "gpio5"),
+	PINCTRL_PIN(6, "gpio6"),
+	PINCTRL_PIN(7, "gpio7"),
+	PINCTRL_PIN(8, "gpio8"),
+	PINCTRL_PIN(9, "gpio9"),
+	PINCTRL_PIN(10, "gpio10"),
+	PINCTRL_PIN(11, "gpio11"),
+	PINCTRL_PIN(12, "gpio12"),
+	PINCTRL_PIN(13, "gpio13"),
+	PINCTRL_PIN(14, "gpio14"),
+	PINCTRL_PIN(15, "gpio15"),
+	PINCTRL_PIN(16, "gpio16"),
+	PINCTRL_PIN(17, "gpio17"),
+	PINCTRL_PIN(18, "gpio18"),
+	PINCTRL_PIN(19, "gpio19"),
+	PINCTRL_PIN(20, "gpio20"),
+	PINCTRL_PIN(21, "gpio21"),
+	PINCTRL_PIN(22, "gpio22"),
+	PINCTRL_PIN(23, "gpio23"),
+	PINCTRL_PIN(24, "gpio24"),
+	PINCTRL_PIN(25, "gpio25"),
+	PINCTRL_PIN(26, "gpio26"),
+	PINCTRL_PIN(27, "gpio27"),
+	PINCTRL_PIN(28, "gpio28"),
+	PINCTRL_PIN(29, "gpio29"),
+	PINCTRL_PIN(30, "gpio30"),
+	PINCTRL_PIN(31, "gpio31"),
+	PINCTRL_PIN(32, "gpio32"),
+	PINCTRL_PIN(33, "gpio33"),
+	PINCTRL_PIN(34, "gpio34"),
+	PINCTRL_PIN(35, "gpio35"),
+	PINCTRL_PIN(36, "gpio36"),
+	PINCTRL_PIN(37, "gpio37"),
+	PINCTRL_PIN(38, "gpio38"),
+	PINCTRL_PIN(39, "gpio39"),
+	PINCTRL_PIN(40, "gpio40"),
+	PINCTRL_PIN(41, "gpio41"),
+	PINCTRL_PIN(42, "gpio42"),
+	PINCTRL_PIN(43, "gpio43"),
+	PINCTRL_PIN(44, "gpio44"),
+	PINCTRL_PIN(45, "gpio45"),
+	PINCTRL_PIN(46, "gpio46"),
+	PINCTRL_PIN(47, "gpio47"),
+	PINCTRL_PIN(48, "gpio48"),
+	PINCTRL_PIN(49, "gpio49"),
+};
+
 static const char * const oxnas_ox810se_fct0_group[] = {
 	"gpio0",  "gpio1",  "gpio2",  "gpio3",
 	"gpio4",  "gpio5",  "gpio6",  "gpio7",
@@ -161,6 +223,40 @@ static const char * const oxnas_ox810se_fct3_group[] = {
 	"gpio34"
 };
 
+static const char * const oxnas_ox820_fct0_group[] = {
+	"gpio0",  "gpio1",  "gpio2",  "gpio3",
+	"gpio4",  "gpio5",  "gpio6",  "gpio7",
+	"gpio8",  "gpio9",  "gpio10", "gpio11",
+	"gpio12", "gpio13", "gpio14", "gpio15",
+	"gpio16", "gpio17", "gpio18", "gpio19",
+	"gpio20", "gpio21", "gpio22", "gpio23",
+	"gpio24", "gpio25", "gpio26", "gpio27",
+	"gpio28", "gpio29", "gpio30", "gpio31",
+	"gpio32", "gpio33", "gpio34", "gpio35",
+	"gpio36", "gpio37", "gpio38", "gpio39",
+	"gpio40", "gpio41", "gpio42", "gpio43",
+	"gpio44", "gpio45", "gpio46", "gpio47",
+	"gpio48", "gpio49"
+};
+
+static const char * const oxnas_ox820_fct1_group[] = {
+	"gpio3", "gpio4",
+	"gpio12", "gpio13", "gpio14", "gpio15",
+	"gpio16", "gpio17", "gpio18", "gpio19",
+	"gpio20", "gpio21", "gpio22", "gpio23",
+	"gpio24"
+};
+
+static const char * const oxnas_ox820_fct4_group[] = {
+	"gpio5", "gpio6", "gpio7", "gpio8",
+	"gpio24", "gpio25", "gpio26", "gpio27",
+	"gpio40", "gpio41", "gpio42", "gpio43"
+};
+
+static const char * const oxnas_ox820_fct5_group[] = {
+	"gpio28", "gpio29", "gpio30", "gpio31"
+};
+
 #define FUNCTION(_name, _gr)					\
 	{							\
 		.name = #_name,					\
@@ -173,6 +269,13 @@ static const struct oxnas_function oxnas_ox810se_functions[] = {
 	FUNCTION(fct3, ox810se_fct3),
 };
 
+static const struct oxnas_function oxnas_ox820_functions[] = {
+	FUNCTION(gpio, ox820_fct0),
+	FUNCTION(fct1, ox820_fct1),
+	FUNCTION(fct4, ox820_fct4),
+	FUNCTION(fct5, ox820_fct5),
+};
+
 #define OXNAS_PINCTRL_GROUP(_pin, _name, ...)				\
 	{								\
 		.name = #_name,						\
@@ -285,6 +388,140 @@ static const struct oxnas_pin_group oxnas_ox810se_groups[] = {
 			OXNAS_PINCTRL_FUNCTION(fct3, 3)),
 };
 
+static const struct oxnas_pin_group oxnas_ox820_groups[] = {
+	OXNAS_PINCTRL_GROUP(0, gpio0,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(1, gpio1,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(2, gpio2,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(3, gpio3,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct1, 1)),
+	OXNAS_PINCTRL_GROUP(4, gpio4,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct1, 1)),
+	OXNAS_PINCTRL_GROUP(5, gpio5,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct4, 4)),
+	OXNAS_PINCTRL_GROUP(6, gpio6,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct4, 4)),
+	OXNAS_PINCTRL_GROUP(7, gpio7,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct4, 4)),
+	OXNAS_PINCTRL_GROUP(8, gpio8,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct4, 4)),
+	OXNAS_PINCTRL_GROUP(9, gpio9,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(10, gpio10,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(11, gpio11,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(12, gpio12,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct1, 1)),
+	OXNAS_PINCTRL_GROUP(13, gpio13,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct1, 1)),
+	OXNAS_PINCTRL_GROUP(14, gpio14,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct1, 1)),
+	OXNAS_PINCTRL_GROUP(15, gpio15,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct1, 1)),
+	OXNAS_PINCTRL_GROUP(16, gpio16,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct1, 1)),
+	OXNAS_PINCTRL_GROUP(17, gpio17,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct1, 1)),
+	OXNAS_PINCTRL_GROUP(18, gpio18,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct1, 1)),
+	OXNAS_PINCTRL_GROUP(19, gpio19,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct1, 1)),
+	OXNAS_PINCTRL_GROUP(20, gpio20,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct1, 1)),
+	OXNAS_PINCTRL_GROUP(21, gpio21,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct1, 1)),
+	OXNAS_PINCTRL_GROUP(22, gpio22,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct1, 1)),
+	OXNAS_PINCTRL_GROUP(23, gpio23,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct1, 1)),
+	OXNAS_PINCTRL_GROUP(24, gpio24,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct1, 1),
+			OXNAS_PINCTRL_FUNCTION(fct4, 5)),
+	OXNAS_PINCTRL_GROUP(25, gpio25,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct4, 4)),
+	OXNAS_PINCTRL_GROUP(26, gpio26,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct4, 4)),
+	OXNAS_PINCTRL_GROUP(27, gpio27,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct4, 4)),
+	OXNAS_PINCTRL_GROUP(28, gpio28,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct5, 5)),
+	OXNAS_PINCTRL_GROUP(29, gpio29,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct5, 5)),
+	OXNAS_PINCTRL_GROUP(30, gpio30,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct5, 5)),
+	OXNAS_PINCTRL_GROUP(31, gpio31,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct5, 5)),
+	OXNAS_PINCTRL_GROUP(32, gpio32,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(33, gpio33,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(34, gpio34,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(35, gpio35,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(36, gpio36,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(37, gpio37,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(38, gpio38,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(39, gpio39,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(40, gpio40,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct4, 4)),
+	OXNAS_PINCTRL_GROUP(41, gpio41,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct4, 4)),
+	OXNAS_PINCTRL_GROUP(42, gpio42,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct4, 4)),
+	OXNAS_PINCTRL_GROUP(43, gpio43,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0),
+			OXNAS_PINCTRL_FUNCTION(fct4, 4)),
+	OXNAS_PINCTRL_GROUP(44, gpio44,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(45, gpio45,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(46, gpio46,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(47, gpio47,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(48, gpio48,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+	OXNAS_PINCTRL_GROUP(49, gpio49,
+			OXNAS_PINCTRL_FUNCTION(gpio, 0)),
+};
+
 static inline struct oxnas_gpio_bank *pctl_to_bank(struct oxnas_pinctrl *pctl,
 						   unsigned int pin)
 {
@@ -405,6 +642,61 @@ static int oxnas_ox810se_pinmux_enable(struct pinctrl_dev *pctldev,
 	return -EINVAL;
 }
 
+static int oxnas_ox820_pinmux_enable(struct pinctrl_dev *pctldev,
+				     unsigned int func, unsigned int group)
+{
+	struct oxnas_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	const struct oxnas_pin_group *pg = &pctl->groups[group];
+	const struct oxnas_function *pf = &pctl->functions[func];
+	const char *fname = pf->name;
+	struct oxnas_desc_function *functions = pg->functions;
+	unsigned int offset = (pg->bank ? PINMUX_820_BANK_OFFSET : 0);
+	u32 mask = BIT(pg->pin);
+
+	while (functions->name) {
+		if (!strcmp(functions->name, fname)) {
+			dev_dbg(pctl->dev,
+				"setting function %s bank %d pin %d fct %d mask %x\n",
+				fname, pg->bank, pg->pin,
+				functions->fct, mask);
+
+			regmap_write_bits(pctl->regmap,
+					  offset + PINMUX_820_SECONDARY_SEL,
+					  mask,
+					  (functions->fct == 1 ?
+						mask : 0));
+			regmap_write_bits(pctl->regmap,
+					  offset + PINMUX_820_TERTIARY_SEL,
+					  mask,
+					  (functions->fct == 2 ?
+						mask : 0));
+			regmap_write_bits(pctl->regmap,
+					  offset + PINMUX_820_QUATERNARY_SEL,
+					  mask,
+					  (functions->fct == 3 ?
+						mask : 0));
+			regmap_write_bits(pctl->regmap,
+					  offset + PINMUX_820_DEBUG_SEL,
+					  mask,
+					  (functions->fct == 4 ?
+						mask : 0));
+			regmap_write_bits(pctl->regmap,
+					  offset + PINMUX_820_ALTERNATIVE_SEL,
+					  mask,
+					  (functions->fct == 5 ?
+						mask : 0));
+
+			return 0;
+		}
+
+		functions++;
+	}
+
+	dev_err(pctl->dev, "cannot mux pin %u to function %u\n", group, func);
+
+	return -EINVAL;
+}
+
 static int oxnas_ox810se_gpio_request_enable(struct pinctrl_dev *pctldev,
 					     struct pinctrl_gpio_range *range,
 					     unsigned int offset)
@@ -435,6 +727,37 @@ static int oxnas_ox810se_gpio_request_enable(struct pinctrl_dev *pctldev,
 	return 0;
 }
 
+static int oxnas_ox820_gpio_request_enable(struct pinctrl_dev *pctldev,
+					   struct pinctrl_gpio_range *range,
+					   unsigned int offset)
+{
+	struct oxnas_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	struct oxnas_gpio_bank *bank = gpiochip_get_data(range->gc);
+	unsigned int bank_offset = (bank->id ? PINMUX_820_BANK_OFFSET : 0);
+	u32 mask = BIT(offset - bank->gpio_chip.base);
+
+	dev_dbg(pctl->dev, "requesting gpio %d in bank %d (id %d) with mask 0x%x\n",
+		offset, bank->gpio_chip.base, bank->id, mask);
+
+	regmap_write_bits(pctl->regmap,
+			  bank_offset + PINMUX_820_SECONDARY_SEL,
+			  mask, 0);
+	regmap_write_bits(pctl->regmap,
+			  bank_offset + PINMUX_820_TERTIARY_SEL,
+			  mask, 0);
+	regmap_write_bits(pctl->regmap,
+			  bank_offset + PINMUX_820_QUATERNARY_SEL,
+			  mask, 0);
+	regmap_write_bits(pctl->regmap,
+			  bank_offset + PINMUX_820_DEBUG_SEL,
+			  mask, 0);
+	regmap_write_bits(pctl->regmap,
+			  bank_offset + PINMUX_820_ALTERNATIVE_SEL,
+			  mask, 0);
+
+	return 0;
+}
+
 static int oxnas_gpio_get_direction(struct gpio_chip *chip,
 				      unsigned int offset)
 {
@@ -510,6 +833,15 @@ static const struct pinmux_ops oxnas_ox810se_pinmux_ops = {
 	.gpio_set_direction = oxnas_gpio_set_direction,
 };
 
+static const struct pinmux_ops oxnas_ox820_pinmux_ops = {
+	.get_functions_count = oxnas_pinmux_get_functions_count,
+	.get_function_name = oxnas_pinmux_get_function_name,
+	.get_function_groups = oxnas_pinmux_get_function_groups,
+	.set_mux = oxnas_ox820_pinmux_enable,
+	.gpio_request_enable = oxnas_ox820_gpio_request_enable,
+	.gpio_set_direction = oxnas_gpio_set_direction,
+};
+
 static int oxnas_ox810se_pinconf_get(struct pinctrl_dev *pctldev,
 				     unsigned int pin, unsigned long *config)
 {
@@ -541,6 +873,36 @@ static int oxnas_ox810se_pinconf_get(struct pinctrl_dev *pctldev,
 	return 0;
 }
 
+static int oxnas_ox820_pinconf_get(struct pinctrl_dev *pctldev,
+				   unsigned int pin, unsigned long *config)
+{
+	struct oxnas_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	struct oxnas_gpio_bank *bank = pctl_to_bank(pctl, pin);
+	unsigned int param = pinconf_to_config_param(*config);
+	unsigned int bank_offset = (bank->id ? PINMUX_820_BANK_OFFSET : 0);
+	u32 mask = BIT(pin - bank->gpio_chip.base);
+	int ret;
+	u32 arg;
+
+	switch (param) {
+	case PIN_CONFIG_BIAS_PULL_UP:
+		ret = regmap_read(pctl->regmap,
+				  bank_offset + PINMUX_820_PULLUP_CTRL,
+				  &arg);
+		if (ret)
+			return ret;
+
+		arg = !!(arg & mask);
+		break;
+	default:
+		return -ENOTSUPP;
+	}
+
+	*config = pinconf_to_config_packed(param, arg);
+
+	return 0;
+}
+
 static int oxnas_ox810se_pinconf_set(struct pinctrl_dev *pctldev,
 				     unsigned int pin, unsigned long *configs,
 				     unsigned int num_configs)
@@ -579,12 +941,55 @@ static int oxnas_ox810se_pinconf_set(struct pinctrl_dev *pctldev,
 	return 0;
 }
 
+static int oxnas_ox820_pinconf_set(struct pinctrl_dev *pctldev,
+				   unsigned int pin, unsigned long *configs,
+				   unsigned int num_configs)
+{
+	struct oxnas_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	struct oxnas_gpio_bank *bank = pctl_to_bank(pctl, pin);
+	unsigned int bank_offset = (bank->id ? PINMUX_820_BANK_OFFSET : 0);
+	unsigned int param;
+	u32 arg;
+	unsigned int i;
+	u32 offset = pin - bank->gpio_chip.base;
+	u32 mask = BIT(offset);
+
+	dev_dbg(pctl->dev, "setting pin %d bank %d mask 0x%x\n",
+		pin, bank->gpio_chip.base, mask);
+
+	for (i = 0; i < num_configs; i++) {
+		param = pinconf_to_config_param(configs[i]);
+		arg = pinconf_to_config_argument(configs[i]);
+
+		switch (param) {
+		case PIN_CONFIG_BIAS_PULL_UP:
+			dev_dbg(pctl->dev, "   pullup\n");
+			regmap_write_bits(pctl->regmap,
+					  bank_offset + PINMUX_820_PULLUP_CTRL,
+					  mask, mask);
+			break;
+		default:
+			dev_err(pctl->dev, "Property %u not supported\n",
+				param);
+			return -ENOTSUPP;
+		}
+	}
+
+	return 0;
+}
+
 static const struct pinconf_ops oxnas_ox810se_pinconf_ops = {
 	.pin_config_get = oxnas_ox810se_pinconf_get,
 	.pin_config_set = oxnas_ox810se_pinconf_set,
 	.is_generic = true,
 };
 
+static const struct pinconf_ops oxnas_ox820_pinconf_ops = {
+	.pin_config_get = oxnas_ox820_pinconf_get,
+	.pin_config_set = oxnas_ox820_pinconf_set,
+	.is_generic = true,
+};
+
 static void oxnas_gpio_irq_ack(struct irq_data *data)
 {
 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
@@ -714,15 +1119,42 @@ static struct pinctrl_desc oxnas_ox810se_pinctrl_desc = {
 	.owner = THIS_MODULE,
 };
 
+static struct oxnas_pinctrl ox820_pinctrl = {
+	.functions = oxnas_ox820_functions,
+	.nfunctions = ARRAY_SIZE(oxnas_ox820_functions),
+	.groups = oxnas_ox820_groups,
+	.ngroups = ARRAY_SIZE(oxnas_ox820_groups),
+	.gpio_banks = oxnas_gpio_banks,
+	.nbanks = ARRAY_SIZE(oxnas_gpio_banks),
+};
+
+static struct pinctrl_desc oxnas_ox820_pinctrl_desc = {
+	.name = "oxnas-pinctrl",
+	.pins = oxnas_ox820_pins,
+	.npins = ARRAY_SIZE(oxnas_ox820_pins),
+	.pctlops = &oxnas_pinctrl_ops,
+	.pmxops = &oxnas_ox820_pinmux_ops,
+	.confops = &oxnas_ox820_pinconf_ops,
+	.owner = THIS_MODULE,
+};
+
 static struct oxnas_pinctrl_data oxnas_ox810se_pinctrl_data = {
 	.desc = &oxnas_ox810se_pinctrl_desc,
 	.pctl = &ox810se_pinctrl,
 };
 
+static struct oxnas_pinctrl_data oxnas_ox820_pinctrl_data = {
+	.desc = &oxnas_ox820_pinctrl_desc,
+	.pctl = &ox820_pinctrl,
+};
+
 static const struct of_device_id oxnas_pinctrl_of_match[] = {
 	{ .compatible = "oxsemi,ox810se-pinctrl",
 	  .data = &oxnas_ox810se_pinctrl_data
 	},
+	{ .compatible = "oxsemi,ox820-pinctrl",
+	  .data = &oxnas_ox820_pinctrl_data,
+	},
 	{ },
 };
 
@@ -847,6 +1279,7 @@ static struct platform_driver oxnas_pinctrl_driver = {
 
 static const struct of_device_id oxnas_gpio_of_match[] = {
 	{ .compatible = "oxsemi,ox810se-gpio", },
+	{ .compatible = "oxsemi,ox820-gpio", },
 	{ },
 };
 
-- 
2.7.0

^ permalink raw reply related

* [PATCH 1/3] pinctrl: oxnas: Move OX810SE specific function and structure as separate
From: Neil Armstrong @ 2016-10-04 13:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161004134148.23028-1-narmstrong@baylibre.com>

Add refactoring to move ox810se specific functions into specific ops structures
an add support for the dt match data to get soc specific structures.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 drivers/pinctrl/pinctrl-oxnas.c | 176 +++++++++++++++++++++++-----------------
 1 file changed, 101 insertions(+), 75 deletions(-)

diff --git a/drivers/pinctrl/pinctrl-oxnas.c b/drivers/pinctrl/pinctrl-oxnas.c
index 917a7d2..218ad48 100644
--- a/drivers/pinctrl/pinctrl-oxnas.c
+++ b/drivers/pinctrl/pinctrl-oxnas.c
@@ -37,15 +37,15 @@
 
 #define GPIO_BANK_START(bank)		((bank) * PINS_PER_BANK)
 
-/* Regmap Offsets */
-#define PINMUX_PRIMARY_SEL0	0x0c
-#define PINMUX_SECONDARY_SEL0	0x14
-#define PINMUX_TERTIARY_SEL0	0x8c
-#define PINMUX_PRIMARY_SEL1	0x10
-#define PINMUX_SECONDARY_SEL1	0x18
-#define PINMUX_TERTIARY_SEL1	0x90
-#define PINMUX_PULLUP_CTRL0	0xac
-#define PINMUX_PULLUP_CTRL1	0xb0
+/* OX810 Regmap Offsets */
+#define PINMUX_810_PRIMARY_SEL0		0x0c
+#define PINMUX_810_SECONDARY_SEL0	0x14
+#define PINMUX_810_TERTIARY_SEL0	0x8c
+#define PINMUX_810_PRIMARY_SEL1		0x10
+#define PINMUX_810_SECONDARY_SEL1	0x18
+#define PINMUX_810_TERTIARY_SEL1	0x90
+#define PINMUX_810_PULLUP_CTRL0		0xac
+#define PINMUX_810_PULLUP_CTRL1		0xb0
 
 /* GPIO Registers */
 #define INPUT_VALUE	0x00
@@ -87,8 +87,6 @@ struct oxnas_pinctrl {
 	struct regmap *regmap;
 	struct device *dev;
 	struct pinctrl_dev *pctldev;
-	const struct pinctrl_pin_desc *pins;
-	unsigned int npins;
 	const struct oxnas_function *functions;
 	unsigned int nfunctions;
 	const struct oxnas_pin_group *groups;
@@ -97,7 +95,12 @@ struct oxnas_pinctrl {
 	unsigned int nbanks;
 };
 
-static const struct pinctrl_pin_desc oxnas_pins[] = {
+struct oxnas_pinctrl_data {
+	struct pinctrl_desc *desc;
+	struct oxnas_pinctrl *pctl;
+};
+
+static const struct pinctrl_pin_desc oxnas_ox810se_pins[] = {
 	PINCTRL_PIN(0, "gpio0"),
 	PINCTRL_PIN(1, "gpio1"),
 	PINCTRL_PIN(2, "gpio2"),
@@ -135,7 +138,7 @@ static const struct pinctrl_pin_desc oxnas_pins[] = {
 	PINCTRL_PIN(34, "gpio34"),
 };
 
-static const char * const oxnas_fct0_group[] = {
+static const char * const oxnas_ox810se_fct0_group[] = {
 	"gpio0",  "gpio1",  "gpio2",  "gpio3",
 	"gpio4",  "gpio5",  "gpio6",  "gpio7",
 	"gpio8",  "gpio9",  "gpio10", "gpio11",
@@ -147,7 +150,7 @@ static const char * const oxnas_fct0_group[] = {
 	"gpio32", "gpio33", "gpio34"
 };
 
-static const char * const oxnas_fct3_group[] = {
+static const char * const oxnas_ox810se_fct3_group[] = {
 	"gpio0",  "gpio1",  "gpio2",  "gpio3",
 	"gpio4",  "gpio5",  "gpio6",  "gpio7",
 	"gpio8",  "gpio9",
@@ -165,9 +168,9 @@ static const char * const oxnas_fct3_group[] = {
 		.ngroups = ARRAY_SIZE(oxnas_##_gr##_group),	\
 	}
 
-static const struct oxnas_function oxnas_functions[] = {
-	FUNCTION(gpio, fct0),
-	FUNCTION(fct3, fct3),
+static const struct oxnas_function oxnas_ox810se_functions[] = {
+	FUNCTION(gpio, ox810se_fct0),
+	FUNCTION(fct3, ox810se_fct3),
 };
 
 #define OXNAS_PINCTRL_GROUP(_pin, _name, ...)				\
@@ -185,7 +188,7 @@ static const struct oxnas_function oxnas_functions[] = {
 		.fct = _fct,				\
 	}
 
-static const struct oxnas_pin_group oxnas_groups[] = {
+static const struct oxnas_pin_group oxnas_ox810se_groups[] = {
 	OXNAS_PINCTRL_GROUP(0, gpio0,
 			OXNAS_PINCTRL_FUNCTION(gpio, 0),
 			OXNAS_PINCTRL_FUNCTION(fct3, 3)),
@@ -352,8 +355,8 @@ static int oxnas_pinmux_get_function_groups(struct pinctrl_dev *pctldev,
 	return 0;
 }
 
-static int oxnas_pinmux_enable(struct pinctrl_dev *pctldev,
-			       unsigned int func, unsigned int group)
+static int oxnas_ox810se_pinmux_enable(struct pinctrl_dev *pctldev,
+				       unsigned int func, unsigned int group)
 {
 	struct oxnas_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
 	const struct oxnas_pin_group *pg = &pctl->groups[group];
@@ -371,22 +374,22 @@ static int oxnas_pinmux_enable(struct pinctrl_dev *pctldev,
 
 			regmap_write_bits(pctl->regmap,
 					  (pg->bank ?
-						PINMUX_PRIMARY_SEL1 :
-						PINMUX_PRIMARY_SEL0),
+						PINMUX_810_PRIMARY_SEL1 :
+						PINMUX_810_PRIMARY_SEL0),
 					  mask,
 					  (functions->fct == 1 ?
 						mask : 0));
 			regmap_write_bits(pctl->regmap,
 					  (pg->bank ?
-						PINMUX_SECONDARY_SEL1 :
-						PINMUX_SECONDARY_SEL0),
+						PINMUX_810_SECONDARY_SEL1 :
+						PINMUX_810_SECONDARY_SEL0),
 					  mask,
 					  (functions->fct == 2 ?
 						mask : 0));
 			regmap_write_bits(pctl->regmap,
 					  (pg->bank ?
-						PINMUX_TERTIARY_SEL1 :
-						PINMUX_TERTIARY_SEL0),
+						PINMUX_810_TERTIARY_SEL1 :
+						PINMUX_810_TERTIARY_SEL0),
 					  mask,
 					  (functions->fct == 3 ?
 						mask : 0));
@@ -402,9 +405,9 @@ static int oxnas_pinmux_enable(struct pinctrl_dev *pctldev,
 	return -EINVAL;
 }
 
-static int oxnas_gpio_request_enable(struct pinctrl_dev *pctldev,
-				     struct pinctrl_gpio_range *range,
-				     unsigned int offset)
+static int oxnas_ox810se_gpio_request_enable(struct pinctrl_dev *pctldev,
+					     struct pinctrl_gpio_range *range,
+					     unsigned int offset)
 {
 	struct oxnas_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
 	struct oxnas_gpio_bank *bank = gpiochip_get_data(range->gc);
@@ -415,18 +418,18 @@ static int oxnas_gpio_request_enable(struct pinctrl_dev *pctldev,
 
 	regmap_write_bits(pctl->regmap,
 			  (bank->id ?
-				PINMUX_PRIMARY_SEL1 :
-				PINMUX_PRIMARY_SEL0),
+				PINMUX_810_PRIMARY_SEL1 :
+				PINMUX_810_PRIMARY_SEL0),
 			  mask, 0);
 	regmap_write_bits(pctl->regmap,
 			  (bank->id ?
-				PINMUX_SECONDARY_SEL1 :
-				PINMUX_SECONDARY_SEL0),
+				PINMUX_810_SECONDARY_SEL1 :
+				PINMUX_810_SECONDARY_SEL0),
 			  mask, 0);
 	regmap_write_bits(pctl->regmap,
 			  (bank->id ?
-				PINMUX_TERTIARY_SEL1 :
-				PINMUX_TERTIARY_SEL0),
+				PINMUX_810_TERTIARY_SEL1 :
+				PINMUX_810_TERTIARY_SEL0),
 			  mask, 0);
 
 	return 0;
@@ -498,17 +501,17 @@ static int oxnas_gpio_set_direction(struct pinctrl_dev *pctldev,
 	return 0;
 }
 
-static const struct pinmux_ops oxnas_pinmux_ops = {
+static const struct pinmux_ops oxnas_ox810se_pinmux_ops = {
 	.get_functions_count = oxnas_pinmux_get_functions_count,
 	.get_function_name = oxnas_pinmux_get_function_name,
 	.get_function_groups = oxnas_pinmux_get_function_groups,
-	.set_mux = oxnas_pinmux_enable,
-	.gpio_request_enable = oxnas_gpio_request_enable,
+	.set_mux = oxnas_ox810se_pinmux_enable,
+	.gpio_request_enable = oxnas_ox810se_gpio_request_enable,
 	.gpio_set_direction = oxnas_gpio_set_direction,
 };
 
-static int oxnas_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
-			     unsigned long *config)
+static int oxnas_ox810se_pinconf_get(struct pinctrl_dev *pctldev,
+				     unsigned int pin, unsigned long *config)
 {
 	struct oxnas_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
 	struct oxnas_gpio_bank *bank = pctl_to_bank(pctl, pin);
@@ -521,8 +524,8 @@ static int oxnas_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
 	case PIN_CONFIG_BIAS_PULL_UP:
 		ret = regmap_read(pctl->regmap,
 				  (bank->id ?
-					PINMUX_PULLUP_CTRL1 :
-					PINMUX_PULLUP_CTRL0),
+					PINMUX_810_PULLUP_CTRL1 :
+					PINMUX_810_PULLUP_CTRL0),
 				  &arg);
 		if (ret)
 			return ret;
@@ -538,8 +541,9 @@ static int oxnas_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
 	return 0;
 }
 
-static int oxnas_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
-			     unsigned long *configs, unsigned int num_configs)
+static int oxnas_ox810se_pinconf_set(struct pinctrl_dev *pctldev,
+				     unsigned int pin, unsigned long *configs,
+				     unsigned int num_configs)
 {
 	struct oxnas_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
 	struct oxnas_gpio_bank *bank = pctl_to_bank(pctl, pin);
@@ -561,8 +565,8 @@ static int oxnas_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
 			dev_dbg(pctl->dev, "   pullup\n");
 			regmap_write_bits(pctl->regmap,
 					  (bank->id ?
-						PINMUX_PULLUP_CTRL1 :
-						PINMUX_PULLUP_CTRL0),
+						PINMUX_810_PULLUP_CTRL1 :
+						PINMUX_810_PULLUP_CTRL0),
 					  mask, mask);
 			break;
 		default:
@@ -575,20 +579,12 @@ static int oxnas_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
 	return 0;
 }
 
-static const struct pinconf_ops oxnas_pinconf_ops = {
-	.pin_config_get = oxnas_pinconf_get,
-	.pin_config_set = oxnas_pinconf_set,
+static const struct pinconf_ops oxnas_ox810se_pinconf_ops = {
+	.pin_config_get = oxnas_ox810se_pinconf_get,
+	.pin_config_set = oxnas_ox810se_pinconf_set,
 	.is_generic = true,
 };
 
-static struct pinctrl_desc oxnas_pinctrl_desc = {
-	.name = "oxnas-pinctrl",
-	.pctlops = &oxnas_pinctrl_ops,
-	.pmxops = &oxnas_pinmux_ops,
-	.confops = &oxnas_pinconf_ops,
-	.owner = THIS_MODULE,
-};
-
 static void oxnas_gpio_irq_ack(struct irq_data *data)
 {
 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
@@ -699,10 +695,51 @@ static struct oxnas_gpio_bank oxnas_gpio_banks[] = {
 	GPIO_BANK(1),
 };
 
+static struct oxnas_pinctrl ox810se_pinctrl = {
+	.functions = oxnas_ox810se_functions,
+	.nfunctions = ARRAY_SIZE(oxnas_ox810se_functions),
+	.groups = oxnas_ox810se_groups,
+	.ngroups = ARRAY_SIZE(oxnas_ox810se_groups),
+	.gpio_banks = oxnas_gpio_banks,
+	.nbanks = ARRAY_SIZE(oxnas_gpio_banks),
+};
+
+static struct pinctrl_desc oxnas_ox810se_pinctrl_desc = {
+	.name = "oxnas-pinctrl",
+	.pins = oxnas_ox810se_pins,
+	.npins = ARRAY_SIZE(oxnas_ox810se_pins),
+	.pctlops = &oxnas_pinctrl_ops,
+	.pmxops = &oxnas_ox810se_pinmux_ops,
+	.confops = &oxnas_ox810se_pinconf_ops,
+	.owner = THIS_MODULE,
+};
+
+static struct oxnas_pinctrl_data oxnas_ox810se_pinctrl_data = {
+	.desc = &oxnas_ox810se_pinctrl_desc,
+	.pctl = &ox810se_pinctrl,
+};
+
+static const struct of_device_id oxnas_pinctrl_of_match[] = {
+	{ .compatible = "oxsemi,ox810se-pinctrl",
+	  .data = &oxnas_ox810se_pinctrl_data
+	},
+	{ },
+};
+
 static int oxnas_pinctrl_probe(struct platform_device *pdev)
 {
+	const struct of_device_id *id;
+	const struct oxnas_pinctrl_data *data;
 	struct oxnas_pinctrl *pctl;
 
+	id = of_match_node(oxnas_pinctrl_of_match, pdev->dev.of_node);
+	if (!id)
+		return -ENODEV;
+
+	data = id->data;
+	if (!data || !data->pctl || !data->desc)
+		return -EINVAL;
+
 	pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
 	if (!pctl)
 		return -ENOMEM;
@@ -716,20 +753,14 @@ static int oxnas_pinctrl_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	pctl->pins = oxnas_pins;
-	pctl->npins = ARRAY_SIZE(oxnas_pins);
-	pctl->functions = oxnas_functions;
-	pctl->nfunctions = ARRAY_SIZE(oxnas_functions);
-	pctl->groups = oxnas_groups;
-	pctl->ngroups = ARRAY_SIZE(oxnas_groups);
-	pctl->gpio_banks = oxnas_gpio_banks;
-	pctl->nbanks = ARRAY_SIZE(oxnas_gpio_banks);
+	pctl->functions = data->pctl->functions;
+	pctl->nfunctions = data->pctl->nfunctions;
+	pctl->groups = data->pctl->groups;
+	pctl->ngroups = data->pctl->ngroups;
+	pctl->gpio_banks = data->pctl->gpio_banks;
+	pctl->nbanks = data->pctl->nbanks;
 
-	oxnas_pinctrl_desc.pins = pctl->pins;
-	oxnas_pinctrl_desc.npins = pctl->npins;
-
-	pctl->pctldev = pinctrl_register(&oxnas_pinctrl_desc,
-					 &pdev->dev, pctl);
+	pctl->pctldev = pinctrl_register(data->desc, &pdev->dev, pctl);
 	if (IS_ERR(pctl->pctldev)) {
 		dev_err(&pdev->dev, "Failed to register pinctrl device\n");
 		return PTR_ERR(pctl->pctldev);
@@ -805,11 +836,6 @@ static int oxnas_gpio_probe(struct platform_device *pdev)
 	return 0;
 }
 
-static const struct of_device_id oxnas_pinctrl_of_match[] = {
-	{ .compatible = "oxsemi,ox810se-pinctrl", },
-	{ },
-};
-
 static struct platform_driver oxnas_pinctrl_driver = {
 	.driver = {
 		.name = "oxnas-pinctrl",
-- 
2.7.0

^ permalink raw reply related

* [PATCH 0/3] pinctrl: oxnas: Add Support for OX820
From: Neil Armstrong @ 2016-10-04 13:41 UTC (permalink / raw)
  To: linux-arm-kernel

This patchset adds support for the Oxford Semiconductor OX820 SoC Pinctrl
registers along he OX810 Pinctrl support.
The GPIO control registers are common enough to leave the code untouched.

Neil Armstrong (3):
  pinctrl: oxnas: Move OX810SE specific function and structure as
    separate
  pinctrl: oxnas: Add support for OX820
  dt-bindings: oxnas: Update Pinctrl and GPIO for OX820 Support

 .../devicetree/bindings/gpio/gpio_oxnas.txt        |   2 +-
 .../devicetree/bindings/pinctrl/oxnas,pinctrl.txt  |   2 +-
 drivers/pinctrl/pinctrl-oxnas.c                    | 605 ++++++++++++++++++---
 3 files changed, 534 insertions(+), 75 deletions(-)

-- 
2.7.0

^ permalink raw reply

* [PATCH v2 5/7] arm64: dts: exynos: Add dts files for Samsung Exynos5433 64bit SoC
From: Geert Uytterhoeven @ 2016-10-04 13:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1472046551-703-6-git-send-email-cw00.choi@samsung.com>

On Wed, Aug 24, 2016 at 3:49 PM, Chanwoo Choi <cw00.choi@samsung.com> wrote:
> --- /dev/null
> +++ b/arch/arm64/boot/dts/exynos/exynos5433.dtsi

> +       cluster_a53_opp_table: opp_table0 {
> +               compatible = "operating-points-v2";
> +               opp-shared;
> +
> +               opp at 400000000 {
> +                       opp-hz = /bits/ 64 <400000000>;
> +                       opp-microvolt = <900000>;
> +               };

With W=1:

Warning (unit_address_vs_reg): Node /opp_table0/opp at 400000000 has a
unit name, but no reg property
...

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* [PATCH] ARM: dts: r8a7794: Fix W=1 dtc warnings
From: Geert Uytterhoeven @ 2016-10-04 13:31 UTC (permalink / raw)
  To: linux-arm-kernel

Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,dvc/dvc at 0 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,dvc/dvc at 1 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,mix/mix at 0 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,mix/mix at 1 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,ctu/ctu at 0 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,ctu/ctu at 1 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,ctu/ctu at 2 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,ctu/ctu at 3 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,ctu/ctu at 4 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,ctu/ctu at 5 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,ctu/ctu at 6 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,ctu/ctu at 7 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,src/src at 0 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,src/src at 1 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,src/src at 2 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,src/src at 3 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,src/src at 4 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,src/src at 5 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,src/src at 6 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,ssi/ssi at 0 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,ssi/ssi at 1 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,ssi/ssi at 2 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,ssi/ssi at 3 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,ssi/ssi at 4 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,ssi/ssi at 5 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,ssi/ssi at 6 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,ssi/ssi at 7 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,ssi/ssi at 8 has a unit name, but no reg property
Warning (unit_address_vs_reg): Node /sound at ec500000/rcar_sound,ssi/ssi at 9 has a unit name, but no reg property

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
 arch/arm/boot/dts/r8a7794.dtsi | 58 +++++++++++++++++++++---------------------
 1 file changed, 29 insertions(+), 29 deletions(-)

diff --git a/arch/arm/boot/dts/r8a7794.dtsi b/arch/arm/boot/dts/r8a7794.dtsi
index f53c75b2b0d33de2..7bfa57f357d4ad2a 100644
--- a/arch/arm/boot/dts/r8a7794.dtsi
+++ b/arch/arm/boot/dts/r8a7794.dtsi
@@ -1502,62 +1502,62 @@
 		status = "disabled";
 
 		rcar_sound,dvc {
-			dvc0: dvc at 0 {
+			dvc0: dvc-0 {
 				dmas = <&audma0 0xbc>;
 				dma-names = "tx";
 			};
-			dvc1: dvc at 1 {
+			dvc1: dvc-1 {
 				dmas = <&audma0 0xbe>;
 				dma-names = "tx";
 			};
 		};
 
 		rcar_sound,mix {
-			mix0: mix at 0 { };
-			mix1: mix at 1 { };
+			mix0: mix-0 { };
+			mix1: mix-1 { };
 		};
 
 		rcar_sound,ctu {
-			ctu00: ctu at 0 { };
-			ctu01: ctu at 1 { };
-			ctu02: ctu at 2 { };
-			ctu03: ctu at 3 { };
-			ctu10: ctu at 4 { };
-			ctu11: ctu at 5 { };
-			ctu12: ctu at 6 { };
-			ctu13: ctu at 7 { };
+			ctu00: ctu-0 { };
+			ctu01: ctu-1 { };
+			ctu02: ctu-2 { };
+			ctu03: ctu-3 { };
+			ctu10: ctu-4 { };
+			ctu11: ctu-5 { };
+			ctu12: ctu-6 { };
+			ctu13: ctu-7 { };
 		};
 
 		rcar_sound,src {
-			src at 0 {
+			src-0 {
 				status = "disabled";
 			};
-			src1: src at 1 {
+			src1: src-1 {
 				interrupts = <GIC_SPI 353 IRQ_TYPE_LEVEL_HIGH>;
 				dmas = <&audma0 0x87>, <&audma0 0x9c>;
 				dma-names = "rx", "tx";
 			};
-			src2: src at 2 {
+			src2: src-2 {
 				interrupts = <GIC_SPI 354 IRQ_TYPE_LEVEL_HIGH>;
 				dmas = <&audma0 0x89>, <&audma0 0x9e>;
 				dma-names = "rx", "tx";
 			};
-			src3: src at 3 {
+			src3: src-3 {
 				interrupts = <GIC_SPI 355 IRQ_TYPE_LEVEL_HIGH>;
 				dmas = <&audma0 0x8b>, <&audma0 0xa0>;
 				dma-names = "rx", "tx";
 			};
-			src4: src at 4 {
+			src4: src-4 {
 				interrupts = <GIC_SPI 356 IRQ_TYPE_LEVEL_HIGH>;
 				dmas = <&audma0 0x8d>, <&audma0 0xb0>;
 				dma-names = "rx", "tx";
 			};
-			src5: src at 5 {
+			src5: src-5 {
 				interrupts = <GIC_SPI 357 IRQ_TYPE_LEVEL_HIGH>;
 				dmas = <&audma0 0x8f>, <&audma0 0xb2>;
 				dma-names = "rx", "tx";
 			};
-			src6: src at 6 {
+			src6: src-6 {
 				interrupts = <GIC_SPI 358 IRQ_TYPE_LEVEL_HIGH>;
 				dmas = <&audma0 0x91>, <&audma0 0xb4>;
 				dma-names = "rx", "tx";
@@ -1565,61 +1565,61 @@
 		};
 
 		rcar_sound,ssi {
-			ssi0: ssi at 0 {
+			ssi0: ssi-0 {
 				interrupts = <GIC_SPI 370 IRQ_TYPE_LEVEL_HIGH>;
 				dmas = <&audma0 0x01>, <&audma0 0x02>,
 				       <&audma0 0x15>, <&audma0 0x16>;
 				dma-names = "rx", "tx", "rxu", "txu";
 			};
-			ssi1: ssi at 1 {
+			ssi1: ssi-1 {
 				interrupts = <GIC_SPI 371 IRQ_TYPE_LEVEL_HIGH>;
 				dmas = <&audma0 0x03>, <&audma0 0x04>,
 				       <&audma0 0x49>, <&audma0 0x4a>;
 				dma-names = "rx", "tx", "rxu", "txu";
 			};
-			ssi2: ssi at 2 {
+			ssi2: ssi-2 {
 				interrupts = <GIC_SPI 372 IRQ_TYPE_LEVEL_HIGH>;
 				dmas = <&audma0 0x05>, <&audma0 0x06>,
 				       <&audma0 0x63>, <&audma0 0x64>;
 				dma-names = "rx", "tx", "rxu", "txu";
 			};
-			ssi3: ssi at 3 {
+			ssi3: ssi-3 {
 				interrupts = <GIC_SPI 373 IRQ_TYPE_LEVEL_HIGH>;
 				dmas = <&audma0 0x07>, <&audma0 0x08>,
 				       <&audma0 0x6f>, <&audma0 0x70>;
 				dma-names = "rx", "tx", "rxu", "txu";
 			};
-			ssi4: ssi at 4 {
+			ssi4: ssi-4 {
 				interrupts = <GIC_SPI 374 IRQ_TYPE_LEVEL_HIGH>;
 				dmas = <&audma0 0x09>, <&audma0 0x0a>,
 				       <&audma0 0x71>, <&audma0 0x72>;
 				dma-names = "rx", "tx", "rxu", "txu";
 			};
-			ssi5: ssi at 5 {
+			ssi5: ssi-5 {
 				interrupts = <GIC_SPI 375 IRQ_TYPE_LEVEL_HIGH>;
 				dmas = <&audma0 0x0b>, <&audma0 0x0c>,
 				       <&audma0 0x73>, <&audma0 0x74>;
 				dma-names = "rx", "tx", "rxu", "txu";
 			};
-			ssi6: ssi at 6 {
+			ssi6: ssi-6 {
 				interrupts = <GIC_SPI 376 IRQ_TYPE_LEVEL_HIGH>;
 				dmas = <&audma0 0x0d>, <&audma0 0x0e>,
 				       <&audma0 0x75>, <&audma0 0x76>;
 				dma-names = "rx", "tx", "rxu", "txu";
 			};
-			ssi7: ssi at 7 {
+			ssi7: ssi-7 {
 				interrupts = <GIC_SPI 377 IRQ_TYPE_LEVEL_HIGH>;
 				dmas = <&audma0 0x0f>, <&audma0 0x10>,
 				       <&audma0 0x79>, <&audma0 0x7a>;
 				dma-names = "rx", "tx", "rxu", "txu";
 			};
-			ssi8: ssi at 8 {
+			ssi8: ssi-8 {
 				interrupts = <GIC_SPI 378 IRQ_TYPE_LEVEL_HIGH>;
 				dmas = <&audma0 0x11>, <&audma0 0x12>,
 				       <&audma0 0x7b>, <&audma0 0x7c>;
 				dma-names = "rx", "tx", "rxu", "txu";
 			};
-			ssi9: ssi at 9 {
+			ssi9: ssi-9 {
 				interrupts = <GIC_SPI 379 IRQ_TYPE_LEVEL_HIGH>;
 				dmas = <&audma0 0x13>, <&audma0 0x14>,
 				       <&audma0 0x7d>, <&audma0 0x7e>;
-- 
1.9.1

^ permalink raw reply related

* [PATCH v26 0/7] arm64: add kdump support
From: Manish Jaggi @ 2016-10-04 13:23 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <57F38A30.2050200@arm.com>



On 10/04/2016 04:23 PM, James Morse wrote:
> Hi Manish,
> 
> On 04/10/16 11:05, Manish Jaggi wrote:
>> On 10/04/2016 03:16 PM, James Morse wrote:
>>> On 03/10/16 13:41, Manish Jaggi wrote:
>>>> On 10/03/2016 04:34 PM, AKASHI Takahiro wrote:
>>>>> On Mon, Oct 03, 2016 at 01:24:34PM +0530, Manish Jaggi wrote:
>>>>>> First kernel is booted with mem=2G crashkernel=1G command line option.
>>>>>> While the system has 64G memory.
>>>
>>>>> Are you saying that "mem=..." doesn't have any effect?
>>>> What I am saying it that If the first kernel is booted using mem= option and crashkernel= option
>>>> the memory for second kernel has to be withing the crashkernel size.
>>>> As per /proc/iomem System RAM the information is correct, but the /proc/meminfo is showing total memory
>>>> much more than the first kernel had in first place.
>>>
>>> So your second crashkernel has 63G of memory? Unless you provide the same 'mem='
>>> to the kdump kernel, this is the expected behaviour. The
>>> DT:/reserved-memory/crash_dump describes the memory not to use.
>>>
>>> On your first boot with 'mem=2G' memblock_mem_limit_remove_map() called from
>>> arm64_memblock_init() removed the top 62G of memory. Neither the first kernel
>>> nor kexec-tools know about the top 62G.
>>> When you run kexec-tools, it describes what it sees in /proc/iomem in the
>>> DT:/reserved-memory/crash_dump, which is just the remaining 1G of memory.
>>>
>>> When we crash and reboot, the crash kernel discovers all 64G of memory from the
>>> EFI memory map.
> 
>> So the iomem and meminfo should be same or different for the second kernel?
>> Also i assumed that crashkernel=1G should restrict the second kernels to 1G.
> 
> Not with v26 of this series. What should it do with the 62G of memory that was
> removed by booting with 'mem=2G'? It isn't part of the crashkernel reserved
> area, and it isn't part of the vmcore described in elfcorehdr either...
> 
> 
>> This is my understanding from the description. It should not require a second mem= option
> 
>>> kexec-tools described the 1G of memory that the first kernel was using in the
>>> DT:/reserved-memory/crash_dump node, so early_init_fdt_scan_reserved_mem()
>>> reserves the 1G of memory the first kernel used. This leaves us with 63G of memory.
>>>
>>> This may change with the next version of kdump if it switches back to using
>>> DT:/chosen/linux,usable-memory-range.
>>> If you need v26 to avoid the top 62G of memory, you need to provide the same
>>> 'mem=' to the first and second kernel.
> 
>> If I provide for second kernel, I dont see any prints after Bye.
>> Have you tired this anytime?
> 
> Yes, on juno-r1 passing 'mem=2G' to both the first and second kernel causes only
> the first 2G of memory to be used with this pattern:
> first kernel:		[1G used for linux]	[1G reserved for Crash kernel] 	[6G memory
> hidden]
> kdump kernel:	[1G vmcore]			[1G used for linux] 			[6G memory hidden]
> 
> 
Oh, ok!
I was giving mem=1G to crashkernel to test. with mem=2G it works.
>>>>>> 1.2 Live crash dump fails with error
>>>
>>> ... do we expect this to work? I don't think it has anything to do with this
>>> series...
>>>
>> Why it should not?
>> I saved the vmcore file while in second kernel. Since crash without vmcore file didnt run,
>> Tried with vmcore file and it worked. Its just that if you want to boot a second kernel
>>  with read only file system without network live crash dump analysis is handy.
> 
> Ah, you want to run /usr/bin/crash with the kdump boot of linux. You still need
> to tell it where to find the memory image: "crash /path/to/vmlinux /proc/vmcore"
> should do the trick.
> 
We should fix the documentation of kdump them.
Since it is not supported, it should be removed.
> 
> Thanks,
> 
> James
> 

^ permalink raw reply

* [PATCH 2/2] ARM: shmobile: r8a7793/gose: Add board part number to DT bindings
From: Geert Uytterhoeven @ 2016-10-04 13:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475587248-13670-1-git-send-email-geert+renesas@glider.be>

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
 Documentation/devicetree/bindings/arm/shmobile.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/arm/shmobile.txt b/Documentation/devicetree/bindings/arm/shmobile.txt
index 6c3ffc2ebeaa346a..18a20a7689c52342 100644
--- a/Documentation/devicetree/bindings/arm/shmobile.txt
+++ b/Documentation/devicetree/bindings/arm/shmobile.txt
@@ -47,7 +47,7 @@ Boards:
     compatible = "renesas,bockw", "renesas,r8a7778"
   - Genmai (RTK772100BC00000BR)
     compatible = "renesas,genmai", "renesas,r7s72100"
-  - Gose
+  - Gose (RTP0RC7793SEB00010S)
     compatible = "renesas,gose", "renesas,r8a7793"
   - H3ULCB (RTP0RC7795SKB00010S)
     compatible = "renesas,h3ulcb", "renesas,r8a7795";
-- 
1.9.1

^ permalink raw reply related

* [PATCH 1/2] ARM: shmobile: r8a7794/alt: Add board part number to DT bindings
From: Geert Uytterhoeven @ 2016-10-04 13:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475587248-13670-1-git-send-email-geert+renesas@glider.be>

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
 Documentation/devicetree/bindings/arm/shmobile.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/arm/shmobile.txt b/Documentation/devicetree/bindings/arm/shmobile.txt
index 19f0a9e4302b508c..6c3ffc2ebeaa346a 100644
--- a/Documentation/devicetree/bindings/arm/shmobile.txt
+++ b/Documentation/devicetree/bindings/arm/shmobile.txt
@@ -35,7 +35,7 @@ SoCs:
 
 Boards:
 
-  - Alt
+  - Alt (RTP0RC7794SEB00010S)
     compatible = "renesas,alt", "renesas,r8a7794"
   - APE6-EVM
     compatible = "renesas,ape6evm", "renesas,r8a73a4"
-- 
1.9.1

^ 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